From e7eaff9d9717bdd5a1b270c47c53605aadef2644 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 18 Nov 2022 15:58:24 +0100 Subject: [PATCH 01/36] Started with multi scheduler suppport, first build schedulers from yaml file --- bartender/schedulers/build.py | 61 +++++++++ bartender/schedulers/memory.py | 6 + bartender/schedulers/runner.py | 9 ++ bartender/schedulers/slurm.py | 11 ++ bartender/settings.py | 2 + bartender/tests/schedulers/test_build.py | 157 +++++++++++++++++++++++ bartender/web/api/applications/submit.py | 5 +- bartender/web/lifetime.py | 17 ++- poetry.lock | 6 +- pyproject.toml | 1 + 10 files changed, 264 insertions(+), 11 deletions(-) create mode 100644 bartender/schedulers/build.py create mode 100644 bartender/tests/schedulers/test_build.py diff --git a/bartender/schedulers/build.py b/bartender/schedulers/build.py new file mode 100644 index 0000000..94d7961 --- /dev/null +++ b/bartender/schedulers/build.py @@ -0,0 +1,61 @@ +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +from yaml import safe_load + +from bartender.schedulers.memory import MemoryScheduler +from bartender.schedulers.runner import LocalCommandRunner, SshCommandRunner +from bartender.schedulers.slurm import SlurmScheduler + + +def build(config_filename: Path): + config = load(config_filename) + return assemble(config) + + +def load(config_filename: Path): + with open(config_filename) as f: + return safe_load(f) + + +def assemble(config: Any): + if "schedulers" not in config: + raise ValueError("No schedulers found") + if len(config["schedulers"]) == 0: + raise ValueError("No schedulers found") + schedulers = {} + for scheduler_name, scheduler_config in config["schedulers"].items(): + schedulers[scheduler_name] = assemble_scheduler(scheduler_config) + return schedulers + + +def assemble_scheduler(config: Any): + if "type" not in config: + raise ValueError("Scheduler without type") + if config["type"] == "memory": + if "slots" in config: + return MemoryScheduler(config["slots"]) + return MemoryScheduler() + if config["type"] == "slurm": + slurm_config = {k: v for k, v in config.items() if k not in {"type", "runner"}} + if "runner" in config: + if "type" not in config["runner"]: + raise ValueError("Runner without type") + + if config["runner"]["type"] == "ssh": + if "hostname" not in config["runner"]: + raise ValueError("Ssh runner without hostname") + runner_config = { + k: v for k, v in config["runner"].items() if k not in {"type"} + } + runner = SshCommandRunner(config=runner_config) + else: + raise ValueError( + f"Runner with type {config['runner']['type']} is unknown" + ) + else: + runner = LocalCommandRunner() + return SlurmScheduler(runner=runner, **slurm_config) + else: + raise ValueError(f'Scheduler with type {config["type"]} is unknown') diff --git a/bartender/schedulers/memory.py b/bartender/schedulers/memory.py index ddfc31d..117b1cf 100644 --- a/bartender/schedulers/memory.py +++ b/bartender/schedulers/memory.py @@ -121,3 +121,9 @@ def _add_worker(self) -> None: def _forget_completed_job(self, job_id: str, state: State) -> None: if state in CompletedStates: self.jobs.pop(job_id) + + def __eq__(self, other: object) -> bool: + return len(self.workers) == len(other.workers) + + def __repr__(self) -> str: + return f"MemoryScheduler(slots={len(self.workers)})" diff --git a/bartender/schedulers/runner.py b/bartender/schedulers/runner.py index 3156c43..8bf85c4 100644 --- a/bartender/schedulers/runner.py +++ b/bartender/schedulers/runner.py @@ -70,6 +70,9 @@ async def run( def close(self) -> None: """Close any connections runner has.""" + def __eq__(self, other: object) -> bool: + return isinstance(other, LocalCommandRunner) + class SshCommandRunner(CommandRunner): """Run command on a remote machine using SSH.""" @@ -133,3 +136,9 @@ def __enter__(self) -> "SshCommandRunner": def __exit__(self, *args: list[Any]) -> None: self.close() + + def __eq__(self, other: object) -> bool: + return isinstance(other, SshCommandRunner) and self.config == other.config + + def __repr__(self) -> str: + return f"SshCommandRunner(config=${self.config})" diff --git a/bartender/schedulers/slurm.py b/bartender/schedulers/slurm.py index b3f8e9d..014d00f 100644 --- a/bartender/schedulers/slurm.py +++ b/bartender/schedulers/slurm.py @@ -142,3 +142,14 @@ def _submit_script(self, description: JobDescription) -> str: echo -n $? > returncode """ return dedent(script) + + def __eq__(self, other: object) -> bool: + return ( + self.runner == other.runner + and self.partition == other.partition + and self.time == other.time + and self.extra_options == other.extra_options + ) + + def __repr__(self) -> str: + return f"SlurmScheduler(runner={self.runner}, partition={self.partition}, time={self.time}, extra_options={self.extra_options})" diff --git a/bartender/settings.py b/bartender/settings.py index 4138859..de22cd7 100644 --- a/bartender/settings.py +++ b/bartender/settings.py @@ -86,6 +86,8 @@ class Settings(BaseSettings): # TODO read scheduler + applications from # yaml/toml formmatted config file instead of env vars. + config_filename: Path = "config.yaml" + @property def db_url(self) -> URL: """ diff --git a/bartender/tests/schedulers/test_build.py b/bartender/tests/schedulers/test_build.py new file mode 100644 index 0000000..8eb0977 --- /dev/null +++ b/bartender/tests/schedulers/test_build.py @@ -0,0 +1,157 @@ +import pytest + +from bartender.schedulers.build import assemble +from bartender.schedulers.memory import MemoryScheduler +from bartender.schedulers.runner import LocalCommandRunner, SshCommandRunner +from bartender.schedulers.slurm import SlurmScheduler + + +def test_no_schedulers_key(): + config = {} + with pytest.raises(ValueError): + assemble(config) + + +def test_zero_schedulers(): + config = {"schedulers": []} + with pytest.raises(ValueError): + assemble(config) + + +def test_single_typeless_scheduler(): + config = {"schedulers": {"local": {}}} + with pytest.raises(ValueError): + assemble(config) + + +def test_single_unknown_scheduler(): + config = {"schedulers": {"local": {type: "unknown"}}} + with pytest.raises(ValueError): + assemble(config) + + +@pytest.mark.anyio +async def test_single_memory_scheduler(): + config = {"schedulers": {"local": {"type": "memory"}}} + result = assemble(config) + + expected = {"local": MemoryScheduler()} + assert result == expected + + +@pytest.mark.anyio +async def test_single_custom_memory_scheduler(): + config = {"schedulers": {"local": {"type": "memory", "slots": 4}}} + result = assemble(config) + + expected = {"local": MemoryScheduler(slots=4)} + assert result == expected + + +@pytest.mark.anyio +async def test_single_localsimplist_slurm_scheduler(): + config = {"schedulers": {"slurm": {"type": "slurm"}}} + result = assemble(config) + + expected = {"slurm": SlurmScheduler(LocalCommandRunner())} + assert result == expected + + +@pytest.mark.anyio +async def test_single_localcustom_slurm_scheduler(): + config = { + "schedulers": { + "slurm": { + "type": "slurm", + "partition": "mypartition", + "time": 60, + "extra_options": ["--nodes 1"], + } + } + } + result = assemble(config) + + expected = { + "slurm": SlurmScheduler( + runner=LocalCommandRunner(), + partition="mypartition", + time=60, + extra_options=["--nodes 1"], + ) + } + assert result == expected + + +@pytest.mark.anyio +async def test_single_typelessrunner_slurm_scheduler(): + config = {"schedulers": {"slurm": {"type": "slurm", "runner": {}}}} + with pytest.raises(ValueError): + assemble(config) + + +@pytest.mark.anyio +async def test_single_unknownrunner_slurm_scheduler(): + config = {"schedulers": {"slurm": {"type": "slurm", "runner": {"type": "unknown"}}}} + with pytest.raises(ValueError): + assemble(config) + + +@pytest.mark.anyio +async def test_single_withouthost_slurm_scheduler(): + config = {"schedulers": {"slurm": {"type": "slurm", "runner": {"type": "ssh"}}}} + with pytest.raises(ValueError): + assemble(config) + + +@pytest.mark.anyio +async def test_single_sshsimplist_slurm_scheduler(): + config = { + "schedulers": { + "slurm": { + "type": "slurm", + "runner": { + "type": "ssh", + "hostname": "localhost", + }, + } + } + } + result = assemble(config) + + expected = { + "slurm": SlurmScheduler(SshCommandRunner(config={"hostname": "localhost"})) + } + assert result == expected + + +@pytest.mark.anyio +async def test_single_sshcustom_slurm_scheduler(): + config = { + "schedulers": { + "slurm": { + "type": "slurm", + "runner": { + "type": "ssh", + "hostname": "localhost", + "port": 10022, + "username": "xenon", + "password": "javagat", + }, + } + } + } + result = assemble(config) + + expected = { + "slurm": SlurmScheduler( + SshCommandRunner( + config={ + "hostname": "localhost", + "port": 10022, + "username": "xenon", + "password": "javagat", + } + ) + ) + } + assert result == expected diff --git a/bartender/web/api/applications/submit.py b/bartender/web/api/applications/submit.py index 915ff89..4d33a8b 100644 --- a/bartender/web/api/applications/submit.py +++ b/bartender/web/api/applications/submit.py @@ -14,7 +14,7 @@ async def submit( job_dir: Path, application: AppSetting, job_dao: JobDAO, - scheduler: AbstractScheduler, + schedulers: dict[str, AbstractScheduler], ) -> None: """Submit job description to scheduler and store job id returned by scheduler in db. @@ -26,5 +26,8 @@ async def submit( """ command = Template(application.command).substitute(config=application.config) description = JobDescription(job_dir=job_dir, command=command) + # TODO dont pick first scheduler + scheduler = list(schedulers.values())[0] + # TODO if scheduler has filesystem then perform upload of job dir and localize description internal_job_id = await scheduler.submit(description) await job_dao.update_internal_job_id(external_job_id, internal_job_id) diff --git a/bartender/web/lifetime.py b/bartender/web/lifetime.py index d7f517b..776ac3e 100644 --- a/bartender/web/lifetime.py +++ b/bartender/web/lifetime.py @@ -4,6 +4,7 @@ from bartender.db.session import make_engine, make_session_factory from bartender.filesystem import setup_job_root_dir +from bartender.schedulers.build import build from bartender.schedulers.memory import MemoryScheduler from bartender.settings import settings @@ -39,7 +40,7 @@ def register_startup_event( async def _startup() -> None: # noqa: WPS430 _setup_db(app) setup_job_root_dir() - _setup_scheduler(app) + _setup_schedulers(app) return _startup @@ -57,22 +58,24 @@ def register_shutdown_event( @app.on_event("shutdown") async def _shutdown() -> None: # noqa: WPS430 await app.state.db_engine.dispose() - await _teardown_scheduler(app) + await _teardown_schedulers(app) return _shutdown -def _setup_scheduler(app: FastAPI) -> None: - """Create scheduler. +def _setup_schedulers(app: FastAPI) -> None: + """Create schedulers. :param app: fastAPI application. """ - app.state.scheduler = MemoryScheduler(settings.scheduler_slots) + app.state.schedulers = build(settings.config_filename) + # app.state.scheduler = MemoryScheduler(settings.scheduler_slots) -async def _teardown_scheduler(app: FastAPI) -> None: +async def _teardown_schedulers(app: FastAPI) -> None: """Teardown scheduler. :param app: fastAPI application. """ - await app.state.scheduler.close() + for scheduler in app.state.schedulers.values(): + await scheduler.close() diff --git a/poetry.lock b/poetry.lock index 3b9622f..348951d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1096,7 +1096,7 @@ optional = false python-versions = "*" [[package]] -name = "PyYAML" +name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" category = "main" @@ -1503,7 +1503,7 @@ tokenize-rt = ">=2.1" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "89bc024a4fbae42ddcae621ba4b783a8a85620b3c01f8fcafe781248914f3a6a" +content-hash = "c9d45ddb63b8ff2838765f94e79e7842c4579dd41ab47d9638590c093551d6d2" [metadata.files] aiofiles = [ @@ -2290,7 +2290,7 @@ pywin32 = [ {file = "pywin32-305-cp39-cp39-win32.whl", hash = "sha256:9d968c677ac4d5cbdaa62fd3014ab241718e619d8e36ef8e11fb930515a1e918"}, {file = "pywin32-305-cp39-cp39-win_amd64.whl", hash = "sha256:50768c6b7c3f0b38b7fb14dd4104da93ebced5f1a50dc0e834594bff6fbe1271"}, ] -PyYAML = [ +pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, diff --git a/pyproject.toml b/pyproject.toml index fdae911..db391d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ python-multipart = "^0.0.5" aiofiles = "^0.8.0" fastapi-users = {extras = ["sqlalchemy", "oauth"], version = "^10.1.5"} asyncssh = "^2.12.0" +pyyaml = "^6.0" [tool.poetry.group.dev.dependencies] pytest = "^7.0" From fb9026830746d57e4ddc057417b17f31ee0aa4cf Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 18 Nov 2022 16:01:54 +0100 Subject: [PATCH 02/36] Add exmple config file --- bartender/schedulers/build.py | 46 +++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/bartender/schedulers/build.py b/bartender/schedulers/build.py index 94d7961..793348d 100644 --- a/bartender/schedulers/build.py +++ b/bartender/schedulers/build.py @@ -1,4 +1,3 @@ -from dataclasses import dataclass from pathlib import Path from typing import Any @@ -8,6 +7,49 @@ from bartender.schedulers.runner import LocalCommandRunner, SshCommandRunner from bartender.schedulers.slurm import SlurmScheduler +""" + +Example config: +```yaml +applications: + haddock3: + command: haddock3 $config + recluster: + command: haddock3 recluster +filesystems: + cluster1: + type: sftp # or local -> assumes shared filesystem and same user + hostname: localhost + port: 10022 + username: xenon + password: javagat + entry: /home/xenon +schedulers: + local: + type: memory + slots: 4 + cluster1: + type: slurm + partition: mypartition + time: '60' # max time is 60 minutes + extra_options: + - --nodes 1 + runner: + type: ssh # or local + hostname: localhost + port: 10022 + username: xenon + password: javagat + filesystem: cluster1 + cluster2: + type: slurm + grid: + type: grid + filesystem: + type: dirac +``` +""" + def build(config_filename: Path): config = load(config_filename) @@ -52,7 +94,7 @@ def assemble_scheduler(config: Any): runner = SshCommandRunner(config=runner_config) else: raise ValueError( - f"Runner with type {config['runner']['type']} is unknown" + f"Runner with type {config['runner']['type']} is unknown", ) else: runner = LocalCommandRunner() From 89345d84258477992c5b27e6e5689284af9c4c55 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Mon, 21 Nov 2022 11:45:20 +0100 Subject: [PATCH 03/36] Added config parser for applications, schedulers and file systems. --- bartender/config.py | 78 ++++++++++++ bartender/destinations.py | 34 +++++ bartender/filesystems/build.py | 25 ++++ bartender/filesystems/local.py | 6 + bartender/filesystems/sftp.py | 16 ++- bartender/schedulers/build.py | 73 +---------- bartender/schedulers/runner.py | 2 +- bartender/schedulers/slurm.py | 3 +- bartender/tests/filesystems/__init__.py | 1 + bartender/tests/filesystems/test_build.py | 82 ++++++++++++ bartender/tests/schedulers/test_build.py | 148 +++++++++------------- bartender/tests/test_config.py | 92 ++++++++++++++ bartender/tests/test_destinations.py | 95 ++++++++++++++ 13 files changed, 493 insertions(+), 162 deletions(-) create mode 100644 bartender/config.py create mode 100644 bartender/destinations.py create mode 100644 bartender/filesystems/build.py create mode 100644 bartender/tests/filesystems/__init__.py create mode 100644 bartender/tests/filesystems/test_build.py create mode 100644 bartender/tests/test_config.py create mode 100644 bartender/tests/test_destinations.py diff --git a/bartender/config.py b/bartender/config.py new file mode 100644 index 0000000..6d0457e --- /dev/null +++ b/bartender/config.py @@ -0,0 +1,78 @@ +""" +Parses config file into a list of applications, schedulers and filesystems. + +Example config: +```yaml +applications: + haddock3: + command: haddock3 $config + recluster: + command: haddock3 recluster +destinations: + cluster1: + filesystem: &cluster1fs + type: sftp + hostname: localhost + port: 10022 + username: xenon + password: javagat + entry: /home/xenon + scheduler: &cluster1sched + type: slurm + partition: mypartition + time: '60' # max time is 60 minutes + extra_options: + - --nodes 1 + runner: + type: ssh # or local + hostname: localhost + port: 10022 + username: xenon + password: javagat + local: + scheduler: + type: memory + slots: 4 + cluster2: # bartender is being run on head node of cluster + scheduler: + type: slurm + cluster3: # show of reuse using yaml anchor and aliases + scheduler: + <<: *cluster1sched + parition: otherpartition + filesystem: *cluster1fs + grid: + scheduler: + type: grid + filesystem: + type: dirac +``` +""" + +from pathlib import Path +from typing import Any + +from yaml import safe_load + +from bartender.destinations import build as build_destinations +from bartender.settings import AppSetting + +def build(config_filename: Path): + config = load(config_filename) + return parse(config) + +def parse(config: Any): + return { + 'applications': build_applications(config['applications']), + 'destinations': build_destinations(config['destinations']) + } + +def load(config_filename: Path) -> Any: + with open(config_filename) as f: + return safe_load(f) + +def build_applications(config: Any) -> dict[str, AppSetting]: + applications = {} + for name, config in config.items(): + applications[name] = AppSetting(**config) + return applications diff --git a/bartender/destinations.py b/bartender/destinations.py new file mode 100644 index 0000000..0ccb8cc --- /dev/null +++ b/bartender/destinations.py @@ -0,0 +1,34 @@ +from dataclasses import dataclass +from typing import Any, Optional + + +from bartender.filesystems.abstract import AbstractFileSystem +from bartender.filesystems.local import LocalFileSystem +from bartender.filesystems.build import build as build_filesystem +from bartender.schedulers.abstract import AbstractScheduler +from bartender.schedulers.memory import MemoryScheduler +from bartender.schedulers.build import build as build_scheduler + + +@dataclass +class Destination: + scheduler: AbstractScheduler + filesystem: Optional[AbstractFileSystem] = None + + +def build(config: Any) -> dict[str, Destination]: + if not config: + return { + "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()) + } + + destinations = {} + for name, dest_config in config.items(): + if not dest_config: + raise KeyError("Destinations needs scheduler and optional file system") + scheduler = build_scheduler(dest_config["scheduler"]) + filesystem = LocalFileSystem() + if "filesystem" in dest_config: + filesystem = build_filesystem(dest_config["filesystem"]) + destinations[name] = Destination(scheduler=scheduler, filesystem=filesystem) + return destinations diff --git a/bartender/filesystems/build.py b/bartender/filesystems/build.py new file mode 100644 index 0000000..d483d74 --- /dev/null +++ b/bartender/filesystems/build.py @@ -0,0 +1,25 @@ +from pathlib import Path +from typing import Any + +from bartender.filesystems.abstract import AbstractFileSystem +from bartender.filesystems.local import LocalFileSystem +from bartender.filesystems.sftp import SftpFileSystem +from bartender._ssh_utils import SshConnectConfig + +def build(config: Any) -> AbstractFileSystem: + if config is None: + return LocalFileSystem() + if "type" not in config: + raise KeyError("File system without type") + if config["type"] == 'local': + return LocalFileSystem() + if config['type'] == 'sftp': + if 'config' not in config: + raise KeyError("Sftp file system without SSH connection configuration.") + sshconfig = SshConnectConfig(**config['config']) + entry = Path('/') + if 'entry' in config: + entry = Path(config['entry']) + return SftpFileSystem(sshconfig, entry) + else: + raise ValueError(f'File system with type {config["type"]} is unknown') diff --git a/bartender/filesystems/local.py b/bartender/filesystems/local.py index 6264b33..87ef24d 100644 --- a/bartender/filesystems/local.py +++ b/bartender/filesystems/local.py @@ -46,3 +46,9 @@ async def download( :param src: Remote directory to copy from. :param target: Local directory to copy to. """ + + def __eq__(self, other: object) -> bool: + return isinstance(other, LocalFileSystem) + + def __repr__(self) -> str: + return f"LocalFileSystem()" diff --git a/bartender/filesystems/sftp.py b/bartender/filesystems/sftp.py index 8254041..721cab3 100644 --- a/bartender/filesystems/sftp.py +++ b/bartender/filesystems/sftp.py @@ -13,13 +13,13 @@ class SftpFileSystem(AbstractFileSystem): def __init__( self, - entry: Path, config: SshConnectConfig, + entry: Path = Path("/"), ): """Constructor. - :param entry: The entry directory. Used to localize description. :param config: SSH connection configuration. + :param entry: The entry directory. Used to localize description. """ self.entry = entry self.config = config @@ -79,3 +79,15 @@ def close(self) -> None: """Close SSH connection.""" if self.conn: self.conn.close() + + # TODO add delete(description), after download you might want to delete the remote job dir + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, SftpFileSystem) + and str(self.entry) == str(other.entry) + and self.config == other.config + ) + + def __repr__(self) -> str: + return f"SftpFileSystem(config={self.config}, entry={self.entry})" diff --git a/bartender/schedulers/build.py b/bartender/schedulers/build.py index 793348d..f12fbac 100644 --- a/bartender/schedulers/build.py +++ b/bartender/schedulers/build.py @@ -1,80 +1,15 @@ -from pathlib import Path from typing import Any -from yaml import safe_load +from bartender.schedulers.abstract import AbstractScheduler from bartender.schedulers.memory import MemoryScheduler from bartender.schedulers.runner import LocalCommandRunner, SshCommandRunner from bartender.schedulers.slurm import SlurmScheduler -""" - -Example config: -```yaml -applications: - haddock3: - command: haddock3 $config - recluster: - command: haddock3 recluster -filesystems: - cluster1: - type: sftp # or local -> assumes shared filesystem and same user - hostname: localhost - port: 10022 - username: xenon - password: javagat - entry: /home/xenon -schedulers: - local: - type: memory - slots: 4 - cluster1: - type: slurm - partition: mypartition - time: '60' # max time is 60 minutes - extra_options: - - --nodes 1 - runner: - type: ssh # or local - hostname: localhost - port: 10022 - username: xenon - password: javagat - filesystem: cluster1 - cluster2: - type: slurm - grid: - type: grid - filesystem: - type: dirac -``` -""" - - -def build(config_filename: Path): - config = load(config_filename) - return assemble(config) - - -def load(config_filename: Path): - with open(config_filename) as f: - return safe_load(f) - - -def assemble(config: Any): - if "schedulers" not in config: - raise ValueError("No schedulers found") - if len(config["schedulers"]) == 0: - raise ValueError("No schedulers found") - schedulers = {} - for scheduler_name, scheduler_config in config["schedulers"].items(): - schedulers[scheduler_name] = assemble_scheduler(scheduler_config) - return schedulers - - -def assemble_scheduler(config: Any): +def build(config: Any) -> AbstractScheduler: + """Build scheduler instance from configuration.""" if "type" not in config: - raise ValueError("Scheduler without type") + raise KeyError("Scheduler without type") if config["type"] == "memory": if "slots" in config: return MemoryScheduler(config["slots"]) diff --git a/bartender/schedulers/runner.py b/bartender/schedulers/runner.py index 8bf85c4..e444230 100644 --- a/bartender/schedulers/runner.py +++ b/bartender/schedulers/runner.py @@ -141,4 +141,4 @@ def __eq__(self, other: object) -> bool: return isinstance(other, SshCommandRunner) and self.config == other.config def __repr__(self) -> str: - return f"SshCommandRunner(config=${self.config})" + return f"SshCommandRunner(config={self.config})" diff --git a/bartender/schedulers/slurm.py b/bartender/schedulers/slurm.py index 014d00f..c43c5ee 100644 --- a/bartender/schedulers/slurm.py +++ b/bartender/schedulers/slurm.py @@ -145,7 +145,8 @@ def _submit_script(self, description: JobDescription) -> str: def __eq__(self, other: object) -> bool: return ( - self.runner == other.runner + isinstance(other, SlurmScheduler) + and self.runner == other.runner and self.partition == other.partition and self.time == other.time and self.extra_options == other.extra_options diff --git a/bartender/tests/filesystems/__init__.py b/bartender/tests/filesystems/__init__.py new file mode 100644 index 0000000..3b7a6ea --- /dev/null +++ b/bartender/tests/filesystems/__init__.py @@ -0,0 +1 @@ +"""Tests for bartender.filesystems.""" \ No newline at end of file diff --git a/bartender/tests/filesystems/test_build.py b/bartender/tests/filesystems/test_build.py new file mode 100644 index 0000000..283575f --- /dev/null +++ b/bartender/tests/filesystems/test_build.py @@ -0,0 +1,82 @@ +from pathlib import Path +import pytest + +from bartender.filesystems.build import build +from bartender.filesystems.local import LocalFileSystem +from bartender.filesystems.sftp import SftpFileSystem +from bartender._ssh_utils import SshConnectConfig + + +def test_none(): + result = build(None) + expected = LocalFileSystem() + assert result == expected + + +def test_emptydict(): + with pytest.raises(KeyError): + build({}) + + +def test_unknowntype(): + config = {"type": "unknown"} + with pytest.raises(ValueError): + build(config) + + +def test_local(): + config = {"type": "local"} + result = build(config) + + expected = LocalFileSystem() + assert result == expected + + +def test_sftp_withoutconfig(): + config = {"type": "sftp"} + with pytest.raises(KeyError): + build(config) + + +def test_sftp_simplest(): + config = {"type": "sftp", "config": {"hostname": "localhost"}} + result = build(config) + + expected = SftpFileSystem(SshConnectConfig(hostname="localhost")) + assert result == expected + + +def test_sftp_entry(): + config = { + "type": "sftp", + "config": {"hostname": "localhost"}, + "entry": "/scratch/jobs", + } + result = build(config) + + expected = SftpFileSystem( + SshConnectConfig(hostname="localhost"), entry=Path("/scratch/jobs") + ) + assert result == expected + + +def test_sftp_verbosist(): + config = { + "type": "sftp", + "config": { + "hostname": "localhost", + "port": 2222, + "username": "someone", + "password": "somepw", + }, + "entry": "/scratch/jobs", + } + result = build(config) + + expected = SftpFileSystem( + SshConnectConfig( + hostname="localhost", port=2222, username="someone", password="somepw" + ), + entry=Path("/scratch/jobs"), + ) + assert result == expected diff --git a/bartender/tests/schedulers/test_build.py b/bartender/tests/schedulers/test_build.py index 8eb0977..70ed653 100644 --- a/bartender/tests/schedulers/test_build.py +++ b/bartender/tests/schedulers/test_build.py @@ -1,157 +1,127 @@ import pytest -from bartender.schedulers.build import assemble +from bartender.schedulers.build import build from bartender.schedulers.memory import MemoryScheduler from bartender.schedulers.runner import LocalCommandRunner, SshCommandRunner from bartender.schedulers.slurm import SlurmScheduler -def test_no_schedulers_key(): - config = {} - with pytest.raises(ValueError): - assemble(config) - - -def test_zero_schedulers(): - config = {"schedulers": []} - with pytest.raises(ValueError): - assemble(config) - - def test_single_typeless_scheduler(): - config = {"schedulers": {"local": {}}} - with pytest.raises(ValueError): - assemble(config) + config = {} + with pytest.raises(KeyError): + build(config) def test_single_unknown_scheduler(): - config = {"schedulers": {"local": {type: "unknown"}}} + config = {type: "unknown"} with pytest.raises(ValueError): - assemble(config) + build(config) @pytest.mark.anyio async def test_single_memory_scheduler(): - config = {"schedulers": {"local": {"type": "memory"}}} - result = assemble(config) + config = {"type": "memory"} + result = build(config) - expected = {"local": MemoryScheduler()} + expected = MemoryScheduler() assert result == expected @pytest.mark.anyio async def test_single_custom_memory_scheduler(): - config = {"schedulers": {"local": {"type": "memory", "slots": 4}}} - result = assemble(config) + config = {"type": "memory", "slots": 4} + result = build(config) - expected = {"local": MemoryScheduler(slots=4)} + expected = MemoryScheduler(slots=4) assert result == expected @pytest.mark.anyio async def test_single_localsimplist_slurm_scheduler(): - config = {"schedulers": {"slurm": {"type": "slurm"}}} - result = assemble(config) + config = {"type": "slurm"} + result = build(config) - expected = {"slurm": SlurmScheduler(LocalCommandRunner())} + expected = SlurmScheduler(LocalCommandRunner()) assert result == expected @pytest.mark.anyio async def test_single_localcustom_slurm_scheduler(): config = { - "schedulers": { - "slurm": { - "type": "slurm", - "partition": "mypartition", - "time": 60, - "extra_options": ["--nodes 1"], - } - } - } - result = assemble(config) - - expected = { - "slurm": SlurmScheduler( - runner=LocalCommandRunner(), - partition="mypartition", - time=60, - extra_options=["--nodes 1"], - ) + "type": "slurm", + "partition": "mypartition", + "time": 60, + "extra_options": ["--nodes 1"], } + result = build(config) + + expected = SlurmScheduler( + runner=LocalCommandRunner(), + partition="mypartition", + time=60, + extra_options=["--nodes 1"], + ) assert result == expected @pytest.mark.anyio async def test_single_typelessrunner_slurm_scheduler(): - config = {"schedulers": {"slurm": {"type": "slurm", "runner": {}}}} + config = {"type": "slurm", "runner": {}} with pytest.raises(ValueError): - assemble(config) + build(config) @pytest.mark.anyio async def test_single_unknownrunner_slurm_scheduler(): - config = {"schedulers": {"slurm": {"type": "slurm", "runner": {"type": "unknown"}}}} + config = {"type": "slurm", "runner": {"type": "unknown"}} with pytest.raises(ValueError): - assemble(config) + build(config) @pytest.mark.anyio async def test_single_withouthost_slurm_scheduler(): - config = {"schedulers": {"slurm": {"type": "slurm", "runner": {"type": "ssh"}}}} + config = {"type": "slurm", "runner": {"type": "ssh"}} with pytest.raises(ValueError): - assemble(config) + build(config) @pytest.mark.anyio async def test_single_sshsimplist_slurm_scheduler(): config = { - "schedulers": { - "slurm": { - "type": "slurm", - "runner": { - "type": "ssh", - "hostname": "localhost", - }, - } - } + "type": "slurm", + "runner": { + "type": "ssh", + "hostname": "localhost", + }, } - result = assemble(config) + result = build(config) - expected = { - "slurm": SlurmScheduler(SshCommandRunner(config={"hostname": "localhost"})) - } + expected = SlurmScheduler(SshCommandRunner(config={"hostname": "localhost"})) assert result == expected @pytest.mark.anyio async def test_single_sshcustom_slurm_scheduler(): config = { - "schedulers": { - "slurm": { - "type": "slurm", - "runner": { - "type": "ssh", - "hostname": "localhost", - "port": 10022, - "username": "xenon", - "password": "javagat", - }, - } - } + "type": "slurm", + "runner": { + "type": "ssh", + "hostname": "localhost", + "port": 10022, + "username": "xenon", + "password": "javagat", + }, } - result = assemble(config) - - expected = { - "slurm": SlurmScheduler( - SshCommandRunner( - config={ - "hostname": "localhost", - "port": 10022, - "username": "xenon", - "password": "javagat", - } - ) + result = build(config) + + expected = SlurmScheduler( + SshCommandRunner( + config={ + "hostname": "localhost", + "port": 10022, + "username": "xenon", + "password": "javagat", + } ) - } + ) assert result == expected diff --git a/bartender/tests/test_config.py b/bartender/tests/test_config.py new file mode 100644 index 0000000..2c61d31 --- /dev/null +++ b/bartender/tests/test_config.py @@ -0,0 +1,92 @@ +from pathlib import Path + +import pytest +from yaml import safe_dump + +from bartender.config import build, parse +from bartender.destinations import Destination +from bartender.filesystems.local import LocalFileSystem +from bartender.filesystems.sftp import SftpFileSystem +from bartender.schedulers.memory import MemoryScheduler +from bartender.schedulers.runner import SshCommandRunner +from bartender.schedulers.slurm import SlurmScheduler +from bartender.settings import AppSetting + +@pytest.mark.anyio +async def test_build_minimal(tmp_path: Path): + file = tmp_path / "config.yaml" + input = { + "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, + "destinations": {}, + } + with file.open("w") as f: + safe_dump(input, f) + + result = build(file) + + expected = { + "applications": {"app1": AppSetting(command="echo", config="/etc/passwd")}, + "destinations": { + "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()) + }, + } + assert result == expected + + +@pytest.mark.parametrize( + "test_input", + [ + ({}), + ({"applications": {}}), + ({"destinations": {}}), + ], +) +def test_parse_keyerrors(test_input): + with pytest.raises(KeyError): + parse(test_input) + +@pytest.mark.anyio +async def test_parse_single_destination(): + input = { + "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, + "destinations": { + "dest2": { + "scheduler": { + "type": "slurm", + "partition": "mypartition", + "runner": { + "type": "ssh", + "hostname": "localhost", + }, + }, + "filesystem": { + "type": "sftp", + "config": { + "hostname": "localhost", + }, + "entry": "/scratch/jobs", + }, + }, + }, + } + + result = parse(input) + + expected = { + "applications": {"app1": AppSetting(command="echo", config="/etc/passwd")}, + "destinations": { + "dest2": Destination( + scheduler=SlurmScheduler( + runner=SshCommandRunner(config={"hostname": "localhost"}), + partition="mypartition", + ), + filesystem=SftpFileSystem( + config={ + "hostname": "localhost", + }, + entry="/scratch/jobs", + ), + ) + }, + } + assert result == expected diff --git a/bartender/tests/test_destinations.py b/bartender/tests/test_destinations.py new file mode 100644 index 0000000..61d5048 --- /dev/null +++ b/bartender/tests/test_destinations.py @@ -0,0 +1,95 @@ +import pytest + +from bartender.destinations import Destination, build +from bartender.filesystems.local import LocalFileSystem +from bartender.filesystems.sftp import SftpFileSystem +from bartender.schedulers.memory import MemoryScheduler +from bartender.schedulers.runner import SshCommandRunner +from bartender.schedulers.slurm import SlurmScheduler + + +@pytest.mark.anyio +async def test_empty(): + config = {} + result = build(config) + + expected = { + "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()) + } + assert result == expected + + +@pytest.mark.anyio +async def test_single_empty(): + config = {"dest1": {}} + with pytest.raises(KeyError): + build(config) + + +@pytest.mark.anyio +async def test_single_memory(): + config = {"dest1": {"scheduler": {"type": "memory"}}} + result = build(config) + + expected = { + "dest1": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()) + } + assert result == expected + + +@pytest.mark.anyio +async def test_single_memory_local(): + config = { + "dest1": {"scheduler": {"type": "memory"}, "filesystem": {"type": "local"}} + } + result = build(config) + + expected = { + "dest1": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()) + } + assert result == expected + + +@pytest.mark.anyio +async def test_double(): + + config = { + "dest1": {"scheduler": {"type": "memory", "slots": 42}}, + "dest2": { + "scheduler": { + "type": "slurm", + "partition": "mypartition", + "runner": { + "type": "ssh", + "hostname": "localhost", + }, + }, + "filesystem": { + "type": "sftp", + "config": { + "hostname": "localhost", + }, + "entry": "/scratch/jobs", + }, + }, + } + result = build(config) + + expected = { + "dest1": Destination( + scheduler=MemoryScheduler(slots=42), filesystem=LocalFileSystem() + ), + "dest2": Destination( + scheduler=SlurmScheduler( + runner=SshCommandRunner(config={"hostname": "localhost"}), + partition="mypartition", + ), + filesystem=SftpFileSystem( + config={ + "hostname": "localhost", + }, + entry="/scratch/jobs", + ), + ), + } + assert result == expected From 056693911388ff60043082704a4972dd14666a0d Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Mon, 21 Nov 2022 13:06:58 +0100 Subject: [PATCH 04/36] Fix flake8 & mypy errors --- bartender/_ssh_utils.py | 23 ++-- bartender/config.py | 147 +++++++++++++--------- bartender/destinations.py | 33 +++-- bartender/filesystems/build.py | 30 +++-- bartender/filesystems/local.py | 2 +- bartender/filesystems/sftp.py | 3 +- bartender/schedulers/build.py | 90 ++++++++----- bartender/schedulers/dependencies.py | 7 +- bartender/schedulers/memory.py | 15 ++- bartender/schedulers/slurm.py | 29 +++-- bartender/settings.py | 2 +- bartender/tests/filesystems/__init__.py | 2 +- bartender/tests/filesystems/test_build.py | 29 +++-- bartender/tests/schedulers/test_build.py | 49 ++++---- bartender/tests/schedulers/test_slurm.py | 12 +- bartender/tests/test_config.py | 62 +++++---- bartender/tests/test_destinations.py | 44 +++---- bartender/web/api/applications/submit.py | 7 +- bartender/web/lifetime.py | 12 +- poetry.lock | 14 ++- pyproject.toml | 1 + 21 files changed, 368 insertions(+), 245 deletions(-) diff --git a/bartender/_ssh_utils.py b/bartender/_ssh_utils.py index f7cbe9f..08d7da3 100644 --- a/bartender/_ssh_utils.py +++ b/bartender/_ssh_utils.py @@ -1,15 +1,17 @@ -from typing import Optional, TypedDict +from dataclasses import dataclass from asyncssh import SSHClientConnection, connect +from asyncssh.misc import DefTuple -class SshConnectConfig(TypedDict): +@dataclass +class SshConnectConfig: """Configuration for ssh connection.""" hostname: str - port: Optional[int] - username: Optional[str] - password: Optional[str] + port: DefTuple[int] = () + username: DefTuple[str] = () + password: DefTuple[str] = () async def ssh_connect(config: SshConnectConfig) -> SSHClientConnection: @@ -21,13 +23,14 @@ async def ssh_connect(config: SshConnectConfig) -> SSHClientConnection: conn_vargs = { "known_hosts": None, } - if config["password"] is not None: + if config.password: + # Do not use SSH agent when password is supplied. conn_vargs["agent_path"] = None return await connect( - host=config["hostname"], - port=config["port"], - username=config["username"], - password=config["password"], + host=config.hostname, + port=config.port, + username=config.username, + password=config.password, **conn_vargs, ) diff --git a/bartender/config.py b/bartender/config.py index 6d0457e..f7cd37d 100644 --- a/bartender/config.py +++ b/bartender/config.py @@ -2,77 +2,106 @@ Parses config file into a list of applications, schedulers and filesystems. Example config: -```yaml -applications: - haddock3: - command: haddock3 $config - recluster: - command: haddock3 recluster -destinations: - cluster1: - filesystem: &cluster1fs - type: sftp - hostname: localhost - port: 10022 - username: xenon - password: javagat - entry: /home/xenon - scheduler: &cluster1sched - type: slurm - partition: mypartition - time: '60' # max time is 60 minutes - extra_options: - - --nodes 1 - runner: - type: ssh # or local - hostname: localhost - port: 10022 - username: xenon - password: javagat - local: - scheduler: - type: memory - slots: 4 - cluster2: # bartender is being run on head node of cluster - scheduler: + +.. code-block: yaml + + applications: + haddock3: + command: haddock3 $config + recluster: + command: haddock3 recluster + destinations: + cluster1: + filesystem: &cluster1fs + type: sftp + hostname: localhost + port: 10022 + username: xenon + password: javagat + entry: /home/xenon + scheduler: &cluster1sched type: slurm - cluster3: # show of reuse using yaml anchor and aliases - scheduler: - <<: *cluster1sched - parition: otherpartition - filesystem: *cluster1fs - grid: - scheduler: - type: grid - filesystem: - type: dirac -``` + partition: mypartition + time: '60' # max time is 60 minutes + extra_options: + - --nodes 1 + runner: + type: ssh # or local + hostname: localhost + port: 10022 + username: xenon + password: javagat + local: + scheduler: + type: memory + slots: 4 + cluster2: # bartender is being run on head node of cluster + scheduler: + type: slurm + cluster3: # show of reuse using yaml anchor and aliases + scheduler: + <<: *cluster1sched + parition: otherpartition + filesystem: *cluster1fs + grid: + scheduler: + type: grid + filesystem: + type: dirac """ +from dataclasses import dataclass from pathlib import Path from typing import Any -from yaml import safe_load +from yaml import safe_load as load_yaml +from bartender.destinations import Destination from bartender.destinations import build as build_destinations from bartender.settings import AppSetting -def build(config_filename: Path): - config = load(config_filename) - return parse(config) -def parse(config: Any): - return { - 'applications': build_applications(config['applications']), - 'destinations': build_destinations(config['destinations']) - } +@dataclass +class Config: + """Bartender configuration. + + The bartender.settings.Settings class is for FastAPI settings. + The bartender.config.Config class is for non-FastAPI configuration. + """ + + applications: dict[str, AppSetting] + destinations: dict[str, Destination] + + +def build_config(config_filename: Path) -> Config: + """Build a config instance from a yaml formatted file. + + :param config_filename: File name of configuration file. + :return: A config instance. + """ + config = _load(config_filename) + return parse_config(config) + + +def parse_config(config: Any) -> Config: + """Parses a plain configuration dict to a config instance. + + :param config: A plain configuration dict + :return: A config instance. + """ + return Config( + applications=_build_applications(config["applications"]), + destinations=build_destinations(config["destinations"]), + ) + + +def _load(config_filename: Path) -> Any: + with open(config_filename) as handle: + return load_yaml(handle) -def load(config_filename: Path) -> Any: - with open(config_filename) as f: - return safe_load(f) -def build_applications(config: Any) -> dict[str, AppSetting]: +def _build_applications(config: Any) -> dict[str, AppSetting]: applications = {} - for name, config in config.items(): - applications[name] = AppSetting(**config) + for name, setting in config.items(): + applications[name] = AppSetting(**setting) return applications diff --git a/bartender/destinations.py b/bartender/destinations.py index 0ccb8cc..e951f69 100644 --- a/bartender/destinations.py +++ b/bartender/destinations.py @@ -1,34 +1,45 @@ from dataclasses import dataclass from typing import Any, Optional - from bartender.filesystems.abstract import AbstractFileSystem -from bartender.filesystems.local import LocalFileSystem from bartender.filesystems.build import build as build_filesystem +from bartender.filesystems.local import LocalFileSystem from bartender.schedulers.abstract import AbstractScheduler -from bartender.schedulers.memory import MemoryScheduler from bartender.schedulers.build import build as build_scheduler +from bartender.schedulers.memory import MemoryScheduler @dataclass class Destination: + """A destination is a combination of a scheduler and filesystem.""" + scheduler: AbstractScheduler filesystem: Optional[AbstractFileSystem] = None def build(config: Any) -> dict[str, Destination]: + """Build job destinations dictionary from a configuration dictionary. + + :param config: The configuration dictionary. + :return: Job destinations dictionary + """ if not config: return { - "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()) + "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), } destinations = {} for name, dest_config in config.items(): - if not dest_config: - raise KeyError("Destinations needs scheduler and optional file system") - scheduler = build_scheduler(dest_config["scheduler"]) - filesystem = LocalFileSystem() - if "filesystem" in dest_config: - filesystem = build_filesystem(dest_config["filesystem"]) - destinations[name] = Destination(scheduler=scheduler, filesystem=filesystem) + destinations[name] = _build_destination(dest_config) return destinations + + +def _build_destination(dest_config: Any) -> Destination: + if not dest_config: + raise KeyError("Destinations needs scheduler and optional file system") + scheduler = build_scheduler(dest_config["scheduler"]) + filesystem: AbstractFileSystem = LocalFileSystem() + filesystem_config = dest_config.get("filesystem") + if filesystem_config is not None: + filesystem = build_filesystem(dest_config["filesystem"]) + return Destination(scheduler=scheduler, filesystem=filesystem) diff --git a/bartender/filesystems/build.py b/bartender/filesystems/build.py index d483d74..2be70a7 100644 --- a/bartender/filesystems/build.py +++ b/bartender/filesystems/build.py @@ -1,25 +1,31 @@ from pathlib import Path from typing import Any +from bartender._ssh_utils import SshConnectConfig from bartender.filesystems.abstract import AbstractFileSystem from bartender.filesystems.local import LocalFileSystem from bartender.filesystems.sftp import SftpFileSystem -from bartender._ssh_utils import SshConnectConfig + def build(config: Any) -> AbstractFileSystem: + """Build a file system from a configuration. + + :param config: The configuration + :raises KeyError: When a key is missing + :raises ValueError: When a value is incorrect. + :return: A file system instance. + """ if config is None: return LocalFileSystem() if "type" not in config: raise KeyError("File system without type") - if config["type"] == 'local': + if config["type"] == "local": return LocalFileSystem() - if config['type'] == 'sftp': - if 'config' not in config: - raise KeyError("Sftp file system without SSH connection configuration.") - sshconfig = SshConnectConfig(**config['config']) - entry = Path('/') - if 'entry' in config: - entry = Path(config['entry']) - return SftpFileSystem(sshconfig, entry) - else: - raise ValueError(f'File system with type {config["type"]} is unknown') + if config["type"] == "sftp": + if "config" not in config: + raise KeyError("Sftp file system without SSH connection configuration.") + ssh_config = SshConnectConfig(**config["config"]) + entry_config = config.get("entry", "/") + entry = Path(entry_config) + return SftpFileSystem(ssh_config, entry) + raise ValueError(f'File system with type {config["type"]} is unknown') diff --git a/bartender/filesystems/local.py b/bartender/filesystems/local.py index 87ef24d..e570de9 100644 --- a/bartender/filesystems/local.py +++ b/bartender/filesystems/local.py @@ -51,4 +51,4 @@ def __eq__(self, other: object) -> bool: return isinstance(other, LocalFileSystem) def __repr__(self) -> str: - return f"LocalFileSystem()" + return "LocalFileSystem()" diff --git a/bartender/filesystems/sftp.py b/bartender/filesystems/sftp.py index 721cab3..447f565 100644 --- a/bartender/filesystems/sftp.py +++ b/bartender/filesystems/sftp.py @@ -80,7 +80,8 @@ def close(self) -> None: if self.conn: self.conn.close() - # TODO add delete(description), after download you might want to delete the remote job dir + # TODO add delete(description), + # after download you might want to delete the remote job dir def __eq__(self, other: object) -> bool: return ( diff --git a/bartender/schedulers/build.py b/bartender/schedulers/build.py index f12fbac..d0e8c87 100644 --- a/bartender/schedulers/build.py +++ b/bartender/schedulers/build.py @@ -1,38 +1,68 @@ from typing import Any +from bartender._ssh_utils import SshConnectConfig from bartender.schedulers.abstract import AbstractScheduler - from bartender.schedulers.memory import MemoryScheduler -from bartender.schedulers.runner import LocalCommandRunner, SshCommandRunner +from bartender.schedulers.runner import ( + CommandRunner, + LocalCommandRunner, + SshCommandRunner, +) from bartender.schedulers.slurm import SlurmScheduler + def build(config: Any) -> AbstractScheduler: - """Build scheduler instance from configuration.""" - if "type" not in config: + """Build scheduler instance from configuration. + + :param config: Configuration for a scheduler. + :raises KeyError: When key is missing in configuration. + :raises ValueError: When value is incorrect in configuration. + :return: A scheduler instance. + + """ + scheduler_type = config.get("type") + if scheduler_type is None: raise KeyError("Scheduler without type") - if config["type"] == "memory": - if "slots" in config: - return MemoryScheduler(config["slots"]) - return MemoryScheduler() - if config["type"] == "slurm": - slurm_config = {k: v for k, v in config.items() if k not in {"type", "runner"}} - if "runner" in config: - if "type" not in config["runner"]: - raise ValueError("Runner without type") - - if config["runner"]["type"] == "ssh": - if "hostname" not in config["runner"]: - raise ValueError("Ssh runner without hostname") - runner_config = { - k: v for k, v in config["runner"].items() if k not in {"type"} - } - runner = SshCommandRunner(config=runner_config) - else: - raise ValueError( - f"Runner with type {config['runner']['type']} is unknown", - ) - else: - runner = LocalCommandRunner() - return SlurmScheduler(runner=runner, **slurm_config) - else: - raise ValueError(f'Scheduler with type {config["type"]} is unknown') + if scheduler_type == "memory": + return _build_memory_scheduler(config) + if scheduler_type == "slurm": + return _build_slurm_scheduler(config) + raise ValueError(f'Scheduler with type {config["type"]} is unknown') + + +def _build_memory_scheduler(config: Any) -> MemoryScheduler: + slots = config.get("slots") + if slots is not None: + return MemoryScheduler(slots) + return MemoryScheduler() + + +def _build_slurm_scheduler(config: Any) -> SlurmScheduler: + slurm_config = { + sched_key: sched_value + for sched_key, sched_value in config.items() + if sched_key not in {"type", "runner"} + } + runner: CommandRunner = LocalCommandRunner() + runner_config = config.get("runner") + if runner_config is not None: + runner = _build_runner(runner_config) + return SlurmScheduler(runner=runner, **slurm_config) + + +def _build_runner(runner_config: Any) -> CommandRunner: + runner_type = runner_config.get("type") + if runner_type is None: + raise ValueError("Runner without type") + if runner_type == "ssh": + if "hostname" not in runner_config: + raise ValueError("Ssh runner without hostname") + runner_config = { + runner_key: runner_value + for runner_key, runner_value in runner_config.items() + if runner_key != "type" + } + return SshCommandRunner(config=SshConnectConfig(**runner_config)) + raise ValueError( + f"Runner with type {runner_type} is unknown", + ) diff --git a/bartender/schedulers/dependencies.py b/bartender/schedulers/dependencies.py index a1fbe43..20ca591 100644 --- a/bartender/schedulers/dependencies.py +++ b/bartender/schedulers/dependencies.py @@ -1,5 +1,6 @@ from starlette.requests import Request +from bartender.config import Config from bartender.schedulers.abstract import AbstractScheduler @@ -9,4 +10,8 @@ def get_scheduler(request: Request) -> AbstractScheduler: :param request: current request. :return: A scheduler. """ - return request.app.scheduler + config: Config = request.app.config + destinations = config.destinations.values() + # TODO dont pick first scheduler + destination = list(destinations)[0] + return destination.scheduler diff --git a/bartender/schedulers/memory.py b/bartender/schedulers/memory.py index 117b1cf..1f499db 100644 --- a/bartender/schedulers/memory.py +++ b/bartender/schedulers/memory.py @@ -112,6 +112,15 @@ async def cancel(self, job_id: str) -> None: # noqa: D102 if job.state == "queued": self.jobs.pop(job_id) + def __eq__(self, other: object) -> bool: + return isinstance(other, MemoryScheduler) and len(self.workers) == len( + other.workers, + ) + + def __repr__(self) -> str: + slots = len(self.workers) + return f"MemoryScheduler(slots={slots})" + def _add_worker(self) -> None: worker_index = len(self.workers) worker = create_task(_worker(self.queue, self.jobs, worker_index)) @@ -121,9 +130,3 @@ def _add_worker(self) -> None: def _forget_completed_job(self, job_id: str, state: State) -> None: if state in CompletedStates: self.jobs.pop(job_id) - - def __eq__(self, other: object) -> bool: - return len(self.workers) == len(other.workers) - - def __repr__(self) -> str: - return f"MemoryScheduler(slots={len(self.workers)})" diff --git a/bartender/schedulers/slurm.py b/bartender/schedulers/slurm.py index c43c5ee..e7cc430 100644 --- a/bartender/schedulers/slurm.py +++ b/bartender/schedulers/slurm.py @@ -109,6 +109,23 @@ async def close(self) -> None: """Cancel all runnning jobs and make scheduler unable to work.""" self.runner.close() + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, SlurmScheduler) # noqa: WPS222 + and self.runner == other.runner + and self.partition == other.partition + and self.time == other.time + and self.extra_options == other.extra_options + ) + + def __repr__(self) -> str: + return dedent( + f"""\ + SlurmScheduler(runner={self.runner}, partition={self.partition}, + time={self.time}, extra_options={self.extra_options} + )""", + ) + async def _state_from_accounting(self, job_id: str) -> str: command = "sacct" args = ["-j", job_id, "--noheader", "--format=state"] @@ -142,15 +159,3 @@ def _submit_script(self, description: JobDescription) -> str: echo -n $? > returncode """ return dedent(script) - - def __eq__(self, other: object) -> bool: - return ( - isinstance(other, SlurmScheduler) - and self.runner == other.runner - and self.partition == other.partition - and self.time == other.time - and self.extra_options == other.extra_options - ) - - def __repr__(self) -> str: - return f"SlurmScheduler(runner={self.runner}, partition={self.partition}, time={self.time}, extra_options={self.extra_options})" diff --git a/bartender/settings.py b/bartender/settings.py index de22cd7..3fe633b 100644 --- a/bartender/settings.py +++ b/bartender/settings.py @@ -86,7 +86,7 @@ class Settings(BaseSettings): # TODO read scheduler + applications from # yaml/toml formmatted config file instead of env vars. - config_filename: Path = "config.yaml" + config_filename: Path = Path("config.yaml") @property def db_url(self) -> URL: diff --git a/bartender/tests/filesystems/__init__.py b/bartender/tests/filesystems/__init__.py index 3b7a6ea..3e4d3d1 100644 --- a/bartender/tests/filesystems/__init__.py +++ b/bartender/tests/filesystems/__init__.py @@ -1 +1 @@ -"""Tests for bartender.filesystems.""" \ No newline at end of file +"""Tests for bartender.filesystems.""" diff --git a/bartender/tests/filesystems/test_build.py b/bartender/tests/filesystems/test_build.py index 283575f..ac7221b 100644 --- a/bartender/tests/filesystems/test_build.py +++ b/bartender/tests/filesystems/test_build.py @@ -1,30 +1,31 @@ from pathlib import Path + import pytest +from bartender._ssh_utils import SshConnectConfig from bartender.filesystems.build import build from bartender.filesystems.local import LocalFileSystem from bartender.filesystems.sftp import SftpFileSystem -from bartender._ssh_utils import SshConnectConfig -def test_none(): +def test_none() -> None: result = build(None) expected = LocalFileSystem() assert result == expected -def test_emptydict(): +def test_emptydict() -> None: with pytest.raises(KeyError): build({}) -def test_unknowntype(): +def test_unknowntype() -> None: config = {"type": "unknown"} with pytest.raises(ValueError): build(config) -def test_local(): +def test_local() -> None: config = {"type": "local"} result = build(config) @@ -32,13 +33,13 @@ def test_local(): assert result == expected -def test_sftp_withoutconfig(): +def test_sftp_withoutconfig() -> None: config = {"type": "sftp"} with pytest.raises(KeyError): build(config) -def test_sftp_simplest(): +def test_sftp_simplest() -> None: config = {"type": "sftp", "config": {"hostname": "localhost"}} result = build(config) @@ -46,7 +47,7 @@ def test_sftp_simplest(): assert result == expected -def test_sftp_entry(): +def test_sftp_entry() -> None: config = { "type": "sftp", "config": {"hostname": "localhost"}, @@ -55,12 +56,13 @@ def test_sftp_entry(): result = build(config) expected = SftpFileSystem( - SshConnectConfig(hostname="localhost"), entry=Path("/scratch/jobs") + SshConnectConfig(hostname="localhost"), + entry=Path("/scratch/jobs"), ) assert result == expected -def test_sftp_verbosist(): +def test_sftp_verbosist() -> None: config = { "type": "sftp", "config": { @@ -74,8 +76,11 @@ def test_sftp_verbosist(): result = build(config) expected = SftpFileSystem( - SshConnectConfig( - hostname="localhost", port=2222, username="someone", password="somepw" + SshConnectConfig( # noqa: S106 + hostname="localhost", + port=2222, + username="someone", + password="somepw", ), entry=Path("/scratch/jobs"), ) diff --git a/bartender/tests/schedulers/test_build.py b/bartender/tests/schedulers/test_build.py index 70ed653..218f8e2 100644 --- a/bartender/tests/schedulers/test_build.py +++ b/bartender/tests/schedulers/test_build.py @@ -1,25 +1,28 @@ +from typing import Any + import pytest +from bartender._ssh_utils import SshConnectConfig from bartender.schedulers.build import build from bartender.schedulers.memory import MemoryScheduler from bartender.schedulers.runner import LocalCommandRunner, SshCommandRunner from bartender.schedulers.slurm import SlurmScheduler -def test_single_typeless_scheduler(): - config = {} +def test_single_typeless_scheduler() -> None: + config: Any = {} with pytest.raises(KeyError): build(config) -def test_single_unknown_scheduler(): +def test_single_unknown_scheduler() -> None: config = {type: "unknown"} with pytest.raises(ValueError): build(config) @pytest.mark.anyio -async def test_single_memory_scheduler(): +async def test_single_memory_scheduler() -> None: config = {"type": "memory"} result = build(config) @@ -28,7 +31,7 @@ async def test_single_memory_scheduler(): @pytest.mark.anyio -async def test_single_custom_memory_scheduler(): +async def test_single_custom_memory_scheduler() -> None: config = {"type": "memory", "slots": 4} result = build(config) @@ -37,7 +40,7 @@ async def test_single_custom_memory_scheduler(): @pytest.mark.anyio -async def test_single_localsimplist_slurm_scheduler(): +async def test_single_localsimplist_slurm_scheduler() -> None: config = {"type": "slurm"} result = build(config) @@ -46,11 +49,11 @@ async def test_single_localsimplist_slurm_scheduler(): @pytest.mark.anyio -async def test_single_localcustom_slurm_scheduler(): +async def test_single_localcustom_slurm_scheduler() -> None: config = { "type": "slurm", "partition": "mypartition", - "time": 60, + "time": "60", "extra_options": ["--nodes 1"], } result = build(config) @@ -58,35 +61,35 @@ async def test_single_localcustom_slurm_scheduler(): expected = SlurmScheduler( runner=LocalCommandRunner(), partition="mypartition", - time=60, + time="60", extra_options=["--nodes 1"], ) assert result == expected @pytest.mark.anyio -async def test_single_typelessrunner_slurm_scheduler(): +async def test_single_typelessrunner_slurm_scheduler() -> None: config = {"type": "slurm", "runner": {}} with pytest.raises(ValueError): build(config) @pytest.mark.anyio -async def test_single_unknownrunner_slurm_scheduler(): +async def test_single_unknownrunner_slurm_scheduler() -> None: config = {"type": "slurm", "runner": {"type": "unknown"}} with pytest.raises(ValueError): build(config) @pytest.mark.anyio -async def test_single_withouthost_slurm_scheduler(): +async def test_single_withouthost_slurm_scheduler() -> None: config = {"type": "slurm", "runner": {"type": "ssh"}} with pytest.raises(ValueError): build(config) @pytest.mark.anyio -async def test_single_sshsimplist_slurm_scheduler(): +async def test_single_sshsimplist_slurm_scheduler() -> None: config = { "type": "slurm", "runner": { @@ -96,12 +99,14 @@ async def test_single_sshsimplist_slurm_scheduler(): } result = build(config) - expected = SlurmScheduler(SshCommandRunner(config={"hostname": "localhost"})) + expected = SlurmScheduler( + SshCommandRunner(config=SshConnectConfig(hostname="localhost")), + ) assert result == expected @pytest.mark.anyio -async def test_single_sshcustom_slurm_scheduler(): +async def test_single_sshcustom_slurm_scheduler() -> None: config = { "type": "slurm", "runner": { @@ -116,12 +121,12 @@ async def test_single_sshcustom_slurm_scheduler(): expected = SlurmScheduler( SshCommandRunner( - config={ - "hostname": "localhost", - "port": 10022, - "username": "xenon", - "password": "javagat", - } - ) + config=SshConnectConfig( # noqa: S106 + hostname="localhost", + port=10022, + username="xenon", + password="javagat", + ), + ), ) assert result == expected diff --git a/bartender/tests/schedulers/test_slurm.py b/bartender/tests/schedulers/test_slurm.py index 96a0d39..7e9bf83 100644 --- a/bartender/tests/schedulers/test_slurm.py +++ b/bartender/tests/schedulers/test_slurm.py @@ -26,12 +26,12 @@ def get_config(self) -> SshConnectConfig: password = "javagat" # noqa: S105 hostname = self.get_container_host_ip() port = int(self.get_exposed_port(self.port_to_expose)) - return { - "hostname": hostname, - "port": port, - "username": username, - "password": password, - } + return SshConnectConfig( + hostname=hostname, + port=port, + username=username, + password=password, + ) def get_runner(self) -> SshCommandRunner: return SshCommandRunner(self.get_config()) diff --git a/bartender/tests/test_config.py b/bartender/tests/test_config.py index 2c61d31..4ec1823 100644 --- a/bartender/tests/test_config.py +++ b/bartender/tests/test_config.py @@ -1,9 +1,11 @@ from pathlib import Path +from typing import Any import pytest -from yaml import safe_dump +from yaml import safe_dump as yaml_dump -from bartender.config import build, parse +from bartender._ssh_utils import SshConnectConfig +from bartender.config import Config, build_config, parse_config from bartender.destinations import Destination from bartender.filesystems.local import LocalFileSystem from bartender.filesystems.sftp import SftpFileSystem @@ -12,24 +14,25 @@ from bartender.schedulers.slurm import SlurmScheduler from bartender.settings import AppSetting + @pytest.mark.anyio -async def test_build_minimal(tmp_path: Path): +async def test_build_minimal(tmp_path: Path) -> None: file = tmp_path / "config.yaml" - input = { + config = { "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, "destinations": {}, } - with file.open("w") as f: - safe_dump(input, f) + with file.open("w") as handle: + yaml_dump(config, handle) - result = build(file) + result = build_config(file) - expected = { - "applications": {"app1": AppSetting(command="echo", config="/etc/passwd")}, - "destinations": { - "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()) + expected = Config( + applications={"app1": AppSetting(command="echo", config="/etc/passwd")}, + destinations={ + "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), }, - } + ) assert result == expected @@ -41,13 +44,14 @@ async def test_build_minimal(tmp_path: Path): ({"destinations": {}}), ], ) -def test_parse_keyerrors(test_input): +def test_parse_keyerrors(test_input: Any) -> None: with pytest.raises(KeyError): - parse(test_input) + parse_config(test_input) + @pytest.mark.anyio -async def test_parse_single_destination(): - input = { +async def test_parse_single_destination() -> None: + config = { "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, "destinations": { "dest2": { @@ -70,23 +74,27 @@ async def test_parse_single_destination(): }, } - result = parse(input) + result = parse_config(config) - expected = { - "applications": {"app1": AppSetting(command="echo", config="/etc/passwd")}, - "destinations": { + expected = Config( + applications={"app1": AppSetting(command="echo", config="/etc/passwd")}, + destinations={ "dest2": Destination( scheduler=SlurmScheduler( - runner=SshCommandRunner(config={"hostname": "localhost"}), + runner=SshCommandRunner( + config=SshConnectConfig( + hostname="localhost", + ), + ), partition="mypartition", ), filesystem=SftpFileSystem( - config={ - "hostname": "localhost", - }, - entry="/scratch/jobs", + config=SshConnectConfig( + hostname="localhost", + ), + entry=Path("/scratch/jobs"), ), - ) + ), }, - } + ) assert result == expected diff --git a/bartender/tests/test_destinations.py b/bartender/tests/test_destinations.py index 61d5048..851ad00 100644 --- a/bartender/tests/test_destinations.py +++ b/bartender/tests/test_destinations.py @@ -1,5 +1,9 @@ +from pathlib import Path +from typing import Any + import pytest +from bartender._ssh_utils import SshConnectConfig from bartender.destinations import Destination, build from bartender.filesystems.local import LocalFileSystem from bartender.filesystems.sftp import SftpFileSystem @@ -9,51 +13,50 @@ @pytest.mark.anyio -async def test_empty(): - config = {} +async def test_empty() -> None: + config: Any = {} result = build(config) expected = { - "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()) + "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), } assert result == expected @pytest.mark.anyio -async def test_single_empty(): - config = {"dest1": {}} +async def test_single_empty() -> None: + config: Any = {"dest1": {}} with pytest.raises(KeyError): build(config) @pytest.mark.anyio -async def test_single_memory(): - config = {"dest1": {"scheduler": {"type": "memory"}}} +async def test_single_memory() -> None: + config: Any = {"dest1": {"scheduler": {"type": "memory"}}} result = build(config) expected = { - "dest1": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()) + "dest1": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), } assert result == expected @pytest.mark.anyio -async def test_single_memory_local(): - config = { - "dest1": {"scheduler": {"type": "memory"}, "filesystem": {"type": "local"}} +async def test_single_memory_local() -> None: + config: Any = { + "dest1": {"scheduler": {"type": "memory"}, "filesystem": {"type": "local"}}, } result = build(config) expected = { - "dest1": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()) + "dest1": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), } assert result == expected @pytest.mark.anyio -async def test_double(): - - config = { +async def test_double() -> None: + config: Any = { "dest1": {"scheduler": {"type": "memory", "slots": 42}}, "dest2": { "scheduler": { @@ -77,18 +80,17 @@ async def test_double(): expected = { "dest1": Destination( - scheduler=MemoryScheduler(slots=42), filesystem=LocalFileSystem() + scheduler=MemoryScheduler(slots=42), + filesystem=LocalFileSystem(), ), "dest2": Destination( scheduler=SlurmScheduler( - runner=SshCommandRunner(config={"hostname": "localhost"}), + runner=SshCommandRunner(config=SshConnectConfig(hostname="localhost")), partition="mypartition", ), filesystem=SftpFileSystem( - config={ - "hostname": "localhost", - }, - entry="/scratch/jobs", + config=SshConnectConfig(hostname="localhost"), + entry=Path("/scratch/jobs"), ), ), } diff --git a/bartender/web/api/applications/submit.py b/bartender/web/api/applications/submit.py index 4d33a8b..4cf629b 100644 --- a/bartender/web/api/applications/submit.py +++ b/bartender/web/api/applications/submit.py @@ -14,7 +14,7 @@ async def submit( job_dir: Path, application: AppSetting, job_dao: JobDAO, - schedulers: dict[str, AbstractScheduler], + scheduler: AbstractScheduler, ) -> None: """Submit job description to scheduler and store job id returned by scheduler in db. @@ -26,8 +26,7 @@ async def submit( """ command = Template(application.command).substitute(config=application.config) description = JobDescription(job_dir=job_dir, command=command) - # TODO dont pick first scheduler - scheduler = list(schedulers.values())[0] - # TODO if scheduler has filesystem then perform upload of job dir and localize description + # TODO if scheduler has filesystem then perform upload + # of job dir and localize description internal_job_id = await scheduler.submit(description) await job_dao.update_internal_job_id(external_job_id, internal_job_id) diff --git a/bartender/web/lifetime.py b/bartender/web/lifetime.py index 776ac3e..7238183 100644 --- a/bartender/web/lifetime.py +++ b/bartender/web/lifetime.py @@ -2,10 +2,9 @@ from fastapi import FastAPI +from bartender.config import build_config from bartender.db.session import make_engine, make_session_factory from bartender.filesystem import setup_job_root_dir -from bartender.schedulers.build import build -from bartender.schedulers.memory import MemoryScheduler from bartender.settings import settings @@ -68,14 +67,13 @@ def _setup_schedulers(app: FastAPI) -> None: :param app: fastAPI application. """ - app.state.schedulers = build(settings.config_filename) - # app.state.scheduler = MemoryScheduler(settings.scheduler_slots) + app.state.config = build_config(settings.config_filename) async def _teardown_schedulers(app: FastAPI) -> None: - """Teardown scheduler. + """Teardown schedulers. :param app: fastAPI application. """ - for scheduler in app.state.schedulers.values(): - await scheduler.close() + for destination in app.state.config.destinations.values(): + await destination.scheduler.close() diff --git a/poetry.lock b/poetry.lock index 348951d..ee0ac83 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1325,6 +1325,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "types-pyyaml" +version = "6.0.12.2" +description = "Typing stubs for PyYAML" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "typing-extensions" version = "4.3.0" @@ -1503,7 +1511,7 @@ tokenize-rt = ">=2.1" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "c9d45ddb63b8ff2838765f94e79e7842c4579dd41ab47d9638590c093551d6d2" +content-hash = "47b05792882c48e783668a1f2090a0b09a3cad7c2ea64582bfd192476c2c8f0d" [metadata.files] aiofiles = [ @@ -2430,6 +2438,10 @@ types-aiofiles = [ {file = "types-aiofiles-0.8.11.tar.gz", hash = "sha256:1f93aa68e47de1379f45eef9acd34faa0f9341628921cd6aede666e6e559a5a8"}, {file = "types_aiofiles-0.8.11-py3-none-any.whl", hash = "sha256:be6715fffd1c7f84c9316000ba8bbc66a884246dbd2902c163ebc2d67315206b"}, ] +types-pyyaml = [ + {file = "types-PyYAML-6.0.12.2.tar.gz", hash = "sha256:6840819871c92deebe6a2067fb800c11b8a063632eb4e3e755914e7ab3604e83"}, + {file = "types_PyYAML-6.0.12.2-py3-none-any.whl", hash = "sha256:1e94e80aafee07a7e798addb2a320e32956a373f376655128ae20637adb2655b"}, +] typing-extensions = [ {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, diff --git a/pyproject.toml b/pyproject.toml index db391d8..579fe36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ pytest-env = "^0.6.2" httpx = "^0.22.0" types-aiofiles = "^0.8.11" testcontainers = "^3.7.0" +types-pyyaml = "^6.0.12.2" [tool.poetry.scripts] bartender = 'bartender.__main__:main' From 9c2dad294e8ef0194ab8cb42c898491bea774e29 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Mon, 21 Nov 2022 15:53:46 +0100 Subject: [PATCH 05/36] Read and use config.yaml --- README.md | 35 +++++- bartender/config.py | 29 ++++- bartender/db/dao/job_dao.py | 11 +- .../versions/2022-11-10-15-25_024e0181541b.py | 1 + bartender/db/models/job_model.py | 1 + bartender/filesystem/__init__.py | 8 +- bartender/filesystems/abstract.py | 3 + bartender/filesystems/local.py | 3 + bartender/settings.py | 28 +---- bartender/tests/conftest.py | 33 +++--- bartender/tests/schedulers/test_build.py | 2 +- bartender/tests/test_config.py | 11 +- bartender/web/api/applications/submit.py | 83 ++++++++++--- bartender/web/api/applications/views.py | 25 ++-- bartender/web/api/job/sync.py | 110 +++++++++++++++--- bartender/web/api/job/views.py | 23 ++-- bartender/web/lifetime.py | 31 +++-- config-example.yaml | 22 ++++ 18 files changed, 339 insertions(+), 120 deletions(-) create mode 100644 config-example.yaml diff --git a/README.md b/README.md index fb2c130..0019d0b 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ - [Project structure](#project-structure) - [Configuration](#configuration) - [Applications](#applications) + - [Job destinations](#job-destinations) - [User management](#user-management) - [GitHub login](#github-login) - [Orcid sandbox login](#orcid-sandbox-login) @@ -108,7 +109,10 @@ bartender ## [Configuration](#configuration) -This application can be configured with environment variables. +This application can be configured with environment variables and `config.yaml` file. +The environment variables are for FastAPI settings like http port and user management. +The `config.yaml` file is for non-FastAPI configuration like which [application can be submitted](#applications) and [where they should submitted](#job-destinations). +See [config-example.yaml](config-example.yaml) for example of a `config.yaml` file. You can create `.env` file in the root directory and place all environment variables here. @@ -134,18 +138,41 @@ You can read more about BaseSettings class here: ` to `.env` file. diff --git a/bartender/config.py b/bartender/config.py index f7cd37d..9748ac3 100644 --- a/bartender/config.py +++ b/bartender/config.py @@ -54,11 +54,23 @@ from pathlib import Path from typing import Any +from fastapi import Request from yaml import safe_load as load_yaml from bartender.destinations import Destination from bartender.destinations import build as build_destinations -from bartender.settings import AppSetting + + +@dataclass +class ApplicatonConfiguration: + """Command to run application. + + `$config` in command string will be replaced + with value of ApplicatonConfiguration.config. + """ + + command: str + config: str @dataclass @@ -69,7 +81,7 @@ class Config: The bartender.config.Config class is for non-FastAPI configuration. """ - applications: dict[str, AppSetting] + applications: dict[str, ApplicatonConfiguration] destinations: dict[str, Destination] @@ -100,8 +112,17 @@ def _load(config_filename: Path) -> Any: return load_yaml(handle) -def _build_applications(config: Any) -> dict[str, AppSetting]: +def _build_applications(config: Any) -> dict[str, ApplicatonConfiguration]: applications = {} for name, setting in config.items(): - applications[name] = AppSetting(**setting) + applications[name] = ApplicatonConfiguration(**setting) return applications + + +def get_config(request: Request) -> Config: + """Get config based on current request. + + :param request: The current FastAPI request. + :return: The config. + """ + return request.app.state.config diff --git a/bartender/db/dao/job_dao.py b/bartender/db/dao/job_dao.py index 340d93b..f33831c 100644 --- a/bartender/db/dao/job_dao.py +++ b/bartender/db/dao/job_dao.py @@ -93,15 +93,22 @@ async def update_job_state(self, jobid: int, state: State) -> None: job.state = state await self.session.commit() - async def update_internal_job_id(self, jobid: int, internal_job_id: str) -> None: + async def update_internal_job_id( + self, + jobid: int, + internal_job_id: str, + destination: str, + ) -> None: """ - Update internal id of a job. + Update internal id and destination of a job. :param jobid: name of job instance. :param internal_job_id: new internal job id of job instance. + :param destination: To which schdeduler/filesystem the job was submitted. """ job = await self.session.get(Job, jobid) if job is None: return job.internal_id = internal_job_id + job.destination = destination await self.session.commit() diff --git a/bartender/db/migrations/versions/2022-11-10-15-25_024e0181541b.py b/bartender/db/migrations/versions/2022-11-10-15-25_024e0181541b.py index f3ef8ab..772327b 100644 --- a/bartender/db/migrations/versions/2022-11-10-15-25_024e0181541b.py +++ b/bartender/db/migrations/versions/2022-11-10-15-25_024e0181541b.py @@ -18,6 +18,7 @@ def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### op.add_column("job", sa.Column("internal_id", sa.String(length=200), nullable=True)) + op.add_column("job", sa.Column("destination", sa.String(length=200), nullable=True)) # ### end Alembic commands ### diff --git a/bartender/db/models/job_model.py b/bartender/db/models/job_model.py index b691832..e025ac4 100644 --- a/bartender/db/models/job_model.py +++ b/bartender/db/models/job_model.py @@ -27,6 +27,7 @@ class Job(Base): submitter: User = relationship("User", back_populates="jobs") # Identifier for job used by the scheduler internal_id = Column(String(length=200)) # noqa: WPS432 + destination = Column(String(length=200)) # noqa: WPS432 created_on = Column(DateTime(timezone=True), default=now, nullable=False) updated_on = Column( DateTime(timezone=True), diff --git a/bartender/filesystem/__init__.py b/bartender/filesystem/__init__.py index b05b75d..819f4b8 100644 --- a/bartender/filesystem/__init__.py +++ b/bartender/filesystem/__init__.py @@ -3,6 +3,7 @@ from pathlib import Path from typing import Literal +from bartender.config import ApplicatonConfiguration from bartender.settings import settings @@ -15,7 +16,10 @@ def setup_job_root_dir() -> None: job_root_dir.mkdir(exist_ok=True) -def has_config_file(application: str, job_dir: Path) -> Literal[True]: +def has_config_file( + application: ApplicatonConfiguration, + job_dir: Path, +) -> Literal[True]: """Check if config file required by application is present in job directory. :param application: Name of application to check config file for. @@ -23,7 +27,7 @@ def has_config_file(application: str, job_dir: Path) -> Literal[True]: :raises IndexError: When config file could not be found :return: True when found. """ - app_config = settings.applications[application].config + app_config = application.config job_config = job_dir / app_config has = job_config.exists() and job_config.is_file() if not has: diff --git a/bartender/filesystems/abstract.py b/bartender/filesystems/abstract.py index 811a32a..6f669a2 100644 --- a/bartender/filesystems/abstract.py +++ b/bartender/filesystems/abstract.py @@ -36,3 +36,6 @@ async def download(self, src: JobDescription, target: JobDescription) -> None: :param src: Remote directory to copy from. :param target: Local directory to copy to. """ + + def close(self) -> None: + """Close filesystem.""" diff --git a/bartender/filesystems/local.py b/bartender/filesystems/local.py index e570de9..f48ea4b 100644 --- a/bartender/filesystems/local.py +++ b/bartender/filesystems/local.py @@ -47,6 +47,9 @@ async def download( :param target: Local directory to copy to. """ + def close(self) -> None: + """Close filesystem.""" + def __eq__(self, other: object) -> bool: return isinstance(other, LocalFileSystem) diff --git a/bartender/settings.py b/bartender/settings.py index 3fe633b..28c5383 100644 --- a/bartender/settings.py +++ b/bartender/settings.py @@ -1,9 +1,8 @@ import enum from pathlib import Path from tempfile import gettempdir -from typing import Dict -from pydantic import BaseModel, BaseSettings +from pydantic import BaseSettings from yarl import URL TEMP_DIR = Path(gettempdir()) @@ -20,16 +19,6 @@ class LogLevel(str, enum.Enum): # noqa: WPS600 FATAL = "FATAL" -class AppSetting(BaseModel): - """Command to run application. - - `$config` in command string will be replaced with value of AppSetting.config. - """ - - command: str - config: str - - class Settings(BaseSettings): """ Application settings. @@ -47,6 +36,7 @@ class Settings(BaseSettings): # file system settings job_root_dir: Path = TEMP_DIR / "jobs" + # TODO move job_root_dir to config.yaml # Current environment environment: str = "dev" @@ -73,19 +63,7 @@ class Settings(BaseSettings): orcid_client_id: str = "" orcid_client_secret: str = "" - # Settings for applications - applications: Dict[str, AppSetting] = { - "wc": AppSetting( - command="wc $config", - config="README.md", - ), - } - - # Settings for schedulers - scheduler_slots = 1 - # TODO read scheduler + applications from - # yaml/toml formmatted config file instead of env vars. - + # Settings for configuration config_filename: Path = Path("config.yaml") @property diff --git a/bartender/tests/conftest.py b/bartender/tests/conftest.py index f245b0f..2924a44 100644 --- a/bartender/tests/conftest.py +++ b/bartender/tests/conftest.py @@ -7,12 +7,12 @@ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine from sqlalchemy.orm import sessionmaker +from bartender.config import ApplicatonConfiguration, Config, get_config from bartender.db.dependencies import get_db_session from bartender.db.utils import create_database, drop_database -from bartender.schedulers.abstract import AbstractScheduler -from bartender.schedulers.dependencies import get_scheduler +from bartender.destinations import Destination from bartender.schedulers.memory import MemoryScheduler -from bartender.settings import AppSetting, settings +from bartender.settings import settings from bartender.web.application import get_app @@ -83,18 +83,23 @@ async def dbsession( @pytest.fixture -async def scheduler() -> AsyncGenerator[AbstractScheduler, None]: - my_scheduler = MemoryScheduler() - try: - yield my_scheduler - finally: - await my_scheduler.close() +async def config() -> AsyncGenerator[Config, None]: + applications = { + "app1": ApplicatonConfiguration( + command="wc $config", + config="job.ini", + ), + } + scheduler = MemoryScheduler() + destinations = {"dest1": Destination(scheduler=scheduler)} + yield Config(applications=applications, destinations=destinations) + await scheduler.close() @pytest.fixture def fastapi_app( dbsession: AsyncSession, - scheduler: AbstractScheduler, + config: Config, ) -> FastAPI: """ Fixture for creating FastAPI app. @@ -103,14 +108,8 @@ def fastapi_app( """ application = get_app() application.dependency_overrides[get_db_session] = lambda: dbsession - application.dependency_overrides[get_scheduler] = lambda: scheduler + application.dependency_overrides[get_config] = lambda: config settings.secret = "testsecret" # noqa: S105 - settings.applications = { - "app1": AppSetting( - command="wc $config", - config="job.ini", - ), - } return application # noqa: WPS331 diff --git a/bartender/tests/schedulers/test_build.py b/bartender/tests/schedulers/test_build.py index 218f8e2..746612d 100644 --- a/bartender/tests/schedulers/test_build.py +++ b/bartender/tests/schedulers/test_build.py @@ -16,7 +16,7 @@ def test_single_typeless_scheduler() -> None: def test_single_unknown_scheduler() -> None: - config = {type: "unknown"} + config = {"type": "unknown"} with pytest.raises(ValueError): build(config) diff --git a/bartender/tests/test_config.py b/bartender/tests/test_config.py index 4ec1823..ecea43e 100644 --- a/bartender/tests/test_config.py +++ b/bartender/tests/test_config.py @@ -5,14 +5,13 @@ from yaml import safe_dump as yaml_dump from bartender._ssh_utils import SshConnectConfig -from bartender.config import Config, build_config, parse_config +from bartender.config import ApplicatonConfiguration, Config, build_config, parse_config from bartender.destinations import Destination from bartender.filesystems.local import LocalFileSystem from bartender.filesystems.sftp import SftpFileSystem from bartender.schedulers.memory import MemoryScheduler from bartender.schedulers.runner import SshCommandRunner from bartender.schedulers.slurm import SlurmScheduler -from bartender.settings import AppSetting @pytest.mark.anyio @@ -28,7 +27,9 @@ async def test_build_minimal(tmp_path: Path) -> None: result = build_config(file) expected = Config( - applications={"app1": AppSetting(command="echo", config="/etc/passwd")}, + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, destinations={ "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), }, @@ -77,7 +78,9 @@ async def test_parse_single_destination() -> None: result = parse_config(config) expected = Config( - applications={"app1": AppSetting(command="echo", config="/etc/passwd")}, + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, destinations={ "dest2": Destination( scheduler=SlurmScheduler( diff --git a/bartender/web/api/applications/submit.py b/bartender/web/api/applications/submit.py index 4cf629b..9b24e29 100644 --- a/bartender/web/api/applications/submit.py +++ b/bartender/web/api/applications/submit.py @@ -1,32 +1,85 @@ from pathlib import Path from string import Template +from bartender.config import ApplicatonConfiguration, Config from bartender.db.dao.job_dao import JobDAO -from bartender.schedulers.abstract import AbstractScheduler, JobDescription -from bartender.settings import AppSetting +from bartender.destinations import Destination +from bartender.schedulers.abstract import JobDescription +from bartender.settings import settings -# TODO submit function should be an adapter, -# which can submit job to one of the available schedulers -# based on job input, application, scheduler resources, phase of moon, etc. async def submit( external_job_id: int, job_dir: Path, - application: AppSetting, + application: str, job_dao: JobDAO, - scheduler: AbstractScheduler, + config: Config, ) -> None: """Submit job description to scheduler and store job id returned by scheduler in db. :param external_job_id: External job id. :param job_dir: Location where job input files are located. - :param application: Application that should be run. + :param application: Application name that should be run. :param job_dao: JobDAO object. - :param scheduler: Current job scheduler. + :param config: Config with applications and destinations. """ - command = Template(application.command).substitute(config=application.config) - description = JobDescription(job_dir=job_dir, command=command) - # TODO if scheduler has filesystem then perform upload - # of job dir and localize description - internal_job_id = await scheduler.submit(description) - await job_dao.update_internal_job_id(external_job_id, internal_job_id) + application_config = config.applications[application] + description = _build_description(job_dir, application_config) + + destination, destinations_name = pick_destination(job_dir, application, config) + + await _upload_input_files(description, destination) + + internal_job_id = await destination.scheduler.submit(description) + + await job_dao.update_internal_job_id( + external_job_id, + internal_job_id, + destinations_name, + ) + + +async def _upload_input_files( + description: JobDescription, + destination: Destination, +) -> None: + if destination.filesystem is not None: + localized_description = destination.filesystem.localize_description( + description, + settings.job_root_dir, + ) + await destination.filesystem.upload( + src=description, + target=localized_description, + ) + + +def _build_description( + job_dir: Path, + application_config: ApplicatonConfiguration, +) -> JobDescription: + command = Template(application_config.command).substitute( + config=application_config.config, + ) + return JobDescription(job_dir=job_dir, command=command) + + +def pick_destination( + job_dir: Path, + application_name: str, + config: Config, +) -> tuple[Destination, str]: + """Pick to which destination a job should be submitted. + + :param job_dir: Location where job input files are located. + :param application_name: Application name that should be run. + :param config: Config with applications and destinations. + :return: Destination where job should be submitted to. + """ + # TODO allow maintaner of service to provide Python function that + # returns the destination for this job + # for now the first destination in the configuration is picked. + destination_names = list(config.destinations.keys()) + destination_name = destination_names[0] + destination = config.destinations[destination_name] + return destination, destination_name diff --git a/bartender/web/api/applications/views.py b/bartender/web/api/applications/views.py index 6a4dc5e..952ea29 100644 --- a/bartender/web/api/applications/views.py +++ b/bartender/web/api/applications/views.py @@ -3,14 +3,12 @@ from starlette import status from starlette.background import BackgroundTask +from bartender.config import Config, get_config from bartender.db.dao.job_dao import JobDAO from bartender.db.models.user import User from bartender.filesystem import has_config_file from bartender.filesystem.assemble_job import assemble_job from bartender.filesystem.stage_job_input import stage_job_input -from bartender.schedulers.abstract import AbstractScheduler -from bartender.schedulers.dependencies import get_scheduler -from bartender.settings import settings from bartender.web.api.applications.submit import submit from bartender.web.users.manager import current_active_user, current_api_token @@ -18,13 +16,14 @@ @router.get("/", response_model=list[str]) -def list_applications() -> list[str]: +def list_applications(config: Config = Depends(get_config)) -> list[str]: """List application names. + :param config: Config with applications. :return: The list. """ - # TODO also return values or atleast the expected config file name for each app - return list(settings.applications.keys()) + # TODO also return values or at least the expected config file name for each app + return list(config.applications.keys()) @router.put( @@ -50,7 +49,7 @@ async def upload_job( # noqa: WPS211 upload: UploadFile = File(description="Archive with config file for application"), job_dao: JobDAO = Depends(), submitter: User = Depends(current_active_user), - scheduler: AbstractScheduler = Depends(get_scheduler), + config: Config = Depends(get_config), ) -> RedirectResponse: """ Creates job model in the database, stage archive locally and submit to scheduler. @@ -60,14 +59,14 @@ async def upload_job( # noqa: WPS211 :param request: request object. :param job_dao: JobDAO object. :param submitter: User who submitted job. - :param scheduler: Current job scheduler. + :param config: Configuration with applications and destinations. :raises IndexError: When job could not created inside database or when config file was not found. :raises KeyError: Application is invalid. :return: redirect response. """ - if application not in settings.applications: - valid = settings.applications.keys() + if application not in config.applications: + valid = config.applications.keys() raise KeyError(f"Invalid application. Valid applications: {valid}") job_id = await job_dao.create_job(upload.filename, application, submitter) if job_id is None: @@ -75,15 +74,15 @@ async def upload_job( # noqa: WPS211 job_dir = assemble_job(job_id, await current_api_token(submitter)) await stage_job_input(job_dir, upload) - has_config_file(application, job_dir) + has_config_file(config.applications[application], job_dir) task = BackgroundTask( submit, job_id, job_dir, - settings.applications[application], + application, job_dao, - scheduler, + config, ) url = request.url_for("retrieve_job", jobid=job_id) diff --git a/bartender/web/api/job/sync.py b/bartender/web/api/job/sync.py index 57ebc68..95e48c8 100644 --- a/bartender/web/api/job/sync.py +++ b/bartender/web/api/job/sync.py @@ -1,44 +1,126 @@ +from pathlib import Path +from typing import Optional + from bartender.db.dao.job_dao import JobDAO -from bartender.db.models.job_model import CompletedStates, Job -from bartender.schedulers.abstract import AbstractScheduler +from bartender.db.models.job_model import CompletedStates, Job, State +from bartender.destinations import Destination +from bartender.filesystems.abstract import AbstractFileSystem +from bartender.schedulers.abstract import JobDescription +from bartender.settings import settings -async def sync_state(job: Job, job_dao: JobDAO, scheduler: AbstractScheduler) -> None: +async def sync_state( + job: Job, + job_dao: JobDAO, + destination: Optional[Destination], +) -> None: """Sync state of job from scheduler to database. :param job: Job instance. :param job_dao: JobDAO object. - :param scheduler: Current job scheduler. + :param destination: Job destination used to submit job. """ - if job.state not in CompletedStates and job.internal_id is not None: + if ( # noqa: WPS337 + job.state not in CompletedStates + and job.internal_id is not None + and destination is not None + ): # TODO throttle getting state from scheduler as getting state could be expensive # could add column to Job to track when state was last fetched - state = await scheduler.state(job.internal_id) + state = await destination.scheduler.state(job.internal_id) + # TODO when scheduler says job is completed then download output files if job.state != state and job.id is not None: + await _download_job_files(job, destination.filesystem) await job_dao.update_job_state(job.id, state) job.state = state async def sync_states( jobs: list[Job], - scheduler: AbstractScheduler, + destinations: dict[str, Destination], job_dao: JobDAO, ) -> None: """Sync state of jobs from scheduler to database. :param jobs: Job instances. + :param destinations: Job destinations. :param job_dao: JobDAO object. - :param scheduler: Current job scheduler. """ jobs2sync = [ job for job in jobs - if job.state not in CompletedStates and job.internal_id is not None + if job.id is not None + and job.state not in CompletedStates + and job.internal_id is not None ] - states = await scheduler.states( - [job.internal_id for job in jobs2sync if job.internal_id is not None], - ) - for job, state in zip(jobs2sync, states): - if job.state != state and job.id is not None: + states = await _states_of_destinations(destinations, jobs2sync) + for job in jobs2sync: + await _store_updated_state(destinations, job_dao, states, job) + + +async def _states_of_destinations( + destinations: dict[str, Destination], + jobs2sync: list[Job], +) -> dict[int, State]: + states: dict[int, State] = {} + for destination_name, destination in destinations.items(): + dest_states = await _states_of_destination( + jobs2sync, + destination_name, + destination, + ) + states.update(dest_states) + return states + + +async def _store_updated_state( + destinations: dict[str, Destination], + job_dao: JobDAO, + states: dict[int, State], + job: Job, +) -> None: + if job.id is not None: + state = states[job.id] + if job.state != state and job.id is not None and job.destination is not None: + filesystem = destinations[job.destination].filesystem + await _download_job_files(job, filesystem) await job_dao.update_job_state(job.id, state) job.state = state + + +async def _states_of_destination( + jobs2sync: list[Job], + destination_name: str, + destination: Destination, +) -> dict[int, State]: + dest_states: dict[int, State] = {} + # List of External job id and internal job id pairs + job_ids: list[tuple[int, str]] = [ + (job.id, job.internal_id) + for job in jobs2sync + if job.id is not None + and job.internal_id is not None + and job.destination == destination_name + ] + if job_ids: + scheduler_states = await destination.scheduler.states( + [job_id[1] for job_id in job_ids], + ) + for index, job_id in enumerate(job_ids): + dest_states[job_id[0]] = scheduler_states[index] + return dest_states + + +async def _download_job_files( + job: Job, + filesystem: Optional[AbstractFileSystem], +) -> None: + if job.state in CompletedStates and filesystem is not None: + job_dir: Path = settings.job_root_dir / str(job.id) + # Command does not matter for downloading so use dummy command. + description = JobDescription(job_dir=job_dir, command="echo") + localized_description = filesystem.localize_description( + description, + settings.job_root_dir, + ) + await filesystem.download(localized_description, description) diff --git a/bartender/web/api/job/views.py b/bartender/web/api/job/views.py index 7ed63c3..afddd81 100644 --- a/bartender/web/api/job/views.py +++ b/bartender/web/api/job/views.py @@ -6,11 +6,10 @@ from sqlalchemy.exc import NoResultFound from starlette import status +from bartender.config import Config, get_config from bartender.db.dao.job_dao import JobDAO from bartender.db.models.job_model import Job from bartender.db.models.user import User -from bartender.schedulers.abstract import AbstractScheduler -from bartender.schedulers.dependencies import get_scheduler from bartender.settings import settings from bartender.web.api.job.schema import JobModelDTO from bartender.web.api.job.sync import sync_state, sync_states @@ -25,7 +24,7 @@ async def retrieve_jobs( offset: int = 0, job_dao: JobDAO = Depends(), user: User = Depends(current_active_user), - scheduler: AbstractScheduler = Depends(get_scheduler), + config: Config = Depends(get_config), ) -> List[Job]: """ Retrieve all jobs of user from the database. @@ -34,7 +33,7 @@ async def retrieve_jobs( :param offset: offset of jobs. :param job_dao: JobDAO object. :param user: Current active user. - :param scheduler: Current job scheduler. + :param config: Config with destinations. :return: stream of jobs. """ # TODO now list jobs that user submitted, @@ -42,7 +41,7 @@ async def retrieve_jobs( # or are shared with current user jobs = await job_dao.get_all_jobs(limit=limit, offset=offset, user=user) # get current state for each job from scheduler - await sync_states(jobs, scheduler, job_dao) + await sync_states(jobs, config.destinations, job_dao) return jobs @@ -51,7 +50,7 @@ async def retrieve_job( jobid: int, job_dao: JobDAO = Depends(), user: User = Depends(current_active_user), - scheduler: AbstractScheduler = Depends(get_scheduler), + config: Config = Depends(get_config), ) -> Job: """ Retrieve specific job from the database. @@ -59,7 +58,7 @@ async def retrieve_job( :param jobid: identifier of job instance. :param job_dao: JobDAO object. :param user: Current active user. - :param scheduler: Current job scheduler. + :param config: Config with destinations. :raises HTTPException: When job is not found or user is not allowed to see job. :return: job models. """ @@ -70,7 +69,9 @@ async def retrieve_job( # TODO When job has state==ok then include URL to applications result page # TODO When job has state==error then include URL to error page job = await job_dao.get_job(jobid=jobid, user=user) - await sync_state(job, job_dao, scheduler) + if job.destination is not None: + destination = config.destinations.get(job.destination) + await sync_state(job, job_dao, destination) return job except NoResultFound as exc: raise HTTPException( @@ -84,14 +85,14 @@ async def retrieve_job_stdout( jobid: int, job_dao: JobDAO = Depends(), user: User = Depends(current_active_user), - scheduler: AbstractScheduler = Depends(get_scheduler), + config: Config = Depends(get_config), ) -> FileResponse: """Retrieve stdout of a job. :param jobid: identifier of job instance. :param job_dao: JobDAO object. :param user: Current active user. - :param scheduler: Current job scheduler. + :param config: Config with destinations. :raises HTTPException: When job is not found or user is not allowed to see job. :return: stdout of job. """ @@ -99,7 +100,7 @@ async def retrieve_job_stdout( jobid=jobid, job_dao=job_dao, user=user, - scheduler=scheduler, + config=config, ) if job.state not in {"ok", "error"}: raise HTTPException( diff --git a/bartender/web/lifetime.py b/bartender/web/lifetime.py index 7238183..5e5ac37 100644 --- a/bartender/web/lifetime.py +++ b/bartender/web/lifetime.py @@ -1,12 +1,17 @@ +import logging +from pathlib import Path from typing import Awaitable, Callable from fastapi import FastAPI from bartender.config import build_config from bartender.db.session import make_engine, make_session_factory +from bartender.destinations import Destination from bartender.filesystem import setup_job_root_dir from bartender.settings import settings +logger = logging.getLogger(__name__) + def _setup_db(app: FastAPI) -> None: # pragma: no cover """ @@ -39,7 +44,7 @@ def register_startup_event( async def _startup() -> None: # noqa: WPS430 _setup_db(app) setup_job_root_dir() - _setup_schedulers(app) + _parse_config(app) return _startup @@ -57,23 +62,33 @@ def register_shutdown_event( @app.on_event("shutdown") async def _shutdown() -> None: # noqa: WPS430 await app.state.db_engine.dispose() - await _teardown_schedulers(app) + await _teardown_confg(app) return _shutdown -def _setup_schedulers(app: FastAPI) -> None: - """Create schedulers. +def _parse_config(app: FastAPI) -> None: + """Parse configuration and instantiate applications, schedulers and filesystems. + + Sets `app.state.config`. :param app: fastAPI application. """ - app.state.config = build_config(settings.config_filename) + try: + app.state.config = build_config(settings.config_filename) + except FileNotFoundError: + fn = settings.config_filename + logger.warn(f"Unable to find {fn} falling back to config-example.yaml") + app.state.config = build_config(Path("config-example.yaml")) -async def _teardown_schedulers(app: FastAPI) -> None: - """Teardown schedulers. +async def _teardown_confg(app: FastAPI) -> None: + """Teardown schedulers and file systems. :param app: fastAPI application. """ - for destination in app.state.config.destinations.values(): + destinations: dict[str, Destination] = app.state.config.destinations + for destination in destinations.values(): await destination.scheduler.close() + if destination.filesystem is not None: + destination.filesystem.close() diff --git a/config-example.yaml b/config-example.yaml new file mode 100644 index 0000000..67ca681 --- /dev/null +++ b/config-example.yaml @@ -0,0 +1,22 @@ +applications: + app1: + command: app1 $config + config: workflow.cfg +destinations: + local: + scheduler: + type: memory + slots: 1 + ## Example of running jobs by current user on snellius. + # remote: + # scheduler: + # type: slurm + # paritiion: thin + # runner: + # type: ssh + # hostname: snellius.surf.nl + # filesystem: + # type: sftp + # config: + # hostname: snellius.surf.nl + # entry: /scratch-shared/bartender/jobs From 8e258803189ee55cfedf3a1d87e42444860efff0 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Tue, 22 Nov 2022 09:57:57 +0100 Subject: [PATCH 06/36] Move stuff around --- bartender/config.py | 13 +++++++++++++ bartender/schedulers/dependencies.py | 17 ----------------- bartender/schedulers/slurm.py | 4 ++-- bartender/web/api/applications/submit.py | 15 ++------------- 4 files changed, 17 insertions(+), 32 deletions(-) delete mode 100644 bartender/schedulers/dependencies.py diff --git a/bartender/config.py b/bartender/config.py index 9748ac3..9f9ed67 100644 --- a/bartender/config.py +++ b/bartender/config.py @@ -52,6 +52,7 @@ from dataclasses import dataclass from pathlib import Path +from string import Template from typing import Any from fastapi import Request @@ -59,6 +60,7 @@ from bartender.destinations import Destination from bartender.destinations import build as build_destinations +from bartender.schedulers.abstract import JobDescription @dataclass @@ -72,6 +74,17 @@ class ApplicatonConfiguration: command: str config: str + def description(self, job_dir: Path) -> JobDescription: + """Construct job description for this application. + + :param job_dir: In which directory are the input files. + :return: A job description. + """ + command = Template(self.command).substitute( + config=self.config, + ) + return JobDescription(job_dir=job_dir, command=command) + @dataclass class Config: diff --git a/bartender/schedulers/dependencies.py b/bartender/schedulers/dependencies.py deleted file mode 100644 index 20ca591..0000000 --- a/bartender/schedulers/dependencies.py +++ /dev/null @@ -1,17 +0,0 @@ -from starlette.requests import Request - -from bartender.config import Config -from bartender.schedulers.abstract import AbstractScheduler - - -def get_scheduler(request: Request) -> AbstractScheduler: - """Retrieve job scheduler. - - :param request: current request. - :return: A scheduler. - """ - config: Config = request.app.config - destinations = config.destinations.values() - # TODO dont pick first scheduler - destination = list(destinations)[0] - return destination.scheduler diff --git a/bartender/schedulers/slurm.py b/bartender/schedulers/slurm.py index e7cc430..4c8ce22 100644 --- a/bartender/schedulers/slurm.py +++ b/bartender/schedulers/slurm.py @@ -3,7 +3,7 @@ from bartender.db.models.job_model import State from bartender.schedulers.abstract import AbstractScheduler, JobDescription -from bartender.schedulers.runner import CommandRunner +from bartender.schedulers.runner import CommandRunner, LocalCommandRunner def _map_slurm_state(slurm_state: str) -> State: @@ -34,7 +34,7 @@ class SlurmScheduler(AbstractScheduler): def __init__( self, - runner: CommandRunner, + runner: CommandRunner = LocalCommandRunner(), partition: Optional[str] = None, time: Optional[str] = None, extra_options: Optional[list[str]] = None, diff --git a/bartender/web/api/applications/submit.py b/bartender/web/api/applications/submit.py index 9b24e29..9282222 100644 --- a/bartender/web/api/applications/submit.py +++ b/bartender/web/api/applications/submit.py @@ -1,7 +1,6 @@ from pathlib import Path -from string import Template -from bartender.config import ApplicatonConfiguration, Config +from bartender.config import Config from bartender.db.dao.job_dao import JobDAO from bartender.destinations import Destination from bartender.schedulers.abstract import JobDescription @@ -24,7 +23,7 @@ async def submit( :param config: Config with applications and destinations. """ application_config = config.applications[application] - description = _build_description(job_dir, application_config) + description = application_config.description(job_dir) destination, destinations_name = pick_destination(job_dir, application, config) @@ -54,16 +53,6 @@ async def _upload_input_files( ) -def _build_description( - job_dir: Path, - application_config: ApplicatonConfiguration, -) -> JobDescription: - command = Template(application_config.command).substitute( - config=application_config.config, - ) - return JobDescription(job_dir=job_dir, command=command) - - def pick_destination( job_dir: Path, application_name: str, From ca05fc4f640dadbc0246d9c6cb9fb824aadbe827 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Tue, 22 Nov 2022 13:36:58 +0100 Subject: [PATCH 07/36] Move job root dir to config + default for destination.filesystem --- bartender/config.py | 14 ++++++++----- bartender/destinations.py | 6 +++--- bartender/filesystem/__init__.py | 10 --------- bartender/filesystem/assemble_job.py | 7 +++---- bartender/settings.py | 4 ---- bartender/tests/conftest.py | 24 ++++++++++------------ bartender/tests/test_filesystem.py | 2 +- bartender/web/api/applications/submit.py | 21 +++++++++---------- bartender/web/api/applications/views.py | 6 +++++- bartender/web/api/job/sync.py | 26 ++++++++++++++---------- bartender/web/api/job/views.py | 9 ++++---- bartender/web/lifetime.py | 7 ++++--- 12 files changed, 65 insertions(+), 71 deletions(-) diff --git a/bartender/config.py b/bartender/config.py index 9f9ed67..d2eb9fc 100644 --- a/bartender/config.py +++ b/bartender/config.py @@ -26,11 +26,11 @@ extra_options: - --nodes 1 runner: - type: ssh # or local - hostname: localhost - port: 10022 - username: xenon - password: javagat + type: ssh # or local + hostname: localhost + port: 10022 + username: xenon + password: javagat local: scheduler: type: memory @@ -53,6 +53,7 @@ from dataclasses import dataclass from pathlib import Path from string import Template +from tempfile import gettempdir from typing import Any from fastapi import Request @@ -62,6 +63,8 @@ from bartender.destinations import build as build_destinations from bartender.schedulers.abstract import JobDescription +TEMP_DIR = Path(gettempdir()) + @dataclass class ApplicatonConfiguration: @@ -96,6 +99,7 @@ class Config: applications: dict[str, ApplicatonConfiguration] destinations: dict[str, Destination] + job_root_dir: Path = TEMP_DIR / "jobs" def build_config(config_filename: Path) -> Config: diff --git a/bartender/destinations.py b/bartender/destinations.py index e951f69..e3fdfad 100644 --- a/bartender/destinations.py +++ b/bartender/destinations.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Any, Optional +from typing import Any from bartender.filesystems.abstract import AbstractFileSystem from bartender.filesystems.build import build as build_filesystem @@ -14,7 +14,7 @@ class Destination: """A destination is a combination of a scheduler and filesystem.""" scheduler: AbstractScheduler - filesystem: Optional[AbstractFileSystem] = None + filesystem: AbstractFileSystem = LocalFileSystem() def build(config: Any) -> dict[str, Destination]: @@ -25,7 +25,7 @@ def build(config: Any) -> dict[str, Destination]: """ if not config: return { - "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), + "": Destination(scheduler=MemoryScheduler()), } destinations = {} diff --git a/bartender/filesystem/__init__.py b/bartender/filesystem/__init__.py index 819f4b8..17375d0 100644 --- a/bartender/filesystem/__init__.py +++ b/bartender/filesystem/__init__.py @@ -4,16 +4,6 @@ from typing import Literal from bartender.config import ApplicatonConfiguration -from bartender.settings import settings - - -def setup_job_root_dir() -> None: - """Make sure job root dir exists. - - Job root dir is retrieved from settings. - """ - job_root_dir = settings.job_root_dir - job_root_dir.mkdir(exist_ok=True) def has_config_file( diff --git a/bartender/filesystem/assemble_job.py b/bartender/filesystem/assemble_job.py index 8913e1b..5ef07d0 100644 --- a/bartender/filesystem/assemble_job.py +++ b/bartender/filesystem/assemble_job.py @@ -5,11 +5,9 @@ from fastapi import HTTPException from starlette import status -from bartender.settings import settings - # TODO: Make this async -def assemble_job(job_id: int, job_token: str) -> Path: +def assemble_job(job_id: int, job_token: str, job_root_dir: Path) -> Path: """ Assembly the job. @@ -18,10 +16,11 @@ def assemble_job(job_id: int, job_token: str) -> Path: :param job_id: id of the job. :param job_token: Token that can be used to talk to bartender service. + :param job_root_dir: Root directory for all jobs. :raises HTTPException: When job directory could not be made. :return: Directory of job. """ - job_dir: Path = settings.job_root_dir / str(job_id) + job_dir: Path = job_root_dir / str(job_id) try: job_dir.mkdir() diff --git a/bartender/settings.py b/bartender/settings.py index 28c5383..6f3968e 100644 --- a/bartender/settings.py +++ b/bartender/settings.py @@ -34,10 +34,6 @@ class Settings(BaseSettings): # Enable uvicorn reloading reload: bool = False - # file system settings - job_root_dir: Path = TEMP_DIR / "jobs" - # TODO move job_root_dir to config.yaml - # Current environment environment: str = "dev" diff --git a/bartender/tests/conftest.py b/bartender/tests/conftest.py index 2924a44..0c8a18c 100644 --- a/bartender/tests/conftest.py +++ b/bartender/tests/conftest.py @@ -83,7 +83,12 @@ async def dbsession( @pytest.fixture -async def config() -> AsyncGenerator[Config, None]: +def job_root_dir(tmp_path: Path) -> Path: + return tmp_path + + +@pytest.fixture +async def config(job_root_dir: Path) -> AsyncGenerator[Config, None]: applications = { "app1": ApplicatonConfiguration( command="wc $config", @@ -92,7 +97,11 @@ async def config() -> AsyncGenerator[Config, None]: } scheduler = MemoryScheduler() destinations = {"dest1": Destination(scheduler=scheduler)} - yield Config(applications=applications, destinations=destinations) + yield Config( + applications=applications, + destinations=destinations, + job_root_dir=job_root_dir, + ) await scheduler.close() @@ -128,17 +137,6 @@ async def client( yield ac -@pytest.fixture -def job_root_dir(tmp_path: Path) -> Path: - """ - Fixture that overrides settings.job_root_dir with temporary test directory. - - :return: Path of job root dir. - """ - settings.job_root_dir = tmp_path - return settings.job_root_dir - - @pytest.fixture async def current_user_token(fastapi_app: FastAPI, client: AsyncClient) -> str: """Registers dummy user and returns its auth token. diff --git a/bartender/tests/test_filesystem.py b/bartender/tests/test_filesystem.py index 2d4b646..f72328f 100644 --- a/bartender/tests/test_filesystem.py +++ b/bartender/tests/test_filesystem.py @@ -8,7 +8,7 @@ def test_assemble_job(job_root_dir: Path) -> None: job_id = 1 token = "mytoken" # noqa: S105 - assemble_job(job_id, token) + assemble_job(job_id, token, job_root_dir) job_dir = job_root_dir / str(job_id) meta_file = job_dir / "meta" diff --git a/bartender/web/api/applications/submit.py b/bartender/web/api/applications/submit.py index 9282222..22f3476 100644 --- a/bartender/web/api/applications/submit.py +++ b/bartender/web/api/applications/submit.py @@ -4,7 +4,6 @@ from bartender.db.dao.job_dao import JobDAO from bartender.destinations import Destination from bartender.schedulers.abstract import JobDescription -from bartender.settings import settings async def submit( @@ -27,7 +26,7 @@ async def submit( destination, destinations_name = pick_destination(job_dir, application, config) - await _upload_input_files(description, destination) + await _upload_input_files(description, destination, config.job_root_dir) internal_job_id = await destination.scheduler.submit(description) @@ -41,16 +40,16 @@ async def submit( async def _upload_input_files( description: JobDescription, destination: Destination, + job_root_dir: Path, ) -> None: - if destination.filesystem is not None: - localized_description = destination.filesystem.localize_description( - description, - settings.job_root_dir, - ) - await destination.filesystem.upload( - src=description, - target=localized_description, - ) + localized_description = destination.filesystem.localize_description( + description, + job_root_dir, + ) + await destination.filesystem.upload( + src=description, + target=localized_description, + ) def pick_destination( diff --git a/bartender/web/api/applications/views.py b/bartender/web/api/applications/views.py index 952ea29..23986d2 100644 --- a/bartender/web/api/applications/views.py +++ b/bartender/web/api/applications/views.py @@ -72,7 +72,11 @@ async def upload_job( # noqa: WPS211 if job_id is None: raise IndexError("Failed to create database entry for job") - job_dir = assemble_job(job_id, await current_api_token(submitter)) + job_dir = assemble_job( + job_id, + await current_api_token(submitter), + config.job_root_dir, + ) await stage_job_input(job_dir, upload) has_config_file(config.applications[application], job_dir) diff --git a/bartender/web/api/job/sync.py b/bartender/web/api/job/sync.py index 95e48c8..76843b5 100644 --- a/bartender/web/api/job/sync.py +++ b/bartender/web/api/job/sync.py @@ -1,24 +1,24 @@ from pathlib import Path -from typing import Optional from bartender.db.dao.job_dao import JobDAO from bartender.db.models.job_model import CompletedStates, Job, State from bartender.destinations import Destination from bartender.filesystems.abstract import AbstractFileSystem from bartender.schedulers.abstract import JobDescription -from bartender.settings import settings async def sync_state( job: Job, job_dao: JobDAO, - destination: Optional[Destination], + destination: Destination, + job_root_dir: Path, ) -> None: """Sync state of job from scheduler to database. :param job: Job instance. :param job_dao: JobDAO object. :param destination: Job destination used to submit job. + :param job_root_dir: Directory where all jobs can be found. """ if ( # noqa: WPS337 job.state not in CompletedStates @@ -30,7 +30,7 @@ async def sync_state( state = await destination.scheduler.state(job.internal_id) # TODO when scheduler says job is completed then download output files if job.state != state and job.id is not None: - await _download_job_files(job, destination.filesystem) + await _download_job_files(job, destination.filesystem, job_root_dir) await job_dao.update_job_state(job.id, state) job.state = state @@ -39,12 +39,14 @@ async def sync_states( jobs: list[Job], destinations: dict[str, Destination], job_dao: JobDAO, + job_root_dir: Path, ) -> None: """Sync state of jobs from scheduler to database. :param jobs: Job instances. :param destinations: Job destinations. :param job_dao: JobDAO object. + :param job_root_dir: Directory where all jobs can be found. """ jobs2sync = [ job @@ -55,7 +57,7 @@ async def sync_states( ] states = await _states_of_destinations(destinations, jobs2sync) for job in jobs2sync: - await _store_updated_state(destinations, job_dao, states, job) + await _store_updated_state(destinations, job_dao, states, job, job_root_dir) async def _states_of_destinations( @@ -78,12 +80,13 @@ async def _store_updated_state( job_dao: JobDAO, states: dict[int, State], job: Job, + job_root_dir: Path, ) -> None: if job.id is not None: state = states[job.id] - if job.state != state and job.id is not None and job.destination is not None: + if job.state != state and job.destination is not None: filesystem = destinations[job.destination].filesystem - await _download_job_files(job, filesystem) + await _download_job_files(job, filesystem, job_root_dir) await job_dao.update_job_state(job.id, state) job.state = state @@ -113,14 +116,15 @@ async def _states_of_destination( async def _download_job_files( job: Job, - filesystem: Optional[AbstractFileSystem], + filesystem: AbstractFileSystem, + job_root_dir: Path, ) -> None: - if job.state in CompletedStates and filesystem is not None: - job_dir: Path = settings.job_root_dir / str(job.id) + if job.state in CompletedStates: + job_dir: Path = job_root_dir / str(job.id) # Command does not matter for downloading so use dummy command. description = JobDescription(job_dir=job_dir, command="echo") localized_description = filesystem.localize_description( description, - settings.job_root_dir, + job_root_dir, ) await filesystem.download(localized_description, description) diff --git a/bartender/web/api/job/views.py b/bartender/web/api/job/views.py index afddd81..c8c19f4 100644 --- a/bartender/web/api/job/views.py +++ b/bartender/web/api/job/views.py @@ -10,7 +10,6 @@ from bartender.db.dao.job_dao import JobDAO from bartender.db.models.job_model import Job from bartender.db.models.user import User -from bartender.settings import settings from bartender.web.api.job.schema import JobModelDTO from bartender.web.api.job.sync import sync_state, sync_states from bartender.web.users.manager import current_active_user @@ -41,7 +40,7 @@ async def retrieve_jobs( # or are shared with current user jobs = await job_dao.get_all_jobs(limit=limit, offset=offset, user=user) # get current state for each job from scheduler - await sync_states(jobs, config.destinations, job_dao) + await sync_states(jobs, config.destinations, job_dao, config.job_root_dir) return jobs @@ -70,8 +69,8 @@ async def retrieve_job( # TODO When job has state==error then include URL to error page job = await job_dao.get_job(jobid=jobid, user=user) if job.destination is not None: - destination = config.destinations.get(job.destination) - await sync_state(job, job_dao, destination) + destination = config.destinations[job.destination] + await sync_state(job, job_dao, destination, config.job_root_dir) return job except NoResultFound as exc: raise HTTPException( @@ -107,5 +106,5 @@ async def retrieve_job_stdout( status_code=status.HTTP_425_TOO_EARLY, detail="Stdout not ready. Job has not completed.", ) - stdout: Path = settings.job_root_dir / str(jobid) / "stdout.txt" + stdout: Path = config.job_root_dir / str(jobid) / "stdout.txt" return FileResponse(stdout) diff --git a/bartender/web/lifetime.py b/bartender/web/lifetime.py index 5e5ac37..ab6d747 100644 --- a/bartender/web/lifetime.py +++ b/bartender/web/lifetime.py @@ -7,7 +7,6 @@ from bartender.config import build_config from bartender.db.session import make_engine, make_session_factory from bartender.destinations import Destination -from bartender.filesystem import setup_job_root_dir from bartender.settings import settings logger = logging.getLogger(__name__) @@ -43,7 +42,6 @@ def register_startup_event( @app.on_event("startup") async def _startup() -> None: # noqa: WPS430 _setup_db(app) - setup_job_root_dir() _parse_config(app) return _startup @@ -75,7 +73,10 @@ def _parse_config(app: FastAPI) -> None: :param app: fastAPI application. """ try: - app.state.config = build_config(settings.config_filename) + config = build_config(settings.config_filename) + # Make sure job root dir exists. + config.job_root_dir.mkdir(exist_ok=True) + app.state.config = config except FileNotFoundError: fn = settings.config_filename logger.warn(f"Unable to find {fn} falling back to config-example.yaml") From 5e053c1bb804c7405445bf9814da3e5d15dcdc20 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Tue, 22 Nov 2022 14:26:54 +0100 Subject: [PATCH 08/36] Standardize ssh config naming --- bartender/config.py | 12 +++---- bartender/filesystems/build.py | 4 +-- bartender/filesystems/sftp.py | 14 ++++---- bartender/schedulers/build.py | 34 +++--------------- bartender/schedulers/slurm.py | 21 +++++++---- bartender/tests/filesystems/test_build.py | 6 ++-- bartender/tests/schedulers/test_build.py | 44 ++++++----------------- bartender/tests/schedulers/test_slurm.py | 15 ++++---- bartender/tests/test_config.py | 17 ++++----- bartender/tests/test_destinations.py | 14 ++++---- config-example.yaml | 40 +++++++++++++++------ 11 files changed, 96 insertions(+), 125 deletions(-) diff --git a/bartender/config.py b/bartender/config.py index d2eb9fc..d960cfa 100644 --- a/bartender/config.py +++ b/bartender/config.py @@ -14,10 +14,11 @@ cluster1: filesystem: &cluster1fs type: sftp - hostname: localhost - port: 10022 - username: xenon - password: javagat + ssh_config: + hostname: localhost + port: 10022 + username: xenon + password: javagat entry: /home/xenon scheduler: &cluster1sched type: slurm @@ -25,8 +26,7 @@ time: '60' # max time is 60 minutes extra_options: - --nodes 1 - runner: - type: ssh # or local + ssh_config: hostname: localhost port: 10022 username: xenon diff --git a/bartender/filesystems/build.py b/bartender/filesystems/build.py index 2be70a7..9a69367 100644 --- a/bartender/filesystems/build.py +++ b/bartender/filesystems/build.py @@ -22,9 +22,9 @@ def build(config: Any) -> AbstractFileSystem: if config["type"] == "local": return LocalFileSystem() if config["type"] == "sftp": - if "config" not in config: + if "ssh_config" not in config: raise KeyError("Sftp file system without SSH connection configuration.") - ssh_config = SshConnectConfig(**config["config"]) + ssh_config = SshConnectConfig(**config["ssh_config"]) entry_config = config.get("entry", "/") entry = Path(entry_config) return SftpFileSystem(ssh_config, entry) diff --git a/bartender/filesystems/sftp.py b/bartender/filesystems/sftp.py index 447f565..3db9823 100644 --- a/bartender/filesystems/sftp.py +++ b/bartender/filesystems/sftp.py @@ -13,16 +13,16 @@ class SftpFileSystem(AbstractFileSystem): def __init__( self, - config: SshConnectConfig, + ssh_config: SshConnectConfig, entry: Path = Path("/"), ): """Constructor. - :param config: SSH connection configuration. + :param ssh_config: SSH connection configuration. :param entry: The entry directory. Used to localize description. """ self.entry = entry - self.config = config + self.ssh_config = ssh_config self.conn: Optional[SSHClientConnection] = None def localize_description( @@ -53,7 +53,7 @@ async def upload(self, src: JobDescription, target: JobDescription) -> None: :param target: Remote directory to copy to. """ if self.conn is None: - self.conn = await ssh_connect(self.config) + self.conn = await ssh_connect(self.ssh_config) async with self.conn.start_sftp_client() as sftp: localpaths = [str(src.job_dir)] remotepath = str(target.job_dir) @@ -66,7 +66,7 @@ async def download(self, src: JobDescription, target: JobDescription) -> None: :param target: Local directory to copy to. """ if self.conn is None: - self.conn = await ssh_connect(self.config) + self.conn = await ssh_connect(self.ssh_config) async with self.conn.start_sftp_client() as sftp: # target.job_dir.parent is used # so /remote/jobid/output becomes /local/jobid/output, @@ -87,8 +87,8 @@ def __eq__(self, other: object) -> bool: return ( isinstance(other, SftpFileSystem) and str(self.entry) == str(other.entry) - and self.config == other.config + and self.ssh_config == other.ssh_config ) def __repr__(self) -> str: - return f"SftpFileSystem(config={self.config}, entry={self.entry})" + return f"SftpFileSystem(ssh_config={self.ssh_config}, entry={self.entry})" diff --git a/bartender/schedulers/build.py b/bartender/schedulers/build.py index d0e8c87..b67eb26 100644 --- a/bartender/schedulers/build.py +++ b/bartender/schedulers/build.py @@ -3,11 +3,6 @@ from bartender._ssh_utils import SshConnectConfig from bartender.schedulers.abstract import AbstractScheduler from bartender.schedulers.memory import MemoryScheduler -from bartender.schedulers.runner import ( - CommandRunner, - LocalCommandRunner, - SshCommandRunner, -) from bartender.schedulers.slurm import SlurmScheduler @@ -41,28 +36,9 @@ def _build_slurm_scheduler(config: Any) -> SlurmScheduler: slurm_config = { sched_key: sched_value for sched_key, sched_value in config.items() - if sched_key not in {"type", "runner"} + if sched_key not in {"type", "ssh_config"} } - runner: CommandRunner = LocalCommandRunner() - runner_config = config.get("runner") - if runner_config is not None: - runner = _build_runner(runner_config) - return SlurmScheduler(runner=runner, **slurm_config) - - -def _build_runner(runner_config: Any) -> CommandRunner: - runner_type = runner_config.get("type") - if runner_type is None: - raise ValueError("Runner without type") - if runner_type == "ssh": - if "hostname" not in runner_config: - raise ValueError("Ssh runner without hostname") - runner_config = { - runner_key: runner_value - for runner_key, runner_value in runner_config.items() - if runner_key != "type" - } - return SshCommandRunner(config=SshConnectConfig(**runner_config)) - raise ValueError( - f"Runner with type {runner_type} is unknown", - ) + ssh_config = config.get("ssh_config") + if ssh_config is not None: + ssh_config = SshConnectConfig(**ssh_config) + return SlurmScheduler(ssh_config=ssh_config, **slurm_config) diff --git a/bartender/schedulers/slurm.py b/bartender/schedulers/slurm.py index 4c8ce22..d0e3491 100644 --- a/bartender/schedulers/slurm.py +++ b/bartender/schedulers/slurm.py @@ -1,9 +1,14 @@ from textwrap import dedent from typing import Optional +from bartender._ssh_utils import SshConnectConfig from bartender.db.models.job_model import State from bartender.schedulers.abstract import AbstractScheduler, JobDescription -from bartender.schedulers.runner import CommandRunner, LocalCommandRunner +from bartender.schedulers.runner import ( + CommandRunner, + LocalCommandRunner, + SshCommandRunner, +) def _map_slurm_state(slurm_state: str) -> State: @@ -34,21 +39,25 @@ class SlurmScheduler(AbstractScheduler): def __init__( self, - runner: CommandRunner = LocalCommandRunner(), + ssh_config: Optional[SshConnectConfig] = None, partition: Optional[str] = None, time: Optional[str] = None, extra_options: Optional[list[str]] = None, ): """Constructor. - :param runner: Runner for running commands. Can be local or ssh. + :param ssh_config: SSH connection configuration. + When set will call SLURM commands on remote system via SSH connection. + When not set will call SLURM commands on local system. :param partition: Partition in which all jobs should be submitted. :param time: Limit on the total run time of the job. :param extra_options: Escape hatch to add extra options to job script. The string `#SBATCH {extra_options[0]}` will be appended to job script. """ - # TODO which option should be set to per scheduler or per job description? - self.runner = runner + self.runner: CommandRunner = LocalCommandRunner() + self.ssh_config = ssh_config + if ssh_config is not None: + self.runner = SshCommandRunner(ssh_config) self.partition = partition self.time = time if extra_options is None: @@ -121,7 +130,7 @@ def __eq__(self, other: object) -> bool: def __repr__(self) -> str: return dedent( f"""\ - SlurmScheduler(runner={self.runner}, partition={self.partition}, + SlurmScheduler(ssh_config={self.ssh_config}, partition={self.partition}, time={self.time}, extra_options={self.extra_options} )""", ) diff --git a/bartender/tests/filesystems/test_build.py b/bartender/tests/filesystems/test_build.py index ac7221b..472005c 100644 --- a/bartender/tests/filesystems/test_build.py +++ b/bartender/tests/filesystems/test_build.py @@ -40,7 +40,7 @@ def test_sftp_withoutconfig() -> None: def test_sftp_simplest() -> None: - config = {"type": "sftp", "config": {"hostname": "localhost"}} + config = {"type": "sftp", "ssh_config": {"hostname": "localhost"}} result = build(config) expected = SftpFileSystem(SshConnectConfig(hostname="localhost")) @@ -50,7 +50,7 @@ def test_sftp_simplest() -> None: def test_sftp_entry() -> None: config = { "type": "sftp", - "config": {"hostname": "localhost"}, + "ssh_config": {"hostname": "localhost"}, "entry": "/scratch/jobs", } result = build(config) @@ -65,7 +65,7 @@ def test_sftp_entry() -> None: def test_sftp_verbosist() -> None: config = { "type": "sftp", - "config": { + "ssh_config": { "hostname": "localhost", "port": 2222, "username": "someone", diff --git a/bartender/tests/schedulers/test_build.py b/bartender/tests/schedulers/test_build.py index 746612d..ddb1b33 100644 --- a/bartender/tests/schedulers/test_build.py +++ b/bartender/tests/schedulers/test_build.py @@ -5,7 +5,6 @@ from bartender._ssh_utils import SshConnectConfig from bartender.schedulers.build import build from bartender.schedulers.memory import MemoryScheduler -from bartender.schedulers.runner import LocalCommandRunner, SshCommandRunner from bartender.schedulers.slurm import SlurmScheduler @@ -44,7 +43,7 @@ async def test_single_localsimplist_slurm_scheduler() -> None: config = {"type": "slurm"} result = build(config) - expected = SlurmScheduler(LocalCommandRunner()) + expected = SlurmScheduler() assert result == expected @@ -59,7 +58,6 @@ async def test_single_localcustom_slurm_scheduler() -> None: result = build(config) expected = SlurmScheduler( - runner=LocalCommandRunner(), partition="mypartition", time="60", extra_options=["--nodes 1"], @@ -67,24 +65,10 @@ async def test_single_localcustom_slurm_scheduler() -> None: assert result == expected -@pytest.mark.anyio -async def test_single_typelessrunner_slurm_scheduler() -> None: - config = {"type": "slurm", "runner": {}} - with pytest.raises(ValueError): - build(config) - - -@pytest.mark.anyio -async def test_single_unknownrunner_slurm_scheduler() -> None: - config = {"type": "slurm", "runner": {"type": "unknown"}} - with pytest.raises(ValueError): - build(config) - - @pytest.mark.anyio async def test_single_withouthost_slurm_scheduler() -> None: - config = {"type": "slurm", "runner": {"type": "ssh"}} - with pytest.raises(ValueError): + config = {"type": "slurm", "ssh_config": {}} + with pytest.raises(TypeError): build(config) @@ -92,16 +76,13 @@ async def test_single_withouthost_slurm_scheduler() -> None: async def test_single_sshsimplist_slurm_scheduler() -> None: config = { "type": "slurm", - "runner": { - "type": "ssh", + "ssh_config": { "hostname": "localhost", }, } result = build(config) - expected = SlurmScheduler( - SshCommandRunner(config=SshConnectConfig(hostname="localhost")), - ) + expected = SlurmScheduler(ssh_config=SshConnectConfig(hostname="localhost")) assert result == expected @@ -109,8 +90,7 @@ async def test_single_sshsimplist_slurm_scheduler() -> None: async def test_single_sshcustom_slurm_scheduler() -> None: config = { "type": "slurm", - "runner": { - "type": "ssh", + "ssh_config": { "hostname": "localhost", "port": 10022, "username": "xenon", @@ -120,13 +100,11 @@ async def test_single_sshcustom_slurm_scheduler() -> None: result = build(config) expected = SlurmScheduler( - SshCommandRunner( - config=SshConnectConfig( # noqa: S106 - hostname="localhost", - port=10022, - username="xenon", - password="javagat", - ), + ssh_config=SshConnectConfig( # noqa: S106 + hostname="localhost", + port=10022, + username="xenon", + password="javagat", ), ) assert result == expected diff --git a/bartender/tests/schedulers/test_slurm.py b/bartender/tests/schedulers/test_slurm.py index 7e9bf83..4fcfa0f 100644 --- a/bartender/tests/schedulers/test_slurm.py +++ b/bartender/tests/schedulers/test_slurm.py @@ -33,12 +33,9 @@ def get_config(self) -> SshConnectConfig: password=password, ) - def get_runner(self) -> SshCommandRunner: - return SshCommandRunner(self.get_config()) - def get_filesystem(self) -> SftpFileSystem: home_dir = Path("/home/xenon") - return SftpFileSystem(entry=home_dir, config=self.get_config()) + return SftpFileSystem(entry=home_dir, ssh_config=self.get_config()) def start(self) -> "SlurmContainer": super().start() @@ -46,7 +43,7 @@ def start(self) -> "SlurmContainer": return self async def _ping(self) -> None: - with self.get_runner() as conn: + with SshCommandRunner(self.get_config()) as conn: await conn.run("echo", []) @wait_container_is_ready(ConnectionLost) @@ -71,8 +68,8 @@ async def test_ok_running_job_with_input_and_output_file( ) -> None: job_dir = tmp_path try: - client = slurm_server.get_runner() - scheduler = SlurmScheduler(runner=client) + ssh_config = slurm_server.get_config() + scheduler = SlurmScheduler(ssh_config=ssh_config) (job_dir / "input").write_text("Lorem ipsum") description = JobDescription( command="echo -n hello && wc input > output", @@ -119,8 +116,8 @@ async def test_ok_running_job_without_iofiles( ) -> None: job_dir = tmp_path try: - client = slurm_server.get_runner() - scheduler = SlurmScheduler(runner=client) + ssh_config = slurm_server.get_config() + scheduler = SlurmScheduler(ssh_config=ssh_config) description = JobDescription(command="echo -n hello", job_dir=str(job_dir)) fs = slurm_server.get_filesystem() localized_description = fs.localize_description(description, job_dir.parent) diff --git a/bartender/tests/test_config.py b/bartender/tests/test_config.py index ecea43e..4c01a6c 100644 --- a/bartender/tests/test_config.py +++ b/bartender/tests/test_config.py @@ -7,10 +7,8 @@ from bartender._ssh_utils import SshConnectConfig from bartender.config import ApplicatonConfiguration, Config, build_config, parse_config from bartender.destinations import Destination -from bartender.filesystems.local import LocalFileSystem from bartender.filesystems.sftp import SftpFileSystem from bartender.schedulers.memory import MemoryScheduler -from bartender.schedulers.runner import SshCommandRunner from bartender.schedulers.slurm import SlurmScheduler @@ -31,7 +29,7 @@ async def test_build_minimal(tmp_path: Path) -> None: "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), }, destinations={ - "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), + "": Destination(scheduler=MemoryScheduler()), }, ) assert result == expected @@ -59,14 +57,13 @@ async def test_parse_single_destination() -> None: "scheduler": { "type": "slurm", "partition": "mypartition", - "runner": { - "type": "ssh", + "ssh_config": { "hostname": "localhost", }, }, "filesystem": { "type": "sftp", - "config": { + "ssh_config": { "hostname": "localhost", }, "entry": "/scratch/jobs", @@ -84,15 +81,13 @@ async def test_parse_single_destination() -> None: destinations={ "dest2": Destination( scheduler=SlurmScheduler( - runner=SshCommandRunner( - config=SshConnectConfig( - hostname="localhost", - ), + ssh_config=SshConnectConfig( + hostname="localhost", ), partition="mypartition", ), filesystem=SftpFileSystem( - config=SshConnectConfig( + ssh_config=SshConnectConfig( hostname="localhost", ), entry=Path("/scratch/jobs"), diff --git a/bartender/tests/test_destinations.py b/bartender/tests/test_destinations.py index 851ad00..5619b9e 100644 --- a/bartender/tests/test_destinations.py +++ b/bartender/tests/test_destinations.py @@ -8,7 +8,6 @@ from bartender.filesystems.local import LocalFileSystem from bartender.filesystems.sftp import SftpFileSystem from bartender.schedulers.memory import MemoryScheduler -from bartender.schedulers.runner import SshCommandRunner from bartender.schedulers.slurm import SlurmScheduler @@ -36,7 +35,7 @@ async def test_single_memory() -> None: result = build(config) expected = { - "dest1": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), + "dest1": Destination(scheduler=MemoryScheduler()), } assert result == expected @@ -49,7 +48,7 @@ async def test_single_memory_local() -> None: result = build(config) expected = { - "dest1": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), + "dest1": Destination(scheduler=MemoryScheduler()), } assert result == expected @@ -62,14 +61,13 @@ async def test_double() -> None: "scheduler": { "type": "slurm", "partition": "mypartition", - "runner": { - "type": "ssh", + "ssh_config": { "hostname": "localhost", }, }, "filesystem": { "type": "sftp", - "config": { + "ssh_config": { "hostname": "localhost", }, "entry": "/scratch/jobs", @@ -85,11 +83,11 @@ async def test_double() -> None: ), "dest2": Destination( scheduler=SlurmScheduler( - runner=SshCommandRunner(config=SshConnectConfig(hostname="localhost")), + ssh_config=SshConnectConfig(hostname="localhost"), partition="mypartition", ), filesystem=SftpFileSystem( - config=SshConnectConfig(hostname="localhost"), + ssh_config=SshConnectConfig(hostname="localhost"), entry=Path("/scratch/jobs"), ), ), diff --git a/config-example.yaml b/config-example.yaml index 67ca681..2f0ab8f 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -9,14 +9,32 @@ destinations: slots: 1 ## Example of running jobs by current user on snellius. # remote: - # scheduler: - # type: slurm - # paritiion: thin - # runner: - # type: ssh - # hostname: snellius.surf.nl - # filesystem: - # type: sftp - # config: - # hostname: snellius.surf.nl - # entry: /scratch-shared/bartender/jobs + # scheduler: + # type: slurm + # partition: thin + # ssh_config: + # hostname: snellius.surf.nl + # filesystem: + # type: sftp + # ssh_config: + # hostname: snellius.surf.nl + # entry: /scratch-shared/bartender/jobs + ## Example of running jobs on a slurm Docker container. + ## Start a container with `docker run --detach --publish 10022:22 xenonmiddleware/slurm:20` + # slurmcontainer: + # scheduler: + # type: slurm + # partition: mypartition + # ssh_config: + # port: 10022 + # hostname: localhost + # username: xenon + # password: javagat + # filesystem: + # type: sftp + # ssh_config: + # port: 10022 + # hostname: localhost + # username: xenon + # password: javagat + # entry: /home/xenon From 9c4a741613171a17ad095e5f2d838e75a1a43a75 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Tue, 22 Nov 2022 16:17:40 +0100 Subject: [PATCH 09/36] Add destination picker to config --- bartender/config.py | 50 ++++++++++++++++++++++-- bartender/db/dao/job_dao.py | 2 +- bartender/destinations.py | 4 +- bartender/filesystems/build.py | 16 +++++--- bartender/tests/test_config.py | 24 ++++++++++++ bartender/web/api/applications/submit.py | 27 +++---------- config-example.yaml | 6 +++ 7 files changed, 95 insertions(+), 34 deletions(-) diff --git a/bartender/config.py b/bartender/config.py index d960cfa..8d4b16c 100644 --- a/bartender/config.py +++ b/bartender/config.py @@ -54,7 +54,7 @@ from pathlib import Path from string import Template from tempfile import gettempdir -from typing import Any +from typing import Any, Callable, Optional from fastapi import Request from yaml import safe_load as load_yaml @@ -63,7 +63,7 @@ from bartender.destinations import build as build_destinations from bartender.schedulers.abstract import JobDescription -TEMP_DIR = Path(gettempdir()) +DEFAULT_JOB_ROOT_DIR = Path(gettempdir()) / "jobs" @dataclass @@ -89,6 +89,46 @@ def description(self, job_dir: Path) -> JobDescription: return JobDescription(job_dir=job_dir, command=command) +DestinationPicker = Callable[[Path, str, "Config"], tuple[Destination, str]] + + +def pick_first( + job_dir: Path, + application_name: str, + config: "Config", +) -> tuple[Destination, str]: + """Pick to which destination a job should be submitted. + + :param job_dir: Location where job input files are located. + :param application_name: Application name that should be run. + :param config: Config with applications and destinations. + :return: Destination where job should be submitted to. + """ + destination_names = list(config.destinations.keys()) + destination_name = destination_names[0] + destination = config.destinations[destination_name] + return destination, destination_name + + +DEFAULT_DESTINATION_PICKER: DestinationPicker = pick_first + + +def import_picker(destination_picker_name: Optional[str]) -> DestinationPicker: + """Import a picker function based on a `.` string. + + :param destination_picker_name: function import as string. + :return: Function that can be used to pick to + which destination a job should be submitted. + """ + if destination_picker_name is None: + return DEFAULT_DESTINATION_PICKER + modules = destination_picker_name.split(".") + function_name = modules.pop() + module_name = ".".join(modules) + module = __import__(module_name) # noqa: WPS421 + return getattr(module, function_name) + + @dataclass class Config: """Bartender configuration. @@ -99,7 +139,8 @@ class Config: applications: dict[str, ApplicatonConfiguration] destinations: dict[str, Destination] - job_root_dir: Path = TEMP_DIR / "jobs" + job_root_dir: Path = DEFAULT_JOB_ROOT_DIR + destination_picker: DestinationPicker = DEFAULT_DESTINATION_PICKER def build_config(config_filename: Path) -> Config: @@ -118,9 +159,12 @@ def parse_config(config: Any) -> Config: :param config: A plain configuration dict :return: A config instance. """ + destination_picker_fn = import_picker(config.get("destination_picker")) return Config( applications=_build_applications(config["applications"]), destinations=build_destinations(config["destinations"]), + job_root_dir=config.get("job_root_dir", DEFAULT_JOB_ROOT_DIR), + destination_picker=destination_picker_fn, ) diff --git a/bartender/db/dao/job_dao.py b/bartender/db/dao/job_dao.py index f33831c..7f5be2c 100644 --- a/bartender/db/dao/job_dao.py +++ b/bartender/db/dao/job_dao.py @@ -104,7 +104,7 @@ async def update_internal_job_id( :param jobid: name of job instance. :param internal_job_id: new internal job id of job instance. - :param destination: To which schdeduler/filesystem the job was submitted. + :param destination: To which scheduler/filesystem the job was submitted. """ job = await self.session.get(Job, jobid) if job is None: diff --git a/bartender/destinations.py b/bartender/destinations.py index e3fdfad..3e9968c 100644 --- a/bartender/destinations.py +++ b/bartender/destinations.py @@ -11,7 +11,7 @@ @dataclass class Destination: - """A destination is a combination of a scheduler and filesystem.""" + """A destination is a combination of a scheduler and optional filesystem.""" scheduler: AbstractScheduler filesystem: AbstractFileSystem = LocalFileSystem() @@ -36,7 +36,7 @@ def build(config: Any) -> dict[str, Destination]: def _build_destination(dest_config: Any) -> Destination: if not dest_config: - raise KeyError("Destinations needs scheduler and optional file system") + raise KeyError("A destination needs scheduler and optional file system") scheduler = build_scheduler(dest_config["scheduler"]) filesystem: AbstractFileSystem = LocalFileSystem() filesystem_config = dest_config.get("filesystem") diff --git a/bartender/filesystems/build.py b/bartender/filesystems/build.py index 9a69367..b890852 100644 --- a/bartender/filesystems/build.py +++ b/bartender/filesystems/build.py @@ -22,10 +22,14 @@ def build(config: Any) -> AbstractFileSystem: if config["type"] == "local": return LocalFileSystem() if config["type"] == "sftp": - if "ssh_config" not in config: - raise KeyError("Sftp file system without SSH connection configuration.") - ssh_config = SshConnectConfig(**config["ssh_config"]) - entry_config = config.get("entry", "/") - entry = Path(entry_config) - return SftpFileSystem(ssh_config, entry) + return _build_sftp_filesystem(config) raise ValueError(f'File system with type {config["type"]} is unknown') + + +def _build_sftp_filesystem(config: Any) -> SftpFileSystem: + if "ssh_config" not in config: + raise KeyError("Sftp file system without SSH connection configuration.") + ssh_config = SshConnectConfig(**config["ssh_config"]) + entry_config = config.get("entry", "/") + entry = Path(entry_config) + return SftpFileSystem(ssh_config, entry) diff --git a/bartender/tests/test_config.py b/bartender/tests/test_config.py index 4c01a6c..9a8e72d 100644 --- a/bartender/tests/test_config.py +++ b/bartender/tests/test_config.py @@ -96,3 +96,27 @@ async def test_parse_single_destination() -> None: }, ) assert result == expected + + +@pytest.mark.anyio +async def test_job_root_dir() -> None: + config = { + "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, + "destinations": {}, + "job_root_dir": Path("/jobs"), + } + result = parse_config(config) + + expected = Config( + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, + destinations={ + "": Destination(scheduler=MemoryScheduler()), + }, + job_root_dir=Path("/jobs"), + ) + assert result == expected + + +# TODO add tests for destination picker diff --git a/bartender/web/api/applications/submit.py b/bartender/web/api/applications/submit.py index 22f3476..16e91ef 100644 --- a/bartender/web/api/applications/submit.py +++ b/bartender/web/api/applications/submit.py @@ -24,7 +24,11 @@ async def submit( application_config = config.applications[application] description = application_config.description(job_dir) - destination, destinations_name = pick_destination(job_dir, application, config) + destination, destinations_name = config.destination_picker( + job_dir, + application, + config, + ) await _upload_input_files(description, destination, config.job_root_dir) @@ -50,24 +54,3 @@ async def _upload_input_files( src=description, target=localized_description, ) - - -def pick_destination( - job_dir: Path, - application_name: str, - config: Config, -) -> tuple[Destination, str]: - """Pick to which destination a job should be submitted. - - :param job_dir: Location where job input files are located. - :param application_name: Application name that should be run. - :param config: Config with applications and destinations. - :return: Destination where job should be submitted to. - """ - # TODO allow maintaner of service to provide Python function that - # returns the destination for this job - # for now the first destination in the configuration is picked. - destination_names = list(config.destinations.keys()) - destination_name = destination_names[0] - destination = config.destinations[destination_name] - return destination, destination_name diff --git a/config-example.yaml b/config-example.yaml index 2f0ab8f..620da66 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -1,3 +1,9 @@ +# By default the files of jobs are stored in /tmp/jobs +# job_root_dir: /tmp/jobs +# By default jobs are submitted to the first destination +# destination_picker: bartender.config.pick_first +# To use a custom picker set `destination_picker` to a `.` +# The picker should have type bartender.config.DestinationPicker . applications: app1: command: app1 $config From c7857102d36d0d17d3728fcd5c6e81ce0b53613c Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Wed, 23 Nov 2022 08:37:38 +0100 Subject: [PATCH 10/36] Use import_module --- bartender/config.py | 11 ++++++----- config-example.yaml | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/bartender/config.py b/bartender/config.py index 8d4b16c..7febdb9 100644 --- a/bartender/config.py +++ b/bartender/config.py @@ -51,6 +51,7 @@ """ from dataclasses import dataclass +from importlib import import_module from pathlib import Path from string import Template from tempfile import gettempdir @@ -92,6 +93,7 @@ def description(self, job_dir: Path) -> JobDescription: DestinationPicker = Callable[[Path, str, "Config"], tuple[Destination, str]] +# TODO move to own module, without causing a circular import def pick_first( job_dir: Path, application_name: str, @@ -114,7 +116,7 @@ def pick_first( def import_picker(destination_picker_name: Optional[str]) -> DestinationPicker: - """Import a picker function based on a `.` string. + """Import a picker function based on a `:` string. :param destination_picker_name: function import as string. :return: Function that can be used to pick to @@ -122,10 +124,9 @@ def import_picker(destination_picker_name: Optional[str]) -> DestinationPicker: """ if destination_picker_name is None: return DEFAULT_DESTINATION_PICKER - modules = destination_picker_name.split(".") - function_name = modules.pop() - module_name = ".".join(modules) - module = __import__(module_name) # noqa: WPS421 + # TODO allow somedir/somefile.py:pick_round_robin + (module_name, function_name) = destination_picker_name.split(":") + module = import_module(module_name) return getattr(module, function_name) diff --git a/config-example.yaml b/config-example.yaml index 620da66..b0e750a 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -1,8 +1,8 @@ # By default the files of jobs are stored in /tmp/jobs # job_root_dir: /tmp/jobs # By default jobs are submitted to the first destination -# destination_picker: bartender.config.pick_first -# To use a custom picker set `destination_picker` to a `.` +# destination_picker: bartender.config:pick_first +# To use a custom picker set `destination_picker` to a `:` # The picker should have type bartender.config.DestinationPicker . applications: app1: From f3c773870af8fc3b64c17f9bf9f1248a45f8509f Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Tue, 3 Jan 2023 13:52:25 +0100 Subject: [PATCH 11/36] Added round robin picker --- bartender/config.py | 40 ++++++++++++++++- bartender/tests/test_config.py | 82 +++++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 3 deletions(-) diff --git a/bartender/config.py b/bartender/config.py index 7febdb9..4f2fd11 100644 --- a/bartender/config.py +++ b/bartender/config.py @@ -99,7 +99,7 @@ def pick_first( application_name: str, config: "Config", ) -> tuple[Destination, str]: - """Pick to which destination a job should be submitted. + """Always picks first destination to where a job should be submitted to. :param job_dir: Location where job input files are located. :param application_name: Application name that should be run. @@ -112,6 +112,44 @@ def pick_first( return destination, destination_name +class PickRound: + """Builder for round robin destination picker.""" + + def __init__(self) -> None: + self.last = "" + + def __call__( + self, + job_dir: Path, + application_name: str, + config: "Config", + ) -> tuple[Destination, str]: + """Always picks the next destination to where a job should be submitted to. + + Takes list of destinations and each time it is called will + pick the next destination in the destination list. + Going around to start when end is reached. + + :param job_dir: Location where job input files are located. + :param application_name: Application name that should be run. + :param config: Config with applications and destinations. + :return: Destination where job should be submitted to. + """ + destination_names = list(config.destinations.keys()) + if self.last == "": + self.last = destination_names[0] + else: + for index, name in enumerate(destination_names): + if name == self.last: + new_index = (index + 1) % len(destination_names) + self.last = destination_names[new_index] + break + + return config.destinations[self.last], self.last + + +pick_round: DestinationPicker = PickRound() + DEFAULT_DESTINATION_PICKER: DestinationPicker = pick_first diff --git a/bartender/tests/test_config.py b/bartender/tests/test_config.py index 9a8e72d..c9608d0 100644 --- a/bartender/tests/test_config.py +++ b/bartender/tests/test_config.py @@ -5,7 +5,14 @@ from yaml import safe_dump as yaml_dump from bartender._ssh_utils import SshConnectConfig -from bartender.config import ApplicatonConfiguration, Config, build_config, parse_config +from bartender.config import ( + ApplicatonConfiguration, + Config, + PickRound, + build_config, + parse_config, + pick_first, +) from bartender.destinations import Destination from bartender.filesystems.sftp import SftpFileSystem from bartender.schedulers.memory import MemoryScheduler @@ -119,4 +126,75 @@ async def test_job_root_dir() -> None: assert result == expected -# TODO add tests for destination picker +class TestPickFirst: + @pytest.mark.anyio + async def test_with2destinations_returns_first(self) -> None: + config = Config( + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, + destinations={ + "d1": Destination(scheduler=MemoryScheduler()), + "d2": Destination(scheduler=MemoryScheduler()), + }, + job_root_dir=Path("/jobs"), + ) + actual = pick_first(config.job_root_dir / "job1", "app1", config) + + expected = (config.destinations["d1"], "d1") + assert actual == expected + + @pytest.mark.anyio + async def test_nodestintations_returns_indexerror(self) -> None: + config = Config( + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, + destinations={}, + job_root_dir=Path("/jobs"), + ) + + with pytest.raises(IndexError): + pick_first(config.job_root_dir / "job1", "app1", config) + + +class TestPickRoundWith2Destinations: + @pytest.fixture + async def config(self) -> Config: + return Config( + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, + destinations={ + "d1": Destination(scheduler=MemoryScheduler()), + "d2": Destination(scheduler=MemoryScheduler()), + }, + job_root_dir=Path("/jobs"), + ) + + @pytest.mark.anyio + async def test_firstcall_returns_first(self, config: Config) -> None: + picker = PickRound() + actual = picker(config.job_root_dir / "job1", "app1", config) + + expected = (config.destinations["d1"], "d1") + assert actual == expected + + @pytest.mark.anyio + async def test_secondcall_returns_second(self, config: Config) -> None: + picker = PickRound() + picker(config.job_root_dir / "job1", "app1", config) + actual = picker(config.job_root_dir / "job1", "app1", config) + + expected = (config.destinations["d2"], "d2") + assert actual == expected + + @pytest.mark.anyio + async def test_thirdcall_returns_first(self, config: Config) -> None: + picker = PickRound() + picker(config.job_root_dir / "job1", "app1", config) + picker(config.job_root_dir / "job1", "app1", config) + actual = picker(config.job_root_dir / "job1", "app1", config) + + expected = (config.destinations["d1"], "d1") + assert actual == expected From 7c0425f4376045cfaf221d7f517bd09361e762e7 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Mon, 9 Jan 2023 16:20:33 +0100 Subject: [PATCH 12/36] Split config into config and context * Document picker in README * Use pydantic to validate config file * Use context for file system/scheduler/picker instances * Split config, context, picker into own files * fix circular imports using TYPE_CHECKING if --- README.md | 15 +- bartender/config.py | 202 ++++++---------- bartender/context.py | 54 +++++ bartender/destinations.py | 72 ++++-- bartender/filesystems/build.py | 36 +-- bartender/filesystems/local.py | 9 + bartender/filesystems/sftp.py | 26 ++- bartender/picker.py | 77 +++++++ bartender/schedulers/build.py | 46 +--- bartender/schedulers/memory.py | 29 ++- bartender/schedulers/slurm.py | 53 +++-- bartender/settings.py | 23 +- bartender/tests/conftest.py | 71 ++++-- bartender/tests/filesystems/test_build.py | 79 +------ bartender/tests/schedulers/test_build.py | 113 ++------- bartender/tests/schedulers/test_memory.py | 12 +- bartender/tests/schedulers/test_slurm.py | 12 +- bartender/tests/test_config.py | 269 ++++++++-------------- bartender/tests/test_context.py | 67 ++++++ bartender/tests/test_destinations.py | 206 ++++++++++------- bartender/tests/test_picker.py | 92 ++++++++ bartender/web/api/applications/submit.py | 25 +- bartender/web/api/applications/views.py | 15 +- bartender/web/api/job/views.py | 24 +- bartender/web/lifetime.py | 38 +-- config-example.yaml | 10 +- 26 files changed, 916 insertions(+), 759 deletions(-) create mode 100644 bartender/context.py create mode 100644 bartender/picker.py create mode 100644 bartender/tests/test_context.py create mode 100644 bartender/tests/test_picker.py diff --git a/README.md b/README.md index 0019d0b..15e1a89 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ - [Configuration](#configuration) - [Applications](#applications) - [Job destinations](#job-destinations) + - [Destination picker](#destination-picker) - [User management](#user-management) - [GitHub login](#github-login) - [Orcid sandbox login](#orcid-sandbox-login) @@ -173,6 +174,18 @@ When the filesystem is on a remote system with non-shared file system or a diffe Destinations can be configured in the `config.yaml` file under `destinations` key. By default a single slot in-memory scheduler with a local filesystem is used. +### [Destination picker](#destination-picker) + +If you have multiple applications and job destinations you need some way to specify to which job submission should go. + +A Python function can be used to pick to which destination a job should go. + +To use a custom picker function set `destination_picker` in `config.yaml` file. +The value should be formatted as `:`, for example to rotate over each destination use `bartender.picker.pick_round` as value. +The picker function should have type `bartender.picker.DestinationPicker`. + +By default jobs are submitted to the first destination. + ## [User management](#user-management) For secure auth add `BARTENDER_SECRET=` to `.env` file. @@ -305,7 +318,7 @@ Use the following steps to run a job: 6. Retrieve result. The word count application (`wc`) outputs to the stdout. 1. Try out the `GET /api/job/{jobid}/stdout` 2. Use job identifier retrieved by submit request as `jobid` parameter value. - 3. Should see something like `404 1556 12928 README.md`. + 3. Should see something like `433 1793 14560 README.md`. Where numbers are counts for newlines, words, bytes. ### Haddock3 example diff --git a/bartender/config.py b/bartender/config.py index 4f2fd11..42fc5a3 100644 --- a/bartender/config.py +++ b/bartender/config.py @@ -6,31 +6,32 @@ .. code-block: yaml applications: - haddock3: - command: haddock3 $config - recluster: - command: haddock3 recluster + haddock3: + command: haddock3 $config + config: workflow.cfg + recluster: + command: haddock3 recluster destinations: - cluster1: - filesystem: &cluster1fs - type: sftp - ssh_config: - hostname: localhost - port: 10022 - username: xenon - password: javagat - entry: /home/xenon - scheduler: &cluster1sched - type: slurm - partition: mypartition - time: '60' # max time is 60 minutes - extra_options: - - --nodes 1 - ssh_config: - hostname: localhost - port: 10022 - username: xenon - password: javagat + cluster1: + filesystem: &cluster1fs + type: sftp + ssh_config: + hostname: localhost + port: 10022 + username: xenon + password: javagat + entry: /home/xenon + scheduler: &cluster1sched + type: slurm + partition: mypartition + time: '60' # max time is 60 minutes + extra_options: + - --nodes 1 + ssh_config: + hostname: localhost + port: 10022 + username: xenon + password: javagat local: scheduler: type: memory @@ -50,22 +51,24 @@ type: dirac """ -from dataclasses import dataclass -from importlib import import_module from pathlib import Path from string import Template from tempfile import gettempdir -from typing import Any, Callable, Optional +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from dataclasses import dataclass +else: + from pydantic.dataclasses import dataclass # noqa: WPS440 from fastapi import Request +from pydantic import Field, validator +from pydantic.types import DirectoryPath from yaml import safe_load as load_yaml -from bartender.destinations import Destination -from bartender.destinations import build as build_destinations +from bartender.destinations import DestinationConfig, default_destinations from bartender.schedulers.abstract import JobDescription -DEFAULT_JOB_ROOT_DIR = Path(gettempdir()) / "jobs" - @dataclass class ApplicatonConfiguration: @@ -77,6 +80,8 @@ class ApplicatonConfiguration: command: str config: str + # TODO make config optional, + # as some commands don't need a config file name as argument def description(self, job_dir: Path) -> JobDescription: """Construct job description for this application. @@ -90,96 +95,43 @@ def description(self, job_dir: Path) -> JobDescription: return JobDescription(job_dir=job_dir, command=command) -DestinationPicker = Callable[[Path, str, "Config"], tuple[Destination, str]] - - -# TODO move to own module, without causing a circular import -def pick_first( - job_dir: Path, - application_name: str, - config: "Config", -) -> tuple[Destination, str]: - """Always picks first destination to where a job should be submitted to. - - :param job_dir: Location where job input files are located. - :param application_name: Application name that should be run. - :param config: Config with applications and destinations. - :return: Destination where job should be submitted to. - """ - destination_names = list(config.destinations.keys()) - destination_name = destination_names[0] - destination = config.destinations[destination_name] - return destination, destination_name - - -class PickRound: - """Builder for round robin destination picker.""" - - def __init__(self) -> None: - self.last = "" - - def __call__( - self, - job_dir: Path, - application_name: str, - config: "Config", - ) -> tuple[Destination, str]: - """Always picks the next destination to where a job should be submitted to. - - Takes list of destinations and each time it is called will - pick the next destination in the destination list. - Going around to start when end is reached. - - :param job_dir: Location where job input files are located. - :param application_name: Application name that should be run. - :param config: Config with applications and destinations. - :return: Destination where job should be submitted to. - """ - destination_names = list(config.destinations.keys()) - if self.last == "": - self.last = destination_names[0] - else: - for index, name in enumerate(destination_names): - if name == self.last: - new_index = (index + 1) % len(destination_names) - self.last = destination_names[new_index] - break - - return config.destinations[self.last], self.last - - -pick_round: DestinationPicker = PickRound() - -DEFAULT_DESTINATION_PICKER: DestinationPicker = pick_first - - -def import_picker(destination_picker_name: Optional[str]) -> DestinationPicker: - """Import a picker function based on a `:` string. - - :param destination_picker_name: function import as string. - :return: Function that can be used to pick to - which destination a job should be submitted. - """ - if destination_picker_name is None: - return DEFAULT_DESTINATION_PICKER - # TODO allow somedir/somefile.py:pick_round_robin - (module_name, function_name) = destination_picker_name.split(":") - module = import_module(module_name) - return getattr(module, function_name) - - @dataclass class Config: """Bartender configuration. The bartender.settings.Settings class is for FastAPI settings. The bartender.config.Config class is for non-FastAPI configuration. + + If config is empty will create a single slot in memory scheduler + with a local file system. + """ applications: dict[str, ApplicatonConfiguration] - destinations: dict[str, Destination] - job_root_dir: Path = DEFAULT_JOB_ROOT_DIR - destination_picker: DestinationPicker = DEFAULT_DESTINATION_PICKER + destinations: dict[str, DestinationConfig] = Field( + default_factory=default_destinations, + ) + job_root_dir: DirectoryPath = Path(gettempdir()) / "jobs" + destination_picker: str = "bartender.picker:pick_first" + + @validator("applications") + def applications_non_empty( + cls, # noqa: N805 following pydantic docs + v: dict[str, ApplicatonConfiguration], # noqa: WPS111 following pydantic docs + ) -> dict[str, ApplicatonConfiguration]: + """Validates that applications dict is filled. + + :param v: The given dict. + :raises ValueError: When dict is empty. + :returns: The given dict. + """ + if not v: + raise ValueError("must contain a at least one application") + return v + + # TODO validate destination_picker + # check string format + # optionally check if it can be imported def build_config(config_filename: Path) -> Config: @@ -188,23 +140,8 @@ def build_config(config_filename: Path) -> Config: :param config_filename: File name of configuration file. :return: A config instance. """ - config = _load(config_filename) - return parse_config(config) - - -def parse_config(config: Any) -> Config: - """Parses a plain configuration dict to a config instance. - - :param config: A plain configuration dict - :return: A config instance. - """ - destination_picker_fn = import_picker(config.get("destination_picker")) - return Config( - applications=_build_applications(config["applications"]), - destinations=build_destinations(config["destinations"]), - job_root_dir=config.get("job_root_dir", DEFAULT_JOB_ROOT_DIR), - destination_picker=destination_picker_fn, - ) + raw_config = _load(config_filename) + return Config(**raw_config) def _load(config_filename: Path) -> Any: @@ -212,13 +149,6 @@ def _load(config_filename: Path) -> Any: return load_yaml(handle) -def _build_applications(config: Any) -> dict[str, ApplicatonConfiguration]: - applications = {} - for name, setting in config.items(): - applications[name] = ApplicatonConfiguration(**setting) - return applications - - def get_config(request: Request) -> Config: """Get config based on current request. diff --git a/bartender/context.py b/bartender/context.py new file mode 100644 index 0000000..89056ee --- /dev/null +++ b/bartender/context.py @@ -0,0 +1,54 @@ +from dataclasses import dataclass as pydataclass +from pathlib import Path + +from fastapi import Request + +from bartender.config import ApplicatonConfiguration, Config +from bartender.destinations import Destination +from bartender.destinations import build as build_destinations +from bartender.picker import DestinationPicker, import_picker + + +@pydataclass +class Context: + """Context for web service.""" + + applications: dict[str, ApplicatonConfiguration] + destinations: dict[str, Destination] + job_root_dir: Path + destination_picker: DestinationPicker + + +def build_context(config: Config) -> Context: + """Parses a plain configuration dict to a context instance. + + :param config: A plain configuration dict + :return: A config instance. + """ + return Context( + applications=config.applications, + job_root_dir=config.job_root_dir, + destinations=build_destinations(config.destinations), + destination_picker=import_picker(config.destination_picker), + ) + + +def get_context(request: Request) -> Context: + """Get context based on current request. + + :param request: The current FastAPI request. + :return: The context. + """ + return request.app.state.context + + +async def close_context(context: Context) -> None: + """Closes destinations in context. + + A destination might have a remote connection that needs to be cleaned-up. + + :param context: The context. + """ + destinations: dict[str, Destination] = context.destinations + for destination in destinations.values(): + await destination.close() diff --git a/bartender/destinations.py b/bartender/destinations.py index 3e9968c..08c1de1 100644 --- a/bartender/destinations.py +++ b/bartender/destinations.py @@ -1,12 +1,42 @@ from dataclasses import dataclass -from typing import Any + +from pydantic import BaseModel, Field from bartender.filesystems.abstract import AbstractFileSystem +from bartender.filesystems.build import FileSystemConfig from bartender.filesystems.build import build as build_filesystem -from bartender.filesystems.local import LocalFileSystem +from bartender.filesystems.local import LocalFileSystemConfig from bartender.schedulers.abstract import AbstractScheduler +from bartender.schedulers.build import SchedulerConfig from bartender.schedulers.build import build as build_scheduler -from bartender.schedulers.memory import MemoryScheduler +from bartender.schedulers.memory import MemorySchedulerConfig + + +class DestinationConfig(BaseModel): + """Configuration for job destination.""" + + scheduler: SchedulerConfig = Field(discriminator="type") + filesystem: FileSystemConfig = Field(discriminator="type") + + # TODO validate that some combinations of scheduler and file system + # are not possible like + # * MemoryScheduler + SftpFileSystem + # In future possible combos + # * AWSBatchScheduler + S3FileSystem + # * DiracScheduler + SrmFileSystem + + +def default_destinations() -> dict[str, DestinationConfig]: + """Default destinations when empty dict was given as Config.destinations. + + :returns: Dict with local in-memory scheduler and file system. + """ + return { + "": DestinationConfig( + scheduler=MemorySchedulerConfig(), + filesystem=LocalFileSystemConfig(), + ), + } @dataclass @@ -14,32 +44,36 @@ class Destination: """A destination is a combination of a scheduler and optional filesystem.""" scheduler: AbstractScheduler - filesystem: AbstractFileSystem = LocalFileSystem() + filesystem: AbstractFileSystem + + async def close(self) -> None: + """Cleanup destination. + + A job destination can have connections to remote systems + call this method to clean those up. + """ + await self.scheduler.close() + self.filesystem.close() -def build(config: Any) -> dict[str, Destination]: +def build(config: dict[str, DestinationConfig]) -> dict[str, Destination]: """Build job destinations dictionary from a configuration dictionary. :param config: The configuration dictionary. :return: Job destinations dictionary """ - if not config: - return { - "": Destination(scheduler=MemoryScheduler()), - } - destinations = {} for name, dest_config in config.items(): - destinations[name] = _build_destination(dest_config) + destinations[name] = build_destination(dest_config) return destinations -def _build_destination(dest_config: Any) -> Destination: - if not dest_config: - raise KeyError("A destination needs scheduler and optional file system") - scheduler = build_scheduler(dest_config["scheduler"]) - filesystem: AbstractFileSystem = LocalFileSystem() - filesystem_config = dest_config.get("filesystem") - if filesystem_config is not None: - filesystem = build_filesystem(dest_config["filesystem"]) +def build_destination(config: DestinationConfig) -> Destination: + """Build job destination from configuration. + + :param config: Configuration for a destination. + :returns: A job destination. + """ + scheduler = build_scheduler(config.scheduler) + filesystem = build_filesystem(config.filesystem) return Destination(scheduler=scheduler, filesystem=filesystem) diff --git a/bartender/filesystems/build.py b/bartender/filesystems/build.py index b890852..4af61a0 100644 --- a/bartender/filesystems/build.py +++ b/bartender/filesystems/build.py @@ -1,35 +1,21 @@ -from pathlib import Path -from typing import Any +from typing import Union -from bartender._ssh_utils import SshConnectConfig from bartender.filesystems.abstract import AbstractFileSystem -from bartender.filesystems.local import LocalFileSystem -from bartender.filesystems.sftp import SftpFileSystem +from bartender.filesystems.local import LocalFileSystem, LocalFileSystemConfig +from bartender.filesystems.sftp import SftpFileSystem, SftpFileSystemConfig +FileSystemConfig = Union[LocalFileSystemConfig, SftpFileSystemConfig] -def build(config: Any) -> AbstractFileSystem: + +def build(config: FileSystemConfig) -> AbstractFileSystem: """Build a file system from a configuration. :param config: The configuration - :raises KeyError: When a key is missing - :raises ValueError: When a value is incorrect. + :raises ValueError: When unknown config is given. :return: A file system instance. """ - if config is None: - return LocalFileSystem() - if "type" not in config: - raise KeyError("File system without type") - if config["type"] == "local": + if isinstance(config, LocalFileSystemConfig): return LocalFileSystem() - if config["type"] == "sftp": - return _build_sftp_filesystem(config) - raise ValueError(f'File system with type {config["type"]} is unknown') - - -def _build_sftp_filesystem(config: Any) -> SftpFileSystem: - if "ssh_config" not in config: - raise KeyError("Sftp file system without SSH connection configuration.") - ssh_config = SshConnectConfig(**config["ssh_config"]) - entry_config = config.get("entry", "/") - entry = Path(entry_config) - return SftpFileSystem(ssh_config, entry) + if isinstance(config, SftpFileSystemConfig): + return SftpFileSystem(config) + raise ValueError(f"Unknown filesystem, recieved config is {config}") diff --git a/bartender/filesystems/local.py b/bartender/filesystems/local.py index f48ea4b..78ddd6b 100644 --- a/bartender/filesystems/local.py +++ b/bartender/filesystems/local.py @@ -1,9 +1,18 @@ +from dataclasses import dataclass from pathlib import Path +from typing import Literal from bartender.filesystems.abstract import AbstractFileSystem from bartender.schedulers.abstract import JobDescription +@dataclass +class LocalFileSystemConfig: + """Configuration for local file system.""" + + type: Literal["local"] = "local" + + class LocalFileSystem(AbstractFileSystem): """File system operations on file system current Python process is running on.""" diff --git a/bartender/filesystems/sftp.py b/bartender/filesystems/sftp.py index 3db9823..5e731d1 100644 --- a/bartender/filesystems/sftp.py +++ b/bartender/filesystems/sftp.py @@ -1,5 +1,6 @@ +from dataclasses import dataclass from pathlib import Path -from typing import Optional +from typing import Literal, Optional from asyncssh import SSHClientConnection @@ -8,21 +9,32 @@ from bartender.schedulers.abstract import JobDescription +@dataclass +class SftpFileSystemConfig: + """Configuration for SFTP file system. + + :param ssh_config: SSH connection configuration. + :param entry: The entry directory. Used to localize description. + """ + + ssh_config: SshConnectConfig + entry: Path = Path("/") + type: Literal["sftp"] = "sftp" + + class SftpFileSystem(AbstractFileSystem): """Remote filesystem using SFTP protocol.""" def __init__( self, - ssh_config: SshConnectConfig, - entry: Path = Path("/"), + config: SftpFileSystemConfig, ): """Constructor. - :param ssh_config: SSH connection configuration. - :param entry: The entry directory. Used to localize description. + :param config: The config. """ - self.entry = entry - self.ssh_config = ssh_config + self.entry = config.entry + self.ssh_config = config.ssh_config self.conn: Optional[SSHClientConnection] = None def localize_description( diff --git a/bartender/picker.py b/bartender/picker.py new file mode 100644 index 0000000..d946ded --- /dev/null +++ b/bartender/picker.py @@ -0,0 +1,77 @@ +from importlib import import_module +from pathlib import Path +from typing import TYPE_CHECKING, Callable + +if TYPE_CHECKING: + from bartender.context import Context + +DestinationPicker = Callable[[Path, str, "Context"], str] + + +# TODO move to own module, without causing a circular import +def pick_first( + job_dir: Path, + application_name: str, + context: "Context", +) -> str: + """Always picks first destination to where a job should be submitted to. + + :param job_dir: Location where job input files are located. + :param application_name: Application name that should be run. + :param context: Context with applications and destinations. + :return: Destination where job should be submitted to. + """ + destination_names = list(context.destinations.keys()) + return destination_names[0] + + +class PickRound: + """Builder for round robin destination picker.""" + + def __init__(self) -> None: + self.last = "" + + def __call__( + self, + job_dir: Path, + application_name: str, + context: "Context", + ) -> str: + """Always picks the next destination to where a job should be submitted to. + + Takes list of destinations and each time it is called will + pick the next destination in the destination list. + Going around to start when end is reached. + + :param job_dir: Location where job input files are located. + :param application_name: Application name that should be run. + :param context: Context with applications and destinations. + :return: Destination where job should be submitted to. + """ + destination_names = list(context.destinations.keys()) + if self.last == "": + self.last = destination_names[0] + else: + for index, name in enumerate(destination_names): + if name == self.last: + new_index = (index + 1) % len(destination_names) + self.last = destination_names[new_index] + break + + return self.last + + +pick_round: DestinationPicker = PickRound() + + +def import_picker(destination_picker_name: str) -> DestinationPicker: + """Import a picker function based on a `:` string. + + :param destination_picker_name: function import as string. + :return: Function that can be used to pick to + which destination a job should be submitted. + """ + # TODO allow somedir/somefile.py:pick_round_robin + (module_name, function_name) = destination_picker_name.split(":") + module = import_module(module_name) + return getattr(module, function_name) diff --git a/bartender/schedulers/build.py b/bartender/schedulers/build.py index b67eb26..f098b51 100644 --- a/bartender/schedulers/build.py +++ b/bartender/schedulers/build.py @@ -1,44 +1,22 @@ -from typing import Any +from typing import Union -from bartender._ssh_utils import SshConnectConfig from bartender.schedulers.abstract import AbstractScheduler -from bartender.schedulers.memory import MemoryScheduler -from bartender.schedulers.slurm import SlurmScheduler +from bartender.schedulers.memory import MemoryScheduler, MemorySchedulerConfig +from bartender.schedulers.slurm import SlurmScheduler, SlurmSchedulerConfig +SchedulerConfig = Union[MemorySchedulerConfig, SlurmSchedulerConfig] -def build(config: Any) -> AbstractScheduler: + +def build(config: SchedulerConfig) -> AbstractScheduler: """Build scheduler instance from configuration. :param config: Configuration for a scheduler. - :raises KeyError: When key is missing in configuration. - :raises ValueError: When value is incorrect in configuration. + :raises ValueError: When config can not be mapped to a scheduler instance. :return: A scheduler instance. """ - scheduler_type = config.get("type") - if scheduler_type is None: - raise KeyError("Scheduler without type") - if scheduler_type == "memory": - return _build_memory_scheduler(config) - if scheduler_type == "slurm": - return _build_slurm_scheduler(config) - raise ValueError(f'Scheduler with type {config["type"]} is unknown') - - -def _build_memory_scheduler(config: Any) -> MemoryScheduler: - slots = config.get("slots") - if slots is not None: - return MemoryScheduler(slots) - return MemoryScheduler() - - -def _build_slurm_scheduler(config: Any) -> SlurmScheduler: - slurm_config = { - sched_key: sched_value - for sched_key, sched_value in config.items() - if sched_key not in {"type", "ssh_config"} - } - ssh_config = config.get("ssh_config") - if ssh_config is not None: - ssh_config = SshConnectConfig(**ssh_config) - return SlurmScheduler(ssh_config=ssh_config, **slurm_config) + if isinstance(config, MemorySchedulerConfig): + return MemoryScheduler(config) + if isinstance(config, SlurmSchedulerConfig): + return SlurmScheduler(config) + raise ValueError(f"Unknown scheduler, recieved config is {config}") diff --git a/bartender/schedulers/memory.py b/bartender/schedulers/memory.py index 1f499db..4646929 100644 --- a/bartender/schedulers/memory.py +++ b/bartender/schedulers/memory.py @@ -6,17 +6,22 @@ create_task, gather, ) -from dataclasses import dataclass -from typing import Optional +from dataclasses import dataclass as pydataclass +from typing import TYPE_CHECKING, Literal, Optional from uuid import uuid4 +if TYPE_CHECKING: + from dataclasses import dataclass +else: + from pydantic.dataclasses import dataclass # noqa: WPS440 + from pydantic.types import PositiveInt from bartender.db.models.job_model import CompletedStates, State from bartender.schedulers.abstract import AbstractScheduler, JobDescription -@dataclass +@pydataclass class _Job: description: JobDescription id: str @@ -69,21 +74,33 @@ async def _worker(queue: Queue[_Job], jobs: dict[str, _Job], worker_index: int) queue.task_done() +@dataclass +class MemorySchedulerConfig: + """Configuration for in memory scheduler. + + :param slots: Maximum number of concurrently runnning jobs. Minimum is 1. + """ + + type: Literal["memory"] = "memory" + slots: PositiveInt = 1 + + class MemoryScheduler(AbstractScheduler): """In memory scheduler. When service is closed any queud or running jobs will disappear. + """ - def __init__(self, slots: PositiveInt = 1): + def __init__(self, config: MemorySchedulerConfig): """In memory scheduler. - :param slots: Maximum number of concurrently runnning jobs. Minimum is 1. + :param config: The config. """ self.queue: Queue[_Job] = Queue() self.jobs: dict[str, _Job] = {} self.workers: list[Task[None]] = [] - for _ in range(slots): + for _ in range(config.slots): self._add_worker() async def close(self) -> None: # noqa: D102 diff --git a/bartender/schedulers/slurm.py b/bartender/schedulers/slurm.py index d0e3491..bb9ae2b 100644 --- a/bartender/schedulers/slurm.py +++ b/bartender/schedulers/slurm.py @@ -1,5 +1,6 @@ +from dataclasses import dataclass from textwrap import dedent -from typing import Optional +from typing import Literal, Optional from bartender._ssh_utils import SshConnectConfig from bartender.db.models.job_model import State @@ -34,36 +35,44 @@ def _map_slurm_state(slurm_state: str) -> State: return "error" +@dataclass +class SlurmSchedulerConfig: + """Configuration for Slurm scheduler. + + :param ssh_config: SSH connection configuration. + When set will call SLURM commands on remote system via SSH connection. + When not set will call SLURM commands on local system. + :param partition: Partition in which all jobs should be submitted. + :param time: Limit on the total run time of the job. + :param extra_options: Escape hatch to add extra options to job script. + The string `#SBATCH {extra_options[0]}` will be appended to job script. + """ + + type: Literal["slurm"] = "slurm" + ssh_config: Optional[SshConnectConfig] = None + partition: Optional[str] = None + time: Optional[str] = None + extra_options: Optional[list[str]] = None + + class SlurmScheduler(AbstractScheduler): """Slurm batch scheduler.""" - def __init__( - self, - ssh_config: Optional[SshConnectConfig] = None, - partition: Optional[str] = None, - time: Optional[str] = None, - extra_options: Optional[list[str]] = None, - ): + def __init__(self, config: SlurmSchedulerConfig): """Constructor. - :param ssh_config: SSH connection configuration. - When set will call SLURM commands on remote system via SSH connection. - When not set will call SLURM commands on local system. - :param partition: Partition in which all jobs should be submitted. - :param time: Limit on the total run time of the job. - :param extra_options: Escape hatch to add extra options to job script. - The string `#SBATCH {extra_options[0]}` will be appended to job script. + :param config: Config for scheduler. """ self.runner: CommandRunner = LocalCommandRunner() - self.ssh_config = ssh_config - if ssh_config is not None: - self.runner = SshCommandRunner(ssh_config) - self.partition = partition - self.time = time - if extra_options is None: + self.ssh_config = config.ssh_config + if config.ssh_config is not None: + self.runner = SshCommandRunner(config.ssh_config) + self.partition = config.partition + self.time = config.time + if config.extra_options is None: self.extra_options = [] else: - self.extra_options = extra_options + self.extra_options = config.extra_options async def submit(self, description: JobDescription) -> str: # noqa: D102): # TODO if runner is a SSHCommandRunner then description.jobdir diff --git a/bartender/settings.py b/bartender/settings.py index 6f3968e..58c0140 100644 --- a/bartender/settings.py +++ b/bartender/settings.py @@ -1,10 +1,14 @@ import enum +import logging from pathlib import Path from tempfile import gettempdir -from pydantic import BaseSettings +from pydantic import BaseSettings, Field +from pydantic.types import FilePath from yarl import URL +logger = logging.getLogger(__name__) + TEMP_DIR = Path(gettempdir()) @@ -19,6 +23,21 @@ class LogLevel(str, enum.Enum): # noqa: WPS600 FATAL = "FATAL" +def default_config_filename() -> Path: + """The default configuration filename. + + Depends on whether default or fallback files exist. + + :returns: Default file name for configuration file. + """ + default = Path("config.yaml") + fallback = Path("config-example.yaml") + if not default.exists() and fallback.exists(): + logger.warn(f"Unable to find {default} falling back to {fallback}") + return fallback + return default + + class Settings(BaseSettings): """ Application settings. @@ -60,7 +79,7 @@ class Settings(BaseSettings): orcid_client_secret: str = "" # Settings for configuration - config_filename: Path = Path("config.yaml") + config_filename: FilePath = Field(default_factory=default_config_filename) @property def db_url(self) -> URL: diff --git a/bartender/tests/conftest.py b/bartender/tests/conftest.py index 0c8a18c..c14de6a 100644 --- a/bartender/tests/conftest.py +++ b/bartender/tests/conftest.py @@ -8,10 +8,13 @@ from sqlalchemy.orm import sessionmaker from bartender.config import ApplicatonConfiguration, Config, get_config +from bartender.context import Context, get_context from bartender.db.dependencies import get_db_session from bartender.db.utils import create_database, drop_database -from bartender.destinations import Destination -from bartender.schedulers.memory import MemoryScheduler +from bartender.destinations import Destination, DestinationConfig +from bartender.filesystems.local import LocalFileSystem, LocalFileSystemConfig +from bartender.picker import pick_first +from bartender.schedulers.memory import MemoryScheduler, MemorySchedulerConfig from bartender.settings import settings from bartender.web.application import get_app @@ -84,31 +87,72 @@ async def dbsession( @pytest.fixture def job_root_dir(tmp_path: Path) -> Path: - return tmp_path + root = tmp_path / "jobs" + root.mkdir() + return root @pytest.fixture -async def config(job_root_dir: Path) -> AsyncGenerator[Config, None]: - applications = { +def demo_applications() -> dict[str, ApplicatonConfiguration]: + return { "app1": ApplicatonConfiguration( command="wc $config", config="job.ini", ), } - scheduler = MemoryScheduler() - destinations = {"dest1": Destination(scheduler=scheduler)} - yield Config( - applications=applications, - destinations=destinations, + + +@pytest.fixture +async def demo_destination() -> AsyncGenerator[Destination, None]: + destination = Destination( + scheduler=MemoryScheduler(MemorySchedulerConfig()), + filesystem=LocalFileSystem(), + ) + yield destination + await destination.close() + + +@pytest.fixture +async def demo_destinations(demo_destination: Destination) -> dict[str, Destination]: + return {"dest1": demo_destination} + + +@pytest.fixture +def demo_config( + job_root_dir: Path, + demo_applications: dict[str, ApplicatonConfiguration], +) -> Config: + return Config( + applications=demo_applications, + job_root_dir=job_root_dir, + destinations={ + "dest1": DestinationConfig( + scheduler=MemorySchedulerConfig(), + filesystem=LocalFileSystemConfig(), + ), + }, + ) + + +@pytest.fixture +def demo_context( + job_root_dir: Path, + demo_applications: dict[str, ApplicatonConfiguration], + demo_destinations: dict[str, Destination], +) -> Context: + return Context( + destination_picker=pick_first, job_root_dir=job_root_dir, + applications=demo_applications, + destinations=demo_destinations, ) - await scheduler.close() @pytest.fixture def fastapi_app( dbsession: AsyncSession, - config: Config, + demo_config: Config, + demo_context: Context, ) -> FastAPI: """ Fixture for creating FastAPI app. @@ -117,7 +161,8 @@ def fastapi_app( """ application = get_app() application.dependency_overrides[get_db_session] = lambda: dbsession - application.dependency_overrides[get_config] = lambda: config + application.dependency_overrides[get_config] = lambda: demo_config + application.dependency_overrides[get_context] = lambda: demo_context settings.secret = "testsecret" # noqa: S105 return application # noqa: WPS331 diff --git a/bartender/tests/filesystems/test_build.py b/bartender/tests/filesystems/test_build.py index 472005c..6d74c62 100644 --- a/bartender/tests/filesystems/test_build.py +++ b/bartender/tests/filesystems/test_build.py @@ -1,87 +1,22 @@ -from pathlib import Path - -import pytest - from bartender._ssh_utils import SshConnectConfig from bartender.filesystems.build import build -from bartender.filesystems.local import LocalFileSystem -from bartender.filesystems.sftp import SftpFileSystem - - -def test_none() -> None: - result = build(None) - expected = LocalFileSystem() - assert result == expected - - -def test_emptydict() -> None: - with pytest.raises(KeyError): - build({}) - - -def test_unknowntype() -> None: - config = {"type": "unknown"} - with pytest.raises(ValueError): - build(config) +from bartender.filesystems.local import LocalFileSystem, LocalFileSystemConfig +from bartender.filesystems.sftp import SftpFileSystem, SftpFileSystemConfig def test_local() -> None: - config = {"type": "local"} - result = build(config) + config = LocalFileSystemConfig() - expected = LocalFileSystem() - assert result == expected - - -def test_sftp_withoutconfig() -> None: - config = {"type": "sftp"} - with pytest.raises(KeyError): - build(config) - - -def test_sftp_simplest() -> None: - config = {"type": "sftp", "ssh_config": {"hostname": "localhost"}} result = build(config) - expected = SftpFileSystem(SshConnectConfig(hostname="localhost")) + expected = LocalFileSystem() assert result == expected -def test_sftp_entry() -> None: - config = { - "type": "sftp", - "ssh_config": {"hostname": "localhost"}, - "entry": "/scratch/jobs", - } - result = build(config) - - expected = SftpFileSystem( - SshConnectConfig(hostname="localhost"), - entry=Path("/scratch/jobs"), - ) - assert result == expected - +def test_sftp() -> None: + config = SftpFileSystemConfig(ssh_config=SshConnectConfig(hostname="localhost")) -def test_sftp_verbosist() -> None: - config = { - "type": "sftp", - "ssh_config": { - "hostname": "localhost", - "port": 2222, - "username": "someone", - "password": "somepw", - }, - "entry": "/scratch/jobs", - } result = build(config) - expected = SftpFileSystem( - SshConnectConfig( # noqa: S106 - hostname="localhost", - port=2222, - username="someone", - password="somepw", - ), - entry=Path("/scratch/jobs"), - ) + expected = SftpFileSystem(config) assert result == expected diff --git a/bartender/tests/schedulers/test_build.py b/bartender/tests/schedulers/test_build.py index ddb1b33..a1a46ee 100644 --- a/bartender/tests/schedulers/test_build.py +++ b/bartender/tests/schedulers/test_build.py @@ -1,110 +1,33 @@ -from typing import Any - import pytest -from bartender._ssh_utils import SshConnectConfig from bartender.schedulers.build import build -from bartender.schedulers.memory import MemoryScheduler -from bartender.schedulers.slurm import SlurmScheduler - - -def test_single_typeless_scheduler() -> None: - config: Any = {} - with pytest.raises(KeyError): - build(config) - - -def test_single_unknown_scheduler() -> None: - config = {"type": "unknown"} - with pytest.raises(ValueError): - build(config) +from bartender.schedulers.memory import MemoryScheduler, MemorySchedulerConfig +from bartender.schedulers.slurm import SlurmScheduler, SlurmSchedulerConfig @pytest.mark.anyio async def test_single_memory_scheduler() -> None: - config = {"type": "memory"} - result = build(config) + try: + config = MemorySchedulerConfig() - expected = MemoryScheduler() - assert result == expected + result = build(config) - -@pytest.mark.anyio -async def test_single_custom_memory_scheduler() -> None: - config = {"type": "memory", "slots": 4} - result = build(config) - - expected = MemoryScheduler(slots=4) - assert result == expected + expected = MemoryScheduler(config) + assert result == expected + finally: + await result.close() + await expected.close() @pytest.mark.anyio async def test_single_localsimplist_slurm_scheduler() -> None: - config = {"type": "slurm"} - result = build(config) - - expected = SlurmScheduler() - assert result == expected - - -@pytest.mark.anyio -async def test_single_localcustom_slurm_scheduler() -> None: - config = { - "type": "slurm", - "partition": "mypartition", - "time": "60", - "extra_options": ["--nodes 1"], - } - result = build(config) - - expected = SlurmScheduler( - partition="mypartition", - time="60", - extra_options=["--nodes 1"], - ) - assert result == expected - - -@pytest.mark.anyio -async def test_single_withouthost_slurm_scheduler() -> None: - config = {"type": "slurm", "ssh_config": {}} - with pytest.raises(TypeError): - build(config) - + try: + config = SlurmSchedulerConfig() -@pytest.mark.anyio -async def test_single_sshsimplist_slurm_scheduler() -> None: - config = { - "type": "slurm", - "ssh_config": { - "hostname": "localhost", - }, - } - result = build(config) - - expected = SlurmScheduler(ssh_config=SshConnectConfig(hostname="localhost")) - assert result == expected - - -@pytest.mark.anyio -async def test_single_sshcustom_slurm_scheduler() -> None: - config = { - "type": "slurm", - "ssh_config": { - "hostname": "localhost", - "port": 10022, - "username": "xenon", - "password": "javagat", - }, - } - result = build(config) + result = build(config) - expected = SlurmScheduler( - ssh_config=SshConnectConfig( # noqa: S106 - hostname="localhost", - port=10022, - username="xenon", - password="javagat", - ), - ) - assert result == expected + expected = SlurmScheduler(config) + assert result == expected + finally: + await result.close() + await expected.close() diff --git a/bartender/tests/schedulers/test_memory.py b/bartender/tests/schedulers/test_memory.py index aca4b68..6011456 100644 --- a/bartender/tests/schedulers/test_memory.py +++ b/bartender/tests/schedulers/test_memory.py @@ -4,13 +4,17 @@ import pytest from bartender.schedulers.abstract import JobDescription -from bartender.schedulers.memory import KILLED_RETURN_CODE, MemoryScheduler +from bartender.schedulers.memory import ( + KILLED_RETURN_CODE, + MemoryScheduler, + MemorySchedulerConfig, +) @pytest.mark.anyio async def test_ok_running_job(tmp_path: Path) -> None: try: - scheduler = MemoryScheduler(slots=1) + scheduler = MemoryScheduler(MemorySchedulerConfig(slots=1)) description = JobDescription(command="echo -n hello", job_dir=str(tmp_path)) jid = await scheduler.submit(description) @@ -27,7 +31,7 @@ async def test_ok_running_job(tmp_path: Path) -> None: @pytest.mark.anyio async def test_bad_running_job(tmp_path: Path) -> None: try: - scheduler = MemoryScheduler(slots=1) + scheduler = MemoryScheduler(MemorySchedulerConfig(slots=1)) description = JobDescription(command="exit 42", job_dir=str(tmp_path)) jid = await scheduler.submit(description) @@ -58,7 +62,7 @@ async def test_cancel_running_job(tmp_path: Path) -> None: async def make_occupied_scheduler( tmp_path: Path, ) -> tuple[MemoryScheduler, str, JobDescription]: - scheduler = MemoryScheduler(slots=1) + scheduler = MemoryScheduler(MemorySchedulerConfig(slots=1)) description = JobDescription(command="sleep 5", job_dir=str(tmp_path)) jid = await scheduler.submit(description) # Wait for job to start running diff --git a/bartender/tests/schedulers/test_slurm.py b/bartender/tests/schedulers/test_slurm.py index 4fcfa0f..98f792a 100644 --- a/bartender/tests/schedulers/test_slurm.py +++ b/bartender/tests/schedulers/test_slurm.py @@ -9,10 +9,10 @@ from bartender._ssh_utils import SshConnectConfig from bartender.db.models.job_model import CompletedStates -from bartender.filesystems.sftp import SftpFileSystem +from bartender.filesystems.sftp import SftpFileSystem, SftpFileSystemConfig from bartender.schedulers.abstract import JobDescription from bartender.schedulers.runner import SshCommandRunner -from bartender.schedulers.slurm import SlurmScheduler +from bartender.schedulers.slurm import SlurmScheduler, SlurmSchedulerConfig class SlurmContainer(DockerContainer): @@ -35,7 +35,9 @@ def get_config(self) -> SshConnectConfig: def get_filesystem(self) -> SftpFileSystem: home_dir = Path("/home/xenon") - return SftpFileSystem(entry=home_dir, ssh_config=self.get_config()) + return SftpFileSystem( + SftpFileSystemConfig(entry=home_dir, ssh_config=self.get_config()), + ) def start(self) -> "SlurmContainer": super().start() @@ -69,7 +71,7 @@ async def test_ok_running_job_with_input_and_output_file( job_dir = tmp_path try: ssh_config = slurm_server.get_config() - scheduler = SlurmScheduler(ssh_config=ssh_config) + scheduler = SlurmScheduler(SlurmSchedulerConfig(ssh_config=ssh_config)) (job_dir / "input").write_text("Lorem ipsum") description = JobDescription( command="echo -n hello && wc input > output", @@ -117,7 +119,7 @@ async def test_ok_running_job_without_iofiles( job_dir = tmp_path try: ssh_config = slurm_server.get_config() - scheduler = SlurmScheduler(ssh_config=ssh_config) + scheduler = SlurmScheduler(SlurmSchedulerConfig(ssh_config=ssh_config)) description = JobDescription(command="echo -n hello", job_dir=str(job_dir)) fs = slurm_server.get_filesystem() localized_description = fs.localize_description(description, job_dir.parent) diff --git a/bartender/tests/test_config.py b/bartender/tests/test_config.py index c9608d0..8ccafc4 100644 --- a/bartender/tests/test_config.py +++ b/bartender/tests/test_config.py @@ -1,30 +1,104 @@ from pathlib import Path +from tempfile import gettempdir from typing import Any +from unittest.mock import MagicMock import pytest +from pydantic import ValidationError from yaml import safe_dump as yaml_dump from bartender._ssh_utils import SshConnectConfig -from bartender.config import ( - ApplicatonConfiguration, - Config, - PickRound, - build_config, - parse_config, - pick_first, -) -from bartender.destinations import Destination -from bartender.filesystems.sftp import SftpFileSystem -from bartender.schedulers.memory import MemoryScheduler -from bartender.schedulers.slurm import SlurmScheduler +from bartender.config import ApplicatonConfiguration, Config, build_config, get_config +from bartender.destinations import DestinationConfig +from bartender.filesystems.local import LocalFileSystemConfig +from bartender.filesystems.sftp import SftpFileSystemConfig +from bartender.schedulers.abstract import JobDescription +from bartender.schedulers.memory import MemorySchedulerConfig +from bartender.schedulers.slurm import SlurmSchedulerConfig + + +class TestApplicatonConfiguration: + def test_description_with_config(self, tmp_path: Path) -> None: + conf = ApplicatonConfiguration(command="wc $config", config="foo.bar") + + description = conf.description(tmp_path) + + expected = JobDescription(job_dir=tmp_path, command="wc foo.bar") + assert description == expected + + +class TestConfig: + def test_zero_apps(self) -> None: + raw_config: Any = {"applications": {}} + with pytest.raises( + ValidationError, + match="must contain a at least one application", + ): + Config(**raw_config) + + def test_minimal(self) -> None: + raw_config: Any = { + "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, + } + config = Config(**raw_config) + + expected = Config( + destination_picker="bartender.picker:pick_first", + job_root_dir=Path(gettempdir()) / "jobs", + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, + destinations={ + "": DestinationConfig( + scheduler=MemorySchedulerConfig(), + filesystem=LocalFileSystemConfig(), + ), + }, + ) + assert config == expected + + def test_no_defaults(self, tmp_path: Path) -> None: + raw_config: Any = { + "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, + "destinations": { + "dest1": { + "scheduler": {"type": "slurm", "partition": "normal"}, + "filesystem": { + "type": "sftp", + "entry": "/scratch", + "ssh_config": {"hostname": "remotehost"}, + }, + }, + }, + "job_root_dir": str(tmp_path), + "destination_picker": "bartender.picker:pick_round", + } + config = Config(**raw_config) + + expected = Config( + destination_picker="bartender.picker:pick_round", + job_root_dir=tmp_path, + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, + destinations={ + "dest1": DestinationConfig( + scheduler=SlurmSchedulerConfig(partition="normal"), + filesystem=SftpFileSystemConfig( + entry=Path("/scratch"), + ssh_config=SshConnectConfig(hostname="remotehost"), + ), + ), + }, + ) + assert config == expected @pytest.mark.anyio -async def test_build_minimal(tmp_path: Path) -> None: +async def test_build_config_minimal(tmp_path: Path) -> None: file = tmp_path / "config.yaml" - config = { + config: Any = { "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, - "destinations": {}, } with file.open("w") as handle: yaml_dump(config, handle) @@ -32,169 +106,26 @@ async def test_build_minimal(tmp_path: Path) -> None: result = build_config(file) expected = Config( + destination_picker="bartender.picker:pick_first", + job_root_dir=Path(gettempdir()) / "jobs", applications={ "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), }, destinations={ - "": Destination(scheduler=MemoryScheduler()), - }, - ) - assert result == expected - - -@pytest.mark.parametrize( - "test_input", - [ - ({}), - ({"applications": {}}), - ({"destinations": {}}), - ], -) -def test_parse_keyerrors(test_input: Any) -> None: - with pytest.raises(KeyError): - parse_config(test_input) - - -@pytest.mark.anyio -async def test_parse_single_destination() -> None: - config = { - "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, - "destinations": { - "dest2": { - "scheduler": { - "type": "slurm", - "partition": "mypartition", - "ssh_config": { - "hostname": "localhost", - }, - }, - "filesystem": { - "type": "sftp", - "ssh_config": { - "hostname": "localhost", - }, - "entry": "/scratch/jobs", - }, - }, - }, - } - - result = parse_config(config) - - expected = Config( - applications={ - "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), - }, - destinations={ - "dest2": Destination( - scheduler=SlurmScheduler( - ssh_config=SshConnectConfig( - hostname="localhost", - ), - partition="mypartition", - ), - filesystem=SftpFileSystem( - ssh_config=SshConnectConfig( - hostname="localhost", - ), - entry=Path("/scratch/jobs"), - ), + "": DestinationConfig( + scheduler=MemorySchedulerConfig(), + filesystem=LocalFileSystemConfig(), ), }, ) assert result == expected -@pytest.mark.anyio -async def test_job_root_dir() -> None: - config = { - "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, - "destinations": {}, - "job_root_dir": Path("/jobs"), - } - result = parse_config(config) - - expected = Config( - applications={ - "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), - }, - destinations={ - "": Destination(scheduler=MemoryScheduler()), - }, - job_root_dir=Path("/jobs"), - ) - assert result == expected - - -class TestPickFirst: - @pytest.mark.anyio - async def test_with2destinations_returns_first(self) -> None: - config = Config( - applications={ - "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), - }, - destinations={ - "d1": Destination(scheduler=MemoryScheduler()), - "d2": Destination(scheduler=MemoryScheduler()), - }, - job_root_dir=Path("/jobs"), - ) - actual = pick_first(config.job_root_dir / "job1", "app1", config) - - expected = (config.destinations["d1"], "d1") - assert actual == expected - - @pytest.mark.anyio - async def test_nodestintations_returns_indexerror(self) -> None: - config = Config( - applications={ - "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), - }, - destinations={}, - job_root_dir=Path("/jobs"), - ) - - with pytest.raises(IndexError): - pick_first(config.job_root_dir / "job1", "app1", config) - - -class TestPickRoundWith2Destinations: - @pytest.fixture - async def config(self) -> Config: - return Config( - applications={ - "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), - }, - destinations={ - "d1": Destination(scheduler=MemoryScheduler()), - "d2": Destination(scheduler=MemoryScheduler()), - }, - job_root_dir=Path("/jobs"), - ) - - @pytest.mark.anyio - async def test_firstcall_returns_first(self, config: Config) -> None: - picker = PickRound() - actual = picker(config.job_root_dir / "job1", "app1", config) - - expected = (config.destinations["d1"], "d1") - assert actual == expected - - @pytest.mark.anyio - async def test_secondcall_returns_second(self, config: Config) -> None: - picker = PickRound() - picker(config.job_root_dir / "job1", "app1", config) - actual = picker(config.job_root_dir / "job1", "app1", config) - - expected = (config.destinations["d2"], "d2") - assert actual == expected +def test_get_config(demo_config: Config) -> None: + fake_request = MagicMock() + fake_request.app.state.config = demo_config - @pytest.mark.anyio - async def test_thirdcall_returns_first(self, config: Config) -> None: - picker = PickRound() - picker(config.job_root_dir / "job1", "app1", config) - picker(config.job_root_dir / "job1", "app1", config) - actual = picker(config.job_root_dir / "job1", "app1", config) + config = get_config(fake_request) - expected = (config.destinations["d1"], "d1") - assert actual == expected + expected = demo_config + assert config == expected diff --git a/bartender/tests/test_context.py b/bartender/tests/test_context.py new file mode 100644 index 0000000..3e4d9c3 --- /dev/null +++ b/bartender/tests/test_context.py @@ -0,0 +1,67 @@ +from pathlib import Path +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from bartender.config import ApplicatonConfiguration, Config +from bartender.context import Context, build_context, close_context, get_context +from bartender.destinations import Destination, DestinationConfig +from bartender.filesystems.local import LocalFileSystemConfig +from bartender.picker import pick_first +from bartender.schedulers.memory import MemorySchedulerConfig + + +@pytest.mark.anyio +async def test_build_minimal( + job_root_dir: Path, + demo_applications: dict[str, ApplicatonConfiguration], + demo_context: Context, +) -> None: + try: + config = Config( + applications=demo_applications, + job_root_dir=job_root_dir, + destinations={ + "dest1": DestinationConfig( + scheduler=MemorySchedulerConfig(), + filesystem=LocalFileSystemConfig(), + ), + }, + ) + + result = build_context(config) + + expected = demo_context + assert result == expected + finally: + await result.destinations["dest1"].scheduler.close() + + +def test_get_context(demo_context: Context) -> None: + fake_request = MagicMock() + fake_request.app.state.context = demo_context + + context = get_context(fake_request) + + expected = demo_context + assert context == expected + + +@pytest.mark.anyio +async def test_close_context( + job_root_dir: Path, + demo_applications: dict[str, ApplicatonConfiguration], +) -> None: + dest1 = MagicMock(Destination) + dest1.close = AsyncMock() + + context = Context( + applications=demo_applications, + job_root_dir=job_root_dir, + destination_picker=pick_first, + destinations={"dest1": dest1}, + ) + + await close_context(context) + + dest1.close.assert_called_once_with() diff --git a/bartender/tests/test_destinations.py b/bartender/tests/test_destinations.py index 5619b9e..7e57322 100644 --- a/bartender/tests/test_destinations.py +++ b/bartender/tests/test_destinations.py @@ -1,95 +1,123 @@ -from pathlib import Path -from typing import Any +from unittest.mock import AsyncMock, MagicMock import pytest - -from bartender._ssh_utils import SshConnectConfig -from bartender.destinations import Destination, build -from bartender.filesystems.local import LocalFileSystem -from bartender.filesystems.sftp import SftpFileSystem -from bartender.schedulers.memory import MemoryScheduler -from bartender.schedulers.slurm import SlurmScheduler - - -@pytest.mark.anyio -async def test_empty() -> None: - config: Any = {} - result = build(config) +from pydantic import ValidationError + +from bartender.destinations import ( + Destination, + DestinationConfig, + build, + default_destinations, +) +from bartender.filesystems.abstract import AbstractFileSystem +from bartender.filesystems.local import LocalFileSystem, LocalFileSystemConfig +from bartender.schedulers.abstract import AbstractScheduler +from bartender.schedulers.memory import MemoryScheduler, MemorySchedulerConfig + + +class TestDestinationConfig: + def test_unknown_scheduler_type(self) -> None: + with pytest.raises(ValidationError): + raw_config = { + "scheduler": {"type": "XYZscheduler"}, + "filesystem": {"type": "local"}, + } + DestinationConfig(**raw_config) + + def test_unknown_filesystem_type(self) -> None: + with pytest.raises(ValidationError): + raw_config = { + "scheduler": {"type": "memory"}, + "filesystem": {"type": "XYZfilesystem"}, + } + DestinationConfig(**raw_config) + + +def test_default_destinations() -> None: + destinations = default_destinations() expected = { - "": Destination(scheduler=MemoryScheduler(), filesystem=LocalFileSystem()), - } - assert result == expected - - -@pytest.mark.anyio -async def test_single_empty() -> None: - config: Any = {"dest1": {}} - with pytest.raises(KeyError): - build(config) - - -@pytest.mark.anyio -async def test_single_memory() -> None: - config: Any = {"dest1": {"scheduler": {"type": "memory"}}} - result = build(config) - - expected = { - "dest1": Destination(scheduler=MemoryScheduler()), - } - assert result == expected - - -@pytest.mark.anyio -async def test_single_memory_local() -> None: - config: Any = { - "dest1": {"scheduler": {"type": "memory"}, "filesystem": {"type": "local"}}, - } - result = build(config) - - expected = { - "dest1": Destination(scheduler=MemoryScheduler()), - } - assert result == expected - - -@pytest.mark.anyio -async def test_double() -> None: - config: Any = { - "dest1": {"scheduler": {"type": "memory", "slots": 42}}, - "dest2": { - "scheduler": { - "type": "slurm", - "partition": "mypartition", - "ssh_config": { - "hostname": "localhost", - }, - }, - "filesystem": { - "type": "sftp", - "ssh_config": { - "hostname": "localhost", - }, - "entry": "/scratch/jobs", - }, - }, - } - result = build(config) - - expected = { - "dest1": Destination( - scheduler=MemoryScheduler(slots=42), - filesystem=LocalFileSystem(), - ), - "dest2": Destination( - scheduler=SlurmScheduler( - ssh_config=SshConnectConfig(hostname="localhost"), - partition="mypartition", - ), - filesystem=SftpFileSystem( - ssh_config=SshConnectConfig(hostname="localhost"), - entry=Path("/scratch/jobs"), - ), + "": DestinationConfig( + scheduler=MemorySchedulerConfig(), + filesystem=LocalFileSystemConfig(), ), } - assert result == expected + assert destinations == expected + + +class TestDestination: + @pytest.mark.anyio + async def test_close(self) -> None: + scheduler = AsyncMock(AbstractScheduler) + filesystem = MagicMock(AbstractFileSystem) + + destination = Destination(scheduler=scheduler, filesystem=filesystem) + + await destination.close() + + filesystem.close.assert_called_once_with() + scheduler.close.assert_called_once_with() + + +class TestBuild: + def test_empty(self) -> None: + config: dict[str, DestinationConfig] = {} + + destinations = build(config) + + expected: dict[str, Destination] = {} + assert destinations == expected + + @pytest.mark.anyio + async def test_one_destination(self) -> None: + try: + config = { + "dest1": DestinationConfig( + scheduler=MemorySchedulerConfig(), + filesystem=LocalFileSystemConfig(), + ), + } + + destinations = build(config) + + expected = { + "dest1": Destination( + scheduler=MemoryScheduler(MemorySchedulerConfig()), + filesystem=LocalFileSystem(), + ), + } + assert destinations == expected + finally: + await destinations["dest1"].close() + await expected["dest1"].close() + + @pytest.mark.anyio + async def test_two_destinations(self) -> None: + try: + config = { + "dest1": DestinationConfig( + scheduler=MemorySchedulerConfig(), + filesystem=LocalFileSystemConfig(), + ), + "dest2": DestinationConfig( + scheduler=MemorySchedulerConfig(slots=2), + filesystem=LocalFileSystemConfig(), + ), + } + + destinations = build(config) + + expected = { + "dest1": Destination( + scheduler=MemoryScheduler(MemorySchedulerConfig()), + filesystem=LocalFileSystem(), + ), + "dest2": Destination( + scheduler=MemoryScheduler(MemorySchedulerConfig(slots=2)), + filesystem=LocalFileSystem(), + ), + } + assert destinations == expected + finally: + await destinations["dest1"].close() + await expected["dest1"].close() diff --git a/bartender/tests/test_picker.py b/bartender/tests/test_picker.py new file mode 100644 index 0000000..93f59ca --- /dev/null +++ b/bartender/tests/test_picker.py @@ -0,0 +1,92 @@ +from pathlib import Path + +import pytest + +from bartender.context import ApplicatonConfiguration, Context +from bartender.destinations import Destination +from bartender.picker import PickRound, pick_first + + +class TestPickFirst: + @pytest.mark.anyio + async def test_with2destinations_returns_first( + self, + demo_destination: Destination, + ) -> None: + context = Context( + destination_picker=pick_first, + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, + destinations={ + "d1": demo_destination, + "d2": demo_destination, + }, + job_root_dir=Path("/jobs"), + ) + actual = pick_first(context.job_root_dir / "job1", "app1", context) + + expected = "d1" + assert actual == expected + + @pytest.mark.anyio + async def test_nodestintations_returns_indexerror(self) -> None: + context = Context( + destination_picker=pick_first, + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, + destinations={}, + job_root_dir=Path("/jobs"), + ) + + with pytest.raises(IndexError): + pick_first(context.job_root_dir / "job1", "app1", context) + + +class TestPickRoundWith2Destinations: + @pytest.fixture + async def context(self, demo_destination: Destination) -> Context: + return Context( + destination_picker=pick_first, + applications={ + "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), + }, + destinations={ + "d1": demo_destination, + "d2": demo_destination, + }, + job_root_dir=Path("/jobs"), + ) + + @pytest.mark.anyio + async def test_firstcall_returns_first(self, context: Context) -> None: + picker = PickRound() + actual = picker(context.job_root_dir / "job1", "app1", context) + + expected = "d1" + assert actual == expected + + @pytest.mark.anyio + async def test_secondcall_returns_second(self, context: Context) -> None: + picker = PickRound() + # first call + picker(context.job_root_dir / "job1", "app1", context) + # second call + actual = picker(context.job_root_dir / "job1", "app1", context) + + expected = "d2" + assert actual == expected + + @pytest.mark.anyio + async def test_thirdcall_returns_first(self, context: Context) -> None: + picker = PickRound() + # 1st call + picker(context.job_root_dir / "job1", "app1", context) + # 2nd call + picker(context.job_root_dir / "job1", "app1", context) + # 3rd call + actual = picker(context.job_root_dir / "job1", "app1", context) + + expected = "d1" + assert actual == expected diff --git a/bartender/web/api/applications/submit.py b/bartender/web/api/applications/submit.py index 16e91ef..6afec11 100644 --- a/bartender/web/api/applications/submit.py +++ b/bartender/web/api/applications/submit.py @@ -1,8 +1,8 @@ from pathlib import Path -from bartender.config import Config +from bartender.context import Context from bartender.db.dao.job_dao import JobDAO -from bartender.destinations import Destination +from bartender.filesystems.abstract import AbstractFileSystem from bartender.schedulers.abstract import JobDescription @@ -11,7 +11,7 @@ async def submit( job_dir: Path, application: str, job_dao: JobDAO, - config: Config, + context: Context, ) -> None: """Submit job description to scheduler and store job id returned by scheduler in db. @@ -19,38 +19,39 @@ async def submit( :param job_dir: Location where job input files are located. :param application: Application name that should be run. :param job_dao: JobDAO object. - :param config: Config with applications and destinations. + :param context: Context with applications and destinations. """ - application_config = config.applications[application] + application_config = context.applications[application] description = application_config.description(job_dir) - destination, destinations_name = config.destination_picker( + destination_name = context.destination_picker( job_dir, application, - config, + context, ) + destination = context.destinations[destination_name] - await _upload_input_files(description, destination, config.job_root_dir) + await _upload_input_files(description, destination.filesystem, context.job_root_dir) internal_job_id = await destination.scheduler.submit(description) await job_dao.update_internal_job_id( external_job_id, internal_job_id, - destinations_name, + destination_name, ) async def _upload_input_files( description: JobDescription, - destination: Destination, + filesystem: AbstractFileSystem, job_root_dir: Path, ) -> None: - localized_description = destination.filesystem.localize_description( + localized_description = filesystem.localize_description( description, job_root_dir, ) - await destination.filesystem.upload( + await filesystem.upload( src=description, target=localized_description, ) diff --git a/bartender/web/api/applications/views.py b/bartender/web/api/applications/views.py index 23986d2..57ad3e1 100644 --- a/bartender/web/api/applications/views.py +++ b/bartender/web/api/applications/views.py @@ -4,6 +4,7 @@ from starlette.background import BackgroundTask from bartender.config import Config, get_config +from bartender.context import Context, get_context from bartender.db.dao.job_dao import JobDAO from bartender.db.models.user import User from bartender.filesystem import has_config_file @@ -49,7 +50,7 @@ async def upload_job( # noqa: WPS211 upload: UploadFile = File(description="Archive with config file for application"), job_dao: JobDAO = Depends(), submitter: User = Depends(current_active_user), - config: Config = Depends(get_config), + context: Context = Depends(get_context), ) -> RedirectResponse: """ Creates job model in the database, stage archive locally and submit to scheduler. @@ -59,14 +60,14 @@ async def upload_job( # noqa: WPS211 :param request: request object. :param job_dao: JobDAO object. :param submitter: User who submitted job. - :param config: Configuration with applications and destinations. + :param context: Context with applications and destinations. :raises IndexError: When job could not created inside database or when config file was not found. :raises KeyError: Application is invalid. :return: redirect response. """ - if application not in config.applications: - valid = config.applications.keys() + if application not in context.applications: + valid = context.applications.keys() raise KeyError(f"Invalid application. Valid applications: {valid}") job_id = await job_dao.create_job(upload.filename, application, submitter) if job_id is None: @@ -75,10 +76,10 @@ async def upload_job( # noqa: WPS211 job_dir = assemble_job( job_id, await current_api_token(submitter), - config.job_root_dir, + context.job_root_dir, ) await stage_job_input(job_dir, upload) - has_config_file(config.applications[application], job_dir) + has_config_file(context.applications[application], job_dir) task = BackgroundTask( submit, @@ -86,7 +87,7 @@ async def upload_job( # noqa: WPS211 job_dir, application, job_dao, - config, + context, ) url = request.url_for("retrieve_job", jobid=job_id) diff --git a/bartender/web/api/job/views.py b/bartender/web/api/job/views.py index c8c19f4..7088492 100644 --- a/bartender/web/api/job/views.py +++ b/bartender/web/api/job/views.py @@ -6,7 +6,7 @@ from sqlalchemy.exc import NoResultFound from starlette import status -from bartender.config import Config, get_config +from bartender.context import Context, get_context from bartender.db.dao.job_dao import JobDAO from bartender.db.models.job_model import Job from bartender.db.models.user import User @@ -23,7 +23,7 @@ async def retrieve_jobs( offset: int = 0, job_dao: JobDAO = Depends(), user: User = Depends(current_active_user), - config: Config = Depends(get_config), + context: Context = Depends(get_context), ) -> List[Job]: """ Retrieve all jobs of user from the database. @@ -32,7 +32,7 @@ async def retrieve_jobs( :param offset: offset of jobs. :param job_dao: JobDAO object. :param user: Current active user. - :param config: Config with destinations. + :param context: Context with destinations. :return: stream of jobs. """ # TODO now list jobs that user submitted, @@ -40,7 +40,7 @@ async def retrieve_jobs( # or are shared with current user jobs = await job_dao.get_all_jobs(limit=limit, offset=offset, user=user) # get current state for each job from scheduler - await sync_states(jobs, config.destinations, job_dao, config.job_root_dir) + await sync_states(jobs, context.destinations, job_dao, context.job_root_dir) return jobs @@ -49,7 +49,7 @@ async def retrieve_job( jobid: int, job_dao: JobDAO = Depends(), user: User = Depends(current_active_user), - config: Config = Depends(get_config), + context: Context = Depends(get_context), ) -> Job: """ Retrieve specific job from the database. @@ -57,7 +57,7 @@ async def retrieve_job( :param jobid: identifier of job instance. :param job_dao: JobDAO object. :param user: Current active user. - :param config: Config with destinations. + :param context: Context with destinations. :raises HTTPException: When job is not found or user is not allowed to see job. :return: job models. """ @@ -69,8 +69,8 @@ async def retrieve_job( # TODO When job has state==error then include URL to error page job = await job_dao.get_job(jobid=jobid, user=user) if job.destination is not None: - destination = config.destinations[job.destination] - await sync_state(job, job_dao, destination, config.job_root_dir) + destination = context.destinations[job.destination] + await sync_state(job, job_dao, destination, context.job_root_dir) return job except NoResultFound as exc: raise HTTPException( @@ -84,14 +84,14 @@ async def retrieve_job_stdout( jobid: int, job_dao: JobDAO = Depends(), user: User = Depends(current_active_user), - config: Config = Depends(get_config), + context: Context = Depends(get_context), ) -> FileResponse: """Retrieve stdout of a job. :param jobid: identifier of job instance. :param job_dao: JobDAO object. :param user: Current active user. - :param config: Config with destinations. + :param context: Context with destinations. :raises HTTPException: When job is not found or user is not allowed to see job. :return: stdout of job. """ @@ -99,12 +99,12 @@ async def retrieve_job_stdout( jobid=jobid, job_dao=job_dao, user=user, - config=config, + context=context, ) if job.state not in {"ok", "error"}: raise HTTPException( status_code=status.HTTP_425_TOO_EARLY, detail="Stdout not ready. Job has not completed.", ) - stdout: Path = config.job_root_dir / str(jobid) / "stdout.txt" + stdout: Path = context.job_root_dir / str(jobid) / "stdout.txt" return FileResponse(stdout) diff --git a/bartender/web/lifetime.py b/bartender/web/lifetime.py index ab6d747..4abf2da 100644 --- a/bartender/web/lifetime.py +++ b/bartender/web/lifetime.py @@ -1,16 +1,12 @@ -import logging -from pathlib import Path from typing import Awaitable, Callable from fastapi import FastAPI from bartender.config import build_config +from bartender.context import build_context, close_context from bartender.db.session import make_engine, make_session_factory -from bartender.destinations import Destination from bartender.settings import settings -logger = logging.getLogger(__name__) - def _setup_db(app: FastAPI) -> None: # pragma: no cover """ @@ -42,7 +38,7 @@ def register_startup_event( @app.on_event("startup") async def _startup() -> None: # noqa: WPS430 _setup_db(app) - _parse_config(app) + _parse_context(app) return _startup @@ -60,36 +56,26 @@ def register_shutdown_event( @app.on_event("shutdown") async def _shutdown() -> None: # noqa: WPS430 await app.state.db_engine.dispose() - await _teardown_confg(app) + await _teardown_context(app) return _shutdown -def _parse_config(app: FastAPI) -> None: +def _parse_context(app: FastAPI) -> None: """Parse configuration and instantiate applications, schedulers and filesystems. - Sets `app.state.config`. + Sets `app.state.config` and `app.state.context`. :param app: fastAPI application. """ - try: - config = build_config(settings.config_filename) - # Make sure job root dir exists. - config.job_root_dir.mkdir(exist_ok=True) - app.state.config = config - except FileNotFoundError: - fn = settings.config_filename - logger.warn(f"Unable to find {fn} falling back to config-example.yaml") - app.state.config = build_config(Path("config-example.yaml")) - - -async def _teardown_confg(app: FastAPI) -> None: + config = build_config(settings.config_filename) + app.state.config = config + app.state.context = build_context(config) + + +async def _teardown_context(app: FastAPI) -> None: """Teardown schedulers and file systems. :param app: fastAPI application. """ - destinations: dict[str, Destination] = app.state.config.destinations - for destination in destinations.values(): - await destination.scheduler.close() - if destination.filesystem is not None: - destination.filesystem.close() + await close_context(app.state.context) diff --git a/config-example.yaml b/config-example.yaml index b0e750a..39d0c4c 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -5,9 +5,13 @@ # To use a custom picker set `destination_picker` to a `:` # The picker should have type bartender.config.DestinationPicker . applications: - app1: - command: app1 $config - config: workflow.cfg + # The label of the application + wc: + # Command line interface command to run application. + # `$config` occurences will be replaced value of config parameter. + command: wc $config + # Name of config file that the application needs + config: README.md destinations: local: scheduler: From 879c1b3b97dcedd1f4af9a02949d0ca02b7e4c77 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Mon, 9 Jan 2023 16:33:42 +0100 Subject: [PATCH 13/36] Dont test default job_root_dir As it requires sub dir in /tmp which is properly isolated --- bartender/tests/test_config.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bartender/tests/test_config.py b/bartender/tests/test_config.py index 8ccafc4..a29a1b3 100644 --- a/bartender/tests/test_config.py +++ b/bartender/tests/test_config.py @@ -1,5 +1,4 @@ from pathlib import Path -from tempfile import gettempdir from typing import Any from unittest.mock import MagicMock @@ -36,15 +35,16 @@ def test_zero_apps(self) -> None: ): Config(**raw_config) - def test_minimal(self) -> None: + def test_minimal(self, tmp_path: Path) -> None: raw_config: Any = { + "job_root_dir": str(tmp_path), "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, } config = Config(**raw_config) expected = Config( destination_picker="bartender.picker:pick_first", - job_root_dir=Path(gettempdir()) / "jobs", + job_root_dir=tmp_path, applications={ "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), }, @@ -98,6 +98,7 @@ def test_no_defaults(self, tmp_path: Path) -> None: async def test_build_config_minimal(tmp_path: Path) -> None: file = tmp_path / "config.yaml" config: Any = { + "job_root_dir": str(tmp_path), "applications": {"app1": {"command": "echo", "config": "/etc/passwd"}}, } with file.open("w") as handle: @@ -107,7 +108,7 @@ async def test_build_config_minimal(tmp_path: Path) -> None: expected = Config( destination_picker="bartender.picker:pick_first", - job_root_dir=Path(gettempdir()) / "jobs", + job_root_dir=tmp_path, applications={ "app1": ApplicatonConfiguration(command="echo", config="/etc/passwd"), }, From d77bfaf287828aa8f218330a8776034d2b71aa8f Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Tue, 10 Jan 2023 11:33:15 +0100 Subject: [PATCH 14/36] Create tables before starting app Fix for https://github.com/i-VRESSE/bartender/pull/6#issuecomment-1376947190 --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 15e1a89..f4ce1b1 100644 --- a/README.md +++ b/README.md @@ -58,11 +58,7 @@ This project was generated using [fastapi_template](https://github.com/s3rius/Fa poetry install ``` -5. Run the application and the database - - ```bash - bartender serve - ``` +5. Run the database for storing users and jobs. Important: **In another terminal** @@ -75,13 +71,19 @@ This project was generated using [fastapi_template](https://github.com/s3rius/Fa postgres:13.6-bullseye ``` -6. Migrate the database +6. Create tables in the database ```bash alembic upgrade "head" ``` -7. Go to the interactive API documentation generated by FastAPI +7. Run the application + + ```bash + bartender serve + ``` + +8. Go to the interactive API documentation generated by FastAPI From 24fa549d37f307d6a6b0a8cd4db196bac7ac693b Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Tue, 10 Jan 2023 14:28:25 +0100 Subject: [PATCH 15/36] Each destination needs a filesystem --- config-example.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config-example.yaml b/config-example.yaml index 39d0c4c..7491461 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -17,6 +17,8 @@ destinations: scheduler: type: memory slots: 1 + filesystem: + type: local ## Example of running jobs by current user on snellius. # remote: # scheduler: From e03ddf25903f5fb5d9bd24f855be9dd565cae3c3 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Thu, 12 Jan 2023 11:06:31 +0100 Subject: [PATCH 16/36] Add GET /api/application/{application} route --- bartender/tests/web/test_application.py | 17 +++++++++++++++++ bartender/web/api/applications/views.py | 17 +++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/bartender/tests/web/test_application.py b/bartender/tests/web/test_application.py index 3ee2720..aa435ac 100644 --- a/bartender/tests/web/test_application.py +++ b/bartender/tests/web/test_application.py @@ -21,6 +21,23 @@ async def test_list_applications( assert apps == ["app1"] +@pytest.mark.anyio +async def test_get_application( + fastapi_app: FastAPI, + client: AsyncClient, +) -> None: + url = fastapi_app.url_path_for("get_application", application="app1") + + response = await client.get(url) + app = response.json() + + expected = { + "command": "wc $config", + "config": "job.ini", + } + assert app == expected + + @pytest.mark.anyio async def test_upload( # noqa: WPS218 fastapi_app: FastAPI, diff --git a/bartender/web/api/applications/views.py b/bartender/web/api/applications/views.py index 57ad3e1..512e215 100644 --- a/bartender/web/api/applications/views.py +++ b/bartender/web/api/applications/views.py @@ -3,7 +3,7 @@ from starlette import status from starlette.background import BackgroundTask -from bartender.config import Config, get_config +from bartender.config import ApplicatonConfiguration, Config, get_config from bartender.context import Context, get_context from bartender.db.dao.job_dao import JobDAO from bartender.db.models.user import User @@ -23,10 +23,23 @@ def list_applications(config: Config = Depends(get_config)) -> list[str]: :param config: Config with applications. :return: The list. """ - # TODO also return values or at least the expected config file name for each app return list(config.applications.keys()) +@router.get("/{application}", response_model=ApplicatonConfiguration) +def get_application( + application: str, + config: Config = Depends(get_config), +) -> ApplicatonConfiguration: + """Retrieve application configuration. + + :param application: Name of application + :param config: Config with applications. + :return: The application config. + """ + return config.applications[application] + + @router.put( "/{application}/job", status_code=status.HTTP_303_SEE_OTHER, From ee4160ed64da8ddb5bdbf55dd84167b5394f40aa Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Thu, 12 Jan 2023 11:07:11 +0100 Subject: [PATCH 17/36] Add example config file to run haddock3 commands --- config-haddock3.yaml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 config-haddock3.yaml diff --git a/config-haddock3.yaml b/config-haddock3.yaml new file mode 100644 index 0000000..b96a32f --- /dev/null +++ b/config-haddock3.yaml @@ -0,0 +1,28 @@ +destination_picker: bartender.config:pick_first +applications: + haddock3: + command: haddock3 $config + config: workflow.cfg + # Below are other haddock3 commands, + # that could be hosted by bartender web service. + # TODO add support for label and parameters keys + haddock3-pp: + label: HADDOCK3 preprocess PDB files + command: haddock3-pp --output-directory . *.pdb + haddock3-analyse: + command: haddock3-analyse --run-dir . --modules $modules + parameters: + modules: + type: array + items: + type: int + haddock3-score: + command: haddock3-score --outputpdb --outputpsf $config + config: complex.pdb +destinations: + local: + scheduler: + type: memory + slots: 1 + filesystem: + type: local From b57ba0ff2052b0a68c4703b22795f80192f73ba3 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 13 Jan 2023 10:43:19 +0100 Subject: [PATCH 18/36] Move /bartender/tests to /tests + /bartender to /src/bartender Fixes #10 --- .pre-commit-config.yaml | 3 +- README.md | 36 ++++++++++--------- alembic.ini | 2 +- bartender/tests/web/__init__.py | 0 pyproject.toml | 2 +- {bartender => src/bartender}/__init__.py | 0 {bartender => src/bartender}/__main__.py | 0 {bartender => src/bartender}/_ssh_utils.py | 0 {bartender => src/bartender}/config.py | 0 {bartender => src/bartender}/context.py | 0 {bartender => src/bartender}/db/base.py | 0 .../bartender}/db/dao/__init__.py | 0 .../bartender}/db/dao/job_dao.py | 0 .../bartender}/db/dao/user_dao.py | 0 .../bartender}/db/dependencies.py | 0 {bartender => src/bartender}/db/meta.py | 0 .../bartender}/db/migrations/__init__.py | 0 .../bartender}/db/migrations/env.py | 0 .../bartender}/db/migrations/script.py.mako | 0 .../versions/2022-08-26-13-06_58af0f14c426.py | 0 .../versions/2022-08-30-15-39_300833a7a4c8.py | 0 .../versions/2022-08-30-17-20_ef872771a841.py | 0 .../versions/2022-09-09-14-54_a31176b2cffa.py | 0 .../versions/2022-11-10-15-25_024e0181541b.py | 0 .../db/migrations/versions/__init__.py | 0 .../bartender}/db/models/__init__.py | 0 .../bartender}/db/models/job_model.py | 0 .../bartender}/db/models/user.py | 0 {bartender => src/bartender}/db/session.py | 0 {bartender => src/bartender}/db/utils.py | 0 {bartender => src/bartender}/destinations.py | 0 .../bartender}/filesystem/__init__.py | 0 .../bartender}/filesystem/assemble_job.py | 0 .../bartender}/filesystem/stage_job_input.py | 0 .../bartender}/filesystems/__init__.py | 0 .../bartender}/filesystems/abstract.py | 0 .../bartender}/filesystems/build.py | 0 .../bartender}/filesystems/local.py | 0 .../bartender}/filesystems/sftp.py | 0 {bartender => src/bartender}/picker.py | 0 .../bartender}/schedulers/__init__.py | 0 .../bartender}/schedulers/abstract.py | 0 .../bartender}/schedulers/build.py | 0 .../bartender}/schedulers/memory.py | 0 .../bartender}/schedulers/runner.py | 0 .../bartender}/schedulers/slurm.py | 0 .../bartender}/services/__init__.py | 0 {bartender => src/bartender}/settings.py | 0 .../static/docs/redoc.standalone.js | 0 .../static/docs/swagger-ui-bundle.js | 0 .../bartender}/static/docs/swagger-ui.css | 0 {bartender => src/bartender}/web/__init__.py | 0 .../bartender}/web/api/__init__.py | 0 .../web/api/applications/__init__.py | 0 .../bartender}/web/api/applications/submit.py | 0 .../bartender}/web/api/applications/views.py | 0 .../bartender}/web/api/job/__init__.py | 0 .../bartender}/web/api/job/schema.py | 0 .../bartender}/web/api/job/sync.py | 0 .../bartender}/web/api/job/views.py | 0 .../bartender}/web/api/monitoring/__init__.py | 0 .../bartender}/web/api/monitoring/views.py | 0 .../bartender}/web/api/router.py | 0 .../bartender}/web/api/user/__init__.py | 0 .../bartender}/web/api/user/schema.py | 0 .../bartender}/web/api/user/views.py | 0 .../bartender}/web/application.py | 0 {bartender => src/bartender}/web/lifetime.py | 0 .../bartender}/web/users/__init__.py | 0 .../bartender}/web/users/manager.py | 0 .../bartender}/web/users/orcid.py | 0 .../bartender}/web/users/router.py | 0 .../bartender}/web/users/schema.py | 0 {bartender/tests => tests}/__init__.py | 0 {bartender/tests => tests}/conftest.py | 0 .../tests => tests}/filesystems/__init__.py | 0 .../tests => tests}/filesystems/test_build.py | 0 .../tests => tests}/schedulers/test_build.py | 0 .../tests => tests}/schedulers/test_memory.py | 0 .../tests => tests}/schedulers/test_slurm.py | 0 {bartender/tests => tests}/test_config.py | 0 {bartender/tests => tests}/test_context.py | 0 .../tests => tests}/test_destinations.py | 0 {bartender/tests => tests}/test_filesystem.py | 0 {bartender/tests => tests}/test_picker.py | 0 tests/web/__init__.py | 1 + .../tests => tests}/web/test_application.py | 0 {bartender/tests => tests}/web/test_health.py | 0 {bartender/tests => tests}/web/test_job.py | 0 {bartender/tests => tests}/web/test_user.py | 0 90 files changed, 25 insertions(+), 19 deletions(-) delete mode 100644 bartender/tests/web/__init__.py rename {bartender => src/bartender}/__init__.py (100%) rename {bartender => src/bartender}/__main__.py (100%) rename {bartender => src/bartender}/_ssh_utils.py (100%) rename {bartender => src/bartender}/config.py (100%) rename {bartender => src/bartender}/context.py (100%) rename {bartender => src/bartender}/db/base.py (100%) rename {bartender => src/bartender}/db/dao/__init__.py (100%) rename {bartender => src/bartender}/db/dao/job_dao.py (100%) rename {bartender => src/bartender}/db/dao/user_dao.py (100%) rename {bartender => src/bartender}/db/dependencies.py (100%) rename {bartender => src/bartender}/db/meta.py (100%) rename {bartender => src/bartender}/db/migrations/__init__.py (100%) rename {bartender => src/bartender}/db/migrations/env.py (100%) rename {bartender => src/bartender}/db/migrations/script.py.mako (100%) rename {bartender => src/bartender}/db/migrations/versions/2022-08-26-13-06_58af0f14c426.py (100%) rename {bartender => src/bartender}/db/migrations/versions/2022-08-30-15-39_300833a7a4c8.py (100%) rename {bartender => src/bartender}/db/migrations/versions/2022-08-30-17-20_ef872771a841.py (100%) rename {bartender => src/bartender}/db/migrations/versions/2022-09-09-14-54_a31176b2cffa.py (100%) rename {bartender => src/bartender}/db/migrations/versions/2022-11-10-15-25_024e0181541b.py (100%) rename {bartender => src/bartender}/db/migrations/versions/__init__.py (100%) rename {bartender => src/bartender}/db/models/__init__.py (100%) rename {bartender => src/bartender}/db/models/job_model.py (100%) rename {bartender => src/bartender}/db/models/user.py (100%) rename {bartender => src/bartender}/db/session.py (100%) rename {bartender => src/bartender}/db/utils.py (100%) rename {bartender => src/bartender}/destinations.py (100%) rename {bartender => src/bartender}/filesystem/__init__.py (100%) rename {bartender => src/bartender}/filesystem/assemble_job.py (100%) rename {bartender => src/bartender}/filesystem/stage_job_input.py (100%) rename {bartender => src/bartender}/filesystems/__init__.py (100%) rename {bartender => src/bartender}/filesystems/abstract.py (100%) rename {bartender => src/bartender}/filesystems/build.py (100%) rename {bartender => src/bartender}/filesystems/local.py (100%) rename {bartender => src/bartender}/filesystems/sftp.py (100%) rename {bartender => src/bartender}/picker.py (100%) rename {bartender => src/bartender}/schedulers/__init__.py (100%) rename {bartender => src/bartender}/schedulers/abstract.py (100%) rename {bartender => src/bartender}/schedulers/build.py (100%) rename {bartender => src/bartender}/schedulers/memory.py (100%) rename {bartender => src/bartender}/schedulers/runner.py (100%) rename {bartender => src/bartender}/schedulers/slurm.py (100%) rename {bartender => src/bartender}/services/__init__.py (100%) rename {bartender => src/bartender}/settings.py (100%) rename {bartender => src/bartender}/static/docs/redoc.standalone.js (100%) rename {bartender => src/bartender}/static/docs/swagger-ui-bundle.js (100%) rename {bartender => src/bartender}/static/docs/swagger-ui.css (100%) rename {bartender => src/bartender}/web/__init__.py (100%) rename {bartender => src/bartender}/web/api/__init__.py (100%) rename {bartender => src/bartender}/web/api/applications/__init__.py (100%) rename {bartender => src/bartender}/web/api/applications/submit.py (100%) rename {bartender => src/bartender}/web/api/applications/views.py (100%) rename {bartender => src/bartender}/web/api/job/__init__.py (100%) rename {bartender => src/bartender}/web/api/job/schema.py (100%) rename {bartender => src/bartender}/web/api/job/sync.py (100%) rename {bartender => src/bartender}/web/api/job/views.py (100%) rename {bartender => src/bartender}/web/api/monitoring/__init__.py (100%) rename {bartender => src/bartender}/web/api/monitoring/views.py (100%) rename {bartender => src/bartender}/web/api/router.py (100%) rename {bartender => src/bartender}/web/api/user/__init__.py (100%) rename {bartender => src/bartender}/web/api/user/schema.py (100%) rename {bartender => src/bartender}/web/api/user/views.py (100%) rename {bartender => src/bartender}/web/application.py (100%) rename {bartender => src/bartender}/web/lifetime.py (100%) rename {bartender => src/bartender}/web/users/__init__.py (100%) rename {bartender => src/bartender}/web/users/manager.py (100%) rename {bartender => src/bartender}/web/users/orcid.py (100%) rename {bartender => src/bartender}/web/users/router.py (100%) rename {bartender => src/bartender}/web/users/schema.py (100%) rename {bartender/tests => tests}/__init__.py (100%) rename {bartender/tests => tests}/conftest.py (100%) rename {bartender/tests => tests}/filesystems/__init__.py (100%) rename {bartender/tests => tests}/filesystems/test_build.py (100%) rename {bartender/tests => tests}/schedulers/test_build.py (100%) rename {bartender/tests => tests}/schedulers/test_memory.py (100%) rename {bartender/tests => tests}/schedulers/test_slurm.py (100%) rename {bartender/tests => tests}/test_config.py (100%) rename {bartender/tests => tests}/test_context.py (100%) rename {bartender/tests => tests}/test_destinations.py (100%) rename {bartender/tests => tests}/test_filesystem.py (100%) rename {bartender/tests => tests}/test_picker.py (100%) create mode 100644 tests/web/__init__.py rename {bartender/tests => tests}/web/test_application.py (100%) rename {bartender/tests => tests}/web/test_health.py (100%) rename {bartender/tests => tests}/web/test_job.py (100%) rename {bartender/tests => tests}/web/test_user.py (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index db193a9..3969d41 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -59,4 +59,5 @@ repos: types: [python] pass_filenames: false args: - - "bartender" + - "src" + - "tests" diff --git a/README.md b/README.md index f4ce1b1..315d4ff 100644 --- a/README.md +++ b/README.md @@ -92,22 +92,26 @@ This project was generated using [fastapi_template](https://github.com/s3rius/Fa ## [Project structure](#project-structure) ```bash -$ tree "bartender" -bartender -├── db # module contains db configurations -│   ├── dao # Data Access Objects. Contains different classes to interact with database. -│   └── models # Package contains different models for ORMs. -├── __main__.py # Startup script. Starts uvicorn. -├── services # Package for different external services such as rabbit or redis etc. -├── settings.py # Main configuration settings for project. -├── static # Static content. -├── tests # Tests for project. - └── conftest.py # Fixtures for all tests. -└── web # Package contains web server. Handlers, startup config. - ├── api # Package with all handlers. - │   └── router.py # Main router. - ├── application.py # FastAPI application configuration. - └── lifetime.py # Contains actions to perform on startup and shutdown. +$ tree . +├── tests # Tests for project. +│ └── conftest.py # Fixtures for all tests. +├── docs # Documentatin for project. +| ├── index.rst # Main documentation page. +│ └── conf.py # Sphinx config file. +└── src + └── bartender + ├── db # module contains db configurations + │   ├── dao # Data Access Objects. Contains different classes to interact with database. + │   └── models # Package contains different models for ORMs. + ├── __main__.py # Startup script. Starts uvicorn. + ├── services # Package for different external services such as rabbit or redis etc. + ├── settings.py # Main configuration settings for project. + ├── static # Static content. + ├── web # Package contains web server. Handlers, startup config. + ├── api # Package with all handlers. + │   └── router.py # Main router. + ├── application.py # FastAPI application configuration. + └── lifetime.py # Contains actions to perform on startup and shutdown. ``` ## [Configuration](#configuration) diff --git a/alembic.ini b/alembic.ini index 6d22970..b4fd154 100644 --- a/alembic.ini +++ b/alembic.ini @@ -1,5 +1,5 @@ [alembic] -script_location = bartender/db/migrations +script_location = src/bartender/db/migrations file_template = %%(year)d-%%(month).2d-%%(day).2d-%%(hour).2d-%%(minute).2d_%%(rev)s prepend_sys_path = . output_encoding = utf-8 diff --git a/bartender/tests/web/__init__.py b/bartender/tests/web/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pyproject.toml b/pyproject.toml index 579fe36..ec2cd37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,7 @@ bartender = 'bartender.__main__:main' [tool.isort] profile = "black" multi_line_output = 3 -src_paths = ["bartender",] +src_paths = ["src","tests"] [tool.mypy] strict = true diff --git a/bartender/__init__.py b/src/bartender/__init__.py similarity index 100% rename from bartender/__init__.py rename to src/bartender/__init__.py diff --git a/bartender/__main__.py b/src/bartender/__main__.py similarity index 100% rename from bartender/__main__.py rename to src/bartender/__main__.py diff --git a/bartender/_ssh_utils.py b/src/bartender/_ssh_utils.py similarity index 100% rename from bartender/_ssh_utils.py rename to src/bartender/_ssh_utils.py diff --git a/bartender/config.py b/src/bartender/config.py similarity index 100% rename from bartender/config.py rename to src/bartender/config.py diff --git a/bartender/context.py b/src/bartender/context.py similarity index 100% rename from bartender/context.py rename to src/bartender/context.py diff --git a/bartender/db/base.py b/src/bartender/db/base.py similarity index 100% rename from bartender/db/base.py rename to src/bartender/db/base.py diff --git a/bartender/db/dao/__init__.py b/src/bartender/db/dao/__init__.py similarity index 100% rename from bartender/db/dao/__init__.py rename to src/bartender/db/dao/__init__.py diff --git a/bartender/db/dao/job_dao.py b/src/bartender/db/dao/job_dao.py similarity index 100% rename from bartender/db/dao/job_dao.py rename to src/bartender/db/dao/job_dao.py diff --git a/bartender/db/dao/user_dao.py b/src/bartender/db/dao/user_dao.py similarity index 100% rename from bartender/db/dao/user_dao.py rename to src/bartender/db/dao/user_dao.py diff --git a/bartender/db/dependencies.py b/src/bartender/db/dependencies.py similarity index 100% rename from bartender/db/dependencies.py rename to src/bartender/db/dependencies.py diff --git a/bartender/db/meta.py b/src/bartender/db/meta.py similarity index 100% rename from bartender/db/meta.py rename to src/bartender/db/meta.py diff --git a/bartender/db/migrations/__init__.py b/src/bartender/db/migrations/__init__.py similarity index 100% rename from bartender/db/migrations/__init__.py rename to src/bartender/db/migrations/__init__.py diff --git a/bartender/db/migrations/env.py b/src/bartender/db/migrations/env.py similarity index 100% rename from bartender/db/migrations/env.py rename to src/bartender/db/migrations/env.py diff --git a/bartender/db/migrations/script.py.mako b/src/bartender/db/migrations/script.py.mako similarity index 100% rename from bartender/db/migrations/script.py.mako rename to src/bartender/db/migrations/script.py.mako diff --git a/bartender/db/migrations/versions/2022-08-26-13-06_58af0f14c426.py b/src/bartender/db/migrations/versions/2022-08-26-13-06_58af0f14c426.py similarity index 100% rename from bartender/db/migrations/versions/2022-08-26-13-06_58af0f14c426.py rename to src/bartender/db/migrations/versions/2022-08-26-13-06_58af0f14c426.py diff --git a/bartender/db/migrations/versions/2022-08-30-15-39_300833a7a4c8.py b/src/bartender/db/migrations/versions/2022-08-30-15-39_300833a7a4c8.py similarity index 100% rename from bartender/db/migrations/versions/2022-08-30-15-39_300833a7a4c8.py rename to src/bartender/db/migrations/versions/2022-08-30-15-39_300833a7a4c8.py diff --git a/bartender/db/migrations/versions/2022-08-30-17-20_ef872771a841.py b/src/bartender/db/migrations/versions/2022-08-30-17-20_ef872771a841.py similarity index 100% rename from bartender/db/migrations/versions/2022-08-30-17-20_ef872771a841.py rename to src/bartender/db/migrations/versions/2022-08-30-17-20_ef872771a841.py diff --git a/bartender/db/migrations/versions/2022-09-09-14-54_a31176b2cffa.py b/src/bartender/db/migrations/versions/2022-09-09-14-54_a31176b2cffa.py similarity index 100% rename from bartender/db/migrations/versions/2022-09-09-14-54_a31176b2cffa.py rename to src/bartender/db/migrations/versions/2022-09-09-14-54_a31176b2cffa.py diff --git a/bartender/db/migrations/versions/2022-11-10-15-25_024e0181541b.py b/src/bartender/db/migrations/versions/2022-11-10-15-25_024e0181541b.py similarity index 100% rename from bartender/db/migrations/versions/2022-11-10-15-25_024e0181541b.py rename to src/bartender/db/migrations/versions/2022-11-10-15-25_024e0181541b.py diff --git a/bartender/db/migrations/versions/__init__.py b/src/bartender/db/migrations/versions/__init__.py similarity index 100% rename from bartender/db/migrations/versions/__init__.py rename to src/bartender/db/migrations/versions/__init__.py diff --git a/bartender/db/models/__init__.py b/src/bartender/db/models/__init__.py similarity index 100% rename from bartender/db/models/__init__.py rename to src/bartender/db/models/__init__.py diff --git a/bartender/db/models/job_model.py b/src/bartender/db/models/job_model.py similarity index 100% rename from bartender/db/models/job_model.py rename to src/bartender/db/models/job_model.py diff --git a/bartender/db/models/user.py b/src/bartender/db/models/user.py similarity index 100% rename from bartender/db/models/user.py rename to src/bartender/db/models/user.py diff --git a/bartender/db/session.py b/src/bartender/db/session.py similarity index 100% rename from bartender/db/session.py rename to src/bartender/db/session.py diff --git a/bartender/db/utils.py b/src/bartender/db/utils.py similarity index 100% rename from bartender/db/utils.py rename to src/bartender/db/utils.py diff --git a/bartender/destinations.py b/src/bartender/destinations.py similarity index 100% rename from bartender/destinations.py rename to src/bartender/destinations.py diff --git a/bartender/filesystem/__init__.py b/src/bartender/filesystem/__init__.py similarity index 100% rename from bartender/filesystem/__init__.py rename to src/bartender/filesystem/__init__.py diff --git a/bartender/filesystem/assemble_job.py b/src/bartender/filesystem/assemble_job.py similarity index 100% rename from bartender/filesystem/assemble_job.py rename to src/bartender/filesystem/assemble_job.py diff --git a/bartender/filesystem/stage_job_input.py b/src/bartender/filesystem/stage_job_input.py similarity index 100% rename from bartender/filesystem/stage_job_input.py rename to src/bartender/filesystem/stage_job_input.py diff --git a/bartender/filesystems/__init__.py b/src/bartender/filesystems/__init__.py similarity index 100% rename from bartender/filesystems/__init__.py rename to src/bartender/filesystems/__init__.py diff --git a/bartender/filesystems/abstract.py b/src/bartender/filesystems/abstract.py similarity index 100% rename from bartender/filesystems/abstract.py rename to src/bartender/filesystems/abstract.py diff --git a/bartender/filesystems/build.py b/src/bartender/filesystems/build.py similarity index 100% rename from bartender/filesystems/build.py rename to src/bartender/filesystems/build.py diff --git a/bartender/filesystems/local.py b/src/bartender/filesystems/local.py similarity index 100% rename from bartender/filesystems/local.py rename to src/bartender/filesystems/local.py diff --git a/bartender/filesystems/sftp.py b/src/bartender/filesystems/sftp.py similarity index 100% rename from bartender/filesystems/sftp.py rename to src/bartender/filesystems/sftp.py diff --git a/bartender/picker.py b/src/bartender/picker.py similarity index 100% rename from bartender/picker.py rename to src/bartender/picker.py diff --git a/bartender/schedulers/__init__.py b/src/bartender/schedulers/__init__.py similarity index 100% rename from bartender/schedulers/__init__.py rename to src/bartender/schedulers/__init__.py diff --git a/bartender/schedulers/abstract.py b/src/bartender/schedulers/abstract.py similarity index 100% rename from bartender/schedulers/abstract.py rename to src/bartender/schedulers/abstract.py diff --git a/bartender/schedulers/build.py b/src/bartender/schedulers/build.py similarity index 100% rename from bartender/schedulers/build.py rename to src/bartender/schedulers/build.py diff --git a/bartender/schedulers/memory.py b/src/bartender/schedulers/memory.py similarity index 100% rename from bartender/schedulers/memory.py rename to src/bartender/schedulers/memory.py diff --git a/bartender/schedulers/runner.py b/src/bartender/schedulers/runner.py similarity index 100% rename from bartender/schedulers/runner.py rename to src/bartender/schedulers/runner.py diff --git a/bartender/schedulers/slurm.py b/src/bartender/schedulers/slurm.py similarity index 100% rename from bartender/schedulers/slurm.py rename to src/bartender/schedulers/slurm.py diff --git a/bartender/services/__init__.py b/src/bartender/services/__init__.py similarity index 100% rename from bartender/services/__init__.py rename to src/bartender/services/__init__.py diff --git a/bartender/settings.py b/src/bartender/settings.py similarity index 100% rename from bartender/settings.py rename to src/bartender/settings.py diff --git a/bartender/static/docs/redoc.standalone.js b/src/bartender/static/docs/redoc.standalone.js similarity index 100% rename from bartender/static/docs/redoc.standalone.js rename to src/bartender/static/docs/redoc.standalone.js diff --git a/bartender/static/docs/swagger-ui-bundle.js b/src/bartender/static/docs/swagger-ui-bundle.js similarity index 100% rename from bartender/static/docs/swagger-ui-bundle.js rename to src/bartender/static/docs/swagger-ui-bundle.js diff --git a/bartender/static/docs/swagger-ui.css b/src/bartender/static/docs/swagger-ui.css similarity index 100% rename from bartender/static/docs/swagger-ui.css rename to src/bartender/static/docs/swagger-ui.css diff --git a/bartender/web/__init__.py b/src/bartender/web/__init__.py similarity index 100% rename from bartender/web/__init__.py rename to src/bartender/web/__init__.py diff --git a/bartender/web/api/__init__.py b/src/bartender/web/api/__init__.py similarity index 100% rename from bartender/web/api/__init__.py rename to src/bartender/web/api/__init__.py diff --git a/bartender/web/api/applications/__init__.py b/src/bartender/web/api/applications/__init__.py similarity index 100% rename from bartender/web/api/applications/__init__.py rename to src/bartender/web/api/applications/__init__.py diff --git a/bartender/web/api/applications/submit.py b/src/bartender/web/api/applications/submit.py similarity index 100% rename from bartender/web/api/applications/submit.py rename to src/bartender/web/api/applications/submit.py diff --git a/bartender/web/api/applications/views.py b/src/bartender/web/api/applications/views.py similarity index 100% rename from bartender/web/api/applications/views.py rename to src/bartender/web/api/applications/views.py diff --git a/bartender/web/api/job/__init__.py b/src/bartender/web/api/job/__init__.py similarity index 100% rename from bartender/web/api/job/__init__.py rename to src/bartender/web/api/job/__init__.py diff --git a/bartender/web/api/job/schema.py b/src/bartender/web/api/job/schema.py similarity index 100% rename from bartender/web/api/job/schema.py rename to src/bartender/web/api/job/schema.py diff --git a/bartender/web/api/job/sync.py b/src/bartender/web/api/job/sync.py similarity index 100% rename from bartender/web/api/job/sync.py rename to src/bartender/web/api/job/sync.py diff --git a/bartender/web/api/job/views.py b/src/bartender/web/api/job/views.py similarity index 100% rename from bartender/web/api/job/views.py rename to src/bartender/web/api/job/views.py diff --git a/bartender/web/api/monitoring/__init__.py b/src/bartender/web/api/monitoring/__init__.py similarity index 100% rename from bartender/web/api/monitoring/__init__.py rename to src/bartender/web/api/monitoring/__init__.py diff --git a/bartender/web/api/monitoring/views.py b/src/bartender/web/api/monitoring/views.py similarity index 100% rename from bartender/web/api/monitoring/views.py rename to src/bartender/web/api/monitoring/views.py diff --git a/bartender/web/api/router.py b/src/bartender/web/api/router.py similarity index 100% rename from bartender/web/api/router.py rename to src/bartender/web/api/router.py diff --git a/bartender/web/api/user/__init__.py b/src/bartender/web/api/user/__init__.py similarity index 100% rename from bartender/web/api/user/__init__.py rename to src/bartender/web/api/user/__init__.py diff --git a/bartender/web/api/user/schema.py b/src/bartender/web/api/user/schema.py similarity index 100% rename from bartender/web/api/user/schema.py rename to src/bartender/web/api/user/schema.py diff --git a/bartender/web/api/user/views.py b/src/bartender/web/api/user/views.py similarity index 100% rename from bartender/web/api/user/views.py rename to src/bartender/web/api/user/views.py diff --git a/bartender/web/application.py b/src/bartender/web/application.py similarity index 100% rename from bartender/web/application.py rename to src/bartender/web/application.py diff --git a/bartender/web/lifetime.py b/src/bartender/web/lifetime.py similarity index 100% rename from bartender/web/lifetime.py rename to src/bartender/web/lifetime.py diff --git a/bartender/web/users/__init__.py b/src/bartender/web/users/__init__.py similarity index 100% rename from bartender/web/users/__init__.py rename to src/bartender/web/users/__init__.py diff --git a/bartender/web/users/manager.py b/src/bartender/web/users/manager.py similarity index 100% rename from bartender/web/users/manager.py rename to src/bartender/web/users/manager.py diff --git a/bartender/web/users/orcid.py b/src/bartender/web/users/orcid.py similarity index 100% rename from bartender/web/users/orcid.py rename to src/bartender/web/users/orcid.py diff --git a/bartender/web/users/router.py b/src/bartender/web/users/router.py similarity index 100% rename from bartender/web/users/router.py rename to src/bartender/web/users/router.py diff --git a/bartender/web/users/schema.py b/src/bartender/web/users/schema.py similarity index 100% rename from bartender/web/users/schema.py rename to src/bartender/web/users/schema.py diff --git a/bartender/tests/__init__.py b/tests/__init__.py similarity index 100% rename from bartender/tests/__init__.py rename to tests/__init__.py diff --git a/bartender/tests/conftest.py b/tests/conftest.py similarity index 100% rename from bartender/tests/conftest.py rename to tests/conftest.py diff --git a/bartender/tests/filesystems/__init__.py b/tests/filesystems/__init__.py similarity index 100% rename from bartender/tests/filesystems/__init__.py rename to tests/filesystems/__init__.py diff --git a/bartender/tests/filesystems/test_build.py b/tests/filesystems/test_build.py similarity index 100% rename from bartender/tests/filesystems/test_build.py rename to tests/filesystems/test_build.py diff --git a/bartender/tests/schedulers/test_build.py b/tests/schedulers/test_build.py similarity index 100% rename from bartender/tests/schedulers/test_build.py rename to tests/schedulers/test_build.py diff --git a/bartender/tests/schedulers/test_memory.py b/tests/schedulers/test_memory.py similarity index 100% rename from bartender/tests/schedulers/test_memory.py rename to tests/schedulers/test_memory.py diff --git a/bartender/tests/schedulers/test_slurm.py b/tests/schedulers/test_slurm.py similarity index 100% rename from bartender/tests/schedulers/test_slurm.py rename to tests/schedulers/test_slurm.py diff --git a/bartender/tests/test_config.py b/tests/test_config.py similarity index 100% rename from bartender/tests/test_config.py rename to tests/test_config.py diff --git a/bartender/tests/test_context.py b/tests/test_context.py similarity index 100% rename from bartender/tests/test_context.py rename to tests/test_context.py diff --git a/bartender/tests/test_destinations.py b/tests/test_destinations.py similarity index 100% rename from bartender/tests/test_destinations.py rename to tests/test_destinations.py diff --git a/bartender/tests/test_filesystem.py b/tests/test_filesystem.py similarity index 100% rename from bartender/tests/test_filesystem.py rename to tests/test_filesystem.py diff --git a/bartender/tests/test_picker.py b/tests/test_picker.py similarity index 100% rename from bartender/tests/test_picker.py rename to tests/test_picker.py diff --git a/tests/web/__init__.py b/tests/web/__init__.py new file mode 100644 index 0000000..87010c2 --- /dev/null +++ b/tests/web/__init__.py @@ -0,0 +1 @@ +"""Tests for bartender web module.""" diff --git a/bartender/tests/web/test_application.py b/tests/web/test_application.py similarity index 100% rename from bartender/tests/web/test_application.py rename to tests/web/test_application.py diff --git a/bartender/tests/web/test_health.py b/tests/web/test_health.py similarity index 100% rename from bartender/tests/web/test_health.py rename to tests/web/test_health.py diff --git a/bartender/tests/web/test_job.py b/tests/web/test_job.py similarity index 100% rename from bartender/tests/web/test_job.py rename to tests/web/test_job.py diff --git a/bartender/tests/web/test_user.py b/tests/web/test_user.py similarity index 100% rename from bartender/tests/web/test_user.py rename to tests/web/test_user.py From 8e97b4f5be9781ee0304413c08ffd4b7d5d2a46f Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 13 Jan 2023 11:14:38 +0100 Subject: [PATCH 19/36] Add missing src/bartender/db/__init__.py --- src/bartender/db/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/bartender/db/__init__.py diff --git a/src/bartender/db/__init__.py b/src/bartender/db/__init__.py new file mode 100644 index 0000000..e326a7f --- /dev/null +++ b/src/bartender/db/__init__.py @@ -0,0 +1 @@ +"""Database module.""" From f816a5bb42002c30505f037ad124c0865c699397 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 13 Jan 2023 12:14:58 +0100 Subject: [PATCH 20/36] Rename src/bartender/_ssh_utils.py -> src/bartender/ssh_utils.py No need to mark it as private. --- src/bartender/config.py | 5 +++++ src/bartender/filesystems/sftp.py | 2 +- src/bartender/schedulers/runner.py | 2 +- src/bartender/schedulers/slurm.py | 2 +- src/bartender/{_ssh_utils.py => ssh_utils.py} | 7 ++++++- tests/filesystems/test_build.py | 2 +- tests/schedulers/test_slurm.py | 2 +- tests/test_config.py | 2 +- 8 files changed, 17 insertions(+), 7 deletions(-) rename src/bartender/{_ssh_utils.py => ssh_utils.py} (82%) diff --git a/src/bartender/config.py b/src/bartender/config.py index 42fc5a3..0a17dc7 100644 --- a/src/bartender/config.py +++ b/src/bartender/config.py @@ -76,6 +76,11 @@ class ApplicatonConfiguration: `$config` in command string will be replaced with value of ApplicatonConfiguration.config. + + .. code-block:: yaml + + command: wc $config + config: README.md """ command: str diff --git a/src/bartender/filesystems/sftp.py b/src/bartender/filesystems/sftp.py index 5e731d1..0a5ba5c 100644 --- a/src/bartender/filesystems/sftp.py +++ b/src/bartender/filesystems/sftp.py @@ -4,9 +4,9 @@ from asyncssh import SSHClientConnection -from bartender._ssh_utils import SshConnectConfig, ssh_connect from bartender.filesystems.abstract import AbstractFileSystem from bartender.schedulers.abstract import JobDescription +from bartender.ssh_utils import SshConnectConfig, ssh_connect @dataclass diff --git a/src/bartender/schedulers/runner.py b/src/bartender/schedulers/runner.py index e444230..9489f0f 100644 --- a/src/bartender/schedulers/runner.py +++ b/src/bartender/schedulers/runner.py @@ -5,7 +5,7 @@ from asyncssh import SSHClientConnection -from bartender._ssh_utils import SshConnectConfig, ssh_connect +from bartender.ssh_utils import SshConnectConfig, ssh_connect class CommandRunner(Protocol): diff --git a/src/bartender/schedulers/slurm.py b/src/bartender/schedulers/slurm.py index bb9ae2b..6a4536e 100644 --- a/src/bartender/schedulers/slurm.py +++ b/src/bartender/schedulers/slurm.py @@ -2,7 +2,6 @@ from textwrap import dedent from typing import Literal, Optional -from bartender._ssh_utils import SshConnectConfig from bartender.db.models.job_model import State from bartender.schedulers.abstract import AbstractScheduler, JobDescription from bartender.schedulers.runner import ( @@ -10,6 +9,7 @@ LocalCommandRunner, SshCommandRunner, ) +from bartender.ssh_utils import SshConnectConfig def _map_slurm_state(slurm_state: str) -> State: diff --git a/src/bartender/_ssh_utils.py b/src/bartender/ssh_utils.py similarity index 82% rename from src/bartender/_ssh_utils.py rename to src/bartender/ssh_utils.py index 08d7da3..5b5cd61 100644 --- a/src/bartender/_ssh_utils.py +++ b/src/bartender/ssh_utils.py @@ -6,7 +6,12 @@ @dataclass class SshConnectConfig: - """Configuration for ssh connection.""" + """Configuration for ssh connection. + + Attributes: + usename: Username to connect with. + When absent will use username of logged in user. + """ hostname: str port: DefTuple[int] = () diff --git a/tests/filesystems/test_build.py b/tests/filesystems/test_build.py index 6d74c62..7962fd8 100644 --- a/tests/filesystems/test_build.py +++ b/tests/filesystems/test_build.py @@ -1,7 +1,7 @@ -from bartender._ssh_utils import SshConnectConfig from bartender.filesystems.build import build from bartender.filesystems.local import LocalFileSystem, LocalFileSystemConfig from bartender.filesystems.sftp import SftpFileSystem, SftpFileSystemConfig +from bartender.ssh_utils import SshConnectConfig def test_local() -> None: diff --git a/tests/schedulers/test_slurm.py b/tests/schedulers/test_slurm.py index 98f792a..3d24768 100644 --- a/tests/schedulers/test_slurm.py +++ b/tests/schedulers/test_slurm.py @@ -7,12 +7,12 @@ from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready -from bartender._ssh_utils import SshConnectConfig from bartender.db.models.job_model import CompletedStates from bartender.filesystems.sftp import SftpFileSystem, SftpFileSystemConfig from bartender.schedulers.abstract import JobDescription from bartender.schedulers.runner import SshCommandRunner from bartender.schedulers.slurm import SlurmScheduler, SlurmSchedulerConfig +from bartender.ssh_utils import SshConnectConfig class SlurmContainer(DockerContainer): diff --git a/tests/test_config.py b/tests/test_config.py index a29a1b3..86764fb 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -6,7 +6,6 @@ from pydantic import ValidationError from yaml import safe_dump as yaml_dump -from bartender._ssh_utils import SshConnectConfig from bartender.config import ApplicatonConfiguration, Config, build_config, get_config from bartender.destinations import DestinationConfig from bartender.filesystems.local import LocalFileSystemConfig @@ -14,6 +13,7 @@ from bartender.schedulers.abstract import JobDescription from bartender.schedulers.memory import MemorySchedulerConfig from bartender.schedulers.slurm import SlurmSchedulerConfig +from bartender.ssh_utils import SshConnectConfig class TestApplicatonConfiguration: From 60309b9e892050a0730a78b0ce21d6be31344f69 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 13 Jan 2023 12:51:30 +0100 Subject: [PATCH 21/36] Add Sphinx doc site Refs #13 --- .gitignore | 1 + README.md | 20 + docs/Makefile | 20 + docs/conf.py | 46 + docs/index.rst | 20 + docs/make.bat | 35 + poetry.lock | 2791 ++++++++++++++++++++++++++++-------------------- pyproject.toml | 10 + 8 files changed, 1763 insertions(+), 1180 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat diff --git a/.gitignore b/.gitignore index ded1f6c..5574afc 100644 --- a/.gitignore +++ b/.gitignore @@ -74,6 +74,7 @@ instance/ # Sphinx documentation docs/_build/ +docs/autoapi/ # PyBuilder .pybuilder/ diff --git a/README.md b/README.md index 315d4ff..2b0bc47 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ - [Reverting migrations](#reverting-migrations) - [Migration generation](#migration-generation) - [Running tests](#running-tests) + - [Documentation](#documentation) + - [Build](#build) *** @@ -468,3 +470,21 @@ To get a PostgreSQL terminal do ```bash docker exec -ti psql -U bartender ``` + +## [Documentation](#documentation) + +### Build + +First install dependencies with + +```shell +poetry install --with docs +``` + +Build with +```shell +cd docs +make html +``` + +Creates documentation site at [docs/_build/html](docs/_build/html/index.html). diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..6950e26 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,46 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "bartender" +release = "0.1.0" # TODO have version only in one place pyproject.toml + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "autoapi.extension", + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx.ext.napoleon", + "sphinx_copybutton", +] + +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "furo" +html_static_path = ["_static"] + +autoapi_type = "python" +autoapi_dirs = ["../src"] +autodoc_typehints = "both" + +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + # Commonly used libraries, uncomment when used in package + # 'numpy': ('http://docs.scipy.org/doc/numpy/', None), + # 'scipy': ('http://docs.scipy.org/doc/scipy/reference/', None), + # 'scikit-learn': ('https://scikit-learn.org/stable/', None), + # 'matplotlib': ('https://matplotlib.org/stable/', None), + # 'pandas': ('http://pandas.pydata.org/docs/', None), + "asyncssh": ("https://asyncssh.readthedocs.io/en/latest/", None), +} diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..8014e0d --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,20 @@ +.. bartender documentation master file, created by + sphinx-quickstart on Fri Jan 13 10:01:47 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to bartender's documentation! +===================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/poetry.lock b/poetry.lock index ee0ac83..626c281 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + [[package]] name = "aiofiles" version = "0.8.0" @@ -5,6 +7,22 @@ description = "File support for asyncio." category = "main" optional = false python-versions = ">=3.6,<4.0" +files = [ + {file = "aiofiles-0.8.0-py3-none-any.whl", hash = "sha256:7a973fc22b29e9962d0897805ace5856e6a566ab1f0c8e5c91ff6c866519c937"}, + {file = "aiofiles-0.8.0.tar.gz", hash = "sha256:8334f23235248a3b2e83b2c3a78a22674f39969b96397126cc93664d9a901e59"}, +] + +[[package]] +name = "alabaster" +version = "0.7.13" +description = "A configurable sidebar-enabled Sphinx theme" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] [[package]] name = "alembic" @@ -13,6 +31,10 @@ description = "A database migration tool for SQLAlchemy." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "alembic-1.8.1-py3-none-any.whl", hash = "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4"}, + {file = "alembic-1.8.1.tar.gz", hash = "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa"}, +] [package.dependencies] Mako = "*" @@ -28,6 +50,10 @@ description = "High level compatibility layer for multiple asynchronous event lo category = "main" optional = false python-versions = ">=3.6.2" +files = [ + {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"}, + {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"}, +] [package.dependencies] idna = ">=2.8" @@ -45,6 +71,10 @@ description = "ASGI specs, helper code, and adapters" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "asgiref-3.5.2-py3-none-any.whl", hash = "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4"}, + {file = "asgiref-3.5.2.tar.gz", hash = "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424"}, +] [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] @@ -56,6 +86,30 @@ description = "Read/rewrite/write Python ASTs" category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +files = [ + {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, + {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, +] + +[[package]] +name = "astroid" +version = "2.13.2" +description = "An abstract syntax tree for Python with inference support." +category = "dev" +optional = false +python-versions = ">=3.7.2" +files = [ + {file = "astroid-2.13.2-py3-none-any.whl", hash = "sha256:8f6a8d40c4ad161d6fc419545ae4b2f275ed86d1c989c97825772120842ee0d2"}, + {file = "astroid-2.13.2.tar.gz", hash = "sha256:3bc7834720e1a24ca797fd785d77efb14f7a28ee8e635ef040b6e2d80ccb3303"}, +] + +[package.dependencies] +lazy-object-proxy = ">=1.4.0" +typing-extensions = ">=4.0.0" +wrapt = [ + {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, + {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, +] [[package]] name = "asyncpg" @@ -64,6 +118,34 @@ description = "An asyncio PostgreSQL driver" category = "main" optional = false python-versions = ">=3.6.0" +files = [ + {file = "asyncpg-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf5e3408a14a17d480f36ebaf0401a12ff6ae5457fdf45e4e2775c51cc9517d3"}, + {file = "asyncpg-0.25.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bc197fc4aca2fd24f60241057998124012469d2e414aed3f992579db0c88e3a"}, + {file = "asyncpg-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a70783f6ffa34cc7dd2de20a873181414a34fd35a4a208a1f1a7f9f695e4ec4"}, + {file = "asyncpg-0.25.0-cp310-cp310-win32.whl", hash = "sha256:43cde84e996a3afe75f325a68300093425c2f47d340c0fc8912765cf24a1c095"}, + {file = "asyncpg-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:56d88d7ef4341412cd9c68efba323a4519c916979ba91b95d4c08799d2ff0c09"}, + {file = "asyncpg-0.25.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a84d30e6f850bac0876990bcd207362778e2208df0bee8be8da9f1558255e634"}, + {file = "asyncpg-0.25.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:beaecc52ad39614f6ca2e48c3ca15d56e24a2c15cbfdcb764a4320cc45f02fd5"}, + {file = "asyncpg-0.25.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:6f8f5fc975246eda83da8031a14004b9197f510c41511018e7b1bedde6968e92"}, + {file = "asyncpg-0.25.0-cp36-cp36m-win32.whl", hash = "sha256:ddb4c3263a8d63dcde3d2c4ac1c25206bfeb31fa83bd70fd539e10f87739dee4"}, + {file = "asyncpg-0.25.0-cp36-cp36m-win_amd64.whl", hash = "sha256:bf6dc9b55b9113f39eaa2057337ce3f9ef7de99a053b8a16360395ce588925cd"}, + {file = "asyncpg-0.25.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:acb311722352152936e58a8ee3c5b8e791b24e84cd7d777c414ff05b3530ca68"}, + {file = "asyncpg-0.25.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0a61fb196ce4dae2f2fa26eb20a778db21bbee484d2e798cb3cc988de13bdd1b"}, + {file = "asyncpg-0.25.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2633331cbc8429030b4f20f712f8d0fbba57fa8555ee9b2f45f981b81328b256"}, + {file = "asyncpg-0.25.0-cp37-cp37m-win32.whl", hash = "sha256:863d36eba4a7caa853fd7d83fad5fd5306f050cc2fe6e54fbe10cdb30420e5e9"}, + {file = "asyncpg-0.25.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fe471ccd915b739ca65e2e4dbd92a11b44a5b37f2e38f70827a1c147dafe0fa8"}, + {file = "asyncpg-0.25.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:72a1e12ea0cf7c1e02794b697e3ca967b2360eaa2ce5d4bfdd8604ec2d6b774b"}, + {file = "asyncpg-0.25.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4327f691b1bdb222df27841938b3e04c14068166b3a97491bec2cb982f49f03e"}, + {file = "asyncpg-0.25.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:739bbd7f89a2b2f6bc44cb8bf967dab12c5bc714fcbe96e68d512be45ecdf962"}, + {file = "asyncpg-0.25.0-cp38-cp38-win32.whl", hash = "sha256:18d49e2d93a7139a2fdbd113e320cc47075049997268a61bfbe0dde680c55471"}, + {file = "asyncpg-0.25.0-cp38-cp38-win_amd64.whl", hash = "sha256:191fe6341385b7fdea7dbdcf47fd6db3fd198827dcc1f2b228476d13c05a03c6"}, + {file = "asyncpg-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fab7f1b2c29e187dd8781fce896249500cf055b63471ad66332e537e9b5f7e"}, + {file = "asyncpg-0.25.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a738f1b2876f30d710d3dc1e7858160a0afe1603ba16bf5f391f5316eb0ed855"}, + {file = "asyncpg-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4105f57ad1e8fbc8b1e535d8fcefa6ce6c71081228f08680c6dea24384ff0e"}, + {file = "asyncpg-0.25.0-cp39-cp39-win32.whl", hash = "sha256:f55918ded7b85723a5eaeb34e86e7b9280d4474be67df853ab5a7fa0cc7c6bf2"}, + {file = "asyncpg-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:649e2966d98cc48d0646d9a4e29abecd8b59d38d55c256d5c857f6b27b7407ac"}, + {file = "asyncpg-0.25.0.tar.gz", hash = "sha256:63f8e6a69733b285497c2855464a34de657f2cccd25aeaeeb5071872e9382540"}, +] [package.extras] dev = ["Cython (>=0.29.24,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "flake8 (>=3.9.2,<3.10.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "uvloop (>=0.15.3)"] @@ -77,6 +159,10 @@ description = "AsyncSSH: Asynchronous SSHv2 client and server library" category = "main" optional = false python-versions = ">= 3.6" +files = [ + {file = "asyncssh-2.12.0-py3-none-any.whl", hash = "sha256:6841c4242c606fd51188c974ec2f4887efeec67ecdfa5b84140711dacd985ab3"}, + {file = "asyncssh-2.12.0.tar.gz", hash = "sha256:274101322c4b941823aeed8e1ab6e7be5191686c6db2d2bd35afeba30505e780"}, +] [package.dependencies] cryptography = ">=3.1" @@ -98,6 +184,10 @@ description = "Classes Without Boilerplate" category = "dev" optional = false python-versions = ">=3.5" +files = [ + {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, + {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, +] [package.extras] dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] @@ -112,11 +202,30 @@ description = "Removes unused imports and unused variables" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "autoflake-1.5.3-py2.py3-none-any.whl", hash = "sha256:90eb8d3f625bd72068eb670338ea7efcddbc5c6e822d3601e3dc9404c06ea8da"}, + {file = "autoflake-1.5.3.tar.gz", hash = "sha256:44f7d7eb2c1c49505b513c0e93a5dfd3f7b4218283f50c5ca0af4df6b975d470"}, +] [package.dependencies] pyflakes = ">=1.1.0" toml = ">=0.10.2" +[[package]] +name = "babel" +version = "2.11.0" +description = "Internationalization utilities" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, + {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, +] + +[package.dependencies] +pytz = ">=2015.7" + [[package]] name = "bandit" version = "1.7.4" @@ -124,6 +233,10 @@ description = "Security oriented static analyser for python code." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, + {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, +] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} @@ -143,11 +256,44 @@ description = "Modern password hashing for your software and your servers" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "bcrypt-4.0.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:845b1daf4df2dd94d2fdbc9454953ca9dd0e12970a0bfc9f3dcc6faea3fa96e4"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8780e69f9deec9d60f947b169507d2c9816e4f11548f1f7ebee2af38b9b22ae4"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c3334446fac200499e8bc04a530ce3cf0b3d7151e0e4ac5c0dddd3d95e97843"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb67f6a6c72dfb0a02f3df51550aa1862708e55128b22543e2b42c74f3620d7"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:7c7dd6c1f05bf89e65261d97ac3a6520f34c2acb369afb57e3ea4449be6ff8fd"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:594780b364fb45f2634c46ec8d3e61c1c0f1811c4f2da60e8eb15594ecbf93ed"}, + {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d0dd19aad87e4ab882ef1d12df505f4c52b28b69666ce83c528f42c07379227"}, + {file = "bcrypt-4.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bf413f2a9b0a2950fc750998899013f2e718d20fa4a58b85ca50b6df5ed1bbf9"}, + {file = "bcrypt-4.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ede0f506554571c8eda80db22b83c139303ec6b595b8f60c4c8157bdd0bdee36"}, + {file = "bcrypt-4.0.0-cp36-abi3-win32.whl", hash = "sha256:dc6ec3dc19b1c193b2f7cf279d3e32e7caf447532fbcb7af0906fe4398900c33"}, + {file = "bcrypt-4.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:0b0f0c7141622a31e9734b7f649451147c04ebb5122327ac0bd23744df84be90"}, + {file = "bcrypt-4.0.0.tar.gz", hash = "sha256:c59c170fc9225faad04dde1ba61d85b413946e8ce2e5f5f5ff30dfd67283f319"}, +] [package.extras] tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] +[[package]] +name = "beautifulsoup4" +version = "4.11.1" +description = "Screen-scraping library" +category = "dev" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, + {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + [[package]] name = "black" version = "22.8.0" @@ -155,6 +301,31 @@ description = "The uncompromising code formatter." category = "dev" optional = false python-versions = ">=3.6.2" +files = [ + {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, + {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, + {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, + {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, + {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, + {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, + {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, + {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, + {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, + {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, + {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, + {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, + {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, + {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, + {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, + {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, + {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, + {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, + {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, + {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, + {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, + {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, + {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, +] [package.dependencies] click = ">=8.0.0" @@ -177,6 +348,10 @@ description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, + {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, +] [[package]] name = "cffi" @@ -185,6 +360,72 @@ description = "Foreign Function Interface for Python calling C code." category = "main" optional = false python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] [package.dependencies] pycparser = "*" @@ -196,6 +437,10 @@ description = "Validate configuration and produce human readable error messages. category = "dev" optional = false python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] [[package]] name = "charset-normalizer" @@ -204,6 +449,10 @@ description = "The Real First Universal Charset Detector. Open, modern and activ category = "main" optional = false python-versions = ">=3.6.0" +files = [ + {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, +] [package.extras] unicode-backport = ["unicodedata2"] @@ -215,6 +464,10 @@ description = "Composable command line interface toolkit" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -226,6 +479,10 @@ description = "Cross-platform colored terminal text." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, +] [[package]] name = "coverage" @@ -234,6 +491,58 @@ description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"}, + {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"}, + {file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"}, + {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"}, + {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"}, + {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"}, + {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"}, + {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"}, + {file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"}, + {file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"}, + {file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"}, + {file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"}, + {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"}, + {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"}, + {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"}, + {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"}, + {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"}, + {file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"}, + {file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"}, + {file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"}, + {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"}, + {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"}, + {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"}, + {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"}, + {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"}, + {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"}, + {file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"}, + {file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"}, + {file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"}, + {file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"}, + {file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"}, + {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"}, + {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"}, + {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"}, + {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"}, + {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"}, + {file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"}, + {file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"}, + {file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"}, + {file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"}, + {file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"}, + {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"}, + {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"}, + {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"}, + {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"}, + {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"}, + {file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"}, + {file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"}, + {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"}, + {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"}, +] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} @@ -248,6 +557,34 @@ description = "cryptography is a package which provides cryptographic recipes an category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f"}, + {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd"}, + {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6"}, + {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a"}, + {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294"}, + {file = "cryptography-38.0.1-cp36-abi3-win32.whl", hash = "sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0"}, + {file = "cryptography-38.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a"}, + {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d"}, + {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9"}, + {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d"}, + {file = "cryptography-38.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818"}, + {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6"}, + {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750"}, + {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013"}, + {file = "cryptography-38.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5"}, + {file = "cryptography-38.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61"}, + {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac"}, + {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb"}, + {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a"}, + {file = "cryptography-38.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b"}, + {file = "cryptography-38.0.1.tar.gz", hash = "sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7"}, +] [package.dependencies] cffi = ">=1.12" @@ -267,6 +604,10 @@ description = "A utility for ensuring Google-style docstrings stay up to date wi category = "dev" optional = false python-versions = ">=3.6,<4.0" +files = [ + {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, + {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, +] [[package]] name = "deprecation" @@ -275,6 +616,10 @@ description = "A library to handle automated deprecations" category = "dev" optional = false python-versions = "*" +files = [ + {file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"}, + {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"}, +] [package.dependencies] packaging = "*" @@ -286,6 +631,10 @@ description = "Distribution utilities" category = "dev" optional = false python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] [[package]] name = "dnspython" @@ -294,6 +643,10 @@ description = "DNS toolkit" category = "main" optional = false python-versions = ">=3.6,<4.0" +files = [ + {file = "dnspython-2.2.1-py3-none-any.whl", hash = "sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"}, + {file = "dnspython-2.2.1.tar.gz", hash = "sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e"}, +] [package.extras] curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] @@ -310,6 +663,10 @@ description = "A Python library for the Docker Engine API." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "docker-6.0.1-py3-none-any.whl", hash = "sha256:dbcb3bd2fa80dca0788ed908218bf43972772009b881ed1e20dfc29a65e49782"}, + {file = "docker-6.0.1.tar.gz", hash = "sha256:896c4282e5c7af5c45e8b683b0b0c33932974fe6e50fc6906a0a83616ab3da97"}, +] [package.dependencies] packaging = ">=14.0" @@ -328,6 +685,10 @@ description = "Docutils -- Python Documentation Utilities" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, + {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, +] [[package]] name = "email-validator" @@ -336,6 +697,10 @@ description = "A robust email syntax and deliverability validation library." category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "email_validator-1.2.1-py2.py3-none-any.whl", hash = "sha256:c8589e691cf73eb99eed8d10ce0e9cbb05a0886ba920c8bcb7c82873f4c5789c"}, + {file = "email_validator-1.2.1.tar.gz", hash = "sha256:6757aea012d40516357c0ac2b1a4c31219ab2f899d26831334c5d069e8b6c3d8"}, +] [package.dependencies] dnspython = ">=1.15.0" @@ -348,6 +713,10 @@ description = "Removes commented-out code." category = "dev" optional = false python-versions = "*" +files = [ + {file = "eradicate-2.1.0-py3-none-any.whl", hash = "sha256:8bfaca181db9227dc88bdbce4d051a9627604c2243e7d85324f6d6ce0fd08bb2"}, + {file = "eradicate-2.1.0.tar.gz", hash = "sha256:aac7384ab25b1bf21c4c012de9b4bf8398945a14c98c911545b2ea50ab558014"}, +] [[package]] name = "fastapi" @@ -356,6 +725,10 @@ description = "FastAPI framework, high performance, easy to learn, fast to code, category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "fastapi-0.85.2-py3-none-any.whl", hash = "sha256:6292db0edd4a11f0d938d6033ccec5f706e9d476958bf33b119e8ddb4e524bde"}, + {file = "fastapi-0.85.2.tar.gz", hash = "sha256:3e10ea0992c700e0b17b6de8c2092d7b9cd763ce92c49ee8d4be10fee3b2f367"}, +] [package.dependencies] pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" @@ -374,6 +747,10 @@ description = "Ready-to-use and customizable users management for FastAPI" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "fastapi-users-10.1.5.tar.gz", hash = "sha256:47cb99eef18eea654170674f5a86c4bed898a6ee666b45007f9805e687ff32f5"}, + {file = "fastapi_users-10.1.5-py3-none-any.whl", hash = "sha256:dded5947d271b80e67b2093f25c591a71eff62975e622ef9871b6283174a87b5"}, +] [package.dependencies] email-validator = ">=1.1.0,<1.3" @@ -399,6 +776,10 @@ description = "FastAPI Users database adapter for SQLAlchemy." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "fastapi-users-db-sqlalchemy-4.0.3.tar.gz", hash = "sha256:7d223742e687cf386bea4e990d6ca8571185c50f365301a34e8eb14ebbda18b2"}, + {file = "fastapi_users_db_sqlalchemy-4.0.3-py3-none-any.whl", hash = "sha256:81102ca68e07895898eb9b9d85c6c2e6378f9a46e8486708a36cb1c096117a81"}, +] [package.dependencies] fastapi-users = ">=10.0.0" @@ -411,6 +792,10 @@ description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, + {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, +] [package.extras] docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] @@ -423,6 +808,10 @@ description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, + {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, +] [package.dependencies] mccabe = ">=0.6.0,<0.7.0" @@ -436,6 +825,10 @@ description = "Automated security testing with bandit and flake8." category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "flake8_bandit-3.0.0-py2.py3-none-any.whl", hash = "sha256:61b617f4f7cdaa0e2b1e6bf7b68afb2b619a227bb3e3ae00dd36c213bd17900a"}, + {file = "flake8_bandit-3.0.0.tar.gz", hash = "sha256:54d19427e6a8d50322a7b02e1841c0a7c22d856975f3459803320e0e18e2d6a1"}, +] [package.dependencies] bandit = ">=1.7.3" @@ -450,6 +843,10 @@ description = "Flake8 plugin to forbid backslashes for line breaks" category = "dev" optional = false python-versions = ">=3.6,<4.0" +files = [ + {file = "flake8-broken-line-0.4.0.tar.gz", hash = "sha256:771aab5aa0997666796fed249d0e48e6c01cdfeca8c95521eea28a38b7ced4c7"}, + {file = "flake8_broken_line-0.4.0-py3-none-any.whl", hash = "sha256:e9c522856862239a2c7ef2c1de0276fa598572aa864bd4e9c7efc2a827538515"}, +] [package.dependencies] flake8 = ">=3.5,<5" @@ -461,6 +858,10 @@ description = "A plugin for flake8 finding likely bugs and design problems in yo category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "flake8-bugbear-22.8.23.tar.gz", hash = "sha256:de0717d11124a082118dd08387b34fd86b2721642ec2d8e92be66cfa5ea7c445"}, + {file = "flake8_bugbear-22.8.23-py3-none-any.whl", hash = "sha256:1b0ebe0873d1cd55bf9f1588bfcb930db339018ef44a3981a26532daa9fd14a8"}, +] [package.dependencies] attrs = ">=19.2.0" @@ -476,6 +877,10 @@ description = "Flake8 lint for trailing commas." category = "dev" optional = false python-versions = "*" +files = [ + {file = "flake8-commas-2.1.0.tar.gz", hash = "sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263"}, + {file = "flake8_commas-2.1.0-py2.py3-none-any.whl", hash = "sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54"}, +] [package.dependencies] flake8 = ">=2" @@ -487,6 +892,10 @@ description = "A flake8 plugin to help you write better list/set/dict comprehens category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "flake8-comprehensions-3.10.0.tar.gz", hash = "sha256:181158f7e7aa26a63a0a38e6017cef28c6adee71278ce56ce11f6ec9c4905058"}, + {file = "flake8_comprehensions-3.10.0-py3-none-any.whl", hash = "sha256:dad454fd3d525039121e98fa1dd90c46bc138708196a4ebbc949ad3c859adedb"}, +] [package.dependencies] flake8 = ">=3.0,<3.2.0 || >3.2.0" @@ -498,6 +907,10 @@ description = "ipdb/pdb statement checker plugin for flake8" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] [package.dependencies] flake8 = ">=3.0" @@ -510,6 +923,10 @@ description = "Extension for flake8 which uses pydocstyle to check docstrings" category = "dev" optional = false python-versions = "*" +files = [ + {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, + {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, +] [package.dependencies] flake8 = ">=3" @@ -522,6 +939,10 @@ description = "Flake8 plugin to find commented out code" category = "dev" optional = false python-versions = ">=3.6,<4.0" +files = [ + {file = "flake8-eradicate-1.3.0.tar.gz", hash = "sha256:e4c98f00d17dc8653e3388cac2624cd81e9735de2fd4a8dcf99029633ebd7a63"}, + {file = "flake8_eradicate-1.3.0-py3-none-any.whl", hash = "sha256:85a71e0c5f4e07f7c6c5fec520483561fd6bd295417d622855bdeade99242e3d"}, +] [package.dependencies] attrs = "*" @@ -536,6 +957,10 @@ description = "flake8 plugin that integrates isort ." category = "dev" optional = false python-versions = "*" +files = [ + {file = "flake8-isort-4.2.0.tar.gz", hash = "sha256:26571500cd54976bbc0cf1006ffbcd1a68dd102f816b7a1051b219616ba9fee0"}, + {file = "flake8_isort-4.2.0-py3-none-any.whl", hash = "sha256:5b87630fb3719bf4c1833fd11e0d9534f43efdeba524863e15d8f14a7ef6adbf"}, +] [package.dependencies] flake8 = ">=3.2.1,<6" @@ -551,6 +976,10 @@ description = "Polyfill package for Flake8 plugins" category = "dev" optional = false python-versions = "*" +files = [ + {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, + {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, +] [package.dependencies] flake8 = "*" @@ -562,6 +991,9 @@ description = "Flake8 lint for quotes." category = "dev" optional = false python-versions = "*" +files = [ + {file = "flake8-quotes-3.3.1.tar.gz", hash = "sha256:633adca6fb8a08131536af0d750b44d6985b9aba46f498871e21588c3e6f525a"}, +] [package.dependencies] flake8 = "*" @@ -573,6 +1005,10 @@ description = "Python docstring reStructuredText (RST) validator" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "flake8-rst-docstrings-0.2.7.tar.gz", hash = "sha256:2740067ab9237559dd45a3434d8c987792c7b259ca563621a3b95efe201f5382"}, + {file = "flake8_rst_docstrings-0.2.7-py3-none-any.whl", hash = "sha256:5d56075dce360bcc9c6775bfe7cb431aa395de600ca7e8d40580a28d50b2a803"}, +] [package.dependencies] flake8 = ">=3.0.0" @@ -586,10 +1022,32 @@ description = "string format checker, plugin for flake8" category = "dev" optional = false python-versions = "*" +files = [ + {file = "flake8-string-format-0.3.0.tar.gz", hash = "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2"}, + {file = "flake8_string_format-0.3.0-py2.py3-none-any.whl", hash = "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"}, +] [package.dependencies] flake8 = "*" +[[package]] +name = "furo" +version = "2022.12.7" +description = "A clean customisable Sphinx documentation theme." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "furo-2022.12.7-py3-none-any.whl", hash = "sha256:7cb76c12a25ef65db85ab0743df907573d03027a33631f17d267e598ebb191f7"}, + {file = "furo-2022.12.7.tar.gz", hash = "sha256:d8008f8efbe7587a97ba533c8b2df1f9c21ee9b3e5cad0d27f61193d38b1a986"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7" +sphinx = ">=5.0,<7.0" +sphinx-basic-ng = "*" + [[package]] name = "gitdb" version = "4.0.9" @@ -597,6 +1055,10 @@ description = "Git Object Database" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, + {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, +] [package.dependencies] smmap = ">=3.0.1,<6" @@ -608,6 +1070,10 @@ description = "GitPython is a python library used to interact with Git repositor category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, + {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, +] [package.dependencies] gitdb = ">=4.0.1,<5" @@ -619,6 +1085,68 @@ description = "Lightweight in-process concurrent programming" category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +files = [ + {file = "greenlet-2.0.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:9ed358312e63bf683b9ef22c8e442ef6c5c02973f0c2a939ec1d7b50c974015c"}, + {file = "greenlet-2.0.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4f09b0010e55bec3239278f642a8a506b91034f03a4fb28289a7d448a67f1515"}, + {file = "greenlet-2.0.1-cp27-cp27m-win32.whl", hash = "sha256:1407fe45246632d0ffb7a3f4a520ba4e6051fc2cbd61ba1f806900c27f47706a"}, + {file = "greenlet-2.0.1-cp27-cp27m-win_amd64.whl", hash = "sha256:3001d00eba6bbf084ae60ec7f4bb8ed375748f53aeaefaf2a37d9f0370558524"}, + {file = "greenlet-2.0.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d566b82e92ff2e09dd6342df7e0eb4ff6275a3f08db284888dcd98134dbd4243"}, + {file = "greenlet-2.0.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:0722c9be0797f544a3ed212569ca3fe3d9d1a1b13942d10dd6f0e8601e484d26"}, + {file = "greenlet-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d37990425b4687ade27810e3b1a1c37825d242ebc275066cfee8cb6b8829ccd"}, + {file = "greenlet-2.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be35822f35f99dcc48152c9839d0171a06186f2d71ef76dc57fa556cc9bf6b45"}, + {file = "greenlet-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c140e7eb5ce47249668056edf3b7e9900c6a2e22fb0eaf0513f18a1b2c14e1da"}, + {file = "greenlet-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d21681f09e297a5adaa73060737e3aa1279a13ecdcfcc6ef66c292cb25125b2d"}, + {file = "greenlet-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fb412b7db83fe56847df9c47b6fe3f13911b06339c2aa02dcc09dce8bbf582cd"}, + {file = "greenlet-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6a08799e9e88052221adca55741bf106ec7ea0710bca635c208b751f0d5b617"}, + {file = "greenlet-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e112e03d37987d7b90c1e98ba5e1b59e1645226d78d73282f45b326f7bddcb9"}, + {file = "greenlet-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56961cfca7da2fdd178f95ca407fa330c64f33289e1804b592a77d5593d9bd94"}, + {file = "greenlet-2.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13ba6e8e326e2116c954074c994da14954982ba2795aebb881c07ac5d093a58a"}, + {file = "greenlet-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bf633a50cc93ed17e494015897361010fc08700d92676c87931d3ea464123ce"}, + {file = "greenlet-2.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9f2c221eecb7ead00b8e3ddb913c67f75cba078fd1d326053225a3f59d850d72"}, + {file = "greenlet-2.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:13ebf93c343dd8bd010cd98e617cb4c1c1f352a0cf2524c82d3814154116aa82"}, + {file = "greenlet-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:6f61d71bbc9b4a3de768371b210d906726535d6ca43506737682caa754b956cd"}, + {file = "greenlet-2.0.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:2d0bac0385d2b43a7bd1d651621a4e0f1380abc63d6fb1012213a401cbd5bf8f"}, + {file = "greenlet-2.0.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:f6327b6907b4cb72f650a5b7b1be23a2aab395017aa6f1adb13069d66360eb3f"}, + {file = "greenlet-2.0.1-cp35-cp35m-win32.whl", hash = "sha256:81b0ea3715bf6a848d6f7149d25bf018fd24554a4be01fcbbe3fdc78e890b955"}, + {file = "greenlet-2.0.1-cp35-cp35m-win_amd64.whl", hash = "sha256:38255a3f1e8942573b067510f9611fc9e38196077b0c8eb7a8c795e105f9ce77"}, + {file = "greenlet-2.0.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:04957dc96669be041e0c260964cfef4c77287f07c40452e61abe19d647505581"}, + {file = "greenlet-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:4aeaebcd91d9fee9aa768c1b39cb12214b30bf36d2b7370505a9f2165fedd8d9"}, + {file = "greenlet-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974a39bdb8c90a85982cdb78a103a32e0b1be986d411303064b28a80611f6e51"}, + {file = "greenlet-2.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dca09dedf1bd8684767bc736cc20c97c29bc0c04c413e3276e0962cd7aeb148"}, + {file = "greenlet-2.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c0757db9bd08470ff8277791795e70d0bf035a011a528ee9a5ce9454b6cba2"}, + {file = "greenlet-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5067920de254f1a2dee8d3d9d7e4e03718e8fd2d2d9db962c8c9fa781ae82a39"}, + {file = "greenlet-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5a8e05057fab2a365c81abc696cb753da7549d20266e8511eb6c9d9f72fe3e92"}, + {file = "greenlet-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:3d75b8d013086b08e801fbbb896f7d5c9e6ccd44f13a9241d2bf7c0df9eda928"}, + {file = "greenlet-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:097e3dae69321e9100202fc62977f687454cd0ea147d0fd5a766e57450c569fd"}, + {file = "greenlet-2.0.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:cb242fc2cda5a307a7698c93173d3627a2a90d00507bccf5bc228851e8304963"}, + {file = "greenlet-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:72b00a8e7c25dcea5946692a2485b1a0c0661ed93ecfedfa9b6687bd89a24ef5"}, + {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b0ff9878333823226d270417f24f4d06f235cb3e54d1103b71ea537a6a86ce"}, + {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be9e0fb2ada7e5124f5282d6381903183ecc73ea019568d6d63d33f25b2a9000"}, + {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b493db84d124805865adc587532ebad30efa68f79ad68f11b336e0a51ec86c2"}, + {file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0459d94f73265744fee4c2d5ec44c6f34aa8a31017e6e9de770f7bcf29710be9"}, + {file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a20d33124935d27b80e6fdacbd34205732660e0a1d35d8b10b3328179a2b51a1"}, + {file = "greenlet-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:ea688d11707d30e212e0110a1aac7f7f3f542a259235d396f88be68b649e47d1"}, + {file = "greenlet-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:afe07421c969e259e9403c3bb658968702bc3b78ec0b6fde3ae1e73440529c23"}, + {file = "greenlet-2.0.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:cd4ccc364cf75d1422e66e247e52a93da6a9b73cefa8cad696f3cbbb75af179d"}, + {file = "greenlet-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c8b1c43e75c42a6cafcc71defa9e01ead39ae80bd733a2608b297412beede68"}, + {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:659f167f419a4609bc0516fb18ea69ed39dbb25594934bd2dd4d0401660e8a1e"}, + {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:356e4519d4dfa766d50ecc498544b44c0249b6de66426041d7f8b751de4d6b48"}, + {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811e1d37d60b47cb8126e0a929b58c046251f28117cb16fcd371eed61f66b764"}, + {file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d38ffd0e81ba8ef347d2be0772e899c289b59ff150ebbbbe05dc61b1246eb4e0"}, + {file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0109af1138afbfb8ae647e31a2b1ab030f58b21dd8528c27beaeb0093b7938a9"}, + {file = "greenlet-2.0.1-cp38-cp38-win32.whl", hash = "sha256:88c8d517e78acdf7df8a2134a3c4b964415b575d2840a2746ddb1cc6175f8608"}, + {file = "greenlet-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:d6ee1aa7ab36475035eb48c01efae87d37936a8173fc4d7b10bb02c2d75dd8f6"}, + {file = "greenlet-2.0.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b1992ba9d4780d9af9726bbcef6a1db12d9ab1ccc35e5773685a24b7fb2758eb"}, + {file = "greenlet-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:b5e83e4de81dcc9425598d9469a624826a0b1211380ac444c7c791d4a2137c19"}, + {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:505138d4fa69462447a562a7c2ef723c6025ba12ac04478bc1ce2fcc279a2db5"}, + {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce1e90dd302f45716a7715517c6aa0468af0bf38e814ad4eab58e88fc09f7f7"}, + {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e9744c657d896c7b580455e739899e492a4a452e2dd4d2b3e459f6b244a638d"}, + {file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:662e8f7cad915ba75d8017b3e601afc01ef20deeeabf281bd00369de196d7726"}, + {file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:41b825d65f31e394b523c84db84f9383a2f7eefc13d987f308f4663794d2687e"}, + {file = "greenlet-2.0.1-cp39-cp39-win32.whl", hash = "sha256:db38f80540083ea33bdab614a9d28bcec4b54daa5aff1668d7827a9fc769ae0a"}, + {file = "greenlet-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b23d2a46d53210b498e5b701a1913697671988f4bf8e10f935433f6e7c332fb6"}, + {file = "greenlet-2.0.1.tar.gz", hash = "sha256:42e602564460da0e8ee67cb6d7236363ee5e131aa15943b6670e44e5c2ed0f67"}, +] [package.extras] docs = ["Sphinx", "docutils (<0.18)"] @@ -631,6 +1159,10 @@ description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, + {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, +] [[package]] name = "httpcore" @@ -639,6 +1171,10 @@ description = "A minimal low-level HTTP client." category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "httpcore-0.14.7-py3-none-any.whl", hash = "sha256:47d772f754359e56dd9d892d9593b6f9870a37aeb8ba51e9a88b09b3d68cfade"}, + {file = "httpcore-0.14.7.tar.gz", hash = "sha256:7503ec1c0f559066e7e39bc4003fd2ce023d01cf51793e3c173b864eb456ead1"}, +] [package.dependencies] anyio = ">=3.0.0,<4.0.0" @@ -657,6 +1193,32 @@ description = "A collection of framework independent HTTP protocol utils." category = "main" optional = false python-versions = ">=3.5.0" +files = [ + {file = "httptools-0.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4137137de8976511a392e27bfdcf231bd926ac13d375e0414e927b08217d779e"}, + {file = "httptools-0.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9f475b642c48b1b78584bdd12a5143e2c512485664331eade9c29ef769a17598"}, + {file = "httptools-0.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4687dfc116a9f1eb22a7d797f0dc6f6e17190d406ca4e729634b38aa98044b17"}, + {file = "httptools-0.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:72ee0e3fb9c6437ab3ae34e9abee67fcee6876f4f58504e3f613dd5882aafdb7"}, + {file = "httptools-0.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:3787c1f46e9722ef7f07ea5c76b0103037483d1b12e34a02c53ceca5afa4e09a"}, + {file = "httptools-0.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c0ac2e0ce6733c55858932e7d37fcc7b67ba6bb23e9648593c55f663de031b93"}, + {file = "httptools-0.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79717080dc3f8b1eeb7f820b9b81528acbc04be6041f323fdd97550da2062575"}, + {file = "httptools-0.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eda95634027200f4b2a6d499e7c2e7fa9b8ee57e045dfda26958ea0af27c070b"}, + {file = "httptools-0.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3f82eb106e1474c63dba36a176067e65b48385f4cecddf3616411aa5d1fbdfec"}, + {file = "httptools-0.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c14576b737d9e6e4f2a86af04918dbe9b62f57ce8102a8695c9a382dbe405c7f"}, + {file = "httptools-0.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:113816f9af7dcfc4aa71ebb5354d77365f666ecf96ac7ff2aa1d24b6bca44165"}, + {file = "httptools-0.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b8ac7dee63af4346e02b1e6d32202e3b5b3706a9928bec6da6d7a5b066217422"}, + {file = "httptools-0.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:04114db99605c9b56ea22a8ec4d7b1485b908128ed4f4a8f6438489c428da794"}, + {file = "httptools-0.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6e676bc3bb911b11f3d7e2144b9a53600bf6b9b21e0e4437aa308e1eef094d97"}, + {file = "httptools-0.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdc3975db86c29817e6d13df14e037c931fc893a710fb71097777a4147090068"}, + {file = "httptools-0.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ac842df4fc3952efa7820b277961ea55e068bbc54cb59a0820400de7ae358d8"}, + {file = "httptools-0.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:47dba2345aaa01b87e4981e8756af441349340708d5b60712c98c55a4d28f4af"}, + {file = "httptools-0.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:5a836bd85ae1fb4304f674808488dae403e136d274aa5bafd0e6ee456f11c371"}, + {file = "httptools-0.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1a8f26327023fa1a947d36e60a0582149e182fbbc949c8a65ec8665754dbbe69"}, + {file = "httptools-0.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a10a5903b5bc0eb647d01cd1e95bec3bb614a9bf53f0af1e01360b2debdf81"}, + {file = "httptools-0.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21e948034f70e47c8abfa2d5e6f1a5661f87a2cddc7bcc70f61579cc87897c70"}, + {file = "httptools-0.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:074afd8afdeec0fa6786cd4a1676e0c0be23dc9a017a86647efa6b695168104f"}, + {file = "httptools-0.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:2119fa619a4c53311f594f25c0205d619350fcb32140ec5057f861952e9b2b4f"}, + {file = "httptools-0.3.0.tar.gz", hash = "sha256:3f9b4856d46ba1f0c850f4e84b264a9a8b4460acb20e865ec00978ad9fbaa4cf"}, +] [package.extras] test = ["Cython (>=0.29.24,<0.30.0)"] @@ -668,6 +1230,10 @@ description = "The next generation HTTP client." category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "httpx-0.22.0-py3-none-any.whl", hash = "sha256:e35e83d1d2b9b2a609ef367cc4c1e66fd80b750348b20cc9e19d1952fc2ca3f6"}, + {file = "httpx-0.22.0.tar.gz", hash = "sha256:d8e778f76d9bbd46af49e7f062467e3157a5a3d2ae4876a4bbfd8a51ed9c9cb4"}, +] [package.dependencies] certifi = "*" @@ -689,6 +1255,10 @@ description = "Async OAuth client using HTTPX" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "httpx-oauth-0.7.0.tar.gz", hash = "sha256:ea84b767274416cc23ce8213e81e8f85436d1526fa64c71a95942590eb0b67ca"}, + {file = "httpx_oauth-0.7.0-py3-none-any.whl", hash = "sha256:5c5380df6727a0571011694fc552980828bfd5223df04a8ff06f61be2e939e90"}, +] [package.dependencies] httpx = ">=0.18,<0.24" @@ -703,6 +1273,10 @@ description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "identify-2.5.5-py2.py3-none-any.whl", hash = "sha256:ef78c0d96098a3b5fe7720be4a97e73f439af7cf088ebf47b620aeaa10fadf97"}, + {file = "identify-2.5.5.tar.gz", hash = "sha256:322a5699daecf7c6fd60e68852f36f2ecbb6a36ff6e6e973e0d2bb6fca203ee6"}, +] [package.extras] license = ["ukkonen"] @@ -714,6 +1288,42 @@ description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false python-versions = ">=3.5" +files = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.0.0" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, + {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "iniconfig" @@ -722,6 +1332,10 @@ description = "iniconfig: brain-dead simple config-ini parsing" category = "dev" optional = false python-versions = "*" +files = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] [[package]] name = "isort" @@ -730,6 +1344,10 @@ description = "A Python utility / library to sort Python imports." category = "dev" optional = false python-versions = ">=3.6.1,<4.0" +files = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, +] [package.extras] colors = ["colorama (>=0.4.3,<0.5.0)"] @@ -738,12 +1356,80 @@ plugins = ["setuptools"] requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] -name = "makefun" -version = "1.15.0" -description = "Small library to dynamically create python functions." -category = "main" +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "lazy-object-proxy" +version = "1.9.0" +description = "A fast and thorough lazy object proxy." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, + {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"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, + {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"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, + {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"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, + {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"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, + {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"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, +] + +[[package]] +name = "makefun" +version = "1.15.0" +description = "Small library to dynamically create python functions." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "makefun-1.15.0-py2.py3-none-any.whl", hash = "sha256:d79319f9e71b6825ca163be0afa45cbc5b1215e682efa35b2d355a03c594279c"}, + {file = "makefun-1.15.0.tar.gz", hash = "sha256:5b110e733d94f7a49d8ac27b1e2d40f2bb0501e98c1d825e0d932d26920dd5df"}, +] [[package]] name = "Mako" @@ -752,6 +1438,10 @@ description = "A super-fast templating language that borrows the best ideas from category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "Mako-1.2.2-py3-none-any.whl", hash = "sha256:8efcb8004681b5f71d09c983ad5a9e6f5c40601a6ec469148753292abc0da534"}, + {file = "Mako-1.2.2.tar.gz", hash = "sha256:3724869b363ba630a272a5f89f68c070352137b8fd1757650017b7e06fda163f"}, +] [package.dependencies] MarkupSafe = ">=0.9.2" @@ -768,6 +1458,48 @@ description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, +] [[package]] name = "mccabe" @@ -776,6 +1508,10 @@ description = "McCabe checker, plugin for flake8" category = "dev" optional = false python-versions = "*" +files = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] [[package]] name = "multidict" @@ -784,6 +1520,67 @@ description = "multidict implementation" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, + {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, + {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"}, + {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"}, + {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"}, + {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"}, + {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"}, + {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"}, + {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"}, + {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"}, + {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"}, + {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"}, + {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"}, + {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"}, + {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"}, + {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"}, + {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"}, + {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"}, + {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"}, +] [[package]] name = "mypy" @@ -792,6 +1589,31 @@ description = "Optional static typing for Python" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"}, + {file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"}, + {file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"}, + {file = "mypy-0.961-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e"}, + {file = "mypy-0.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"}, + {file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"}, + {file = "mypy-0.961-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b"}, + {file = "mypy-0.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"}, + {file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"}, + {file = "mypy-0.961-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e"}, + {file = "mypy-0.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"}, + {file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"}, + {file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"}, + {file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"}, + {file = "mypy-0.961-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b"}, + {file = "mypy-0.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"}, + {file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"}, + {file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"}, + {file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"}, + {file = "mypy-0.961-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950"}, + {file = "mypy-0.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"}, + {file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"}, + {file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"}, +] [package.dependencies] mypy-extensions = ">=0.4.3" @@ -810,6 +1632,10 @@ description = "Experimental type system extensions for programs checked with the category = "main" optional = false python-versions = "*" +files = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] [[package]] name = "nodeenv" @@ -818,6 +1644,10 @@ description = "Node.js virtual environment builder" category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, + {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, +] [package.dependencies] setuptools = "*" @@ -829,6 +1659,10 @@ description = "Core utilities for Python packages" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] [package.dependencies] pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" @@ -840,6 +1674,10 @@ description = "comprehensive password hashing framework supporting over 30 schem category = "main" optional = false python-versions = "*" +files = [ + {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"}, + {file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"}, +] [package.dependencies] bcrypt = {version = ">=3.1.0", optional = true, markers = "extra == \"bcrypt\""} @@ -857,6 +1695,10 @@ description = "Utility library for gitignore style pattern matching of file path category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, + {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, +] [[package]] name = "pbr" @@ -865,6 +1707,10 @@ description = "Python Build Reasonableness" category = "dev" optional = false python-versions = ">=2.6" +files = [ + {file = "pbr-5.10.0-py2.py3-none-any.whl", hash = "sha256:da3e18aac0a3c003e9eea1a81bd23e5a3a75d745670dcf736317b7d966887fdf"}, + {file = "pbr-5.10.0.tar.gz", hash = "sha256:cfcc4ff8e698256fc17ea3ff796478b050852585aa5bae79ecd05b2ab7b39b9a"}, +] [[package]] name = "pep8-naming" @@ -873,6 +1719,10 @@ description = "Check PEP-8 naming conventions, plugin for flake8" category = "dev" optional = false python-versions = "*" +files = [ + {file = "pep8-naming-0.12.1.tar.gz", hash = "sha256:bb2455947757d162aa4cad55dba4ce029005cd1692f2899a21d51d8630ca7841"}, + {file = "pep8_naming-0.12.1-py2.py3-none-any.whl", hash = "sha256:4a8daeaeb33cfcde779309fc0c9c0a68a3bbe2ad8a8308b763c5068f86eb9f37"}, +] [package.dependencies] flake8 = ">=3.9.1" @@ -885,6 +1735,10 @@ description = "A small Python module for determining appropriate platform-specif category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +] [package.extras] docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] @@ -897,6 +1751,10 @@ description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] [package.extras] dev = ["pre-commit", "tox"] @@ -909,6 +1767,10 @@ description = "A framework for managing and maintaining multi-language pre-commi category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, + {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, +] [package.dependencies] cfgv = ">=2.0.0" @@ -925,6 +1787,10 @@ description = "library with cross-python path, ini-parsing, io, code, log facili category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] [[package]] name = "pycodestyle" @@ -933,6 +1799,10 @@ description = "Python style guide checker" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, + {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, +] [[package]] name = "pycparser" @@ -941,6 +1811,10 @@ description = "C parser in Python" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] [[package]] name = "pydantic" @@ -949,6 +1823,44 @@ description = "Data validation and settings management using python type hints" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"}, + {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"}, + {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"}, + {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"}, + {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"}, + {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"}, + {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"}, + {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"}, + {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"}, + {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"}, + {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"}, + {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"}, + {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"}, + {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"}, + {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"}, + {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"}, + {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"}, + {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"}, + {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"}, + {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"}, + {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"}, + {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"}, + {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"}, + {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"}, + {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"}, + {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"}, + {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"}, + {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"}, + {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"}, + {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"}, + {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"}, + {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"}, + {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"}, + {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"}, + {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"}, + {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"}, +] [package.dependencies] python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""} @@ -965,6 +1877,10 @@ description = "Python docstring style checker" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, + {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, +] [package.dependencies] snowballstemmer = "*" @@ -979,6 +1895,10 @@ description = "passive checker of Python programs" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, + {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, +] [[package]] name = "Pygments" @@ -987,6 +1907,10 @@ description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, + {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, +] [package.extras] plugins = ["importlib-metadata"] @@ -998,6 +1922,10 @@ description = "JSON Web Token implementation in Python" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "PyJWT-2.4.0-py3-none-any.whl", hash = "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf"}, + {file = "PyJWT-2.4.0.tar.gz", hash = "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba"}, +] [package.dependencies] cryptography = {version = ">=3.3.1", optional = true, markers = "extra == \"crypto\""} @@ -1015,6 +1943,10 @@ description = "pyparsing module - Classes and methods to define and execute pars category = "dev" optional = false python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] [package.extras] diagrams = ["jinja2", "railroad-diagrams"] @@ -1026,6 +1958,10 @@ description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"}, + {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"}, +] [package.dependencies] attrs = ">=19.2.0" @@ -1046,6 +1982,10 @@ description = "Pytest plugin for measuring coverage." category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} @@ -1061,6 +2001,9 @@ description = "py.test plugin that allows you to add environment variables." category = "dev" optional = false python-versions = "*" +files = [ + {file = "pytest-env-0.6.2.tar.gz", hash = "sha256:7e94956aef7f2764f3c147d216ce066bf6c42948bb9e293169b1b1c880a580c2"}, +] [package.dependencies] pytest = ">=2.6.0" @@ -1072,6 +2015,10 @@ description = "Read key-value pairs from a .env file and set them as environment category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"}, + {file = "python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5"}, +] [package.extras] cli = ["click (>=5.0)"] @@ -1083,17 +2030,48 @@ description = "A streaming multipart parser for Python" category = "main" optional = false python-versions = "*" +files = [ + {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"}, +] [package.dependencies] six = ">=1.4.0" [[package]] -name = "pywin32" -version = "305" -description = "Python for Window Extensions" +name = "pytz" +version = "2022.7" +description = "World timezone definitions, modern and historical" category = "dev" optional = false python-versions = "*" +files = [ + {file = "pytz-2022.7-py2.py3-none-any.whl", hash = "sha256:93007def75ae22f7cd991c84e02d434876818661f8df9ad5df9e950ff4e52cfd"}, + {file = "pytz-2022.7.tar.gz", hash = "sha256:7ccfae7b4b2c067464a6733c6261673fdb8fd1be905460396b97a073e9fa683a"}, +] + +[[package]] +name = "pywin32" +version = "305" +description = "Python for Window Extensions" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-305-cp310-cp310-win32.whl", hash = "sha256:421f6cd86e84bbb696d54563c48014b12a23ef95a14e0bdba526be756d89f116"}, + {file = "pywin32-305-cp310-cp310-win_amd64.whl", hash = "sha256:73e819c6bed89f44ff1d690498c0a811948f73777e5f97c494c152b850fad478"}, + {file = "pywin32-305-cp310-cp310-win_arm64.whl", hash = "sha256:742eb905ce2187133a29365b428e6c3b9001d79accdc30aa8969afba1d8470f4"}, + {file = "pywin32-305-cp311-cp311-win32.whl", hash = "sha256:19ca459cd2e66c0e2cc9a09d589f71d827f26d47fe4a9d09175f6aa0256b51c2"}, + {file = "pywin32-305-cp311-cp311-win_amd64.whl", hash = "sha256:326f42ab4cfff56e77e3e595aeaf6c216712bbdd91e464d167c6434b28d65990"}, + {file = "pywin32-305-cp311-cp311-win_arm64.whl", hash = "sha256:4ecd404b2c6eceaca52f8b2e3e91b2187850a1ad3f8b746d0796a98b4cea04db"}, + {file = "pywin32-305-cp36-cp36m-win32.whl", hash = "sha256:48d8b1659284f3c17b68587af047d110d8c44837736b8932c034091683e05863"}, + {file = "pywin32-305-cp36-cp36m-win_amd64.whl", hash = "sha256:13362cc5aa93c2beaf489c9c9017c793722aeb56d3e5166dadd5ef82da021fe1"}, + {file = "pywin32-305-cp37-cp37m-win32.whl", hash = "sha256:a55db448124d1c1484df22fa8bbcbc45c64da5e6eae74ab095b9ea62e6d00496"}, + {file = "pywin32-305-cp37-cp37m-win_amd64.whl", hash = "sha256:109f98980bfb27e78f4df8a51a8198e10b0f347257d1e265bb1a32993d0c973d"}, + {file = "pywin32-305-cp38-cp38-win32.whl", hash = "sha256:9dd98384da775afa009bc04863426cb30596fd78c6f8e4e2e5bbf4edf8029504"}, + {file = "pywin32-305-cp38-cp38-win_amd64.whl", hash = "sha256:56d7a9c6e1a6835f521788f53b5af7912090674bb84ef5611663ee1595860fc7"}, + {file = "pywin32-305-cp39-cp39-win32.whl", hash = "sha256:9d968c677ac4d5cbdaa62fd3014ab241718e619d8e36ef8e11fb930515a1e918"}, + {file = "pywin32-305-cp39-cp39-win_amd64.whl", hash = "sha256:50768c6b7c3f0b38b7fb14dd4104da93ebced5f1a50dc0e834594bff6fbe1271"}, +] [[package]] name = "pyyaml" @@ -1102,6 +2080,48 @@ description = "YAML parser and emitter for Python" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] [[package]] name = "requests" @@ -1110,6 +2130,10 @@ description = "Python HTTP for Humans." category = "dev" optional = false python-versions = ">=3.7, <4" +files = [ + {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, + {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, +] [package.dependencies] certifi = ">=2017.4.17" @@ -1128,6 +2152,9 @@ description = "reStructuredText linter" category = "dev" optional = false python-versions = "*" +files = [ + {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, +] [package.dependencies] docutils = ">=0.11,<1.0" @@ -1139,6 +2166,10 @@ description = "Validating URI References per RFC 3986" category = "main" optional = false python-versions = "*" +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] [package.dependencies] idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} @@ -1153,6 +2184,10 @@ description = "Easily download, build, install, upgrade, and uninstall Python pa category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "setuptools-65.3.0-py3-none-any.whl", hash = "sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82"}, + {file = "setuptools-65.3.0.tar.gz", hash = "sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"}, +] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] @@ -1166,6 +2201,10 @@ description = "Python 2 and 3 compatibility utilities" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] [[package]] name = "smmap" @@ -1174,6 +2213,10 @@ description = "A pure Python implementation of a sliding window memory map manag category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, + {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, +] [[package]] name = "sniffio" @@ -1182,6 +2225,10 @@ description = "Sniff out which async library your code is running under" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] [[package]] name = "snowballstemmer" @@ -1190,6 +2237,214 @@ description = "This package provides 29 stemmers for 28 languages generated from category = "dev" optional = false python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.3.2.post1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, + {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, +] + +[[package]] +name = "sphinx" +version = "6.1.3" +description = "Python documentation generator" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "Sphinx-6.1.3.tar.gz", hash = "sha256:0dac3b698538ffef41716cf97ba26c1c7788dba73ce6f150c1ff5b4720786dd2"}, + {file = "sphinx-6.1.3-py3-none-any.whl", hash = "sha256:807d1cb3d6be87eb78a381c3e70ebd8d346b9a25f3753e9947e866b2786865fc"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.18,<0.20" +imagesize = ">=1.3" +importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.13" +requests = ">=2.25.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] +test = ["cython", "html5lib", "pytest (>=4.6)"] + +[[package]] +name = "sphinx-autoapi" +version = "2.0.0" +description = "Sphinx API documentation generator" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-autoapi-2.0.0.tar.gz", hash = "sha256:97dcf1b5b54cd0d8efef867594e4a4f3e2d3a2c0ec1e5a891e0a61bc77046006"}, + {file = "sphinx_autoapi-2.0.0-py2.py3-none-any.whl", hash = "sha256:dab2753a38cad907bf4e61473c0da365a26bfbe69fbf5aa6e4f7d48e1cf8a148"}, +] + +[package.dependencies] +astroid = ">=2.7" +Jinja2 = "*" +PyYAML = "*" +sphinx = ">=4.0" +unidecode = "*" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme"] +dotnet = ["sphinxcontrib-dotnetdomain"] +go = ["sphinxcontrib-golangdomain"] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b1" +description = "A modern skeleton for Sphinx themes." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_basic_ng-1.0.0b1-py3-none-any.whl", hash = "sha256:ade597a3029c7865b24ad0eda88318766bcc2f9f4cef60df7e28126fde94db2a"}, + {file = "sphinx_basic_ng-1.0.0b1.tar.gz", hash = "sha256:89374bd3ccd9452a301786781e28c8718e99960f2d4f411845ea75fc7bb5a9b0"}, +] + +[package.dependencies] +sphinx = ">=4.0" + +[package.extras] +docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] + +[[package]] +name = "sphinx-copybutton" +version = "0.5.1" +description = "Add a copy button to each of your code cells." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-copybutton-0.5.1.tar.gz", hash = "sha256:366251e28a6f6041514bfb5439425210418d6c750e98d3a695b73e56866a677a"}, + {file = "sphinx_copybutton-0.5.1-py3-none-any.whl", hash = "sha256:0842851b5955087a7ec7fc870b622cb168618ad408dee42692e9a5c97d071da8"}, +] + +[package.dependencies] +sphinx = ">=1.8" + +[package.extras] +code-style = ["pre-commit (==2.12.1)"] +rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.3" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinxcontrib.applehelp-1.0.3-py3-none-any.whl", hash = "sha256:ba0f2a22e6eeada8da6428d0d520215ee8864253f32facf958cca81e426f661d"}, + {file = "sphinxcontrib.applehelp-1.0.3.tar.gz", hash = "sha256:83749f09f6ac843b8cb685277dbc818a8bf2d76cc19602699094fe9a74db529e"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, + {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] [[package]] name = "SQLAlchemy" @@ -1198,6 +2453,49 @@ description = "Database Abstraction Library" category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "SQLAlchemy-1.4.41-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:13e397a9371ecd25573a7b90bd037db604331cf403f5318038c46ee44908c44d"}, + {file = "SQLAlchemy-1.4.41-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2d6495f84c4fd11584f34e62f9feec81bf373787b3942270487074e35cbe5330"}, + {file = "SQLAlchemy-1.4.41-cp27-cp27m-win32.whl", hash = "sha256:e570cfc40a29d6ad46c9aeaddbdcee687880940a3a327f2c668dd0e4ef0a441d"}, + {file = "SQLAlchemy-1.4.41-cp27-cp27m-win_amd64.whl", hash = "sha256:5facb7fd6fa8a7353bbe88b95695e555338fb038ad19ceb29c82d94f62775a05"}, + {file = "SQLAlchemy-1.4.41-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f37fa70d95658763254941ddd30ecb23fc4ec0c5a788a7c21034fc2305dab7cc"}, + {file = "SQLAlchemy-1.4.41-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:361f6b5e3f659e3c56ea3518cf85fbdae1b9e788ade0219a67eeaaea8a4e4d2a"}, + {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0990932f7cca97fece8017414f57fdd80db506a045869d7ddf2dda1d7cf69ecc"}, + {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd767cf5d7252b1c88fcfb58426a32d7bd14a7e4942497e15b68ff5d822b41ad"}, + {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5102fb9ee2c258a2218281adcb3e1918b793c51d6c2b4666ce38c35101bb940e"}, + {file = "SQLAlchemy-1.4.41-cp310-cp310-win32.whl", hash = "sha256:2082a2d2fca363a3ce21cfa3d068c5a1ce4bf720cf6497fb3a9fc643a8ee4ddd"}, + {file = "SQLAlchemy-1.4.41-cp310-cp310-win_amd64.whl", hash = "sha256:e4b12e3d88a8fffd0b4ca559f6d4957ed91bd4c0613a4e13846ab8729dc5c251"}, + {file = "SQLAlchemy-1.4.41-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:90484a2b00baedad361402c257895b13faa3f01780f18f4a104a2f5c413e4536"}, + {file = "SQLAlchemy-1.4.41-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b67fc780cfe2b306180e56daaa411dd3186bf979d50a6a7c2a5b5036575cbdbb"}, + {file = "SQLAlchemy-1.4.41-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad2b727fc41c7f8757098903f85fafb4bf587ca6605f82d9bf5604bd9c7cded"}, + {file = "SQLAlchemy-1.4.41-cp311-cp311-win32.whl", hash = "sha256:59bdc291165b6119fc6cdbc287c36f7f2859e6051dd923bdf47b4c55fd2f8bd0"}, + {file = "SQLAlchemy-1.4.41-cp311-cp311-win_amd64.whl", hash = "sha256:d2e054aed4645f9b755db85bc69fc4ed2c9020c19c8027976f66576b906a74f1"}, + {file = "SQLAlchemy-1.4.41-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:4ba7e122510bbc07258dc42be6ed45997efdf38129bde3e3f12649be70683546"}, + {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0dcf127bb99458a9d211e6e1f0f3edb96c874dd12f2503d4d8e4f1fd103790b"}, + {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e16c2be5cb19e2c08da7bd3a87fed2a0d4e90065ee553a940c4fc1a0fb1ab72b"}, + {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ebeeec5c14533221eb30bad716bc1fd32f509196318fb9caa7002c4a364e4c"}, + {file = "SQLAlchemy-1.4.41-cp36-cp36m-win32.whl", hash = "sha256:3e2ef592ac3693c65210f8b53d0edcf9f4405925adcfc031ff495e8d18169682"}, + {file = "SQLAlchemy-1.4.41-cp36-cp36m-win_amd64.whl", hash = "sha256:eb30cf008850c0a26b72bd1b9be6730830165ce049d239cfdccd906f2685f892"}, + {file = "SQLAlchemy-1.4.41-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:c23d64a0b28fc78c96289ffbd0d9d1abd48d267269b27f2d34e430ea73ce4b26"}, + {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8eb8897367a21b578b26f5713833836f886817ee2ffba1177d446fa3f77e67c8"}, + {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:14576238a5f89bcf504c5f0a388d0ca78df61fb42cb2af0efe239dc965d4f5c9"}, + {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:639e1ae8d48b3c86ffe59c0daa9a02e2bfe17ca3d2b41611b30a0073937d4497"}, + {file = "SQLAlchemy-1.4.41-cp37-cp37m-win32.whl", hash = "sha256:0005bd73026cd239fc1e8ccdf54db58b6193be9a02b3f0c5983808f84862c767"}, + {file = "SQLAlchemy-1.4.41-cp37-cp37m-win_amd64.whl", hash = "sha256:5323252be2bd261e0aa3f33cb3a64c45d76829989fa3ce90652838397d84197d"}, + {file = "SQLAlchemy-1.4.41-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:05f0de3a1dc3810a776275763764bb0015a02ae0f698a794646ebc5fb06fad33"}, + {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0002e829142b2af00b4eaa26c51728f3ea68235f232a2e72a9508a3116bd6ed0"}, + {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22ff16cedab5b16a0db79f1bc99e46a6ddececb60c396562e50aab58ddb2871c"}, + {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccfd238f766a5bb5ee5545a62dd03f316ac67966a6a658efb63eeff8158a4bbf"}, + {file = "SQLAlchemy-1.4.41-cp38-cp38-win32.whl", hash = "sha256:58bb65b3274b0c8a02cea9f91d6f44d0da79abc993b33bdedbfec98c8440175a"}, + {file = "SQLAlchemy-1.4.41-cp38-cp38-win_amd64.whl", hash = "sha256:ce8feaa52c1640de9541eeaaa8b5fb632d9d66249c947bb0d89dd01f87c7c288"}, + {file = "SQLAlchemy-1.4.41-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:199a73c31ac8ea59937cc0bf3dfc04392e81afe2ec8a74f26f489d268867846c"}, + {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676d51c9f6f6226ae8f26dc83ec291c088fe7633269757d333978df78d931ab"}, + {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:036d8472356e1d5f096c5e0e1a7e0f9182140ada3602f8fff6b7329e9e7cfbcd"}, + {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2307495d9e0ea00d0c726be97a5b96615035854972cc538f6e7eaed23a35886c"}, + {file = "SQLAlchemy-1.4.41-cp39-cp39-win32.whl", hash = "sha256:9c56e19780cd1344fcd362fd6265a15f48aa8d365996a37fab1495cae8fcd97d"}, + {file = "SQLAlchemy-1.4.41-cp39-cp39-win_amd64.whl", hash = "sha256:f5fa526d027d804b1f85cdda1eb091f70bde6fb7d87892f6dd5a48925bc88898"}, + {file = "SQLAlchemy-1.4.41.tar.gz", hash = "sha256:0292f70d1797e3c54e862e6f30ae474014648bc9c723e14a2fda730adb0a9791"}, +] [package.dependencies] greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} @@ -1206,7 +2504,7 @@ sqlalchemy2-stubs = {version = "*", optional = true, markers = "extra == \"mypy\ [package.extras] aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"] asyncio = ["greenlet (!=0.4.17)"] asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] @@ -1216,14 +2514,14 @@ mssql-pyodbc = ["pyodbc"] mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] mysql-connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] +oracle = ["cx-oracle (>=7)", "cx-oracle (>=7,<8)"] postgresql = ["psycopg2 (>=2.7)"] postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] postgresql-psycopg2binary = ["psycopg2-binary"] postgresql-psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql", "pymysql (<1)"] -sqlcipher = ["sqlcipher3_binary"] +sqlcipher = ["sqlcipher3-binary"] [[package]] name = "sqlalchemy2-stubs" @@ -1232,6 +2530,10 @@ description = "Typing Stubs for SQLAlchemy 1.4" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "sqlalchemy2-stubs-0.0.2a27.tar.gz", hash = "sha256:f79bce50b7837a2c2374ef4480b41e2b8a8226f313f347dc2a70526a4191db93"}, + {file = "sqlalchemy2_stubs-0.0.2a27-py3-none-any.whl", hash = "sha256:6cea12fec3c261f6e0e14a95d2cc4914e373095e68ec4fc2eb473183ac2b17a2"}, +] [package.dependencies] typing-extensions = ">=3.7.4" @@ -1243,6 +2545,10 @@ description = "The little ASGI library that shines." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "starlette-0.20.4-py3-none-any.whl", hash = "sha256:c0414d5a56297d37f3db96a84034d61ce29889b9eaccf65eb98a0b39441fcaa3"}, + {file = "starlette-0.20.4.tar.gz", hash = "sha256:42fcf3122f998fefce3e2c5ad7e5edbf0f02cf685d646a83a08d404726af5084"}, +] [package.dependencies] anyio = ">=3.4.0,<5" @@ -1258,6 +2564,10 @@ description = "Manage dynamic plugins for Python applications" category = "dev" optional = false python-versions = ">=3.8" +files = [ + {file = "stevedore-4.0.0-py3-none-any.whl", hash = "sha256:87e4d27fe96d0d7e4fc24f0cbe3463baae4ec51e81d95fbe60d2474636e0c7d8"}, + {file = "stevedore-4.0.0.tar.gz", hash = "sha256:f82cc99a1ff552310d19c379827c2c64dd9f85a38bcd5559db2470161867b786"}, +] [package.dependencies] pbr = ">=2.0.0,<2.1.0 || >2.1.0" @@ -1269,1184 +2579,113 @@ description = "Library provides lightweight, throwaway instances of common datab category = "dev" optional = false python-versions = ">=3.6" - -[package.dependencies] -deprecation = "*" -docker = ">=4.0.0" -wrapt = "*" - -[package.extras] -arangodb = ["python-arango"] -azurite = ["azure-storage-blob"] -clickhouse = ["clickhouse-driver"] -docker-compose = ["docker-compose"] -google-cloud-pubsub = ["google-cloud-pubsub (<2)"] -kafka = ["kafka-python"] -keycloak = ["python-keycloak"] -mongo = ["pymongo"] -mssqlserver = ["pymssql"] -mysql = ["pymysql", "sqlalchemy"] -neo4j = ["neo4j"] -oracle = ["cx-Oracle", "sqlalchemy"] -postgresql = ["psycopg2-binary", "sqlalchemy"] -rabbitmq = ["pika"] -redis = ["redis"] -selenium = ["selenium"] - -[[package]] -name = "tokenize-rt" -version = "4.2.1" -description = "A wrapper around the stdlib `tokenize` which roundtrips." -category = "dev" -optional = false -python-versions = ">=3.6.1" - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "types-aiofiles" -version = "0.8.11" -description = "Typing stubs for aiofiles" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "types-pyyaml" -version = "6.0.12.2" -description = "Typing stubs for PyYAML" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "typing-extensions" -version = "4.3.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "ujson" -version = "5.4.0" -description = "Ultra fast JSON encoder and decoder for Python" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "urllib3" -version = "1.26.12" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "uvicorn" -version = "0.17.5" -description = "The lightning-fast ASGI server." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -asgiref = ">=3.4.0" -click = ">=7.0" -colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} -h11 = ">=0.8" -httptools = {version = ">=0.2.0,<0.4.0", optional = true, markers = "extra == \"standard\""} -python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -PyYAML = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} -watchgod = {version = ">=0.6", optional = true, markers = "extra == \"standard\""} -websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""} - -[package.extras] -standard = ["PyYAML (>=5.1)", "colorama (>=0.4)", "httptools (>=0.2.0,<0.4.0)", "python-dotenv (>=0.13)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchgod (>=0.6)", "websockets (>=10.0)"] - -[[package]] -name = "uvloop" -version = "0.16.0" -description = "Fast implementation of asyncio event loop on top of libuv" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -dev = ["Cython (>=0.29.24,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"] - -[[package]] -name = "virtualenv" -version = "20.16.5" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -distlib = ">=0.3.5,<1" -filelock = ">=3.4.1,<4" -platformdirs = ">=2.4,<3" - -[package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] -testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "watchgod" -version = "0.8.2" -description = "Simple, modern file watching and code reload in python." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -anyio = ">=3.0.0,<4" - -[[package]] -name = "websocket-client" -version = "1.4.2" -description = "WebSocket client for Python with low level API options" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] -optional = ["python-socks", "wsaccel"] -test = ["websockets"] - -[[package]] -name = "websockets" -version = "10.3" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "wemake-python-styleguide" -version = "0.16.1" -description = "The strictest and most opinionated python linter ever" -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -astor = ">=0.8,<0.9" -attrs = "*" -darglint = ">=1.2,<2.0" -flake8 = ">=3.7,<5" -flake8-bandit = ">=2.1,<4" -flake8-broken-line = ">=0.3,<0.5" -flake8-bugbear = ">=20.1,<23.0" -flake8-commas = ">=2.0,<3.0" -flake8-comprehensions = ">=3.1,<4.0" -flake8-debugger = ">=4.0,<5.0" -flake8-docstrings = ">=1.3,<2.0" -flake8-eradicate = ">=1.0,<2.0" -flake8-isort = ">=4.0,<5.0" -flake8-quotes = ">=3.0,<4.0" -flake8-rst-docstrings = ">=0.2,<0.3" -flake8-string-format = ">=0.3,<0.4" -pep8-naming = ">=0.11,<0.13" -pygments = ">=2.4,<3.0" -typing_extensions = ">=3.6,<5.0" - -[[package]] -name = "wrapt" -version = "1.14.1" -description = "Module for decorators, wrappers and monkey patching." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[[package]] -name = "yarl" -version = "1.8.1" -description = "Yet another URL library" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" - -[[package]] -name = "yesqa" -version = "1.4.0" -description = "Automatically remove unnecessary `# noqa` comments." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -flake8 = ">=3.9" -tokenize-rt = ">=2.1" - -[metadata] -lock-version = "1.1" -python-versions = "^3.9" -content-hash = "47b05792882c48e783668a1f2090a0b09a3cad7c2ea64582bfd192476c2c8f0d" - -[metadata.files] -aiofiles = [ - {file = "aiofiles-0.8.0-py3-none-any.whl", hash = "sha256:7a973fc22b29e9962d0897805ace5856e6a566ab1f0c8e5c91ff6c866519c937"}, - {file = "aiofiles-0.8.0.tar.gz", hash = "sha256:8334f23235248a3b2e83b2c3a78a22674f39969b96397126cc93664d9a901e59"}, -] -alembic = [ - {file = "alembic-1.8.1-py3-none-any.whl", hash = "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4"}, - {file = "alembic-1.8.1.tar.gz", hash = "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa"}, -] -anyio = [ - {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"}, - {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"}, -] -asgiref = [ - {file = "asgiref-3.5.2-py3-none-any.whl", hash = "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4"}, - {file = "asgiref-3.5.2.tar.gz", hash = "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424"}, -] -astor = [ - {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, - {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, -] -asyncpg = [ - {file = "asyncpg-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf5e3408a14a17d480f36ebaf0401a12ff6ae5457fdf45e4e2775c51cc9517d3"}, - {file = "asyncpg-0.25.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bc197fc4aca2fd24f60241057998124012469d2e414aed3f992579db0c88e3a"}, - {file = "asyncpg-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a70783f6ffa34cc7dd2de20a873181414a34fd35a4a208a1f1a7f9f695e4ec4"}, - {file = "asyncpg-0.25.0-cp310-cp310-win32.whl", hash = "sha256:43cde84e996a3afe75f325a68300093425c2f47d340c0fc8912765cf24a1c095"}, - {file = "asyncpg-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:56d88d7ef4341412cd9c68efba323a4519c916979ba91b95d4c08799d2ff0c09"}, - {file = "asyncpg-0.25.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a84d30e6f850bac0876990bcd207362778e2208df0bee8be8da9f1558255e634"}, - {file = "asyncpg-0.25.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:beaecc52ad39614f6ca2e48c3ca15d56e24a2c15cbfdcb764a4320cc45f02fd5"}, - {file = "asyncpg-0.25.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:6f8f5fc975246eda83da8031a14004b9197f510c41511018e7b1bedde6968e92"}, - {file = "asyncpg-0.25.0-cp36-cp36m-win32.whl", hash = "sha256:ddb4c3263a8d63dcde3d2c4ac1c25206bfeb31fa83bd70fd539e10f87739dee4"}, - {file = "asyncpg-0.25.0-cp36-cp36m-win_amd64.whl", hash = "sha256:bf6dc9b55b9113f39eaa2057337ce3f9ef7de99a053b8a16360395ce588925cd"}, - {file = "asyncpg-0.25.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:acb311722352152936e58a8ee3c5b8e791b24e84cd7d777c414ff05b3530ca68"}, - {file = "asyncpg-0.25.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0a61fb196ce4dae2f2fa26eb20a778db21bbee484d2e798cb3cc988de13bdd1b"}, - {file = "asyncpg-0.25.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2633331cbc8429030b4f20f712f8d0fbba57fa8555ee9b2f45f981b81328b256"}, - {file = "asyncpg-0.25.0-cp37-cp37m-win32.whl", hash = "sha256:863d36eba4a7caa853fd7d83fad5fd5306f050cc2fe6e54fbe10cdb30420e5e9"}, - {file = "asyncpg-0.25.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fe471ccd915b739ca65e2e4dbd92a11b44a5b37f2e38f70827a1c147dafe0fa8"}, - {file = "asyncpg-0.25.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:72a1e12ea0cf7c1e02794b697e3ca967b2360eaa2ce5d4bfdd8604ec2d6b774b"}, - {file = "asyncpg-0.25.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4327f691b1bdb222df27841938b3e04c14068166b3a97491bec2cb982f49f03e"}, - {file = "asyncpg-0.25.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:739bbd7f89a2b2f6bc44cb8bf967dab12c5bc714fcbe96e68d512be45ecdf962"}, - {file = "asyncpg-0.25.0-cp38-cp38-win32.whl", hash = "sha256:18d49e2d93a7139a2fdbd113e320cc47075049997268a61bfbe0dde680c55471"}, - {file = "asyncpg-0.25.0-cp38-cp38-win_amd64.whl", hash = "sha256:191fe6341385b7fdea7dbdcf47fd6db3fd198827dcc1f2b228476d13c05a03c6"}, - {file = "asyncpg-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fab7f1b2c29e187dd8781fce896249500cf055b63471ad66332e537e9b5f7e"}, - {file = "asyncpg-0.25.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a738f1b2876f30d710d3dc1e7858160a0afe1603ba16bf5f391f5316eb0ed855"}, - {file = "asyncpg-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4105f57ad1e8fbc8b1e535d8fcefa6ce6c71081228f08680c6dea24384ff0e"}, - {file = "asyncpg-0.25.0-cp39-cp39-win32.whl", hash = "sha256:f55918ded7b85723a5eaeb34e86e7b9280d4474be67df853ab5a7fa0cc7c6bf2"}, - {file = "asyncpg-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:649e2966d98cc48d0646d9a4e29abecd8b59d38d55c256d5c857f6b27b7407ac"}, - {file = "asyncpg-0.25.0.tar.gz", hash = "sha256:63f8e6a69733b285497c2855464a34de657f2cccd25aeaeeb5071872e9382540"}, -] -asyncssh = [ - {file = "asyncssh-2.12.0-py3-none-any.whl", hash = "sha256:6841c4242c606fd51188c974ec2f4887efeec67ecdfa5b84140711dacd985ab3"}, - {file = "asyncssh-2.12.0.tar.gz", hash = "sha256:274101322c4b941823aeed8e1ab6e7be5191686c6db2d2bd35afeba30505e780"}, -] -attrs = [ - {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, - {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, -] -autoflake = [ - {file = "autoflake-1.5.3-py2.py3-none-any.whl", hash = "sha256:90eb8d3f625bd72068eb670338ea7efcddbc5c6e822d3601e3dc9404c06ea8da"}, - {file = "autoflake-1.5.3.tar.gz", hash = "sha256:44f7d7eb2c1c49505b513c0e93a5dfd3f7b4218283f50c5ca0af4df6b975d470"}, -] -bandit = [ - {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, - {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, -] -bcrypt = [ - {file = "bcrypt-4.0.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:845b1daf4df2dd94d2fdbc9454953ca9dd0e12970a0bfc9f3dcc6faea3fa96e4"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8780e69f9deec9d60f947b169507d2c9816e4f11548f1f7ebee2af38b9b22ae4"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c3334446fac200499e8bc04a530ce3cf0b3d7151e0e4ac5c0dddd3d95e97843"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb67f6a6c72dfb0a02f3df51550aa1862708e55128b22543e2b42c74f3620d7"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:7c7dd6c1f05bf89e65261d97ac3a6520f34c2acb369afb57e3ea4449be6ff8fd"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:594780b364fb45f2634c46ec8d3e61c1c0f1811c4f2da60e8eb15594ecbf93ed"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d0dd19aad87e4ab882ef1d12df505f4c52b28b69666ce83c528f42c07379227"}, - {file = "bcrypt-4.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bf413f2a9b0a2950fc750998899013f2e718d20fa4a58b85ca50b6df5ed1bbf9"}, - {file = "bcrypt-4.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ede0f506554571c8eda80db22b83c139303ec6b595b8f60c4c8157bdd0bdee36"}, - {file = "bcrypt-4.0.0-cp36-abi3-win32.whl", hash = "sha256:dc6ec3dc19b1c193b2f7cf279d3e32e7caf447532fbcb7af0906fe4398900c33"}, - {file = "bcrypt-4.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:0b0f0c7141622a31e9734b7f649451147c04ebb5122327ac0bd23744df84be90"}, - {file = "bcrypt-4.0.0.tar.gz", hash = "sha256:c59c170fc9225faad04dde1ba61d85b413946e8ce2e5f5f5ff30dfd67283f319"}, -] -black = [ - {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, - {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, - {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, - {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, - {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, - {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, - {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, - {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, - {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, - {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, - {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, - {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, - {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, - {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, - {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, - {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, - {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, - {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, - {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, -] -certifi = [ - {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, - {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, -] -cffi = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] -cfgv = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, - {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, -] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] -colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, -] -coverage = [ - {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"}, - {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"}, - {file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"}, - {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"}, - {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"}, - {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"}, - {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"}, - {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"}, - {file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"}, - {file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"}, - {file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"}, - {file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"}, - {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"}, - {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"}, - {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"}, - {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"}, - {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"}, - {file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"}, - {file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"}, - {file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"}, - {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"}, - {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"}, - {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"}, - {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"}, - {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"}, - {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"}, - {file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"}, - {file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"}, - {file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"}, - {file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"}, - {file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"}, - {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"}, - {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"}, - {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"}, - {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"}, - {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"}, - {file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"}, - {file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"}, - {file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"}, - {file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"}, - {file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"}, - {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"}, - {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"}, - {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"}, - {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"}, - {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"}, - {file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"}, - {file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"}, - {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"}, - {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"}, -] -cryptography = [ - {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f"}, - {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6"}, - {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a"}, - {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294"}, - {file = "cryptography-38.0.1-cp36-abi3-win32.whl", hash = "sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0"}, - {file = "cryptography-38.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a"}, - {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d"}, - {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9"}, - {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b"}, - {file = "cryptography-38.0.1.tar.gz", hash = "sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7"}, -] -darglint = [ - {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, - {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, -] -deprecation = [ - {file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"}, - {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"}, -] -distlib = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] -dnspython = [ - {file = "dnspython-2.2.1-py3-none-any.whl", hash = "sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"}, - {file = "dnspython-2.2.1.tar.gz", hash = "sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e"}, -] -docker = [ - {file = "docker-6.0.1-py3-none-any.whl", hash = "sha256:dbcb3bd2fa80dca0788ed908218bf43972772009b881ed1e20dfc29a65e49782"}, - {file = "docker-6.0.1.tar.gz", hash = "sha256:896c4282e5c7af5c45e8b683b0b0c33932974fe6e50fc6906a0a83616ab3da97"}, -] -docutils = [ - {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, - {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, -] -email-validator = [ - {file = "email_validator-1.2.1-py2.py3-none-any.whl", hash = "sha256:c8589e691cf73eb99eed8d10ce0e9cbb05a0886ba920c8bcb7c82873f4c5789c"}, - {file = "email_validator-1.2.1.tar.gz", hash = "sha256:6757aea012d40516357c0ac2b1a4c31219ab2f899d26831334c5d069e8b6c3d8"}, -] -eradicate = [ - {file = "eradicate-2.1.0-py3-none-any.whl", hash = "sha256:8bfaca181db9227dc88bdbce4d051a9627604c2243e7d85324f6d6ce0fd08bb2"}, - {file = "eradicate-2.1.0.tar.gz", hash = "sha256:aac7384ab25b1bf21c4c012de9b4bf8398945a14c98c911545b2ea50ab558014"}, -] -fastapi = [ - {file = "fastapi-0.85.2-py3-none-any.whl", hash = "sha256:6292db0edd4a11f0d938d6033ccec5f706e9d476958bf33b119e8ddb4e524bde"}, - {file = "fastapi-0.85.2.tar.gz", hash = "sha256:3e10ea0992c700e0b17b6de8c2092d7b9cd763ce92c49ee8d4be10fee3b2f367"}, -] -fastapi-users = [ - {file = "fastapi-users-10.1.5.tar.gz", hash = "sha256:47cb99eef18eea654170674f5a86c4bed898a6ee666b45007f9805e687ff32f5"}, - {file = "fastapi_users-10.1.5-py3-none-any.whl", hash = "sha256:dded5947d271b80e67b2093f25c591a71eff62975e622ef9871b6283174a87b5"}, -] -fastapi-users-db-sqlalchemy = [ - {file = "fastapi-users-db-sqlalchemy-4.0.3.tar.gz", hash = "sha256:7d223742e687cf386bea4e990d6ca8571185c50f365301a34e8eb14ebbda18b2"}, - {file = "fastapi_users_db_sqlalchemy-4.0.3-py3-none-any.whl", hash = "sha256:81102ca68e07895898eb9b9d85c6c2e6378f9a46e8486708a36cb1c096117a81"}, -] -filelock = [ - {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, - {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, -] -flake8 = [ - {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, - {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, -] -flake8-bandit = [ - {file = "flake8_bandit-3.0.0-py2.py3-none-any.whl", hash = "sha256:61b617f4f7cdaa0e2b1e6bf7b68afb2b619a227bb3e3ae00dd36c213bd17900a"}, - {file = "flake8_bandit-3.0.0.tar.gz", hash = "sha256:54d19427e6a8d50322a7b02e1841c0a7c22d856975f3459803320e0e18e2d6a1"}, -] -flake8-broken-line = [ - {file = "flake8-broken-line-0.4.0.tar.gz", hash = "sha256:771aab5aa0997666796fed249d0e48e6c01cdfeca8c95521eea28a38b7ced4c7"}, - {file = "flake8_broken_line-0.4.0-py3-none-any.whl", hash = "sha256:e9c522856862239a2c7ef2c1de0276fa598572aa864bd4e9c7efc2a827538515"}, -] -flake8-bugbear = [ - {file = "flake8-bugbear-22.8.23.tar.gz", hash = "sha256:de0717d11124a082118dd08387b34fd86b2721642ec2d8e92be66cfa5ea7c445"}, - {file = "flake8_bugbear-22.8.23-py3-none-any.whl", hash = "sha256:1b0ebe0873d1cd55bf9f1588bfcb930db339018ef44a3981a26532daa9fd14a8"}, -] -flake8-commas = [ - {file = "flake8-commas-2.1.0.tar.gz", hash = "sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263"}, - {file = "flake8_commas-2.1.0-py2.py3-none-any.whl", hash = "sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54"}, -] -flake8-comprehensions = [ - {file = "flake8-comprehensions-3.10.0.tar.gz", hash = "sha256:181158f7e7aa26a63a0a38e6017cef28c6adee71278ce56ce11f6ec9c4905058"}, - {file = "flake8_comprehensions-3.10.0-py3-none-any.whl", hash = "sha256:dad454fd3d525039121e98fa1dd90c46bc138708196a4ebbc949ad3c859adedb"}, -] -flake8-debugger = [ - {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, - {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, -] -flake8-docstrings = [ - {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, - {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, -] -flake8-eradicate = [ - {file = "flake8-eradicate-1.3.0.tar.gz", hash = "sha256:e4c98f00d17dc8653e3388cac2624cd81e9735de2fd4a8dcf99029633ebd7a63"}, - {file = "flake8_eradicate-1.3.0-py3-none-any.whl", hash = "sha256:85a71e0c5f4e07f7c6c5fec520483561fd6bd295417d622855bdeade99242e3d"}, -] -flake8-isort = [ - {file = "flake8-isort-4.2.0.tar.gz", hash = "sha256:26571500cd54976bbc0cf1006ffbcd1a68dd102f816b7a1051b219616ba9fee0"}, - {file = "flake8_isort-4.2.0-py3-none-any.whl", hash = "sha256:5b87630fb3719bf4c1833fd11e0d9534f43efdeba524863e15d8f14a7ef6adbf"}, -] -flake8-polyfill = [ - {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, - {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, -] -flake8-quotes = [ - {file = "flake8-quotes-3.3.1.tar.gz", hash = "sha256:633adca6fb8a08131536af0d750b44d6985b9aba46f498871e21588c3e6f525a"}, -] -flake8-rst-docstrings = [ - {file = "flake8-rst-docstrings-0.2.7.tar.gz", hash = "sha256:2740067ab9237559dd45a3434d8c987792c7b259ca563621a3b95efe201f5382"}, - {file = "flake8_rst_docstrings-0.2.7-py3-none-any.whl", hash = "sha256:5d56075dce360bcc9c6775bfe7cb431aa395de600ca7e8d40580a28d50b2a803"}, -] -flake8-string-format = [ - {file = "flake8-string-format-0.3.0.tar.gz", hash = "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2"}, - {file = "flake8_string_format-0.3.0-py2.py3-none-any.whl", hash = "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"}, -] -gitdb = [ - {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, - {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, -] -GitPython = [ - {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, - {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, -] -greenlet = [ - {file = "greenlet-2.0.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:9ed358312e63bf683b9ef22c8e442ef6c5c02973f0c2a939ec1d7b50c974015c"}, - {file = "greenlet-2.0.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4f09b0010e55bec3239278f642a8a506b91034f03a4fb28289a7d448a67f1515"}, - {file = "greenlet-2.0.1-cp27-cp27m-win32.whl", hash = "sha256:1407fe45246632d0ffb7a3f4a520ba4e6051fc2cbd61ba1f806900c27f47706a"}, - {file = "greenlet-2.0.1-cp27-cp27m-win_amd64.whl", hash = "sha256:3001d00eba6bbf084ae60ec7f4bb8ed375748f53aeaefaf2a37d9f0370558524"}, - {file = "greenlet-2.0.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d566b82e92ff2e09dd6342df7e0eb4ff6275a3f08db284888dcd98134dbd4243"}, - {file = "greenlet-2.0.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:0722c9be0797f544a3ed212569ca3fe3d9d1a1b13942d10dd6f0e8601e484d26"}, - {file = "greenlet-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d37990425b4687ade27810e3b1a1c37825d242ebc275066cfee8cb6b8829ccd"}, - {file = "greenlet-2.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be35822f35f99dcc48152c9839d0171a06186f2d71ef76dc57fa556cc9bf6b45"}, - {file = "greenlet-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c140e7eb5ce47249668056edf3b7e9900c6a2e22fb0eaf0513f18a1b2c14e1da"}, - {file = "greenlet-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d21681f09e297a5adaa73060737e3aa1279a13ecdcfcc6ef66c292cb25125b2d"}, - {file = "greenlet-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fb412b7db83fe56847df9c47b6fe3f13911b06339c2aa02dcc09dce8bbf582cd"}, - {file = "greenlet-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6a08799e9e88052221adca55741bf106ec7ea0710bca635c208b751f0d5b617"}, - {file = "greenlet-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e112e03d37987d7b90c1e98ba5e1b59e1645226d78d73282f45b326f7bddcb9"}, - {file = "greenlet-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56961cfca7da2fdd178f95ca407fa330c64f33289e1804b592a77d5593d9bd94"}, - {file = "greenlet-2.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13ba6e8e326e2116c954074c994da14954982ba2795aebb881c07ac5d093a58a"}, - {file = "greenlet-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bf633a50cc93ed17e494015897361010fc08700d92676c87931d3ea464123ce"}, - {file = "greenlet-2.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9f2c221eecb7ead00b8e3ddb913c67f75cba078fd1d326053225a3f59d850d72"}, - {file = "greenlet-2.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:13ebf93c343dd8bd010cd98e617cb4c1c1f352a0cf2524c82d3814154116aa82"}, - {file = "greenlet-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:6f61d71bbc9b4a3de768371b210d906726535d6ca43506737682caa754b956cd"}, - {file = "greenlet-2.0.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:2d0bac0385d2b43a7bd1d651621a4e0f1380abc63d6fb1012213a401cbd5bf8f"}, - {file = "greenlet-2.0.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:f6327b6907b4cb72f650a5b7b1be23a2aab395017aa6f1adb13069d66360eb3f"}, - {file = "greenlet-2.0.1-cp35-cp35m-win32.whl", hash = "sha256:81b0ea3715bf6a848d6f7149d25bf018fd24554a4be01fcbbe3fdc78e890b955"}, - {file = "greenlet-2.0.1-cp35-cp35m-win_amd64.whl", hash = "sha256:38255a3f1e8942573b067510f9611fc9e38196077b0c8eb7a8c795e105f9ce77"}, - {file = "greenlet-2.0.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:04957dc96669be041e0c260964cfef4c77287f07c40452e61abe19d647505581"}, - {file = "greenlet-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:4aeaebcd91d9fee9aa768c1b39cb12214b30bf36d2b7370505a9f2165fedd8d9"}, - {file = "greenlet-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974a39bdb8c90a85982cdb78a103a32e0b1be986d411303064b28a80611f6e51"}, - {file = "greenlet-2.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dca09dedf1bd8684767bc736cc20c97c29bc0c04c413e3276e0962cd7aeb148"}, - {file = "greenlet-2.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c0757db9bd08470ff8277791795e70d0bf035a011a528ee9a5ce9454b6cba2"}, - {file = "greenlet-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5067920de254f1a2dee8d3d9d7e4e03718e8fd2d2d9db962c8c9fa781ae82a39"}, - {file = "greenlet-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5a8e05057fab2a365c81abc696cb753da7549d20266e8511eb6c9d9f72fe3e92"}, - {file = "greenlet-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:3d75b8d013086b08e801fbbb896f7d5c9e6ccd44f13a9241d2bf7c0df9eda928"}, - {file = "greenlet-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:097e3dae69321e9100202fc62977f687454cd0ea147d0fd5a766e57450c569fd"}, - {file = "greenlet-2.0.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:cb242fc2cda5a307a7698c93173d3627a2a90d00507bccf5bc228851e8304963"}, - {file = "greenlet-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:72b00a8e7c25dcea5946692a2485b1a0c0661ed93ecfedfa9b6687bd89a24ef5"}, - {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b0ff9878333823226d270417f24f4d06f235cb3e54d1103b71ea537a6a86ce"}, - {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be9e0fb2ada7e5124f5282d6381903183ecc73ea019568d6d63d33f25b2a9000"}, - {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b493db84d124805865adc587532ebad30efa68f79ad68f11b336e0a51ec86c2"}, - {file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0459d94f73265744fee4c2d5ec44c6f34aa8a31017e6e9de770f7bcf29710be9"}, - {file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a20d33124935d27b80e6fdacbd34205732660e0a1d35d8b10b3328179a2b51a1"}, - {file = "greenlet-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:ea688d11707d30e212e0110a1aac7f7f3f542a259235d396f88be68b649e47d1"}, - {file = "greenlet-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:afe07421c969e259e9403c3bb658968702bc3b78ec0b6fde3ae1e73440529c23"}, - {file = "greenlet-2.0.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:cd4ccc364cf75d1422e66e247e52a93da6a9b73cefa8cad696f3cbbb75af179d"}, - {file = "greenlet-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c8b1c43e75c42a6cafcc71defa9e01ead39ae80bd733a2608b297412beede68"}, - {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:659f167f419a4609bc0516fb18ea69ed39dbb25594934bd2dd4d0401660e8a1e"}, - {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:356e4519d4dfa766d50ecc498544b44c0249b6de66426041d7f8b751de4d6b48"}, - {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811e1d37d60b47cb8126e0a929b58c046251f28117cb16fcd371eed61f66b764"}, - {file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d38ffd0e81ba8ef347d2be0772e899c289b59ff150ebbbbe05dc61b1246eb4e0"}, - {file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0109af1138afbfb8ae647e31a2b1ab030f58b21dd8528c27beaeb0093b7938a9"}, - {file = "greenlet-2.0.1-cp38-cp38-win32.whl", hash = "sha256:88c8d517e78acdf7df8a2134a3c4b964415b575d2840a2746ddb1cc6175f8608"}, - {file = "greenlet-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:d6ee1aa7ab36475035eb48c01efae87d37936a8173fc4d7b10bb02c2d75dd8f6"}, - {file = "greenlet-2.0.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b1992ba9d4780d9af9726bbcef6a1db12d9ab1ccc35e5773685a24b7fb2758eb"}, - {file = "greenlet-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:b5e83e4de81dcc9425598d9469a624826a0b1211380ac444c7c791d4a2137c19"}, - {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:505138d4fa69462447a562a7c2ef723c6025ba12ac04478bc1ce2fcc279a2db5"}, - {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce1e90dd302f45716a7715517c6aa0468af0bf38e814ad4eab58e88fc09f7f7"}, - {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e9744c657d896c7b580455e739899e492a4a452e2dd4d2b3e459f6b244a638d"}, - {file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:662e8f7cad915ba75d8017b3e601afc01ef20deeeabf281bd00369de196d7726"}, - {file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:41b825d65f31e394b523c84db84f9383a2f7eefc13d987f308f4663794d2687e"}, - {file = "greenlet-2.0.1-cp39-cp39-win32.whl", hash = "sha256:db38f80540083ea33bdab614a9d28bcec4b54daa5aff1668d7827a9fc769ae0a"}, - {file = "greenlet-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b23d2a46d53210b498e5b701a1913697671988f4bf8e10f935433f6e7c332fb6"}, - {file = "greenlet-2.0.1.tar.gz", hash = "sha256:42e602564460da0e8ee67cb6d7236363ee5e131aa15943b6670e44e5c2ed0f67"}, -] -h11 = [ - {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, - {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, -] -httpcore = [ - {file = "httpcore-0.14.7-py3-none-any.whl", hash = "sha256:47d772f754359e56dd9d892d9593b6f9870a37aeb8ba51e9a88b09b3d68cfade"}, - {file = "httpcore-0.14.7.tar.gz", hash = "sha256:7503ec1c0f559066e7e39bc4003fd2ce023d01cf51793e3c173b864eb456ead1"}, -] -httptools = [ - {file = "httptools-0.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4137137de8976511a392e27bfdcf231bd926ac13d375e0414e927b08217d779e"}, - {file = "httptools-0.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9f475b642c48b1b78584bdd12a5143e2c512485664331eade9c29ef769a17598"}, - {file = "httptools-0.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4687dfc116a9f1eb22a7d797f0dc6f6e17190d406ca4e729634b38aa98044b17"}, - {file = "httptools-0.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:72ee0e3fb9c6437ab3ae34e9abee67fcee6876f4f58504e3f613dd5882aafdb7"}, - {file = "httptools-0.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:3787c1f46e9722ef7f07ea5c76b0103037483d1b12e34a02c53ceca5afa4e09a"}, - {file = "httptools-0.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c0ac2e0ce6733c55858932e7d37fcc7b67ba6bb23e9648593c55f663de031b93"}, - {file = "httptools-0.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79717080dc3f8b1eeb7f820b9b81528acbc04be6041f323fdd97550da2062575"}, - {file = "httptools-0.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eda95634027200f4b2a6d499e7c2e7fa9b8ee57e045dfda26958ea0af27c070b"}, - {file = "httptools-0.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3f82eb106e1474c63dba36a176067e65b48385f4cecddf3616411aa5d1fbdfec"}, - {file = "httptools-0.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c14576b737d9e6e4f2a86af04918dbe9b62f57ce8102a8695c9a382dbe405c7f"}, - {file = "httptools-0.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:113816f9af7dcfc4aa71ebb5354d77365f666ecf96ac7ff2aa1d24b6bca44165"}, - {file = "httptools-0.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b8ac7dee63af4346e02b1e6d32202e3b5b3706a9928bec6da6d7a5b066217422"}, - {file = "httptools-0.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:04114db99605c9b56ea22a8ec4d7b1485b908128ed4f4a8f6438489c428da794"}, - {file = "httptools-0.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6e676bc3bb911b11f3d7e2144b9a53600bf6b9b21e0e4437aa308e1eef094d97"}, - {file = "httptools-0.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdc3975db86c29817e6d13df14e037c931fc893a710fb71097777a4147090068"}, - {file = "httptools-0.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ac842df4fc3952efa7820b277961ea55e068bbc54cb59a0820400de7ae358d8"}, - {file = "httptools-0.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:47dba2345aaa01b87e4981e8756af441349340708d5b60712c98c55a4d28f4af"}, - {file = "httptools-0.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:5a836bd85ae1fb4304f674808488dae403e136d274aa5bafd0e6ee456f11c371"}, - {file = "httptools-0.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1a8f26327023fa1a947d36e60a0582149e182fbbc949c8a65ec8665754dbbe69"}, - {file = "httptools-0.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a10a5903b5bc0eb647d01cd1e95bec3bb614a9bf53f0af1e01360b2debdf81"}, - {file = "httptools-0.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21e948034f70e47c8abfa2d5e6f1a5661f87a2cddc7bcc70f61579cc87897c70"}, - {file = "httptools-0.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:074afd8afdeec0fa6786cd4a1676e0c0be23dc9a017a86647efa6b695168104f"}, - {file = "httptools-0.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:2119fa619a4c53311f594f25c0205d619350fcb32140ec5057f861952e9b2b4f"}, - {file = "httptools-0.3.0.tar.gz", hash = "sha256:3f9b4856d46ba1f0c850f4e84b264a9a8b4460acb20e865ec00978ad9fbaa4cf"}, -] -httpx = [ - {file = "httpx-0.22.0-py3-none-any.whl", hash = "sha256:e35e83d1d2b9b2a609ef367cc4c1e66fd80b750348b20cc9e19d1952fc2ca3f6"}, - {file = "httpx-0.22.0.tar.gz", hash = "sha256:d8e778f76d9bbd46af49e7f062467e3157a5a3d2ae4876a4bbfd8a51ed9c9cb4"}, -] -httpx-oauth = [ - {file = "httpx-oauth-0.7.0.tar.gz", hash = "sha256:ea84b767274416cc23ce8213e81e8f85436d1526fa64c71a95942590eb0b67ca"}, - {file = "httpx_oauth-0.7.0-py3-none-any.whl", hash = "sha256:5c5380df6727a0571011694fc552980828bfd5223df04a8ff06f61be2e939e90"}, -] -identify = [ - {file = "identify-2.5.5-py2.py3-none-any.whl", hash = "sha256:ef78c0d96098a3b5fe7720be4a97e73f439af7cf088ebf47b620aeaa10fadf97"}, - {file = "identify-2.5.5.tar.gz", hash = "sha256:322a5699daecf7c6fd60e68852f36f2ecbb6a36ff6e6e973e0d2bb6fca203ee6"}, -] -idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -isort = [ - {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, - {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, -] -makefun = [ - {file = "makefun-1.15.0-py2.py3-none-any.whl", hash = "sha256:d79319f9e71b6825ca163be0afa45cbc5b1215e682efa35b2d355a03c594279c"}, - {file = "makefun-1.15.0.tar.gz", hash = "sha256:5b110e733d94f7a49d8ac27b1e2d40f2bb0501e98c1d825e0d932d26920dd5df"}, -] -Mako = [ - {file = "Mako-1.2.2-py3-none-any.whl", hash = "sha256:8efcb8004681b5f71d09c983ad5a9e6f5c40601a6ec469148753292abc0da534"}, - {file = "Mako-1.2.2.tar.gz", hash = "sha256:3724869b363ba630a272a5f89f68c070352137b8fd1757650017b7e06fda163f"}, -] -MarkupSafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -multidict = [ - {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, - {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, - {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"}, - {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"}, - {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"}, - {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"}, - {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"}, - {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"}, - {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"}, - {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"}, - {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"}, - {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"}, - {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"}, - {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"}, - {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"}, - {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"}, - {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"}, - {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"}, - {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"}, -] -mypy = [ - {file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"}, - {file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"}, - {file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"}, - {file = "mypy-0.961-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e"}, - {file = "mypy-0.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"}, - {file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"}, - {file = "mypy-0.961-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b"}, - {file = "mypy-0.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"}, - {file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"}, - {file = "mypy-0.961-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e"}, - {file = "mypy-0.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"}, - {file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"}, - {file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"}, - {file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"}, - {file = "mypy-0.961-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b"}, - {file = "mypy-0.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"}, - {file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"}, - {file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"}, - {file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"}, - {file = "mypy-0.961-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950"}, - {file = "mypy-0.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"}, - {file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"}, - {file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -nodeenv = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -passlib = [ - {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"}, - {file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"}, -] -pathspec = [ - {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, - {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, -] -pbr = [ - {file = "pbr-5.10.0-py2.py3-none-any.whl", hash = "sha256:da3e18aac0a3c003e9eea1a81bd23e5a3a75d745670dcf736317b7d966887fdf"}, - {file = "pbr-5.10.0.tar.gz", hash = "sha256:cfcc4ff8e698256fc17ea3ff796478b050852585aa5bae79ecd05b2ab7b39b9a"}, -] -pep8-naming = [ - {file = "pep8-naming-0.12.1.tar.gz", hash = "sha256:bb2455947757d162aa4cad55dba4ce029005cd1692f2899a21d51d8630ca7841"}, - {file = "pep8_naming-0.12.1-py2.py3-none-any.whl", hash = "sha256:4a8daeaeb33cfcde779309fc0c9c0a68a3bbe2ad8a8308b763c5068f86eb9f37"}, -] -platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -pre-commit = [ - {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, - {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pycodestyle = [ - {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, - {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, -] -pycparser = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] -pydantic = [ - {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"}, - {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"}, - {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"}, - {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"}, - {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"}, - {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"}, - {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"}, - {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"}, - {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"}, - {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"}, - {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"}, - {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"}, - {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"}, - {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"}, - {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"}, - {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"}, - {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"}, - {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"}, - {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"}, - {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"}, - {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"}, - {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"}, - {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"}, - {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"}, - {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"}, - {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"}, - {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"}, - {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"}, - {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"}, - {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"}, - {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"}, - {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"}, - {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"}, - {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"}, - {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"}, - {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"}, -] -pydocstyle = [ - {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, - {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, -] -pyflakes = [ - {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, - {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, -] -Pygments = [ - {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, - {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, -] -PyJWT = [ - {file = "PyJWT-2.4.0-py3-none-any.whl", hash = "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf"}, - {file = "PyJWT-2.4.0.tar.gz", hash = "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba"}, -] -pyparsing = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] -pytest = [ - {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"}, - {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"}, -] -pytest-cov = [ - {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, - {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, -] -pytest-env = [ - {file = "pytest-env-0.6.2.tar.gz", hash = "sha256:7e94956aef7f2764f3c147d216ce066bf6c42948bb9e293169b1b1c880a580c2"}, -] -python-dotenv = [ - {file = "python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"}, - {file = "python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5"}, -] -python-multipart = [ - {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"}, -] -pywin32 = [ - {file = "pywin32-305-cp310-cp310-win32.whl", hash = "sha256:421f6cd86e84bbb696d54563c48014b12a23ef95a14e0bdba526be756d89f116"}, - {file = "pywin32-305-cp310-cp310-win_amd64.whl", hash = "sha256:73e819c6bed89f44ff1d690498c0a811948f73777e5f97c494c152b850fad478"}, - {file = "pywin32-305-cp310-cp310-win_arm64.whl", hash = "sha256:742eb905ce2187133a29365b428e6c3b9001d79accdc30aa8969afba1d8470f4"}, - {file = "pywin32-305-cp311-cp311-win32.whl", hash = "sha256:19ca459cd2e66c0e2cc9a09d589f71d827f26d47fe4a9d09175f6aa0256b51c2"}, - {file = "pywin32-305-cp311-cp311-win_amd64.whl", hash = "sha256:326f42ab4cfff56e77e3e595aeaf6c216712bbdd91e464d167c6434b28d65990"}, - {file = "pywin32-305-cp311-cp311-win_arm64.whl", hash = "sha256:4ecd404b2c6eceaca52f8b2e3e91b2187850a1ad3f8b746d0796a98b4cea04db"}, - {file = "pywin32-305-cp36-cp36m-win32.whl", hash = "sha256:48d8b1659284f3c17b68587af047d110d8c44837736b8932c034091683e05863"}, - {file = "pywin32-305-cp36-cp36m-win_amd64.whl", hash = "sha256:13362cc5aa93c2beaf489c9c9017c793722aeb56d3e5166dadd5ef82da021fe1"}, - {file = "pywin32-305-cp37-cp37m-win32.whl", hash = "sha256:a55db448124d1c1484df22fa8bbcbc45c64da5e6eae74ab095b9ea62e6d00496"}, - {file = "pywin32-305-cp37-cp37m-win_amd64.whl", hash = "sha256:109f98980bfb27e78f4df8a51a8198e10b0f347257d1e265bb1a32993d0c973d"}, - {file = "pywin32-305-cp38-cp38-win32.whl", hash = "sha256:9dd98384da775afa009bc04863426cb30596fd78c6f8e4e2e5bbf4edf8029504"}, - {file = "pywin32-305-cp38-cp38-win_amd64.whl", hash = "sha256:56d7a9c6e1a6835f521788f53b5af7912090674bb84ef5611663ee1595860fc7"}, - {file = "pywin32-305-cp39-cp39-win32.whl", hash = "sha256:9d968c677ac4d5cbdaa62fd3014ab241718e619d8e36ef8e11fb930515a1e918"}, - {file = "pywin32-305-cp39-cp39-win_amd64.whl", hash = "sha256:50768c6b7c3f0b38b7fb14dd4104da93ebced5f1a50dc0e834594bff6fbe1271"}, -] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -requests = [ - {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, - {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, -] -restructuredtext-lint = [ - {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, -] -rfc3986 = [ - {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, - {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, -] -setuptools = [ - {file = "setuptools-65.3.0-py3-none-any.whl", hash = "sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82"}, - {file = "setuptools-65.3.0.tar.gz", hash = "sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -smmap = [ - {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, - {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, -] -sniffio = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, -] -snowballstemmer = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] -SQLAlchemy = [ - {file = "SQLAlchemy-1.4.41-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:13e397a9371ecd25573a7b90bd037db604331cf403f5318038c46ee44908c44d"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2d6495f84c4fd11584f34e62f9feec81bf373787b3942270487074e35cbe5330"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27m-win32.whl", hash = "sha256:e570cfc40a29d6ad46c9aeaddbdcee687880940a3a327f2c668dd0e4ef0a441d"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27m-win_amd64.whl", hash = "sha256:5facb7fd6fa8a7353bbe88b95695e555338fb038ad19ceb29c82d94f62775a05"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f37fa70d95658763254941ddd30ecb23fc4ec0c5a788a7c21034fc2305dab7cc"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:361f6b5e3f659e3c56ea3518cf85fbdae1b9e788ade0219a67eeaaea8a4e4d2a"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0990932f7cca97fece8017414f57fdd80db506a045869d7ddf2dda1d7cf69ecc"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd767cf5d7252b1c88fcfb58426a32d7bd14a7e4942497e15b68ff5d822b41ad"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5102fb9ee2c258a2218281adcb3e1918b793c51d6c2b4666ce38c35101bb940e"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-win32.whl", hash = "sha256:2082a2d2fca363a3ce21cfa3d068c5a1ce4bf720cf6497fb3a9fc643a8ee4ddd"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-win_amd64.whl", hash = "sha256:e4b12e3d88a8fffd0b4ca559f6d4957ed91bd4c0613a4e13846ab8729dc5c251"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:90484a2b00baedad361402c257895b13faa3f01780f18f4a104a2f5c413e4536"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b67fc780cfe2b306180e56daaa411dd3186bf979d50a6a7c2a5b5036575cbdbb"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad2b727fc41c7f8757098903f85fafb4bf587ca6605f82d9bf5604bd9c7cded"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-win32.whl", hash = "sha256:59bdc291165b6119fc6cdbc287c36f7f2859e6051dd923bdf47b4c55fd2f8bd0"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-win_amd64.whl", hash = "sha256:d2e054aed4645f9b755db85bc69fc4ed2c9020c19c8027976f66576b906a74f1"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:4ba7e122510bbc07258dc42be6ed45997efdf38129bde3e3f12649be70683546"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0dcf127bb99458a9d211e6e1f0f3edb96c874dd12f2503d4d8e4f1fd103790b"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e16c2be5cb19e2c08da7bd3a87fed2a0d4e90065ee553a940c4fc1a0fb1ab72b"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ebeeec5c14533221eb30bad716bc1fd32f509196318fb9caa7002c4a364e4c"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-win32.whl", hash = "sha256:3e2ef592ac3693c65210f8b53d0edcf9f4405925adcfc031ff495e8d18169682"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-win_amd64.whl", hash = "sha256:eb30cf008850c0a26b72bd1b9be6730830165ce049d239cfdccd906f2685f892"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:c23d64a0b28fc78c96289ffbd0d9d1abd48d267269b27f2d34e430ea73ce4b26"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8eb8897367a21b578b26f5713833836f886817ee2ffba1177d446fa3f77e67c8"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:14576238a5f89bcf504c5f0a388d0ca78df61fb42cb2af0efe239dc965d4f5c9"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:639e1ae8d48b3c86ffe59c0daa9a02e2bfe17ca3d2b41611b30a0073937d4497"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-win32.whl", hash = "sha256:0005bd73026cd239fc1e8ccdf54db58b6193be9a02b3f0c5983808f84862c767"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-win_amd64.whl", hash = "sha256:5323252be2bd261e0aa3f33cb3a64c45d76829989fa3ce90652838397d84197d"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:05f0de3a1dc3810a776275763764bb0015a02ae0f698a794646ebc5fb06fad33"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0002e829142b2af00b4eaa26c51728f3ea68235f232a2e72a9508a3116bd6ed0"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22ff16cedab5b16a0db79f1bc99e46a6ddececb60c396562e50aab58ddb2871c"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccfd238f766a5bb5ee5545a62dd03f316ac67966a6a658efb63eeff8158a4bbf"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-win32.whl", hash = "sha256:58bb65b3274b0c8a02cea9f91d6f44d0da79abc993b33bdedbfec98c8440175a"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-win_amd64.whl", hash = "sha256:ce8feaa52c1640de9541eeaaa8b5fb632d9d66249c947bb0d89dd01f87c7c288"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:199a73c31ac8ea59937cc0bf3dfc04392e81afe2ec8a74f26f489d268867846c"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676d51c9f6f6226ae8f26dc83ec291c088fe7633269757d333978df78d931ab"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:036d8472356e1d5f096c5e0e1a7e0f9182140ada3602f8fff6b7329e9e7cfbcd"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2307495d9e0ea00d0c726be97a5b96615035854972cc538f6e7eaed23a35886c"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-win32.whl", hash = "sha256:9c56e19780cd1344fcd362fd6265a15f48aa8d365996a37fab1495cae8fcd97d"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-win_amd64.whl", hash = "sha256:f5fa526d027d804b1f85cdda1eb091f70bde6fb7d87892f6dd5a48925bc88898"}, - {file = "SQLAlchemy-1.4.41.tar.gz", hash = "sha256:0292f70d1797e3c54e862e6f30ae474014648bc9c723e14a2fda730adb0a9791"}, -] -sqlalchemy2-stubs = [ - {file = "sqlalchemy2-stubs-0.0.2a27.tar.gz", hash = "sha256:f79bce50b7837a2c2374ef4480b41e2b8a8226f313f347dc2a70526a4191db93"}, - {file = "sqlalchemy2_stubs-0.0.2a27-py3-none-any.whl", hash = "sha256:6cea12fec3c261f6e0e14a95d2cc4914e373095e68ec4fc2eb473183ac2b17a2"}, -] -starlette = [ - {file = "starlette-0.20.4-py3-none-any.whl", hash = "sha256:c0414d5a56297d37f3db96a84034d61ce29889b9eaccf65eb98a0b39441fcaa3"}, - {file = "starlette-0.20.4.tar.gz", hash = "sha256:42fcf3122f998fefce3e2c5ad7e5edbf0f02cf685d646a83a08d404726af5084"}, -] -stevedore = [ - {file = "stevedore-4.0.0-py3-none-any.whl", hash = "sha256:87e4d27fe96d0d7e4fc24f0cbe3463baae4ec51e81d95fbe60d2474636e0c7d8"}, - {file = "stevedore-4.0.0.tar.gz", hash = "sha256:f82cc99a1ff552310d19c379827c2c64dd9f85a38bcd5559db2470161867b786"}, -] -testcontainers = [ +files = [ {file = "testcontainers-3.7.0-py2.py3-none-any.whl", hash = "sha256:e40fece4085ae06f8facb067e83020e273738e31f958b01d2b5eaa84e2dab478"}, ] -tokenize-rt = [ + +[package.dependencies] +deprecation = "*" +docker = ">=4.0.0" +wrapt = "*" + +[package.extras] +arangodb = ["python-arango"] +azurite = ["azure-storage-blob"] +clickhouse = ["clickhouse-driver"] +docker-compose = ["docker-compose"] +google-cloud-pubsub = ["google-cloud-pubsub (<2)"] +kafka = ["kafka-python"] +keycloak = ["python-keycloak"] +mongo = ["pymongo"] +mssqlserver = ["pymssql"] +mysql = ["pymysql", "sqlalchemy"] +neo4j = ["neo4j"] +oracle = ["cx-Oracle", "sqlalchemy"] +postgresql = ["psycopg2-binary", "sqlalchemy"] +rabbitmq = ["pika"] +redis = ["redis"] +selenium = ["selenium"] + +[[package]] +name = "tokenize-rt" +version = "4.2.1" +description = "A wrapper around the stdlib `tokenize` which roundtrips." +category = "dev" +optional = false +python-versions = ">=3.6.1" +files = [ {file = "tokenize_rt-4.2.1-py2.py3-none-any.whl", hash = "sha256:08a27fa032a81cf45e8858d0ac706004fcd523e8463415ddf1442be38e204ea8"}, {file = "tokenize_rt-4.2.1.tar.gz", hash = "sha256:0d4f69026fed520f8a1e0103aa36c406ef4661417f20ca643f913e33531b3b94"}, ] -toml = [ + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -tomli = [ + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -types-aiofiles = [ + +[[package]] +name = "types-aiofiles" +version = "0.8.11" +description = "Typing stubs for aiofiles" +category = "dev" +optional = false +python-versions = "*" +files = [ {file = "types-aiofiles-0.8.11.tar.gz", hash = "sha256:1f93aa68e47de1379f45eef9acd34faa0f9341628921cd6aede666e6e559a5a8"}, {file = "types_aiofiles-0.8.11-py3-none-any.whl", hash = "sha256:be6715fffd1c7f84c9316000ba8bbc66a884246dbd2902c163ebc2d67315206b"}, ] -types-pyyaml = [ + +[[package]] +name = "types-pyyaml" +version = "6.0.12.2" +description = "Typing stubs for PyYAML" +category = "dev" +optional = false +python-versions = "*" +files = [ {file = "types-PyYAML-6.0.12.2.tar.gz", hash = "sha256:6840819871c92deebe6a2067fb800c11b8a063632eb4e3e755914e7ab3604e83"}, {file = "types_PyYAML-6.0.12.2-py3-none-any.whl", hash = "sha256:1e94e80aafee07a7e798addb2a320e32956a373f376655128ae20637adb2655b"}, ] -typing-extensions = [ + +[[package]] +name = "typing-extensions" +version = "4.3.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, ] -ujson = [ + +[[package]] +name = "ujson" +version = "5.4.0" +description = "Ultra fast JSON encoder and decoder for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "ujson-5.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:511aa641a5b91d19280183b134fb6c473039d4dd82e987ac810cffba783521ac"}, {file = "ujson-5.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b045ca5497a950cc3492840adb3bcb3b9e305ed6599ed14c6aeaa08011aa463f"}, {file = "ujson-5.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa00b746138835271653b0c3da171d2a8b510c579381f71e8b8e03484d50d825"}, @@ -2498,15 +2737,71 @@ ujson = [ {file = "ujson-5.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:025758cf6561af6986d77cd4af9367ab56dde5c7c50f13f59e6964b4b25df73e"}, {file = "ujson-5.4.0.tar.gz", hash = "sha256:6b953e09441e307504130755e5bd6b15850178d591f66292bba4608c4f7f9b00"}, ] -urllib3 = [ + +[[package]] +name = "unidecode" +version = "1.3.6" +description = "ASCII transliterations of Unicode text" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "Unidecode-1.3.6-py3-none-any.whl", hash = "sha256:547d7c479e4f377b430dd91ac1275d593308dce0fc464fb2ab7d41f82ec653be"}, + {file = "Unidecode-1.3.6.tar.gz", hash = "sha256:fed09cf0be8cf415b391642c2a5addfc72194407caee4f98719e40ec2a72b830"}, +] + +[[package]] +name = "urllib3" +version = "1.26.12" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" +files = [ {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, ] -uvicorn = [ + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "uvicorn" +version = "0.17.5" +description = "The lightning-fast ASGI server." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "uvicorn-0.17.5-py3-none-any.whl", hash = "sha256:8adddf629b79857b48b999ae1b14d6c92c95d4d7840bd86461f09bee75f1653e"}, {file = "uvicorn-0.17.5.tar.gz", hash = "sha256:c04a9c069111489c324f427501b3840d306c6b91a77b00affc136a840a3f45f1"}, ] -uvloop = [ + +[package.dependencies] +asgiref = ">=3.4.0" +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.2.0,<0.4.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +PyYAML = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchgod = {version = ">=0.6", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["PyYAML (>=5.1)", "colorama (>=0.4)", "httptools (>=0.2.0,<0.4.0)", "python-dotenv (>=0.13)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchgod (>=0.6)", "websockets (>=10.0)"] + +[[package]] +name = "uvloop" +version = "0.16.0" +description = "Fast implementation of asyncio event loop on top of libuv" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"}, {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c"}, {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64"}, @@ -2524,19 +2819,73 @@ uvloop = [ {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861"}, {file = "uvloop-0.16.0.tar.gz", hash = "sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228"}, ] -virtualenv = [ + +[package.extras] +dev = ["Cython (>=0.29.24,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"] + +[[package]] +name = "virtualenv" +version = "20.16.5" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ {file = "virtualenv-20.16.5-py3-none-any.whl", hash = "sha256:d07dfc5df5e4e0dbc92862350ad87a36ed505b978f6c39609dc489eadd5b0d27"}, {file = "virtualenv-20.16.5.tar.gz", hash = "sha256:227ea1b9994fdc5ea31977ba3383ef296d7472ea85be9d6732e42a91c04e80da"}, ] -watchgod = [ + +[package.dependencies] +distlib = ">=0.3.5,<1" +filelock = ">=3.4.1,<4" +platformdirs = ">=2.4,<3" + +[package.extras] +docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "watchgod" +version = "0.8.2" +description = "Simple, modern file watching and code reload in python." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "watchgod-0.8.2-py3-none-any.whl", hash = "sha256:2f3e8137d98f493ff58af54ea00f4d1433a6afe2ed08ab331a657df468c6bfce"}, {file = "watchgod-0.8.2.tar.gz", hash = "sha256:cb11ff66657befba94d828e3b622d5fb76f22fbda1376f355f3e6e51e97d9450"}, ] -websocket-client = [ + +[package.dependencies] +anyio = ">=3.0.0,<4" + +[[package]] +name = "websocket-client" +version = "1.4.2" +description = "WebSocket client for Python with low level API options" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "websocket-client-1.4.2.tar.gz", hash = "sha256:d6e8f90ca8e2dd4e8027c4561adeb9456b54044312dba655e7cae652ceb9ae59"}, {file = "websocket_client-1.4.2-py3-none-any.whl", hash = "sha256:d6b06432f184438d99ac1f456eaf22fe1ade524c3dd16e661142dc54e9cba574"}, ] -websockets = [ + +[package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "websockets" +version = "10.3" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "websockets-10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978"}, {file = "websockets-10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500"}, {file = "websockets-10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b"}, @@ -2586,11 +2935,48 @@ websockets = [ {file = "websockets-10.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55"}, {file = "websockets-10.3.tar.gz", hash = "sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4"}, ] -wemake-python-styleguide = [ + +[[package]] +name = "wemake-python-styleguide" +version = "0.16.1" +description = "The strictest and most opinionated python linter ever" +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" +files = [ {file = "wemake-python-styleguide-0.16.1.tar.gz", hash = "sha256:4fcd78dd55732679b5fc8bc37fd7e04bbaa5cdc1b1a829ad265e8f6b0d853cf6"}, {file = "wemake_python_styleguide-0.16.1-py3-none-any.whl", hash = "sha256:202c22ecfee1f5caf0555048602cd52f2435cd57903e6b0cd46b5aaa3f652140"}, ] -wrapt = [ + +[package.dependencies] +astor = ">=0.8,<0.9" +attrs = "*" +darglint = ">=1.2,<2.0" +flake8 = ">=3.7,<5" +flake8-bandit = ">=2.1,<4" +flake8-broken-line = ">=0.3,<0.5" +flake8-bugbear = ">=20.1,<23.0" +flake8-commas = ">=2.0,<3.0" +flake8-comprehensions = ">=3.1,<4.0" +flake8-debugger = ">=4.0,<5.0" +flake8-docstrings = ">=1.3,<2.0" +flake8-eradicate = ">=1.0,<2.0" +flake8-isort = ">=4.0,<5.0" +flake8-quotes = ">=3.0,<4.0" +flake8-rst-docstrings = ">=0.2,<0.3" +flake8-string-format = ">=0.3,<0.4" +pep8-naming = ">=0.11,<0.13" +pygments = ">=2.4,<3.0" +typing_extensions = ">=3.6,<5.0" + +[[package]] +name = "wrapt" +version = "1.14.1" +description = "Module for decorators, wrappers and monkey patching." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, @@ -2656,7 +3042,15 @@ wrapt = [ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] -yarl = [ + +[[package]] +name = "yarl" +version = "1.8.1" +description = "Yet another URL library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28"}, {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3"}, {file = "yarl-1.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9de955d98e02fab288c7718662afb33aab64212ecb368c5dc866d9a57bf48880"}, @@ -2717,7 +3111,44 @@ yarl = [ {file = "yarl-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:de49d77e968de6626ba7ef4472323f9d2e5a56c1d85b7c0e2a190b2173d3b9be"}, {file = "yarl-1.8.1.tar.gz", hash = "sha256:af887845b8c2e060eb5605ff72b6f2dd2aab7a761379373fd89d314f4752abbf"}, ] -yesqa = [ + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[[package]] +name = "yesqa" +version = "1.4.0" +description = "Automatically remove unnecessary `# noqa` comments." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "yesqa-1.4.0-py2.py3-none-any.whl", hash = "sha256:8d66f162731173b20b29f0107804b51a7c8573f5c8d82f8ee2c8cf1724a2c84b"}, {file = "yesqa-1.4.0.tar.gz", hash = "sha256:7d9d784b7fb91987f9eded7d03b1a5af578e51dee59b97f0475898a2fe6a9527"}, ] + +[package.dependencies] +flake8 = ">=3.9" +tokenize-rt = ">=2.1" + +[[package]] +name = "zipp" +version = "3.11.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, + {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "7a9538d73639f13b3fc625050cf9c99b19d642513b2500b5bf3af3e1e3642b7f" diff --git a/pyproject.toml b/pyproject.toml index ec2cd37..bda18ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,16 @@ types-aiofiles = "^0.8.11" testcontainers = "^3.7.0" types-pyyaml = "^6.0.12.2" +[tool.poetry.group.docs] +optional = true + +[tool.poetry.group.docs.dependencies] +sphinx = "^6.1.3" +sphinx-autoapi = "^2.0.0" +furo = "^2022.12.7" +sphinx-copybutton = "^0.5.1" +# TODO add myst-parser for CommonMark support, but release with Sphinx 6 support not available yet + [tool.poetry.scripts] bartender = 'bartender.__main__:main' From a36c4a5839ab2737d263a9999ec6a64b2120bfff Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 13 Jan 2023 13:50:40 +0100 Subject: [PATCH 22/36] Use picker module --- config-example.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config-example.yaml b/config-example.yaml index 7491461..fef043f 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -1,9 +1,9 @@ # By default the files of jobs are stored in /tmp/jobs # job_root_dir: /tmp/jobs # By default jobs are submitted to the first destination -# destination_picker: bartender.config:pick_first +# destination_picker: bartender.picker:pick_first # To use a custom picker set `destination_picker` to a `:` -# The picker should have type bartender.config.DestinationPicker . +# The picker should have type bartender.picker.DestinationPicker . applications: # The label of the application wc: From 030734e303882ace2defa1808659f9e4607c55e2 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 13 Jan 2023 14:00:12 +0100 Subject: [PATCH 23/36] Use localized path on remote scheduler + download when scheduler says job is ok --- src/bartender/web/api/applications/submit.py | 22 ++++++++++++-------- src/bartender/web/api/job/sync.py | 9 ++++---- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/bartender/web/api/applications/submit.py b/src/bartender/web/api/applications/submit.py index 6afec11..39a0e65 100644 --- a/src/bartender/web/api/applications/submit.py +++ b/src/bartender/web/api/applications/submit.py @@ -21,8 +21,7 @@ async def submit( :param job_dao: JobDAO object. :param context: Context with applications and destinations. """ - application_config = context.applications[application] - description = application_config.description(job_dir) + description = context.applications[application].description(job_dir) destination_name = context.destination_picker( job_dir, @@ -31,9 +30,18 @@ async def submit( ) destination = context.destinations[destination_name] - await _upload_input_files(description, destination.filesystem, context.job_root_dir) + localized_description = destination.filesystem.localize_description( + description, + context.job_root_dir, + ) - internal_job_id = await destination.scheduler.submit(description) + await _upload_input_files( + description, + destination.filesystem, + localized_description, + ) + + internal_job_id = await destination.scheduler.submit(localized_description) await job_dao.update_internal_job_id( external_job_id, @@ -45,12 +53,8 @@ async def submit( async def _upload_input_files( description: JobDescription, filesystem: AbstractFileSystem, - job_root_dir: Path, + localized_description: JobDescription, ) -> None: - localized_description = filesystem.localize_description( - description, - job_root_dir, - ) await filesystem.upload( src=description, target=localized_description, diff --git a/src/bartender/web/api/job/sync.py b/src/bartender/web/api/job/sync.py index 76843b5..4fa8742 100644 --- a/src/bartender/web/api/job/sync.py +++ b/src/bartender/web/api/job/sync.py @@ -28,9 +28,9 @@ async def sync_state( # TODO throttle getting state from scheduler as getting state could be expensive # could add column to Job to track when state was last fetched state = await destination.scheduler.state(job.internal_id) - # TODO when scheduler says job is completed then download output files + # when scheduler says job is completed then download output files if job.state != state and job.id is not None: - await _download_job_files(job, destination.filesystem, job_root_dir) + await _download_job_files(job, state, destination.filesystem, job_root_dir) await job_dao.update_job_state(job.id, state) job.state = state @@ -86,7 +86,7 @@ async def _store_updated_state( state = states[job.id] if job.state != state and job.destination is not None: filesystem = destinations[job.destination].filesystem - await _download_job_files(job, filesystem, job_root_dir) + await _download_job_files(job, state, filesystem, job_root_dir) await job_dao.update_job_state(job.id, state) job.state = state @@ -116,10 +116,11 @@ async def _states_of_destination( async def _download_job_files( job: Job, + state: State, filesystem: AbstractFileSystem, job_root_dir: Path, ) -> None: - if job.state in CompletedStates: + if state in CompletedStates: job_dir: Path = job_root_dir / str(job.id) # Command does not matter for downloading so use dummy command. description = JobDescription(job_dir=job_dir, command="echo") From 6461be3811e15116731715593886a25cb40b1540 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Mon, 16 Jan 2023 12:50:11 +0100 Subject: [PATCH 24/36] Tell poetry about src layout --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index bda18ba..c0f3d16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ maintainers = [ ] readme = "README.md" +packages = [{include = "bartender", from = "src"}] [tool.poetry.dependencies] python = "^3.9" From 8cbad0d45da06236099aa29eb427e354c3445aff Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Mon, 16 Jan 2023 12:57:50 +0100 Subject: [PATCH 25/36] Added TODOs --- src/bartender/schedulers/memory.py | 8 ++++++++ src/bartender/web/api/applications/views.py | 4 ++++ src/bartender/web/api/job/sync.py | 3 +++ src/bartender/web/api/job/views.py | 10 ++++++++++ 4 files changed, 25 insertions(+) diff --git a/src/bartender/schedulers/memory.py b/src/bartender/schedulers/memory.py index 4646929..28cd7a1 100644 --- a/src/bartender/schedulers/memory.py +++ b/src/bartender/schedulers/memory.py @@ -92,6 +92,14 @@ class MemoryScheduler(AbstractScheduler): """ + # TODO when running uvicorn with workers>1 then + # there are multiple instances of this class + # this can cause + # * having multiple jobs running even when slot=1 + # * asking job state from instance that is not running that job + # Should add warning to only use with workers=1 or reload=True + # And add alternative redis scheduler using Celery, RQ or arq + def __init__(self, config: MemorySchedulerConfig): """In memory scheduler. diff --git a/src/bartender/web/api/applications/views.py b/src/bartender/web/api/applications/views.py index 512e215..e60c409 100644 --- a/src/bartender/web/api/applications/views.py +++ b/src/bartender/web/api/applications/views.py @@ -91,6 +91,10 @@ async def upload_job( # noqa: WPS211 await current_api_token(submitter), context.job_root_dir, ) + # TODO uploaded file can be big, and thus take long time to unpack, + # not nice to do it in request/response handling, + # as request could timeout on consumer side. + # Move to background task or have dedicated routes for preparing input files. await stage_job_input(job_dir, upload) has_config_file(context.applications[application], job_dir) diff --git a/src/bartender/web/api/job/sync.py b/src/bartender/web/api/job/sync.py index 4fa8742..e41b7c7 100644 --- a/src/bartender/web/api/job/sync.py +++ b/src/bartender/web/api/job/sync.py @@ -85,6 +85,8 @@ async def _store_updated_state( if job.id is not None: state = states[job.id] if job.state != state and job.destination is not None: + # TODO prevent downloading multiple times, when state is polled frequently + # aka add staging_out state filesystem = destinations[job.destination].filesystem await _download_job_files(job, state, filesystem, job_root_dir) await job_dao.update_job_state(job.id, state) @@ -129,3 +131,4 @@ async def _download_job_files( job_root_dir, ) await filesystem.download(localized_description, description) + # TODO for non-local file system should also remove remote files? diff --git a/src/bartender/web/api/job/views.py b/src/bartender/web/api/job/views.py index 7088492..6d3ba4d 100644 --- a/src/bartender/web/api/job/views.py +++ b/src/bartender/web/api/job/views.py @@ -70,6 +70,8 @@ async def retrieve_job( job = await job_dao.get_job(jobid=jobid, user=user) if job.destination is not None: destination = context.destinations[job.destination] + # TODO perform syncing of states in background task + # sync can include downloading of big job dir from remote to local, await sync_state(job, job_dao, destination, context.job_root_dir) return job except NoResultFound as exc: @@ -108,3 +110,11 @@ async def retrieve_job_stdout( ) stdout: Path = context.job_root_dir / str(jobid) / "stdout.txt" return FileResponse(stdout) + + +# TODO add job deletion route. +# Should job be removed from db? +# Decide what to do in following cases: +# * when job queued +# * when job is running +# * when job is completed From 8bc4f83fbc4cfb82669ae4a015ccaa3fb9b94110 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Mon, 16 Jan 2023 13:14:45 +0100 Subject: [PATCH 26/36] Update dependencies * uvicorn --reload was causing high load, so upgraded it. * httptools was not used anywhere so dropped. Also add more todos --- poetry.lock | 1620 +++++++++++---------- pyproject.toml | 3 +- src/bartender/schedulers/memory.py | 1 + src/bartender/web/api/monitoring/views.py | 3 + 4 files changed, 884 insertions(+), 743 deletions(-) diff --git a/poetry.lock b/poetry.lock index 626c281..cc9a48f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -26,14 +26,14 @@ files = [ [[package]] name = "alembic" -version = "1.8.1" +version = "1.9.2" description = "A database migration tool for SQLAlchemy." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "alembic-1.8.1-py3-none-any.whl", hash = "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4"}, - {file = "alembic-1.8.1.tar.gz", hash = "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa"}, + {file = "alembic-1.9.2-py3-none-any.whl", hash = "sha256:e8a6ff9f3b1887e1fed68bfb8fb9a000d8f61c21bdcc85b67bb9f87fcbc4fce3"}, + {file = "alembic-1.9.2.tar.gz", hash = "sha256:6880dec4f28dd7bd999d2ed13fbe7c9d4337700a44d11a524c0ce0c59aaf0dbd"}, ] [package.dependencies] @@ -45,14 +45,14 @@ tz = ["python-dateutil"] [[package]] name = "anyio" -version = "3.6.1" +version = "3.6.2" description = "High level compatibility layer for multiple asynchronous event loop implementations" category = "main" optional = false python-versions = ">=3.6.2" files = [ - {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"}, - {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"}, + {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, + {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, ] [package.dependencies] @@ -62,22 +62,7 @@ sniffio = ">=1.1" [package.extras] doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] -trio = ["trio (>=0.16)"] - -[[package]] -name = "asgiref" -version = "3.5.2" -description = "ASGI specs, helper code, and adapters" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "asgiref-3.5.2-py3-none-any.whl", hash = "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4"}, - {file = "asgiref-3.5.2.tar.gz", hash = "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424"}, -] - -[package.extras] -tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] +trio = ["trio (>=0.16,<0.22)"] [[package]] name = "astor" @@ -154,14 +139,14 @@ test = ["flake8 (>=3.9.2,<3.10.0)", "pycodestyle (>=2.7.0,<2.8.0)", "uvloop (>=0 [[package]] name = "asyncssh" -version = "2.12.0" +version = "2.13.0" description = "AsyncSSH: Asynchronous SSHv2 client and server library" category = "main" optional = false python-versions = ">= 3.6" files = [ - {file = "asyncssh-2.12.0-py3-none-any.whl", hash = "sha256:6841c4242c606fd51188c974ec2f4887efeec67ecdfa5b84140711dacd985ab3"}, - {file = "asyncssh-2.12.0.tar.gz", hash = "sha256:274101322c4b941823aeed8e1ab6e7be5191686c6db2d2bd35afeba30505e780"}, + {file = "asyncssh-2.13.0-py3-none-any.whl", hash = "sha256:d81955bd6c54862926baed3bc2ed8b525cc533855c552c7cc2655423a9ba742d"}, + {file = "asyncssh-2.13.0.tar.gz", hash = "sha256:be7e1cb47225dc9899e56472fdc4daac03584a6843675329c0ce67179cb20e29"}, ] [package.dependencies] @@ -179,37 +164,38 @@ pywin32 = ["pywin32 (>=227)"] [[package]] name = "attrs" -version = "22.1.0" +version = "22.2.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, - {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, ] [package.extras] -dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] -docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] -tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] -tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] [[package]] name = "autoflake" -version = "1.5.3" +version = "1.7.8" description = "Removes unused imports and unused variables" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "autoflake-1.5.3-py2.py3-none-any.whl", hash = "sha256:90eb8d3f625bd72068eb670338ea7efcddbc5c6e822d3601e3dc9404c06ea8da"}, - {file = "autoflake-1.5.3.tar.gz", hash = "sha256:44f7d7eb2c1c49505b513c0e93a5dfd3f7b4218283f50c5ca0af4df6b975d470"}, + {file = "autoflake-1.7.8-py3-none-any.whl", hash = "sha256:46373ef69b6714f5064c923bb28bd797c4f8a9497f557d87fc36665c6d956b39"}, + {file = "autoflake-1.7.8.tar.gz", hash = "sha256:e7e46372dee46fa1c97acf310d99d922b63d369718a270809d7c278d34a194cf"}, ] [package.dependencies] -pyflakes = ">=1.1.0" -toml = ">=0.10.2" +pyflakes = ">=1.1.0,<3" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [[package]] name = "babel" @@ -251,24 +237,33 @@ yaml = ["PyYAML"] [[package]] name = "bcrypt" -version = "4.0.0" +version = "4.0.1" description = "Modern password hashing for your software and your servers" category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "bcrypt-4.0.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:845b1daf4df2dd94d2fdbc9454953ca9dd0e12970a0bfc9f3dcc6faea3fa96e4"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8780e69f9deec9d60f947b169507d2c9816e4f11548f1f7ebee2af38b9b22ae4"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c3334446fac200499e8bc04a530ce3cf0b3d7151e0e4ac5c0dddd3d95e97843"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb67f6a6c72dfb0a02f3df51550aa1862708e55128b22543e2b42c74f3620d7"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:7c7dd6c1f05bf89e65261d97ac3a6520f34c2acb369afb57e3ea4449be6ff8fd"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:594780b364fb45f2634c46ec8d3e61c1c0f1811c4f2da60e8eb15594ecbf93ed"}, - {file = "bcrypt-4.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d0dd19aad87e4ab882ef1d12df505f4c52b28b69666ce83c528f42c07379227"}, - {file = "bcrypt-4.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bf413f2a9b0a2950fc750998899013f2e718d20fa4a58b85ca50b6df5ed1bbf9"}, - {file = "bcrypt-4.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ede0f506554571c8eda80db22b83c139303ec6b595b8f60c4c8157bdd0bdee36"}, - {file = "bcrypt-4.0.0-cp36-abi3-win32.whl", hash = "sha256:dc6ec3dc19b1c193b2f7cf279d3e32e7caf447532fbcb7af0906fe4398900c33"}, - {file = "bcrypt-4.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:0b0f0c7141622a31e9734b7f649451147c04ebb5122327ac0bd23744df84be90"}, - {file = "bcrypt-4.0.0.tar.gz", hash = "sha256:c59c170fc9225faad04dde1ba61d85b413946e8ce2e5f5f5ff30dfd67283f319"}, + {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"}, + {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"}, + {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"}, + {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"}, ] [package.extras] @@ -296,35 +291,24 @@ lxml = ["lxml"] [[package]] name = "black" -version = "22.8.0" +version = "22.12.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7" files = [ - {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, - {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, - {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, - {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, - {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, - {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, - {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, - {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, - {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, - {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, - {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, - {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, - {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, - {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, - {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, - {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, - {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, - {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, - {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, ] [package.dependencies] @@ -343,14 +327,14 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2022.6.15" +version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, - {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, ] [[package]] @@ -444,19 +428,102 @@ files = [ [[package]] name = "charset-normalizer" -version = "2.1.1" +version = "3.0.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false -python-versions = ">=3.6.0" +python-versions = "*" files = [ - {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, - {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, + {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-win32.whl", hash = "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-win32.whl", hash = "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-win32.whl", hash = "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-win32.whl", hash = "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-win32.whl", hash = "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"}, + {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"}, ] -[package.extras] -unicode-backport = ["unicodedata2"] - [[package]] name = "click" version = "8.1.3" @@ -474,74 +541,75 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "colorama" -version = "0.4.5" +version = "0.4.6" description = "Cross-platform colored terminal text." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" -version = "6.4.4" +version = "7.0.5" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"}, - {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"}, - {file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"}, - {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"}, - {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"}, - {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"}, - {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"}, - {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"}, - {file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"}, - {file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"}, - {file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"}, - {file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"}, - {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"}, - {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"}, - {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"}, - {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"}, - {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"}, - {file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"}, - {file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"}, - {file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"}, - {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"}, - {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"}, - {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"}, - {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"}, - {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"}, - {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"}, - {file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"}, - {file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"}, - {file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"}, - {file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"}, - {file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"}, - {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"}, - {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"}, - {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"}, - {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"}, - {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"}, - {file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"}, - {file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"}, - {file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"}, - {file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"}, - {file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"}, - {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"}, - {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"}, - {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"}, - {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"}, - {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"}, - {file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"}, - {file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"}, - {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"}, - {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"}, + {file = "coverage-7.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a7f23bbaeb2a87f90f607730b45564076d870f1fb07b9318d0c21f36871932b"}, + {file = "coverage-7.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c18d47f314b950dbf24a41787ced1474e01ca816011925976d90a88b27c22b89"}, + {file = "coverage-7.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef14d75d86f104f03dea66c13188487151760ef25dd6b2dbd541885185f05f40"}, + {file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66e50680e888840c0995f2ad766e726ce71ca682e3c5f4eee82272c7671d38a2"}, + {file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9fed35ca8c6e946e877893bbac022e8563b94404a605af1d1e6accc7eb73289"}, + {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d8d04e755934195bdc1db45ba9e040b8d20d046d04d6d77e71b3b34a8cc002d0"}, + {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e109f1c9a3ece676597831874126555997c48f62bddbcace6ed17be3e372de8"}, + {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a1890fca2962c4f1ad16551d660b46ea77291fba2cc21c024cd527b9d9c8809"}, + {file = "coverage-7.0.5-cp310-cp310-win32.whl", hash = "sha256:be9fcf32c010da0ba40bf4ee01889d6c737658f4ddff160bd7eb9cac8f094b21"}, + {file = "coverage-7.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:cbfcba14a3225b055a28b3199c3d81cd0ab37d2353ffd7f6fd64844cebab31ad"}, + {file = "coverage-7.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30b5fec1d34cc932c1bc04017b538ce16bf84e239378b8f75220478645d11fca"}, + {file = "coverage-7.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1caed2367b32cc80a2b7f58a9f46658218a19c6cfe5bc234021966dc3daa01f0"}, + {file = "coverage-7.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d254666d29540a72d17cc0175746cfb03d5123db33e67d1020e42dae611dc196"}, + {file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19245c249aa711d954623d94f23cc94c0fd65865661f20b7781210cb97c471c0"}, + {file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b05ed4b35bf6ee790832f68932baf1f00caa32283d66cc4d455c9e9d115aafc"}, + {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:29de916ba1099ba2aab76aca101580006adfac5646de9b7c010a0f13867cba45"}, + {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e057e74e53db78122a3979f908973e171909a58ac20df05c33998d52e6d35757"}, + {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:411d4ff9d041be08fdfc02adf62e89c735b9468f6d8f6427f8a14b6bb0a85095"}, + {file = "coverage-7.0.5-cp311-cp311-win32.whl", hash = "sha256:52ab14b9e09ce052237dfe12d6892dd39b0401690856bcfe75d5baba4bfe2831"}, + {file = "coverage-7.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:1f66862d3a41674ebd8d1a7b6f5387fe5ce353f8719040a986551a545d7d83ea"}, + {file = "coverage-7.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b69522b168a6b64edf0c33ba53eac491c0a8f5cc94fa4337f9c6f4c8f2f5296c"}, + {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436e103950d05b7d7f55e39beeb4d5be298ca3e119e0589c0227e6d0b01ee8c7"}, + {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c56bec53d6e3154eaff6ea941226e7bd7cc0d99f9b3756c2520fc7a94e6d96"}, + {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a38362528a9115a4e276e65eeabf67dcfaf57698e17ae388599568a78dcb029"}, + {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f67472c09a0c7486e27f3275f617c964d25e35727af952869dd496b9b5b7f6a3"}, + {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:220e3fa77d14c8a507b2d951e463b57a1f7810a6443a26f9b7591ef39047b1b2"}, + {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecb0f73954892f98611e183f50acdc9e21a4653f294dfbe079da73c6378a6f47"}, + {file = "coverage-7.0.5-cp37-cp37m-win32.whl", hash = "sha256:d8f3e2e0a1d6777e58e834fd5a04657f66affa615dae61dd67c35d1568c38882"}, + {file = "coverage-7.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9e662e6fc4f513b79da5d10a23edd2b87685815b337b1a30cd11307a6679148d"}, + {file = "coverage-7.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:790e4433962c9f454e213b21b0fd4b42310ade9c077e8edcb5113db0818450cb"}, + {file = "coverage-7.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49640bda9bda35b057b0e65b7c43ba706fa2335c9a9896652aebe0fa399e80e6"}, + {file = "coverage-7.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d66187792bfe56f8c18ba986a0e4ae44856b1c645336bd2c776e3386da91e1dd"}, + {file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:276f4cd0001cd83b00817c8db76730938b1ee40f4993b6a905f40a7278103b3a"}, + {file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95304068686545aa368b35dfda1cdfbbdbe2f6fe43de4a2e9baa8ebd71be46e2"}, + {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:17e01dd8666c445025c29684d4aabf5a90dc6ef1ab25328aa52bedaa95b65ad7"}, + {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea76dbcad0b7b0deb265d8c36e0801abcddf6cc1395940a24e3595288b405ca0"}, + {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:50a6adc2be8edd7ee67d1abc3cd20678987c7b9d79cd265de55941e3d0d56499"}, + {file = "coverage-7.0.5-cp38-cp38-win32.whl", hash = "sha256:e4ce984133b888cc3a46867c8b4372c7dee9cee300335e2925e197bcd45b9e16"}, + {file = "coverage-7.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4a950f83fd3f9bca23b77442f3a2b2ea4ac900944d8af9993743774c4fdc57af"}, + {file = "coverage-7.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c2155943896ac78b9b0fd910fb381186d0c345911f5333ee46ac44c8f0e43ab"}, + {file = "coverage-7.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54f7e9705e14b2c9f6abdeb127c390f679f6dbe64ba732788d3015f7f76ef637"}, + {file = "coverage-7.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee30375b409d9a7ea0f30c50645d436b6f5dfee254edffd27e45a980ad2c7f4"}, + {file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b78729038abea6a5df0d2708dce21e82073463b2d79d10884d7d591e0f385ded"}, + {file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13250b1f0bd023e0c9f11838bdeb60214dd5b6aaf8e8d2f110c7e232a1bff83b"}, + {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c407b1950b2d2ffa091f4e225ca19a66a9bd81222f27c56bd12658fc5ca1209"}, + {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c76a3075e96b9c9ff00df8b5f7f560f5634dffd1658bafb79eb2682867e94f78"}, + {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f26648e1b3b03b6022b48a9b910d0ae209e2d51f50441db5dce5b530fad6d9b1"}, + {file = "coverage-7.0.5-cp39-cp39-win32.whl", hash = "sha256:ba3027deb7abf02859aca49c865ece538aee56dcb4871b4cced23ba4d5088904"}, + {file = "coverage-7.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:949844af60ee96a376aac1ded2a27e134b8c8d35cc006a52903fc06c24a3296f"}, + {file = "coverage-7.0.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:b9727ac4f5cf2cbf87880a63870b5b9730a8ae3a4a360241a0fdaa2f71240ff0"}, + {file = "coverage-7.0.5.tar.gz", hash = "sha256:051afcbd6d2ac39298d62d340f94dbb6a1f31de06dfaf6fcef7b759dd3860c45"}, ] [package.dependencies] @@ -552,47 +620,44 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "38.0.1" +version = "39.0.0" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f"}, - {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6"}, - {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a"}, - {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294"}, - {file = "cryptography-38.0.1-cp36-abi3-win32.whl", hash = "sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0"}, - {file = "cryptography-38.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a"}, - {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d"}, - {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9"}, - {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b"}, - {file = "cryptography-38.0.1.tar.gz", hash = "sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7"}, + {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288"}, + {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717"}, + {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df"}, + {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1"}, + {file = "cryptography-39.0.0-cp36-abi3-win32.whl", hash = "sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de"}, + {file = "cryptography-39.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39"}, + {file = "cryptography-39.0.0.tar.gz", hash = "sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf"}, ] [package.dependencies] cffi = ">=1.12" [package.extras] -docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] -pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +pep8test = ["black", "ruff"] sdist = ["setuptools-rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] @@ -638,22 +703,23 @@ files = [ [[package]] name = "dnspython" -version = "2.2.1" +version = "2.3.0" description = "DNS toolkit" category = "main" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.7,<4.0" files = [ - {file = "dnspython-2.2.1-py3-none-any.whl", hash = "sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"}, - {file = "dnspython-2.2.1.tar.gz", hash = "sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e"}, + {file = "dnspython-2.3.0-py3-none-any.whl", hash = "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46"}, + {file = "dnspython-2.3.0.tar.gz", hash = "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9"}, ] [package.extras] curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] -dnssec = ["cryptography (>=2.6,<37.0)"] -doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.10.0)"] +dnssec = ["cryptography (>=2.6,<40.0)"] +doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.11.0)"] +doq = ["aioquic (>=0.9.20)"] idna = ["idna (>=2.1,<4.0)"] -trio = ["trio (>=0.14,<0.20)"] +trio = ["trio (>=0.14,<0.23)"] wmi = ["wmi (>=1.5.1,<2.0.0)"] [[package]] @@ -692,14 +758,14 @@ files = [ [[package]] name = "email-validator" -version = "1.2.1" -description = "A robust email syntax and deliverability validation library." +version = "1.3.0" +description = "A robust email address syntax and deliverability validation library." category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ - {file = "email_validator-1.2.1-py2.py3-none-any.whl", hash = "sha256:c8589e691cf73eb99eed8d10ce0e9cbb05a0886ba920c8bcb7c82873f4c5789c"}, - {file = "email_validator-1.2.1.tar.gz", hash = "sha256:6757aea012d40516357c0ac2b1a4c31219ab2f899d26831334c5d069e8b6c3d8"}, + {file = "email_validator-1.3.0-py2.py3-none-any.whl", hash = "sha256:816073f2a7cffef786b29928f58ec16cdac42710a53bb18aa94317e3e145ec5c"}, + {file = "email_validator-1.3.0.tar.gz", hash = "sha256:553a66f8be2ec2dea641ae1d3f29017ab89e9d603d4a25cdaac39eefa283d769"}, ] [package.dependencies] @@ -718,6 +784,21 @@ files = [ {file = "eradicate-2.1.0.tar.gz", hash = "sha256:aac7384ab25b1bf21c4c012de9b4bf8398945a14c98c911545b2ea50ab558014"}, ] +[[package]] +name = "exceptiongroup" +version = "1.1.0" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, + {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, +] + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "fastapi" version = "0.85.2" @@ -742,43 +823,42 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.8.0)", "databases[sqlite] ( [[package]] name = "fastapi-users" -version = "10.1.5" +version = "10.3.0" description = "Ready-to-use and customizable users management for FastAPI" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "fastapi-users-10.1.5.tar.gz", hash = "sha256:47cb99eef18eea654170674f5a86c4bed898a6ee666b45007f9805e687ff32f5"}, - {file = "fastapi_users-10.1.5-py3-none-any.whl", hash = "sha256:dded5947d271b80e67b2093f25c591a71eff62975e622ef9871b6283174a87b5"}, + {file = "fastapi_users-10.3.0-py3-none-any.whl", hash = "sha256:54596acad3df21bc3a06b7e67584d0e087186d726b99c7629e82f66da94aea91"}, + {file = "fastapi_users-10.3.0.tar.gz", hash = "sha256:ab9dd218db088bc2d04474ec5fd0040971cea027186041d6d33979f990917d9a"}, ] [package.dependencies] -email-validator = ">=1.1.0,<1.3" +email-validator = ">=1.1.0,<1.4" fastapi = ">=0.65.2" fastapi-users-db-sqlalchemy = {version = ">=4.0.0", optional = true, markers = "extra == \"sqlalchemy\""} -httpx-oauth = {version = ">=0.4,<0.8", optional = true, markers = "extra == \"oauth\""} +httpx-oauth = {version = ">=0.4,<0.11", optional = true, markers = "extra == \"oauth\""} makefun = ">=1.11.2,<2.0.0" passlib = {version = "1.7.4", extras = ["bcrypt"]} -pyjwt = {version = "2.4.0", extras = ["crypto"]} +pyjwt = {version = "2.6.0", extras = ["crypto"]} python-multipart = "0.0.5" [package.extras] beanie = ["fastapi-users-db-beanie (>=1.0.0)"] -dev = ["asgi_lifespan", "black", "bumpversion", "codecov", "flake8", "flake8-docstrings", "flit", "httpx", "httpx-oauth", "isort", "markdown-include", "mike", "mkdocs", "mkdocs-material", "mkdocs-mermaid2-plugin", "mypy", "pygments", "pymdown-extensions", "pytest", "pytest-asyncio", "pytest-cov", "pytest-mock", "requests", "types-redis", "uvicorn"] -oauth = ["httpx-oauth (>=0.4,<0.8)"] +oauth = ["httpx-oauth (>=0.4,<0.11)"] redis = ["redis (>=4.3.3,<5.0.0)"] sqlalchemy = ["fastapi-users-db-sqlalchemy (>=4.0.0)"] [[package]] name = "fastapi-users-db-sqlalchemy" -version = "4.0.3" -description = "FastAPI Users database adapter for SQLAlchemy." +version = "4.0.4" +description = "FastAPI Users database adapter for SQLAlchemy" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "fastapi-users-db-sqlalchemy-4.0.3.tar.gz", hash = "sha256:7d223742e687cf386bea4e990d6ca8571185c50f365301a34e8eb14ebbda18b2"}, - {file = "fastapi_users_db_sqlalchemy-4.0.3-py3-none-any.whl", hash = "sha256:81102ca68e07895898eb9b9d85c6c2e6378f9a46e8486708a36cb1c096117a81"}, + {file = "fastapi_users_db_sqlalchemy-4.0.4-py3-none-any.whl", hash = "sha256:6e82ff1262e1400a332209528d7e670a0e0278d370e6a61a55d23161b891ec28"}, + {file = "fastapi_users_db_sqlalchemy-4.0.4.tar.gz", hash = "sha256:4e44a7de070ba853e2bee3cefb3d7f084c09dafeceb9f8c5f04b2b92768246f4"}, ] [package.dependencies] @@ -787,19 +867,19 @@ sqlalchemy = {version = ">=1.4", extras = ["asyncio"]} [[package]] name = "filelock" -version = "3.8.0" +version = "3.9.0" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, - {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, + {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, + {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, ] [package.extras] -docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] -testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] [[package]] name = "flake8" @@ -853,14 +933,14 @@ flake8 = ">=3.5,<5" [[package]] name = "flake8-bugbear" -version = "22.8.23" +version = "22.12.6" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "flake8-bugbear-22.8.23.tar.gz", hash = "sha256:de0717d11124a082118dd08387b34fd86b2721642ec2d8e92be66cfa5ea7c445"}, - {file = "flake8_bugbear-22.8.23-py3-none-any.whl", hash = "sha256:1b0ebe0873d1cd55bf9f1588bfcb930db339018ef44a3981a26532daa9fd14a8"}, + {file = "flake8-bugbear-22.12.6.tar.gz", hash = "sha256:4cdb2c06e229971104443ae293e75e64c6107798229202fbe4f4091427a30ac0"}, + {file = "flake8_bugbear-22.12.6-py3-none-any.whl", hash = "sha256:b69a510634f8a9c298dfda2b18a8036455e6b19ecac4fe582e4d7a0abfa50a30"}, ] [package.dependencies] @@ -868,7 +948,7 @@ attrs = ">=19.2.0" flake8 = ">=3.0.0" [package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit"] +dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "tox"] [[package]] name = "flake8-commas" @@ -887,14 +967,14 @@ flake8 = ">=2" [[package]] name = "flake8-comprehensions" -version = "3.10.0" +version = "3.10.1" description = "A flake8 plugin to help you write better list/set/dict comprehensions." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "flake8-comprehensions-3.10.0.tar.gz", hash = "sha256:181158f7e7aa26a63a0a38e6017cef28c6adee71278ce56ce11f6ec9c4905058"}, - {file = "flake8_comprehensions-3.10.0-py3-none-any.whl", hash = "sha256:dad454fd3d525039121e98fa1dd90c46bc138708196a4ebbc949ad3c859adedb"}, + {file = "flake8-comprehensions-3.10.1.tar.gz", hash = "sha256:412052ac4a947f36b891143430fef4859705af11b2572fbb689f90d372cf26ab"}, + {file = "flake8_comprehensions-3.10.1-py3-none-any.whl", hash = "sha256:d763de3c74bc18a79c039a7ec732e0a1985b0c79309ceb51e56401ad0a2cd44e"}, ] [package.dependencies] @@ -934,21 +1014,20 @@ pydocstyle = ">=2.1" [[package]] name = "flake8-eradicate" -version = "1.3.0" +version = "1.4.0" description = "Flake8 plugin to find commented out code" category = "dev" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.7,<4.0" files = [ - {file = "flake8-eradicate-1.3.0.tar.gz", hash = "sha256:e4c98f00d17dc8653e3388cac2624cd81e9735de2fd4a8dcf99029633ebd7a63"}, - {file = "flake8_eradicate-1.3.0-py3-none-any.whl", hash = "sha256:85a71e0c5f4e07f7c6c5fec520483561fd6bd295417d622855bdeade99242e3d"}, + {file = "flake8-eradicate-1.4.0.tar.gz", hash = "sha256:3088cfd6717d1c9c6c3ac45ef2e5f5b6c7267f7504d5a74b781500e95cb9c7e1"}, + {file = "flake8_eradicate-1.4.0-py3-none-any.whl", hash = "sha256:e3bbd0871be358e908053c1ab728903c114f062ba596b4d40c852fd18f473d56"}, ] [package.dependencies] attrs = "*" eradicate = ">=2.0,<3.0" flake8 = ">=3.5,<6" -setuptools = "*" [[package]] name = "flake8-isort" @@ -986,13 +1065,13 @@ flake8 = "*" [[package]] name = "flake8-quotes" -version = "3.3.1" +version = "3.3.2" description = "Flake8 lint for quotes." category = "dev" optional = false python-versions = "*" files = [ - {file = "flake8-quotes-3.3.1.tar.gz", hash = "sha256:633adca6fb8a08131536af0d750b44d6985b9aba46f498871e21588c3e6f525a"}, + {file = "flake8-quotes-3.3.2.tar.gz", hash = "sha256:6e26892b632dacba517bf27219c459a8396dcfac0f5e8204904c5a4ba9b480e1"}, ] [package.dependencies] @@ -1050,29 +1129,29 @@ sphinx-basic-ng = "*" [[package]] name = "gitdb" -version = "4.0.9" +version = "4.0.10" description = "Git Object Database" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, - {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, + {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"}, + {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"}, ] [package.dependencies] smmap = ">=3.0.1,<6" [[package]] -name = "GitPython" -version = "3.1.27" +name = "gitpython" +version = "3.1.30" description = "GitPython is a python library used to interact with Git repositories" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, - {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, + {file = "GitPython-3.1.30-py3-none-any.whl", hash = "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"}, + {file = "GitPython-3.1.30.tar.gz", hash = "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8"}, ] [package.dependencies] @@ -1188,36 +1267,53 @@ socks = ["socksio (>=1.0.0,<2.0.0)"] [[package]] name = "httptools" -version = "0.3.0" +version = "0.5.0" description = "A collection of framework independent HTTP protocol utils." category = "main" optional = false python-versions = ">=3.5.0" files = [ - {file = "httptools-0.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4137137de8976511a392e27bfdcf231bd926ac13d375e0414e927b08217d779e"}, - {file = "httptools-0.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9f475b642c48b1b78584bdd12a5143e2c512485664331eade9c29ef769a17598"}, - {file = "httptools-0.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4687dfc116a9f1eb22a7d797f0dc6f6e17190d406ca4e729634b38aa98044b17"}, - {file = "httptools-0.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:72ee0e3fb9c6437ab3ae34e9abee67fcee6876f4f58504e3f613dd5882aafdb7"}, - {file = "httptools-0.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:3787c1f46e9722ef7f07ea5c76b0103037483d1b12e34a02c53ceca5afa4e09a"}, - {file = "httptools-0.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c0ac2e0ce6733c55858932e7d37fcc7b67ba6bb23e9648593c55f663de031b93"}, - {file = "httptools-0.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79717080dc3f8b1eeb7f820b9b81528acbc04be6041f323fdd97550da2062575"}, - {file = "httptools-0.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eda95634027200f4b2a6d499e7c2e7fa9b8ee57e045dfda26958ea0af27c070b"}, - {file = "httptools-0.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3f82eb106e1474c63dba36a176067e65b48385f4cecddf3616411aa5d1fbdfec"}, - {file = "httptools-0.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c14576b737d9e6e4f2a86af04918dbe9b62f57ce8102a8695c9a382dbe405c7f"}, - {file = "httptools-0.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:113816f9af7dcfc4aa71ebb5354d77365f666ecf96ac7ff2aa1d24b6bca44165"}, - {file = "httptools-0.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b8ac7dee63af4346e02b1e6d32202e3b5b3706a9928bec6da6d7a5b066217422"}, - {file = "httptools-0.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:04114db99605c9b56ea22a8ec4d7b1485b908128ed4f4a8f6438489c428da794"}, - {file = "httptools-0.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6e676bc3bb911b11f3d7e2144b9a53600bf6b9b21e0e4437aa308e1eef094d97"}, - {file = "httptools-0.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdc3975db86c29817e6d13df14e037c931fc893a710fb71097777a4147090068"}, - {file = "httptools-0.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ac842df4fc3952efa7820b277961ea55e068bbc54cb59a0820400de7ae358d8"}, - {file = "httptools-0.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:47dba2345aaa01b87e4981e8756af441349340708d5b60712c98c55a4d28f4af"}, - {file = "httptools-0.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:5a836bd85ae1fb4304f674808488dae403e136d274aa5bafd0e6ee456f11c371"}, - {file = "httptools-0.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1a8f26327023fa1a947d36e60a0582149e182fbbc949c8a65ec8665754dbbe69"}, - {file = "httptools-0.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a10a5903b5bc0eb647d01cd1e95bec3bb614a9bf53f0af1e01360b2debdf81"}, - {file = "httptools-0.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21e948034f70e47c8abfa2d5e6f1a5661f87a2cddc7bcc70f61579cc87897c70"}, - {file = "httptools-0.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:074afd8afdeec0fa6786cd4a1676e0c0be23dc9a017a86647efa6b695168104f"}, - {file = "httptools-0.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:2119fa619a4c53311f594f25c0205d619350fcb32140ec5057f861952e9b2b4f"}, - {file = "httptools-0.3.0.tar.gz", hash = "sha256:3f9b4856d46ba1f0c850f4e84b264a9a8b4460acb20e865ec00978ad9fbaa4cf"}, + {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f470c79061599a126d74385623ff4744c4e0f4a0997a353a44923c0b561ee51"}, + {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e90491a4d77d0cb82e0e7a9cb35d86284c677402e4ce7ba6b448ccc7325c5421"}, + {file = "httptools-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1d2357f791b12d86faced7b5736dea9ef4f5ecdc6c3f253e445ee82da579449"}, + {file = "httptools-0.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f90cd6fd97c9a1b7fe9215e60c3bd97336742a0857f00a4cb31547bc22560c2"}, + {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5230a99e724a1bdbbf236a1b58d6e8504b912b0552721c7c6b8570925ee0ccde"}, + {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3a47a34f6015dd52c9eb629c0f5a8a5193e47bf2a12d9a3194d231eaf1bc451a"}, + {file = "httptools-0.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:24bb4bb8ac3882f90aa95403a1cb48465de877e2d5298ad6ddcfdebec060787d"}, + {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e67d4f8734f8054d2c4858570cc4b233bf753f56e85217de4dfb2495904cf02e"}, + {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e5eefc58d20e4c2da82c78d91b2906f1a947ef42bd668db05f4ab4201a99f49"}, + {file = "httptools-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0297822cea9f90a38df29f48e40b42ac3d48a28637368f3ec6d15eebefd182f9"}, + {file = "httptools-0.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:557be7fbf2bfa4a2ec65192c254e151684545ebab45eca5d50477d562c40f986"}, + {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:54465401dbbec9a6a42cf737627fb0f014d50dc7365a6b6cd57753f151a86ff0"}, + {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4d9ebac23d2de960726ce45f49d70eb5466725c0087a078866043dad115f850f"}, + {file = "httptools-0.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e8a34e4c0ab7b1ca17b8763613783e2458e77938092c18ac919420ab8655c8c1"}, + {file = "httptools-0.5.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f659d7a48401158c59933904040085c200b4be631cb5f23a7d561fbae593ec1f"}, + {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1616b3ba965cd68e6f759eeb5d34fbf596a79e84215eeceebf34ba3f61fdc7"}, + {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3625a55886257755cb15194efbf209584754e31d336e09e2ffe0685a76cb4b60"}, + {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:72ad589ba5e4a87e1d404cc1cb1b5780bfcb16e2aec957b88ce15fe879cc08ca"}, + {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:850fec36c48df5a790aa735417dca8ce7d4b48d59b3ebd6f83e88a8125cde324"}, + {file = "httptools-0.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f222e1e9d3f13b68ff8a835574eda02e67277d51631d69d7cf7f8e07df678c86"}, + {file = "httptools-0.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3cb8acf8f951363b617a8420768a9f249099b92e703c052f9a51b66342eea89b"}, + {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550059885dc9c19a072ca6d6735739d879be3b5959ec218ba3e013fd2255a11b"}, + {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a04fe458a4597aa559b79c7f48fe3dceabef0f69f562daf5c5e926b153817281"}, + {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d0c1044bce274ec6711f0770fd2d5544fe392591d204c68328e60a46f88843b"}, + {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c6eeefd4435055a8ebb6c5cc36111b8591c192c56a95b45fe2af22d9881eee25"}, + {file = "httptools-0.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b65be160adcd9de7a7e6413a4966665756e263f0d5ddeffde277ffeee0576a5"}, + {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fe9c766a0c35b7e3d6b6939393c8dfdd5da3ac5dec7f971ec9134f284c6c36d6"}, + {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:85b392aba273566c3d5596a0a490978c085b79700814fb22bfd537d381dd230c"}, + {file = "httptools-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e3088f4ed33947e16fd865b8200f9cfae1144f41b64a8cf19b599508e096bc"}, + {file = "httptools-0.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c2a56b6aad7cc8f5551d8e04ff5a319d203f9d870398b94702300de50190f63"}, + {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b571b281a19762adb3f48a7731f6842f920fa71108aff9be49888320ac3e24d"}, + {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa47ffcf70ba6f7848349b8a6f9b481ee0f7637931d91a9860a1838bfc586901"}, + {file = "httptools-0.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:bede7ee075e54b9a5bde695b4fc8f569f30185891796b2e4e09e2226801d09bd"}, + {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:64eba6f168803a7469866a9c9b5263a7463fa8b7a25b35e547492aa7322036b6"}, + {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4b098e4bb1174096a93f48f6193e7d9aa7071506a5877da09a783509ca5fff42"}, + {file = "httptools-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9423a2de923820c7e82e18980b937893f4aa8251c43684fa1772e341f6e06887"}, + {file = "httptools-0.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca1b7becf7d9d3ccdbb2f038f665c0f4857e08e1d8481cbcc1a86a0afcfb62b2"}, + {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:50d4613025f15f4b11f1c54bbed4761c0020f7f921b95143ad6d58c151198142"}, + {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8ffce9d81c825ac1deaa13bc9694c0562e2840a48ba21cfc9f3b4c922c16f372"}, + {file = "httptools-0.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:1af91b3650ce518d226466f30bbba5b6376dbd3ddb1b2be8b0658c6799dd450b"}, + {file = "httptools-0.5.0.tar.gz", hash = "sha256:295874861c173f9101960bba332429bb77ed4dcd8cdf5cee9922eb00e4f6bc09"}, ] [package.extras] @@ -1250,32 +1346,29 @@ socks = ["socksio (>=1.0.0,<2.0.0)"] [[package]] name = "httpx-oauth" -version = "0.7.0" +version = "0.10.2" description = "Async OAuth client using HTTPX" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "httpx-oauth-0.7.0.tar.gz", hash = "sha256:ea84b767274416cc23ce8213e81e8f85436d1526fa64c71a95942590eb0b67ca"}, - {file = "httpx_oauth-0.7.0-py3-none-any.whl", hash = "sha256:5c5380df6727a0571011694fc552980828bfd5223df04a8ff06f61be2e939e90"}, + {file = "httpx_oauth-0.10.2-py3-none-any.whl", hash = "sha256:1739531e0c1f767688b08eb6a9192f38b4d48361ec8307cc52738d61c7d77666"}, + {file = "httpx_oauth-0.10.2.tar.gz", hash = "sha256:be92ce28fce16d443288e811fe11b71f4aab4273ee9b5e1404692addca4bca98"}, ] [package.dependencies] httpx = ">=0.18,<0.24" -[package.extras] -dev = ["black", "bumpversion", "codecov", "fastapi", "flake8", "flake8-docstrings", "flit", "isort", "markdown-include", "mkdocs", "mkdocs-material", "mypy", "pygments", "pymdown-extensions", "pytest", "pytest-asyncio", "pytest-cov", "pytest-mock", "respx"] - [[package]] name = "identify" -version = "2.5.5" +version = "2.5.13" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "identify-2.5.5-py2.py3-none-any.whl", hash = "sha256:ef78c0d96098a3b5fe7720be4a97e73f439af7cf088ebf47b620aeaa10fadf97"}, - {file = "identify-2.5.5.tar.gz", hash = "sha256:322a5699daecf7c6fd60e68852f36f2ecbb6a36ff6e6e973e0d2bb6fca203ee6"}, + {file = "identify-2.5.13-py2.py3-none-any.whl", hash = "sha256:8aa48ce56e38c28b6faa9f261075dea0a942dfbb42b341b4e711896cbb40f3f7"}, + {file = "identify-2.5.13.tar.gz", hash = "sha256:abb546bca6f470228785338a01b539de8a85bbf46491250ae03363956d8ebb10"}, ] [package.extras] @@ -1283,14 +1376,14 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.3" +version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] [[package]] @@ -1327,26 +1420,26 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag [[package]] name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "isort" -version = "5.10.1" +version = "5.11.4" description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=3.6.1,<4.0" +python-versions = ">=3.7.0" files = [ - {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, - {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, + {file = "isort-5.11.4-py3-none-any.whl", hash = "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b"}, + {file = "isort-5.11.4.tar.gz", hash = "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6"}, ] [package.extras] @@ -1432,15 +1525,15 @@ files = [ ] [[package]] -name = "Mako" -version = "1.2.2" +name = "mako" +version = "1.2.4" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "Mako-1.2.2-py3-none-any.whl", hash = "sha256:8efcb8004681b5f71d09c983ad5a9e6f5c40601a6ec469148753292abc0da534"}, - {file = "Mako-1.2.2.tar.gz", hash = "sha256:3724869b363ba630a272a5f89f68c070352137b8fd1757650017b7e06fda163f"}, + {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, + {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, ] [package.dependencies] @@ -1452,7 +1545,7 @@ lingua = ["lingua"] testing = ["pytest"] [[package]] -name = "MarkupSafe" +name = "markupsafe" version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" @@ -1515,71 +1608,86 @@ files = [ [[package]] name = "multidict" -version = "6.0.2" +version = "6.0.4" description = "multidict implementation" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, - {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, - {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"}, - {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"}, - {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"}, - {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"}, - {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"}, - {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"}, - {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"}, - {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"}, - {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"}, - {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"}, - {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"}, - {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"}, - {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"}, - {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"}, - {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"}, - {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"}, - {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, + {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, + {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, + {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, + {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, + {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, + {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, + {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, + {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, + {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, + {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, + {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, + {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, ] [[package]] @@ -1654,19 +1762,16 @@ setuptools = "*" [[package]] name = "packaging" -version = "21.3" +version = "23.0" description = "Core utilities for Python packages" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, + {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, + {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, ] -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" - [[package]] name = "passlib" version = "1.7.4" @@ -1690,26 +1795,26 @@ totp = ["cryptography"] [[package]] name = "pathspec" -version = "0.10.1" +version = "0.10.3" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, - {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, + {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, + {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, ] [[package]] name = "pbr" -version = "5.10.0" +version = "5.11.1" description = "Python Build Reasonableness" category = "dev" optional = false python-versions = ">=2.6" files = [ - {file = "pbr-5.10.0-py2.py3-none-any.whl", hash = "sha256:da3e18aac0a3c003e9eea1a81bd23e5a3a75d745670dcf736317b7d966887fdf"}, - {file = "pbr-5.10.0.tar.gz", hash = "sha256:cfcc4ff8e698256fc17ea3ff796478b050852585aa5bae79ecd05b2ab7b39b9a"}, + {file = "pbr-5.11.1-py2.py3-none-any.whl", hash = "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b"}, + {file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"}, ] [[package]] @@ -1730,19 +1835,19 @@ flake8-polyfill = ">=1.0.2,<2" [[package]] name = "platformdirs" -version = "2.5.2" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "2.6.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, + {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, + {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, ] [package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" @@ -1762,14 +1867,14 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.20.0" +version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, - {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, ] [package.dependencies] @@ -1777,20 +1882,7 @@ cfgv = ">=2.0.0" identify = ">=1.0.0" nodeenv = ">=0.11.1" pyyaml = ">=5.1" -toml = "*" -virtualenv = ">=20.0.8" - -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] +virtualenv = ">=20.10.0" [[package]] name = "pycodestyle" @@ -1818,53 +1910,53 @@ files = [ [[package]] name = "pydantic" -version = "1.10.2" +version = "1.10.4" description = "Data validation and settings management using python type hints" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"}, - {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"}, - {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"}, - {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"}, - {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"}, - {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"}, - {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"}, - {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"}, - {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"}, - {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"}, - {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"}, - {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"}, - {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"}, - {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"}, - {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"}, - {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"}, - {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"}, - {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"}, - {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"}, - {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"}, - {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"}, - {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"}, - {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"}, - {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"}, - {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"}, - {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"}, - {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"}, - {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"}, - {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"}, - {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"}, - {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"}, - {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"}, - {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"}, - {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"}, - {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"}, - {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"}, + {file = "pydantic-1.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5635de53e6686fe7a44b5cf25fcc419a0d5e5c1a1efe73d49d48fe7586db854"}, + {file = "pydantic-1.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6dc1cc241440ed7ca9ab59d9929075445da6b7c94ced281b3dd4cfe6c8cff817"}, + {file = "pydantic-1.10.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51bdeb10d2db0f288e71d49c9cefa609bca271720ecd0c58009bd7504a0c464c"}, + {file = "pydantic-1.10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cec42b95dbb500a1f7120bdf95c401f6abb616bbe8785ef09887306792e66e"}, + {file = "pydantic-1.10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8775d4ef5e7299a2f4699501077a0defdaac5b6c4321173bcb0f3c496fbadf85"}, + {file = "pydantic-1.10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:572066051eeac73d23f95ba9a71349c42a3e05999d0ee1572b7860235b850cc6"}, + {file = "pydantic-1.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:7feb6a2d401f4d6863050f58325b8d99c1e56f4512d98b11ac64ad1751dc647d"}, + {file = "pydantic-1.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39f4a73e5342b25c2959529f07f026ef58147249f9b7431e1ba8414a36761f53"}, + {file = "pydantic-1.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:983e720704431a6573d626b00662eb78a07148c9115129f9b4351091ec95ecc3"}, + {file = "pydantic-1.10.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d52162fe6b2b55964fbb0af2ee58e99791a3138588c482572bb6087953113a"}, + {file = "pydantic-1.10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdf8d759ef326962b4678d89e275ffc55b7ce59d917d9f72233762061fd04a2d"}, + {file = "pydantic-1.10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05a81b006be15655b2a1bae5faa4280cf7c81d0e09fcb49b342ebf826abe5a72"}, + {file = "pydantic-1.10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d88c4c0e5c5dfd05092a4b271282ef0588e5f4aaf345778056fc5259ba098857"}, + {file = "pydantic-1.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:6a05a9db1ef5be0fe63e988f9617ca2551013f55000289c671f71ec16f4985e3"}, + {file = "pydantic-1.10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:887ca463c3bc47103c123bc06919c86720e80e1214aab79e9b779cda0ff92a00"}, + {file = "pydantic-1.10.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdf88ab63c3ee282c76d652fc86518aacb737ff35796023fae56a65ced1a5978"}, + {file = "pydantic-1.10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a48f1953c4a1d9bd0b5167ac50da9a79f6072c63c4cef4cf2a3736994903583e"}, + {file = "pydantic-1.10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a9f2de23bec87ff306aef658384b02aa7c32389766af3c5dee9ce33e80222dfa"}, + {file = "pydantic-1.10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:cd8702c5142afda03dc2b1ee6bc358b62b3735b2cce53fc77b31ca9f728e4bc8"}, + {file = "pydantic-1.10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6e7124d6855b2780611d9f5e1e145e86667eaa3bd9459192c8dc1a097f5e9903"}, + {file = "pydantic-1.10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b53e1d41e97063d51a02821b80538053ee4608b9a181c1005441f1673c55423"}, + {file = "pydantic-1.10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:55b1625899acd33229c4352ce0ae54038529b412bd51c4915349b49ca575258f"}, + {file = "pydantic-1.10.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:301d626a59edbe5dfb48fcae245896379a450d04baeed50ef40d8199f2733b06"}, + {file = "pydantic-1.10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6f9d649892a6f54a39ed56b8dfd5e08b5f3be5f893da430bed76975f3735d15"}, + {file = "pydantic-1.10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d7b5a3821225f5c43496c324b0d6875fde910a1c2933d726a743ce328fbb2a8c"}, + {file = "pydantic-1.10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f2f7eb6273dd12472d7f218e1fef6f7c7c2f00ac2e1ecde4db8824c457300416"}, + {file = "pydantic-1.10.4-cp38-cp38-win_amd64.whl", hash = "sha256:4b05697738e7d2040696b0a66d9f0a10bec0efa1883ca75ee9e55baf511909d6"}, + {file = "pydantic-1.10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a9a6747cac06c2beb466064dda999a13176b23535e4c496c9d48e6406f92d42d"}, + {file = "pydantic-1.10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb992a1ef739cc7b543576337bebfc62c0e6567434e522e97291b251a41dad7f"}, + {file = "pydantic-1.10.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:990406d226dea0e8f25f643b370224771878142155b879784ce89f633541a024"}, + {file = "pydantic-1.10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e82a6d37a95e0b1b42b82ab340ada3963aea1317fd7f888bb6b9dfbf4fff57c"}, + {file = "pydantic-1.10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9193d4f4ee8feca58bc56c8306bcb820f5c7905fd919e0750acdeeeef0615b28"}, + {file = "pydantic-1.10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2b3ce5f16deb45c472dde1a0ee05619298c864a20cded09c4edd820e1454129f"}, + {file = "pydantic-1.10.4-cp39-cp39-win_amd64.whl", hash = "sha256:9cbdc268a62d9a98c56e2452d6c41c0263d64a2009aac69246486f01b4f594c4"}, + {file = "pydantic-1.10.4-py3-none-any.whl", hash = "sha256:4948f264678c703f3877d1c8877c4e3b2e12e549c57795107f08cf70c6ec7774"}, + {file = "pydantic-1.10.4.tar.gz", hash = "sha256:b9a3859f24eb4e097502a3be1fb4b2abb79b6103dd9e2e0edb70613a4459a648"}, ] [package.dependencies] python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""} -typing-extensions = ">=4.1.0" +typing-extensions = ">=4.2.0" [package.extras] dotenv = ["python-dotenv (>=0.10.4)"] @@ -1872,21 +1964,21 @@ email = ["email-validator (>=1.0.3)"] [[package]] name = "pydocstyle" -version = "6.1.1" +version = "6.2.3" description = "Python docstring style checker" category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, - {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, + {file = "pydocstyle-6.2.3-py3-none-any.whl", hash = "sha256:a04ed1e6fe0be0970eddbb1681a7ab59b11eb92729fdb4b9b24f0eb11a25629e"}, + {file = "pydocstyle-6.2.3.tar.gz", hash = "sha256:d867acad25e48471f2ad8a40ef9813125e954ad675202245ca836cb6e28b2297"}, ] [package.dependencies] -snowballstemmer = "*" +snowballstemmer = ">=2.2.0" [package.extras] -toml = ["toml"] +toml = ["tomli (>=1.2.3)"] [[package]] name = "pyflakes" @@ -1901,76 +1993,61 @@ files = [ ] [[package]] -name = "Pygments" -version = "2.13.0" +name = "pygments" +version = "2.14.0" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, - {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, + {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"}, + {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"}, ] [package.extras] plugins = ["importlib-metadata"] [[package]] -name = "PyJWT" -version = "2.4.0" +name = "pyjwt" +version = "2.6.0" description = "JSON Web Token implementation in Python" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "PyJWT-2.4.0-py3-none-any.whl", hash = "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf"}, - {file = "PyJWT-2.4.0.tar.gz", hash = "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba"}, + {file = "PyJWT-2.6.0-py3-none-any.whl", hash = "sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14"}, + {file = "PyJWT-2.6.0.tar.gz", hash = "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd"}, ] [package.dependencies] -cryptography = {version = ">=3.3.1", optional = true, markers = "extra == \"crypto\""} +cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""} [package.extras] -crypto = ["cryptography (>=3.3.1)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.3.1)", "mypy", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "dev" -optional = false -python-versions = ">=3.6.8" -files = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - [[package]] name = "pytest" -version = "7.1.3" +version = "7.2.1" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"}, - {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"}, + {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"}, + {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"}, ] [package.dependencies] attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -tomli = ">=1.0.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] @@ -2039,14 +2116,14 @@ six = ">=1.4.0" [[package]] name = "pytz" -version = "2022.7" +version = "2022.7.1" description = "World timezone definitions, modern and historical" category = "dev" optional = false python-versions = "*" files = [ - {file = "pytz-2022.7-py2.py3-none-any.whl", hash = "sha256:93007def75ae22f7cd991c84e02d434876818661f8df9ad5df9e950ff4e52cfd"}, - {file = "pytz-2022.7.tar.gz", hash = "sha256:7ccfae7b4b2c067464a6733c6261673fdb8fd1be905460396b97a073e9fa683a"}, + {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, + {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, ] [[package]] @@ -2088,13 +2165,6 @@ files = [ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, @@ -2125,19 +2195,19 @@ files = [ [[package]] name = "requests" -version = "2.28.1" +version = "2.28.2" description = "Python HTTP for Humans." category = "dev" optional = false python-versions = ">=3.7, <4" files = [ - {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, - {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, + {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, + {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<3" +charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" @@ -2179,19 +2249,19 @@ idna2008 = ["idna"] [[package]] name = "setuptools" -version = "65.3.0" +version = "66.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-65.3.0-py3-none-any.whl", hash = "sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82"}, - {file = "setuptools-65.3.0.tar.gz", hash = "sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"}, + {file = "setuptools-66.0.0-py3-none-any.whl", hash = "sha256:a78d01d1e2c175c474884671dde039962c9d74c7223db7369771fcf6e29ceeab"}, + {file = "setuptools-66.0.0.tar.gz", hash = "sha256:bd6eb2d6722568de6d14b87c44a96fac54b2a45ff5e940e639979a3d1792adb6"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -2447,54 +2517,54 @@ lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] -name = "SQLAlchemy" -version = "1.4.41" +name = "sqlalchemy" +version = "1.4.46" description = "Database Abstraction Library" category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "SQLAlchemy-1.4.41-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:13e397a9371ecd25573a7b90bd037db604331cf403f5318038c46ee44908c44d"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2d6495f84c4fd11584f34e62f9feec81bf373787b3942270487074e35cbe5330"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27m-win32.whl", hash = "sha256:e570cfc40a29d6ad46c9aeaddbdcee687880940a3a327f2c668dd0e4ef0a441d"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27m-win_amd64.whl", hash = "sha256:5facb7fd6fa8a7353bbe88b95695e555338fb038ad19ceb29c82d94f62775a05"}, - {file = "SQLAlchemy-1.4.41-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f37fa70d95658763254941ddd30ecb23fc4ec0c5a788a7c21034fc2305dab7cc"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:361f6b5e3f659e3c56ea3518cf85fbdae1b9e788ade0219a67eeaaea8a4e4d2a"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0990932f7cca97fece8017414f57fdd80db506a045869d7ddf2dda1d7cf69ecc"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd767cf5d7252b1c88fcfb58426a32d7bd14a7e4942497e15b68ff5d822b41ad"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5102fb9ee2c258a2218281adcb3e1918b793c51d6c2b4666ce38c35101bb940e"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-win32.whl", hash = "sha256:2082a2d2fca363a3ce21cfa3d068c5a1ce4bf720cf6497fb3a9fc643a8ee4ddd"}, - {file = "SQLAlchemy-1.4.41-cp310-cp310-win_amd64.whl", hash = "sha256:e4b12e3d88a8fffd0b4ca559f6d4957ed91bd4c0613a4e13846ab8729dc5c251"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:90484a2b00baedad361402c257895b13faa3f01780f18f4a104a2f5c413e4536"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b67fc780cfe2b306180e56daaa411dd3186bf979d50a6a7c2a5b5036575cbdbb"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad2b727fc41c7f8757098903f85fafb4bf587ca6605f82d9bf5604bd9c7cded"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-win32.whl", hash = "sha256:59bdc291165b6119fc6cdbc287c36f7f2859e6051dd923bdf47b4c55fd2f8bd0"}, - {file = "SQLAlchemy-1.4.41-cp311-cp311-win_amd64.whl", hash = "sha256:d2e054aed4645f9b755db85bc69fc4ed2c9020c19c8027976f66576b906a74f1"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:4ba7e122510bbc07258dc42be6ed45997efdf38129bde3e3f12649be70683546"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0dcf127bb99458a9d211e6e1f0f3edb96c874dd12f2503d4d8e4f1fd103790b"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e16c2be5cb19e2c08da7bd3a87fed2a0d4e90065ee553a940c4fc1a0fb1ab72b"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ebeeec5c14533221eb30bad716bc1fd32f509196318fb9caa7002c4a364e4c"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-win32.whl", hash = "sha256:3e2ef592ac3693c65210f8b53d0edcf9f4405925adcfc031ff495e8d18169682"}, - {file = "SQLAlchemy-1.4.41-cp36-cp36m-win_amd64.whl", hash = "sha256:eb30cf008850c0a26b72bd1b9be6730830165ce049d239cfdccd906f2685f892"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:c23d64a0b28fc78c96289ffbd0d9d1abd48d267269b27f2d34e430ea73ce4b26"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8eb8897367a21b578b26f5713833836f886817ee2ffba1177d446fa3f77e67c8"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:14576238a5f89bcf504c5f0a388d0ca78df61fb42cb2af0efe239dc965d4f5c9"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:639e1ae8d48b3c86ffe59c0daa9a02e2bfe17ca3d2b41611b30a0073937d4497"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-win32.whl", hash = "sha256:0005bd73026cd239fc1e8ccdf54db58b6193be9a02b3f0c5983808f84862c767"}, - {file = "SQLAlchemy-1.4.41-cp37-cp37m-win_amd64.whl", hash = "sha256:5323252be2bd261e0aa3f33cb3a64c45d76829989fa3ce90652838397d84197d"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:05f0de3a1dc3810a776275763764bb0015a02ae0f698a794646ebc5fb06fad33"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0002e829142b2af00b4eaa26c51728f3ea68235f232a2e72a9508a3116bd6ed0"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22ff16cedab5b16a0db79f1bc99e46a6ddececb60c396562e50aab58ddb2871c"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccfd238f766a5bb5ee5545a62dd03f316ac67966a6a658efb63eeff8158a4bbf"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-win32.whl", hash = "sha256:58bb65b3274b0c8a02cea9f91d6f44d0da79abc993b33bdedbfec98c8440175a"}, - {file = "SQLAlchemy-1.4.41-cp38-cp38-win_amd64.whl", hash = "sha256:ce8feaa52c1640de9541eeaaa8b5fb632d9d66249c947bb0d89dd01f87c7c288"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:199a73c31ac8ea59937cc0bf3dfc04392e81afe2ec8a74f26f489d268867846c"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676d51c9f6f6226ae8f26dc83ec291c088fe7633269757d333978df78d931ab"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:036d8472356e1d5f096c5e0e1a7e0f9182140ada3602f8fff6b7329e9e7cfbcd"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2307495d9e0ea00d0c726be97a5b96615035854972cc538f6e7eaed23a35886c"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-win32.whl", hash = "sha256:9c56e19780cd1344fcd362fd6265a15f48aa8d365996a37fab1495cae8fcd97d"}, - {file = "SQLAlchemy-1.4.41-cp39-cp39-win_amd64.whl", hash = "sha256:f5fa526d027d804b1f85cdda1eb091f70bde6fb7d87892f6dd5a48925bc88898"}, - {file = "SQLAlchemy-1.4.41.tar.gz", hash = "sha256:0292f70d1797e3c54e862e6f30ae474014648bc9c723e14a2fda730adb0a9791"}, + {file = "SQLAlchemy-1.4.46-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:7001f16a9a8e06488c3c7154827c48455d1c1507d7228d43e781afbc8ceccf6d"}, + {file = "SQLAlchemy-1.4.46-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c7a46639ba058d320c9f53a81db38119a74b8a7a1884df44d09fbe807d028aaf"}, + {file = "SQLAlchemy-1.4.46-cp27-cp27m-win32.whl", hash = "sha256:c04144a24103135ea0315d459431ac196fe96f55d3213bfd6d39d0247775c854"}, + {file = "SQLAlchemy-1.4.46-cp27-cp27m-win_amd64.whl", hash = "sha256:7b81b1030c42b003fc10ddd17825571603117f848814a344d305262d370e7c34"}, + {file = "SQLAlchemy-1.4.46-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:939f9a018d2ad04036746e15d119c0428b1e557470361aa798e6e7d7f5875be0"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b7f4b6aa6e87991ec7ce0e769689a977776db6704947e562102431474799a857"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbf17ac9a61e7a3f1c7ca47237aac93cabd7f08ad92ac5b96d6f8dea4287fc1"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7f8267682eb41a0584cf66d8a697fef64b53281d01c93a503e1344197f2e01fe"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cb0ad8a190bc22d2112001cfecdec45baffdf41871de777239da6a28ed74b6"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-win32.whl", hash = "sha256:5f752676fc126edc1c4af0ec2e4d2adca48ddfae5de46bb40adbd3f903eb2120"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-win_amd64.whl", hash = "sha256:31de1e2c45e67a5ec1ecca6ec26aefc299dd5151e355eb5199cd9516b57340be"}, + {file = "SQLAlchemy-1.4.46-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d68e1762997bfebf9e5cf2a9fd0bcf9ca2fdd8136ce7b24bbd3bbfa4328f3e4a"}, + {file = "SQLAlchemy-1.4.46-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d112b0f3c1bc5ff70554a97344625ef621c1bfe02a73c5d97cac91f8cd7a41e"}, + {file = "SQLAlchemy-1.4.46-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69fac0a7054d86b997af12dc23f581cf0b25fb1c7d1fed43257dee3af32d3d6d"}, + {file = "SQLAlchemy-1.4.46-cp311-cp311-win32.whl", hash = "sha256:887865924c3d6e9a473dc82b70977395301533b3030d0f020c38fd9eba5419f2"}, + {file = "SQLAlchemy-1.4.46-cp311-cp311-win_amd64.whl", hash = "sha256:984ee13543a346324319a1fb72b698e521506f6f22dc37d7752a329e9cd00a32"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:9167d4227b56591a4cc5524f1b79ccd7ea994f36e4c648ab42ca995d28ebbb96"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d61e9ecc849d8d44d7f80894ecff4abe347136e9d926560b818f6243409f3c86"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3ec187acf85984263299a3f15c34a6c0671f83565d86d10f43ace49881a82718"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9883f5fae4fd8e3f875adc2add69f8b945625811689a6c65866a35ee9c0aea23"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-win32.whl", hash = "sha256:535377e9b10aff5a045e3d9ada8a62d02058b422c0504ebdcf07930599890eb0"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-win_amd64.whl", hash = "sha256:18cafdb27834fa03569d29f571df7115812a0e59fd6a3a03ccb0d33678ec8420"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:a1ad90c97029cc3ab4ffd57443a20fac21d2ec3c89532b084b073b3feb5abff3"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4847f4b1d822754e35707db913396a29d874ee77b9c3c3ef3f04d5a9a6209618"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c5a99282848b6cae0056b85da17392a26b2d39178394fc25700bcf967e06e97a"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4b1cc7835b39835c75cf7c20c926b42e97d074147c902a9ebb7cf2c840dc4e2"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-win32.whl", hash = "sha256:c522e496f9b9b70296a7675272ec21937ccfc15da664b74b9f58d98a641ce1b6"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-win_amd64.whl", hash = "sha256:ae067ab639fa499f67ded52f5bc8e084f045d10b5ac7bb928ae4ca2b6c0429a5"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:e3c1808008124850115a3f7e793a975cfa5c8a26ceeeb9ff9cbb4485cac556df"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d164df3d83d204c69f840da30b292ac7dc54285096c6171245b8d7807185aa"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b33ffbdbbf5446cf36cd4cc530c9d9905d3c2fe56ed09e25c22c850cdb9fac92"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d94682732d1a0def5672471ba42a29ff5e21bb0aae0afa00bb10796fc1e28dd"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-win32.whl", hash = "sha256:f8cb80fe8d14307e4124f6fad64dfd87ab749c9d275f82b8b4ec84c84ecebdbe"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-win_amd64.whl", hash = "sha256:07e48cbcdda6b8bc7a59d6728bd3f5f574ffe03f2c9fb384239f3789c2d95c2e"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1b1e5e96e2789d89f023d080bee432e2fef64d95857969e70d3cadec80bd26f0"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3714e5b33226131ac0da60d18995a102a17dddd42368b7bdd206737297823ad"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:955162ad1a931fe416eded6bb144ba891ccbf9b2e49dc7ded39274dd9c5affc5"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6e4cb5c63f705c9d546a054c60d326cbde7421421e2d2565ce3e2eee4e1a01f"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-win32.whl", hash = "sha256:51e1ba2884c6a2b8e19109dc08c71c49530006c1084156ecadfaadf5f9b8b053"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-win_amd64.whl", hash = "sha256:315676344e3558f1f80d02535f410e80ea4e8fddba31ec78fe390eff5fb8f466"}, + {file = "SQLAlchemy-1.4.46.tar.gz", hash = "sha256:6913b8247d8a292ef8315162a51931e2b40ce91681f1b6f18f697045200c4a30"}, ] [package.dependencies] @@ -2525,14 +2595,14 @@ sqlcipher = ["sqlcipher3-binary"] [[package]] name = "sqlalchemy2-stubs" -version = "0.0.2a27" +version = "0.0.2a31" description = "Typing Stubs for SQLAlchemy 1.4" category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "sqlalchemy2-stubs-0.0.2a27.tar.gz", hash = "sha256:f79bce50b7837a2c2374ef4480b41e2b8a8226f313f347dc2a70526a4191db93"}, - {file = "sqlalchemy2_stubs-0.0.2a27-py3-none-any.whl", hash = "sha256:6cea12fec3c261f6e0e14a95d2cc4914e373095e68ec4fc2eb473183ac2b17a2"}, + {file = "sqlalchemy2-stubs-0.0.2a31.tar.gz", hash = "sha256:18e70515eec96c48825f3e1da0c60b04e155593213e25eb28c4cee994a315e4d"}, + {file = "sqlalchemy2_stubs-0.0.2a31-py3-none-any.whl", hash = "sha256:515a97fbe4b7cd299b43166c51ce6f356f1222128bf2ba57b0619ce8d89dfab4"}, ] [package.dependencies] @@ -2559,14 +2629,14 @@ full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] [[package]] name = "stevedore" -version = "4.0.0" +version = "4.1.1" description = "Manage dynamic plugins for Python applications" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "stevedore-4.0.0-py3-none-any.whl", hash = "sha256:87e4d27fe96d0d7e4fc24f0cbe3463baae4ec51e81d95fbe60d2474636e0c7d8"}, - {file = "stevedore-4.0.0.tar.gz", hash = "sha256:f82cc99a1ff552310d19c379827c2c64dd9f85a38bcd5559db2470161867b786"}, + {file = "stevedore-4.1.1-py3-none-any.whl", hash = "sha256:aa6436565c069b2946fe4ebff07f5041e0c8bf18c7376dd29edf80cf7d524e4e"}, + {file = "stevedore-4.1.1.tar.gz", hash = "sha256:7f8aeb6e3f90f96832c301bff21a7eb5eefbe894c88c506483d355565d88cc1a"}, ] [package.dependencies] @@ -2574,13 +2644,13 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" [[package]] name = "testcontainers" -version = "3.7.0" +version = "3.7.1" description = "Library provides lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "testcontainers-3.7.0-py2.py3-none-any.whl", hash = "sha256:e40fece4085ae06f8facb067e83020e273738e31f958b01d2b5eaa84e2dab478"}, + {file = "testcontainers-3.7.1-py2.py3-none-any.whl", hash = "sha256:7f48cef4bf0ccd78f1a4534d4b701a003a3bace851f24eae58a32f9e3f0aeba0"}, ] [package.dependencies] @@ -2608,26 +2678,14 @@ selenium = ["selenium"] [[package]] name = "tokenize-rt" -version = "4.2.1" +version = "5.0.0" description = "A wrapper around the stdlib `tokenize` which roundtrips." category = "dev" optional = false -python-versions = ">=3.6.1" -files = [ - {file = "tokenize_rt-4.2.1-py2.py3-none-any.whl", hash = "sha256:08a27fa032a81cf45e8858d0ac706004fcd523e8463415ddf1442be38e204ea8"}, - {file = "tokenize_rt-4.2.1.tar.gz", hash = "sha256:0d4f69026fed520f8a1e0103aa36c406ef4661417f20ca643f913e33531b3b94"}, -] - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.7" files = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, + {file = "tokenize_rt-5.0.0-py2.py3-none-any.whl", hash = "sha256:c67772c662c6b3dc65edf66808577968fb10badfc2042e3027196bed4daf9e5a"}, + {file = "tokenize_rt-5.0.0.tar.gz", hash = "sha256:3160bc0c3e8491312d0485171dea861fc160a240f5f5766b72a1165408d10740"}, ] [[package]] @@ -2668,74 +2726,89 @@ files = [ [[package]] name = "typing-extensions" -version = "4.3.0" +version = "4.4.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] [[package]] name = "ujson" -version = "5.4.0" +version = "5.7.0" description = "Ultra fast JSON encoder and decoder for Python" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "ujson-5.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:511aa641a5b91d19280183b134fb6c473039d4dd82e987ac810cffba783521ac"}, - {file = "ujson-5.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b045ca5497a950cc3492840adb3bcb3b9e305ed6599ed14c6aeaa08011aa463f"}, - {file = "ujson-5.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa00b746138835271653b0c3da171d2a8b510c579381f71e8b8e03484d50d825"}, - {file = "ujson-5.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91edcf9978ee401119e9c8589376ae37fd3e6e75ee365c49385cb005eaff1535"}, - {file = "ujson-5.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05e411627e5d6ee773232960ca7307e66017f78e3fa74f7e95c3a8cc5cb05415"}, - {file = "ujson-5.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7d4c9ccd30e621e714ec24ca911ad8873567dc1ac1e5e914405ea9dd16b9d40c"}, - {file = "ujson-5.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:dd0d4ec694cab8a0a4d85f45f81ae0065465c4670f0db72ba48d6c4e7ae42834"}, - {file = "ujson-5.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aaa77af91df3f71858a1f792c74d3f2d3abf3875f93ab1a2b9a24b3797743b02"}, - {file = "ujson-5.4.0-cp310-cp310-win32.whl", hash = "sha256:fbea46c0fbc1c3bc8f957afd8dbb25b4ea3a356e18ee6dd79ace6cf32bd4cff7"}, - {file = "ujson-5.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:2d98248f1df1e1aab67e0374ab98945dd36bc1764753d71fd8aea5f296360b76"}, - {file = "ujson-5.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f5c547d49a7e9d3f231e9323171bbbbcef63173fb007a2787cd4f05ac6269315"}, - {file = "ujson-5.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39bb702ca1612253b5e4b6004e0f20208c98a446606aa351f9a7ba5ceaff0eb8"}, - {file = "ujson-5.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381c97d326d1ec569d318cc0ae83940ea2df125ede1000871680fefd5b7fdea9"}, - {file = "ujson-5.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a0707f381f97e1287c0dbf94d95bd6c0bbf6e4eeeaa656f0076b7883010c818"}, - {file = "ujson-5.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a20f2f6e8818c1ab89dd4be6bbad3fc2ddb15287f89e7ea35f3eb849afebbd9"}, - {file = "ujson-5.4.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8cd6117e33233f2de6bc896eea6a5a59b58a37db08f371157264e0ec5e51c76a"}, - {file = "ujson-5.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:31bdb6d771d5ef6d37134b42211500bfe176c55d399f3317e569783dc42ed38e"}, - {file = "ujson-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:baa76a6f707a6d22437fe9c7ec9719672fb04d4d9435a3e80ee9b1aaeb2089d9"}, - {file = "ujson-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ee29cf5cfc1e841708297633e1ce749aa851fb96830bbe51f2e5940741ff2441"}, - {file = "ujson-5.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e12272361e9722777c83b3f5b0bb91d402531f36e80c6e5fafb6acb89e897e3"}, - {file = "ujson-5.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3212847d3885bfd4f5fd56cdc37645a8f8e8a80d6cb569505da22fd9eb0e1a02"}, - {file = "ujson-5.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcde3135265ecdd5714a7de4fdc167925390d7b17ca325e59980f4114c962b8"}, - {file = "ujson-5.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0551c1ba0bc9e05b69d9c18266dbc93252b5fa3cd9940051bc88a0dd33607b19"}, - {file = "ujson-5.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13297a7d501f9c8c53e409d4fa57cc574e4fbfbe8807ef2c4c7ce2e3ec933a85"}, - {file = "ujson-5.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2974b17bc522ef86d98b498959d82f03c02e07d9eb08746026415298f4a4bca3"}, - {file = "ujson-5.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5df8b6369ee5ee2685fcc917f6c46b34e599c6e9a512fada6dfd752b909fa06a"}, - {file = "ujson-5.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:754f422aba8db8201a1073f25e2f732effc6471f8755708b16e6ebf19dd23634"}, - {file = "ujson-5.4.0-cp38-cp38-win32.whl", hash = "sha256:ea7fbc540bc04d5b05e5cd54e60ee8745ac665eedf2bad2ba9d12d5c7a7b7d2e"}, - {file = "ujson-5.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:8d472efa9c92e1b2933a22d2f1dbd5237087997136b24ac2b913bf4e8be03135"}, - {file = "ujson-5.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e2a9ddb5c6d1427056b8d62a1a172a18ae522b14d9ba5996b8281b09cba87edd"}, - {file = "ujson-5.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1120c8263f7d85e89533a2b46d80cc6def15114772010ede4d197739e111dba6"}, - {file = "ujson-5.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:400e4ca8a59f71398e8fa56c4d2d6f535e2a121ddb57284ec15752ffce2dd63a"}, - {file = "ujson-5.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e844be0831042aa91e847e5ab03bddd1089ab1a8dd0a1bf90411abf864f058b2"}, - {file = "ujson-5.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b46aee21e5d75426c4058dfdb42f7e7b1d130c664ee5027a8dbbc50872dc32b"}, - {file = "ujson-5.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:326a96324ed9215b0bc9f1a5af324fb33900b6b0901516bcc421475d6596de0d"}, - {file = "ujson-5.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fd82932aaa224abd7d01e823b77aef9970f5ac1695027331d99e7f5fda9d37f5"}, - {file = "ujson-5.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cce79ce47c37132373fbdf55b683883c262a3a60763130e080b8394c1201d32"}, - {file = "ujson-5.4.0-cp39-cp39-win32.whl", hash = "sha256:191f88d5865740497b9827ef9b7c12f37a79872ac984e09f0901a10024019380"}, - {file = "ujson-5.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:68c7f753aec490c6566fd3cd301887c413ac3a588316e446f30a4134ac665668"}, - {file = "ujson-5.4.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a2e645325f844f9c890c9d956fc2d35ca91f38c857278238ef6516c2f99cf7c"}, - {file = "ujson-5.4.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cec010d318a0238b1333ea9f40d5603d374cc026c29c4471e2661712c6682da1"}, - {file = "ujson-5.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b40a3757a563ef77c3f2f9ea1732c2924e8b3b2bda3fa89513f949472ad40b6e"}, - {file = "ujson-5.4.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f4e2fa81e1d99c01e7b1978ab0cbf3c9a8b663f683a709f87baad110d5b940"}, - {file = "ujson-5.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9ae1d0094ce730e39e09656bc14074d9573cdd80adec1a55b06d8bf1f9613a01"}, - {file = "ujson-5.4.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:784dbd12925845a3f0757a956447e2fd31418abb5aeaebf3aca1203195f16fd1"}, - {file = "ujson-5.4.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422653083c6df6cec17fdb5d6106c209aad9b0c94131c53b073980403db22167"}, - {file = "ujson-5.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e91947fda8354ea7faf698b084ebcdbabd239e7b15d8436fb74394f59a207ac9"}, - {file = "ujson-5.4.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef985eb2770900a485431910bd3f333b56d1a34b65f8c26a6ed8e8adf55f98d9"}, - {file = "ujson-5.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:025758cf6561af6986d77cd4af9367ab56dde5c7c50f13f59e6964b4b25df73e"}, - {file = "ujson-5.4.0.tar.gz", hash = "sha256:6b953e09441e307504130755e5bd6b15850178d591f66292bba4608c4f7f9b00"}, + {file = "ujson-5.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5eba5e69e4361ac3a311cf44fa71bc619361b6e0626768a494771aacd1c2f09b"}, + {file = "ujson-5.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aae4d9e1b4c7b61780f0a006c897a4a1904f862fdab1abb3ea8f45bd11aa58f3"}, + {file = "ujson-5.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2e43ccdba1cb5c6d3448eadf6fc0dae7be6c77e357a3abc968d1b44e265866d"}, + {file = "ujson-5.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54384ce4920a6d35fa9ea8e580bc6d359e3eb961fa7e43f46c78e3ed162d56ff"}, + {file = "ujson-5.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24ad1aa7fc4e4caa41d3d343512ce68e41411fb92adf7f434a4d4b3749dc8f58"}, + {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:afff311e9f065a8f03c3753db7011bae7beb73a66189c7ea5fcb0456b7041ea4"}, + {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e80f0d03e7e8646fc3d79ed2d875cebd4c83846e129737fdc4c2532dbd43d9e"}, + {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:137831d8a0db302fb6828ee21c67ad63ac537bddc4376e1aab1c8573756ee21c"}, + {file = "ujson-5.7.0-cp310-cp310-win32.whl", hash = "sha256:7df3fd35ebc14dafeea031038a99232b32f53fa4c3ecddb8bed132a43eefb8ad"}, + {file = "ujson-5.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:af4639f684f425177d09ae409c07602c4096a6287027469157bfb6f83e01448b"}, + {file = "ujson-5.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b0f2680ce8a70f77f5d70aaf3f013d53e6af6d7058727a35d8ceb4a71cdd4e9"}, + {file = "ujson-5.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a19fd8e7d8cc58a169bea99fed5666023adf707a536d8f7b0a3c51dd498abf"}, + {file = "ujson-5.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6abb8e6d8f1ae72f0ed18287245f5b6d40094e2656d1eab6d99d666361514074"}, + {file = "ujson-5.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8cd622c069368d5074bd93817b31bdb02f8d818e57c29e206f10a1f9c6337dd"}, + {file = "ujson-5.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14f9082669f90e18e64792b3fd0bf19f2b15e7fe467534a35ea4b53f3bf4b755"}, + {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7ff6ebb43bc81b057724e89550b13c9a30eda0f29c2f506f8b009895438f5a6"}, + {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f7f241488879d91a136b299e0c4ce091996c684a53775e63bb442d1a8e9ae22a"}, + {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5593263a7fcfb934107444bcfba9dde8145b282de0ee9f61e285e59a916dda0f"}, + {file = "ujson-5.7.0-cp311-cp311-win32.whl", hash = "sha256:26c2b32b489c393106e9cb68d0a02e1a7b9d05a07429d875c46b94ee8405bdb7"}, + {file = "ujson-5.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed24406454bb5a31df18f0a423ae14beb27b28cdfa34f6268e7ebddf23da807e"}, + {file = "ujson-5.7.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18679484e3bf9926342b1c43a3bd640f93a9eeeba19ef3d21993af7b0c44785d"}, + {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee295761e1c6c30400641f0a20d381633d7622633cdf83a194f3c876a0e4b7e"}, + {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b738282e12a05f400b291966630a98d622da0938caa4bc93cf65adb5f4281c60"}, + {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00343501dbaa5172e78ef0e37f9ebd08040110e11c12420ff7c1f9f0332d939e"}, + {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c0d1f7c3908357ee100aa64c4d1cf91edf99c40ac0069422a4fd5fd23b263263"}, + {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a5d2f44331cf04689eafac7a6596c71d6657967c07ac700b0ae1c921178645da"}, + {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:16b2254a77b310f118717715259a196662baa6b1f63b1a642d12ab1ff998c3d7"}, + {file = "ujson-5.7.0-cp37-cp37m-win32.whl", hash = "sha256:6faf46fa100b2b89e4db47206cf8a1ffb41542cdd34dde615b2fc2288954f194"}, + {file = "ujson-5.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ff0004c3f5a9a6574689a553d1b7819d1a496b4f005a7451f339dc2d9f4cf98c"}, + {file = "ujson-5.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:75204a1dd7ec6158c8db85a2f14a68d2143503f4bafb9a00b63fe09d35762a5e"}, + {file = "ujson-5.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7312731c7826e6c99cdd3ac503cd9acd300598e7a80bcf41f604fee5f49f566c"}, + {file = "ujson-5.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b9dc5a90e2149643df7f23634fe202fed5ebc787a2a1be95cf23632b4d90651"}, + {file = "ujson-5.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6a6961fc48821d84b1198a09516e396d56551e910d489692126e90bf4887d29"}, + {file = "ujson-5.7.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b01a9af52a0d5c46b2c68e3f258fdef2eacaa0ce6ae3e9eb97983f5b1166edb6"}, + {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7316d3edeba8a403686cdcad4af737b8415493101e7462a70ff73dd0609eafc"}, + {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ee997799a23227e2319a3f8817ce0b058923dbd31904761b788dc8f53bd3e30"}, + {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dda9aa4c33435147262cd2ea87c6b7a1ca83ba9b3933ff7df34e69fee9fced0c"}, + {file = "ujson-5.7.0-cp38-cp38-win32.whl", hash = "sha256:bea8d30e362180aafecabbdcbe0e1f0b32c9fa9e39c38e4af037b9d3ca36f50c"}, + {file = "ujson-5.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:c96e3b872bf883090ddf32cc41957edf819c5336ab0007d0cf3854e61841726d"}, + {file = "ujson-5.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6411aea4c94a8e93c2baac096fbf697af35ba2b2ed410b8b360b3c0957a952d3"}, + {file = "ujson-5.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d3b3499c55911f70d4e074c626acdb79a56f54262c3c83325ffb210fb03e44d"}, + {file = "ujson-5.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:341f891d45dd3814d31764626c55d7ab3fd21af61fbc99d070e9c10c1190680b"}, + {file = "ujson-5.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f242eec917bafdc3f73a1021617db85f9958df80f267db69c76d766058f7b19"}, + {file = "ujson-5.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3af9f9f22a67a8c9466a32115d9073c72a33ae627b11de6f592df0ee09b98b6"}, + {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a3d794afbf134df3056a813e5c8a935208cddeae975bd4bc0ef7e89c52f0ce0"}, + {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:800bf998e78dae655008dd10b22ca8dc93bdcfcc82f620d754a411592da4bbf2"}, + {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b5ac3d5c5825e30b438ea92845380e812a476d6c2a1872b76026f2e9d8060fc2"}, + {file = "ujson-5.7.0-cp39-cp39-win32.whl", hash = "sha256:cd90027e6d93e8982f7d0d23acf88c896d18deff1903dd96140613389b25c0dd"}, + {file = "ujson-5.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:523ee146cdb2122bbd827f4dcc2a8e66607b3f665186bce9e4f78c9710b6d8ab"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e87cec407ec004cf1b04c0ed7219a68c12860123dfb8902ef880d3d87a71c172"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bab10165db6a7994e67001733f7f2caf3400b3e11538409d8756bc9b1c64f7e8"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b522be14a28e6ac1cf818599aeff1004a28b42df4ed4d7bc819887b9dac915fc"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7592f40175c723c032cdbe9fe5165b3b5903604f774ab0849363386e99e1f253"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ed22f9665327a981f288a4f758a432824dc0314e4195a0eaeb0da56a477da94d"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:adf445a49d9a97a5a4c9bb1d652a1528de09dd1c48b29f79f3d66cea9f826bf6"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64772a53f3c4b6122ed930ae145184ebaed38534c60f3d859d8c3f00911eb122"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35209cb2c13fcb9d76d249286105b4897b75a5e7f0efb0c0f4b90f222ce48910"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90712dfc775b2c7a07d4d8e059dd58636bd6ff1776d79857776152e693bddea6"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0e4e8981c6e7e9e637e637ad8ffe948a09e5434bc5f52ecbb82b4b4cfc092bfb"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:581c945b811a3d67c27566539bfcb9705ea09cb27c4be0002f7a553c8886b817"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d36a807a24c7d44f71686685ae6fbc8793d784bca1adf4c89f5f780b835b6243"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b4257307e3662aa65e2644a277ca68783c5d51190ed9c49efebdd3cbfd5fa44"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea7423d8a2f9e160c5e011119741682414c5b8dce4ae56590a966316a07a4618"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c592eb91a5968058a561d358d0fef59099ed152cfb3e1cd14eee51a7a93879e"}, + {file = "ujson-5.7.0.tar.gz", hash = "sha256:e788e5d5dcae8f6118ac9b45d0b891a0d55f7ac480eddcb7f07263f2bcf37b23"}, ] [[package]] @@ -2752,14 +2825,14 @@ files = [ [[package]] name = "urllib3" -version = "1.26.12" +version = "1.26.14" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, - {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, + {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, + {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, ] [package.extras] @@ -2769,97 +2842,126 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "uvicorn" -version = "0.17.5" +version = "0.20.0" description = "The lightning-fast ASGI server." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "uvicorn-0.17.5-py3-none-any.whl", hash = "sha256:8adddf629b79857b48b999ae1b14d6c92c95d4d7840bd86461f09bee75f1653e"}, - {file = "uvicorn-0.17.5.tar.gz", hash = "sha256:c04a9c069111489c324f427501b3840d306c6b91a77b00affc136a840a3f45f1"}, + {file = "uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"}, + {file = "uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"}, ] [package.dependencies] -asgiref = ">=3.4.0" click = ">=7.0" colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} h11 = ">=0.8" -httptools = {version = ">=0.2.0,<0.4.0", optional = true, markers = "extra == \"standard\""} +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -PyYAML = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} -watchgod = {version = ">=0.6", optional = true, markers = "extra == \"standard\""} -websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} [package.extras] -standard = ["PyYAML (>=5.1)", "colorama (>=0.4)", "httptools (>=0.2.0,<0.4.0)", "python-dotenv (>=0.13)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchgod (>=0.6)", "websockets (>=10.0)"] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] [[package]] name = "uvloop" -version = "0.16.0" +version = "0.17.0" description = "Fast implementation of asyncio event loop on top of libuv" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"}, - {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c"}, - {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64"}, - {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9"}, - {file = "uvloop-0.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638"}, - {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450"}, - {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805"}, - {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382"}, - {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee"}, - {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464"}, - {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab"}, - {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f"}, - {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897"}, - {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f"}, - {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861"}, - {file = "uvloop-0.16.0.tar.gz", hash = "sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228"}, -] - -[package.extras] -dev = ["Cython (>=0.29.24,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] + {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce9f61938d7155f79d3cb2ffa663147d4a76d16e08f65e2c66b77bd41b356718"}, + {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:68532f4349fd3900b839f588972b3392ee56042e440dd5873dfbbcd2cc67617c"}, + {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0949caf774b9fcefc7c5756bacbbbd3fc4c05a6b7eebc7c7ad6f825b23998d6d"}, + {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff3d00b70ce95adce264462c930fbaecb29718ba6563db354608f37e49e09024"}, + {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a5abddb3558d3f0a78949c750644a67be31e47936042d4f6c888dd6f3c95f4aa"}, + {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8efcadc5a0003d3a6e887ccc1fb44dec25594f117a94e3127954c05cf144d811"}, + {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3378eb62c63bf336ae2070599e49089005771cc651c8769aaad72d1bd9385a7c"}, + {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6aafa5a78b9e62493539456f8b646f85abc7093dd997f4976bb105537cf2635e"}, + {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c686a47d57ca910a2572fddfe9912819880b8765e2f01dc0dd12a9bf8573e539"}, + {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:864e1197139d651a76c81757db5eb199db8866e13acb0dfe96e6fc5d1cf45fc4"}, + {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2a6149e1defac0faf505406259561bc14b034cdf1d4711a3ddcdfbaa8d825a05"}, + {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6708f30db9117f115eadc4f125c2a10c1a50d711461699a0cbfaa45b9a78e376"}, + {file = "uvloop-0.17.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:23609ca361a7fc587031429fa25ad2ed7242941adec948f9d10c045bfecab06b"}, + {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2deae0b0fb00a6af41fe60a675cec079615b01d68beb4cc7b722424406b126a8"}, + {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45cea33b208971e87a31c17622e4b440cac231766ec11e5d22c76fab3bf9df62"}, + {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9b09e0f0ac29eee0451d71798878eae5a4e6a91aa275e114037b27f7db72702d"}, + {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dbbaf9da2ee98ee2531e0c780455f2841e4675ff580ecf93fe5c48fe733b5667"}, + {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a4aee22ece20958888eedbad20e4dbb03c37533e010fb824161b4f05e641f738"}, + {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:307958f9fc5c8bb01fad752d1345168c0abc5d62c1b72a4a8c6c06f042b45b20"}, + {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ebeeec6a6641d0adb2ea71dcfb76017602ee2bfd8213e3fcc18d8f699c5104f"}, + {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1436c8673c1563422213ac6907789ecb2b070f5939b9cbff9ef7113f2b531595"}, + {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8887d675a64cfc59f4ecd34382e5b4f0ef4ae1da37ed665adba0c2badf0d6578"}, + {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3db8de10ed684995a7f34a001f15b374c230f7655ae840964d51496e2f8a8474"}, + {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7d37dccc7ae63e61f7b96ee2e19c40f153ba6ce730d8ba4d3b4e9738c1dccc1b"}, + {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cbbe908fda687e39afd6ea2a2f14c2c3e43f2ca88e3a11964b297822358d0e6c"}, + {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d97672dc709fa4447ab83276f344a165075fd9f366a97b712bdd3fee05efae8"}, + {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e507c9ee39c61bfddd79714e4f85900656db1aec4d40c6de55648e85c2799c"}, + {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c092a2c1e736086d59ac8e41f9c98f26bbf9b9222a76f21af9dfe949b99b2eb9"}, + {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30babd84706115626ea78ea5dbc7dd8d0d01a2e9f9b306d24ca4ed5796c66ded"}, + {file = "uvloop-0.17.0.tar.gz", hash = "sha256:0ddf6baf9cf11a1a22c71487f39f15b2cf78eb5bde7e5b45fbb99e8a9d91b9e1"}, +] + +[package.extras] +dev = ["Cython (>=0.29.32,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"] +test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"] [[package]] name = "virtualenv" -version = "20.16.5" +version = "20.17.1" description = "Virtual Python Environment builder" category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "virtualenv-20.16.5-py3-none-any.whl", hash = "sha256:d07dfc5df5e4e0dbc92862350ad87a36ed505b978f6c39609dc489eadd5b0d27"}, - {file = "virtualenv-20.16.5.tar.gz", hash = "sha256:227ea1b9994fdc5ea31977ba3383ef296d7472ea85be9d6732e42a91c04e80da"}, + {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, + {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, ] [package.dependencies] -distlib = ">=0.3.5,<1" +distlib = ">=0.3.6,<1" filelock = ">=3.4.1,<4" platformdirs = ">=2.4,<3" [package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] +docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] [[package]] -name = "watchgod" -version = "0.8.2" -description = "Simple, modern file watching and code reload in python." +name = "watchfiles" +version = "0.18.1" +description = "Simple, modern and high performance file watching and code reload in python." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "watchgod-0.8.2-py3-none-any.whl", hash = "sha256:2f3e8137d98f493ff58af54ea00f4d1433a6afe2ed08ab331a657df468c6bfce"}, - {file = "watchgod-0.8.2.tar.gz", hash = "sha256:cb11ff66657befba94d828e3b622d5fb76f22fbda1376f355f3e6e51e97d9450"}, + {file = "watchfiles-0.18.1-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:9891d3c94272108bcecf5597a592e61105279def1313521e637f2d5acbe08bc9"}, + {file = "watchfiles-0.18.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:7102342d60207fa635e24c02a51c6628bf0472e5fef067f78a612386840407fc"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:00ea0081eca5e8e695cffbc3a726bb90da77f4e3f78ce29b86f0d95db4e70ef7"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8e6db99e49cd7125d8a4c9d33c0735eea7b75a942c6ad68b75be3e91c242fb"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc7c726855f04f22ac79131b51bf0c9f728cb2117419ed830a43828b2c4a5fcb"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbaff354d12235002e62d9d3fa8bcf326a8490c1179aa5c17195a300a9e5952f"}, + {file = "watchfiles-0.18.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:888db233e06907c555eccd10da99b9cd5ed45deca47e41766954292dc9f7b198"}, + {file = "watchfiles-0.18.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:dde79930d1b28f15994ad6613aa2865fc7a403d2bb14585a8714a53233b15717"}, + {file = "watchfiles-0.18.1-cp37-abi3-win32.whl", hash = "sha256:e2b2bdd26bf8d6ed90763e6020b475f7634f919dbd1730ea1b6f8cb88e21de5d"}, + {file = "watchfiles-0.18.1-cp37-abi3-win_amd64.whl", hash = "sha256:c541e0f2c3e95e83e4f84561c893284ba984e9d0025352057396d96dceb09f44"}, + {file = "watchfiles-0.18.1-cp37-abi3-win_arm64.whl", hash = "sha256:9a26272ef3e930330fc0c2c148cc29706cc2c40d25760c7ccea8d768a8feef8b"}, + {file = "watchfiles-0.18.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:9fb12a5e2b42e0b53769455ff93546e6bc9ab14007fbd436978d827a95ca5bd1"}, + {file = "watchfiles-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:548d6b42303d40264118178053c78820533b683b20dfbb254a8706ca48467357"}, + {file = "watchfiles-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0d8fdfebc50ac7569358f5c75f2b98bb473befccf9498cf23b3e39993bb45a"}, + {file = "watchfiles-0.18.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0f9a22fff1745e2bb930b1e971c4c5b67ea3b38ae17a6adb9019371f80961219"}, + {file = "watchfiles-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b02e7fa03cd4059dd61ff0600080a5a9e7a893a85cb8e5178943533656eec65e"}, + {file = "watchfiles-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a868ce2c7565137f852bd4c863a164dc81306cae7378dbdbe4e2aca51ddb8857"}, + {file = "watchfiles-0.18.1.tar.gz", hash = "sha256:4ec0134a5e31797eb3c6c624dbe9354f2a8ee9c720e0b46fc5b7bab472b7c6d4"}, ] [package.dependencies] -anyio = ">=3.0.0,<4" +anyio = ">=3.0.0" [[package]] name = "websocket-client" @@ -2880,60 +2982,81 @@ test = ["websockets"] [[package]] name = "websockets" -version = "10.3" +version = "10.4" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "websockets-10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978"}, - {file = "websockets-10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500"}, - {file = "websockets-10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b"}, - {file = "websockets-10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c"}, - {file = "websockets-10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8"}, - {file = "websockets-10.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677"}, - {file = "websockets-10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e"}, - {file = "websockets-10.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f"}, - {file = "websockets-10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47"}, - {file = "websockets-10.3-cp310-cp310-win32.whl", hash = "sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae"}, - {file = "websockets-10.3-cp310-cp310-win_amd64.whl", hash = "sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079"}, - {file = "websockets-10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916"}, - {file = "websockets-10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb"}, - {file = "websockets-10.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79"}, - {file = "websockets-10.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d"}, - {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98"}, - {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e"}, - {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6"}, - {file = "websockets-10.3-cp37-cp37m-win32.whl", hash = "sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1"}, - {file = "websockets-10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4"}, - {file = "websockets-10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36"}, - {file = "websockets-10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69"}, - {file = "websockets-10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd"}, - {file = "websockets-10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2"}, - {file = "websockets-10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c"}, - {file = "websockets-10.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e"}, - {file = "websockets-10.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991"}, - {file = "websockets-10.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442"}, - {file = "websockets-10.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76"}, - {file = "websockets-10.3-cp38-cp38-win32.whl", hash = "sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559"}, - {file = "websockets-10.3-cp38-cp38-win_amd64.whl", hash = "sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d"}, - {file = "websockets-10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094"}, - {file = "websockets-10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667"}, - {file = "websockets-10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731"}, - {file = "websockets-10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9"}, - {file = "websockets-10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680"}, - {file = "websockets-10.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247"}, - {file = "websockets-10.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af"}, - {file = "websockets-10.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3"}, - {file = "websockets-10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8"}, - {file = "websockets-10.3-cp39-cp39-win32.whl", hash = "sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582"}, - {file = "websockets-10.3-cp39-cp39-win_amd64.whl", hash = "sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02"}, - {file = "websockets-10.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7"}, - {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f"}, - {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4"}, - {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755"}, - {file = "websockets-10.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55"}, - {file = "websockets-10.3.tar.gz", hash = "sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4"}, + {file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"}, + {file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"}, + {file = "websockets-10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331"}, + {file = "websockets-10.4-cp310-cp310-win32.whl", hash = "sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a"}, + {file = "websockets-10.4-cp310-cp310-win_amd64.whl", hash = "sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f"}, + {file = "websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9"}, + {file = "websockets-10.4-cp311-cp311-win32.whl", hash = "sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8"}, + {file = "websockets-10.4-cp311-cp311-win_amd64.whl", hash = "sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882"}, + {file = "websockets-10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c"}, + {file = "websockets-10.4-cp37-cp37m-win32.whl", hash = "sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038"}, + {file = "websockets-10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63"}, + {file = "websockets-10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56"}, + {file = "websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"}, + {file = "websockets-10.4-cp38-cp38-win_amd64.whl", hash = "sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112"}, + {file = "websockets-10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b"}, + {file = "websockets-10.4-cp39-cp39-win32.whl", hash = "sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588"}, + {file = "websockets-10.4-cp39-cp39-win_amd64.whl", hash = "sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74"}, + {file = "websockets-10.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485"}, + {file = "websockets-10.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631"}, + {file = "websockets-10.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f"}, + {file = "websockets-10.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1"}, + {file = "websockets-10.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13"}, + {file = "websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"}, + {file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"}, ] [[package]] @@ -3045,71 +3168,86 @@ files = [ [[package]] name = "yarl" -version = "1.8.1" +version = "1.8.2" description = "Yet another URL library" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28"}, - {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3"}, - {file = "yarl-1.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9de955d98e02fab288c7718662afb33aab64212ecb368c5dc866d9a57bf48880"}, - {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ec362167e2c9fd178f82f252b6d97669d7245695dc057ee182118042026da40"}, - {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20df6ff4089bc86e4a66e3b1380460f864df3dd9dccaf88d6b3385d24405893b"}, - {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5999c4662631cb798496535afbd837a102859568adc67d75d2045e31ec3ac497"}, - {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed19b74e81b10b592084a5ad1e70f845f0aacb57577018d31de064e71ffa267a"}, - {file = "yarl-1.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e4808f996ca39a6463f45182e2af2fae55e2560be586d447ce8016f389f626f"}, - {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2d800b9c2eaf0684c08be5f50e52bfa2aa920e7163c2ea43f4f431e829b4f0fd"}, - {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6628d750041550c5d9da50bb40b5cf28a2e63b9388bac10fedd4f19236ef4957"}, - {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f5af52738e225fcc526ae64071b7e5342abe03f42e0e8918227b38c9aa711e28"}, - {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:76577f13333b4fe345c3704811ac7509b31499132ff0181f25ee26619de2c843"}, - {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c03f456522d1ec815893d85fccb5def01ffaa74c1b16ff30f8aaa03eb21e453"}, - {file = "yarl-1.8.1-cp310-cp310-win32.whl", hash = "sha256:ea30a42dc94d42f2ba4d0f7c0ffb4f4f9baa1b23045910c0c32df9c9902cb272"}, - {file = "yarl-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:9130ddf1ae9978abe63808b6b60a897e41fccb834408cde79522feb37fb72fb0"}, - {file = "yarl-1.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0ab5a138211c1c366404d912824bdcf5545ccba5b3ff52c42c4af4cbdc2c5035"}, - {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0fb2cb4204ddb456a8e32381f9a90000429489a25f64e817e6ff94879d432fc"}, - {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85cba594433915d5c9a0d14b24cfba0339f57a2fff203a5d4fd070e593307d0b"}, - {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ca7e596c55bd675432b11320b4eacc62310c2145d6801a1f8e9ad160685a231"}, - {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f77539733e0ec2475ddcd4e26777d08996f8cd55d2aef82ec4d3896687abda"}, - {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29e256649f42771829974e742061c3501cc50cf16e63f91ed8d1bf98242e5507"}, - {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7fce6cbc6c170ede0221cc8c91b285f7f3c8b9fe28283b51885ff621bbe0f8ee"}, - {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:59ddd85a1214862ce7c7c66457f05543b6a275b70a65de366030d56159a979f0"}, - {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:12768232751689c1a89b0376a96a32bc7633c08da45ad985d0c49ede691f5c0d"}, - {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:b19255dde4b4f4c32e012038f2c169bb72e7f081552bea4641cab4d88bc409dd"}, - {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6c8148e0b52bf9535c40c48faebb00cb294ee577ca069d21bd5c48d302a83780"}, - {file = "yarl-1.8.1-cp37-cp37m-win32.whl", hash = "sha256:de839c3a1826a909fdbfe05f6fe2167c4ab033f1133757b5936efe2f84904c07"}, - {file = "yarl-1.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:dd032e8422a52e5a4860e062eb84ac94ea08861d334a4bcaf142a63ce8ad4802"}, - {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:19cd801d6f983918a3f3a39f3a45b553c015c5aac92ccd1fac619bd74beece4a"}, - {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6347f1a58e658b97b0a0d1ff7658a03cb79bdbda0331603bed24dd7054a6dea1"}, - {file = "yarl-1.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c0da7e44d0c9108d8b98469338705e07f4bb7dab96dbd8fa4e91b337db42548"}, - {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5587bba41399854703212b87071c6d8638fa6e61656385875f8c6dff92b2e461"}, - {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31a9a04ecccd6b03e2b0e12e82131f1488dea5555a13a4d32f064e22a6003cfe"}, - {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205904cffd69ae972a1707a1bd3ea7cded594b1d773a0ce66714edf17833cdae"}, - {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea513a25976d21733bff523e0ca836ef1679630ef4ad22d46987d04b372d57fc"}, - {file = "yarl-1.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0b51530877d3ad7a8d47b2fff0c8df3b8f3b8deddf057379ba50b13df2a5eae"}, - {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d2b8f245dad9e331540c350285910b20dd913dc86d4ee410c11d48523c4fd546"}, - {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ab2a60d57ca88e1d4ca34a10e9fb4ab2ac5ad315543351de3a612bbb0560bead"}, - {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:449c957ffc6bc2309e1fbe67ab7d2c1efca89d3f4912baeb8ead207bb3cc1cd4"}, - {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a165442348c211b5dea67c0206fc61366212d7082ba8118c8c5c1c853ea4d82e"}, - {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b3ded839a5c5608eec8b6f9ae9a62cb22cd037ea97c627f38ae0841a48f09eae"}, - {file = "yarl-1.8.1-cp38-cp38-win32.whl", hash = "sha256:c1445a0c562ed561d06d8cbc5c8916c6008a31c60bc3655cdd2de1d3bf5174a0"}, - {file = "yarl-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:56c11efb0a89700987d05597b08a1efcd78d74c52febe530126785e1b1a285f4"}, - {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e80ed5a9939ceb6fda42811542f31c8602be336b1fb977bccb012e83da7e4936"}, - {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6afb336e23a793cd3b6476c30f030a0d4c7539cd81649683b5e0c1b0ab0bf350"}, - {file = "yarl-1.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c322cbaa4ed78a8aac89b2174a6df398faf50e5fc12c4c191c40c59d5e28357"}, - {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fae37373155f5ef9b403ab48af5136ae9851151f7aacd9926251ab26b953118b"}, - {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5395da939ffa959974577eff2cbfc24b004a2fb6c346918f39966a5786874e54"}, - {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:076eede537ab978b605f41db79a56cad2e7efeea2aa6e0fa8f05a26c24a034fb"}, - {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d1a50e461615747dd93c099f297c1994d472b0f4d2db8a64e55b1edf704ec1c"}, - {file = "yarl-1.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7de89c8456525650ffa2bb56a3eee6af891e98f498babd43ae307bd42dca98f6"}, - {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a88510731cd8d4befaba5fbd734a7dd914de5ab8132a5b3dde0bbd6c9476c64"}, - {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2d93a049d29df172f48bcb09acf9226318e712ce67374f893b460b42cc1380ae"}, - {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:21ac44b763e0eec15746a3d440f5e09ad2ecc8b5f6dcd3ea8cb4773d6d4703e3"}, - {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d0272228fabe78ce00a3365ffffd6f643f57a91043e119c289aaba202f4095b0"}, - {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99449cd5366fe4608e7226c6cae80873296dfa0cde45d9b498fefa1de315a09e"}, - {file = "yarl-1.8.1-cp39-cp39-win32.whl", hash = "sha256:8b0af1cf36b93cee99a31a545fe91d08223e64390c5ecc5e94c39511832a4bb6"}, - {file = "yarl-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:de49d77e968de6626ba7ef4472323f9d2e5a56c1d85b7c0e2a190b2173d3b9be"}, - {file = "yarl-1.8.1.tar.gz", hash = "sha256:af887845b8c2e060eb5605ff72b6f2dd2aab7a761379373fd89d314f4752abbf"}, + {file = "yarl-1.8.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bb81f753c815f6b8e2ddd2eef3c855cf7da193b82396ac013c661aaa6cc6b0a5"}, + {file = "yarl-1.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:47d49ac96156f0928f002e2424299b2c91d9db73e08c4cd6742923a086f1c863"}, + {file = "yarl-1.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3fc056e35fa6fba63248d93ff6e672c096f95f7836938241ebc8260e062832fe"}, + {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58a3c13d1c3005dbbac5c9f0d3210b60220a65a999b1833aa46bd6677c69b08e"}, + {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10b08293cda921157f1e7c2790999d903b3fd28cd5c208cf8826b3b508026996"}, + {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de986979bbd87272fe557e0a8fcb66fd40ae2ddfe28a8b1ce4eae22681728fef"}, + {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c4fcfa71e2c6a3cb568cf81aadc12768b9995323186a10827beccf5fa23d4f8"}, + {file = "yarl-1.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae4d7ff1049f36accde9e1ef7301912a751e5bae0a9d142459646114c70ecba6"}, + {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf071f797aec5b96abfc735ab97da9fd8f8768b43ce2abd85356a3127909d146"}, + {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:74dece2bfc60f0f70907c34b857ee98f2c6dd0f75185db133770cd67300d505f"}, + {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:df60a94d332158b444301c7f569659c926168e4d4aad2cfbf4bce0e8fb8be826"}, + {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:63243b21c6e28ec2375f932a10ce7eda65139b5b854c0f6b82ed945ba526bff3"}, + {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cfa2bbca929aa742b5084fd4663dd4b87c191c844326fcb21c3afd2d11497f80"}, + {file = "yarl-1.8.2-cp310-cp310-win32.whl", hash = "sha256:b05df9ea7496df11b710081bd90ecc3a3db6adb4fee36f6a411e7bc91a18aa42"}, + {file = "yarl-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:24ad1d10c9db1953291f56b5fe76203977f1ed05f82d09ec97acb623a7976574"}, + {file = "yarl-1.8.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2a1fca9588f360036242f379bfea2b8b44cae2721859b1c56d033adfd5893634"}, + {file = "yarl-1.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f37db05c6051eff17bc832914fe46869f8849de5b92dc4a3466cd63095d23dfd"}, + {file = "yarl-1.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77e913b846a6b9c5f767b14dc1e759e5aff05502fe73079f6f4176359d832581"}, + {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0978f29222e649c351b173da2b9b4665ad1feb8d1daa9d971eb90df08702668a"}, + {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388a45dc77198b2460eac0aca1efd6a7c09e976ee768b0d5109173e521a19daf"}, + {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2305517e332a862ef75be8fad3606ea10108662bc6fe08509d5ca99503ac2aee"}, + {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42430ff511571940d51e75cf42f1e4dbdded477e71c1b7a17f4da76c1da8ea76"}, + {file = "yarl-1.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3150078118f62371375e1e69b13b48288e44f6691c1069340081c3fd12c94d5b"}, + {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c15163b6125db87c8f53c98baa5e785782078fbd2dbeaa04c6141935eb6dab7a"}, + {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4d04acba75c72e6eb90745447d69f84e6c9056390f7a9724605ca9c56b4afcc6"}, + {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e7fd20d6576c10306dea2d6a5765f46f0ac5d6f53436217913e952d19237efc4"}, + {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:75c16b2a900b3536dfc7014905a128a2bea8fb01f9ee26d2d7d8db0a08e7cb2c"}, + {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6d88056a04860a98341a0cf53e950e3ac9f4e51d1b6f61a53b0609df342cc8b2"}, + {file = "yarl-1.8.2-cp311-cp311-win32.whl", hash = "sha256:fb742dcdd5eec9f26b61224c23baea46c9055cf16f62475e11b9b15dfd5c117b"}, + {file = "yarl-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8c46d3d89902c393a1d1e243ac847e0442d0196bbd81aecc94fcebbc2fd5857c"}, + {file = "yarl-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ceff9722e0df2e0a9e8a79c610842004fa54e5b309fe6d218e47cd52f791d7ef"}, + {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f6b4aca43b602ba0f1459de647af954769919c4714706be36af670a5f44c9c1"}, + {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1684a9bd9077e922300ecd48003ddae7a7474e0412bea38d4631443a91d61077"}, + {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ebb78745273e51b9832ef90c0898501006670d6e059f2cdb0e999494eb1450c2"}, + {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3adeef150d528ded2a8e734ebf9ae2e658f4c49bf413f5f157a470e17a4a2e89"}, + {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57a7c87927a468e5a1dc60c17caf9597161d66457a34273ab1760219953f7f4c"}, + {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:efff27bd8cbe1f9bd127e7894942ccc20c857aa8b5a0327874f30201e5ce83d0"}, + {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a783cd344113cb88c5ff7ca32f1f16532a6f2142185147822187913eb989f739"}, + {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:705227dccbe96ab02c7cb2c43e1228e2826e7ead880bb19ec94ef279e9555b5b"}, + {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:34c09b43bd538bf6c4b891ecce94b6fa4f1f10663a8d4ca589a079a5018f6ed7"}, + {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a48f4f7fea9a51098b02209d90297ac324241bf37ff6be6d2b0149ab2bd51b37"}, + {file = "yarl-1.8.2-cp37-cp37m-win32.whl", hash = "sha256:0414fd91ce0b763d4eadb4456795b307a71524dbacd015c657bb2a39db2eab89"}, + {file = "yarl-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d881d152ae0007809c2c02e22aa534e702f12071e6b285e90945aa3c376463c5"}, + {file = "yarl-1.8.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5df5e3d04101c1e5c3b1d69710b0574171cc02fddc4b23d1b2813e75f35a30b1"}, + {file = "yarl-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7a66c506ec67eb3159eea5096acd05f5e788ceec7b96087d30c7d2865a243918"}, + {file = "yarl-1.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2b4fa2606adf392051d990c3b3877d768771adc3faf2e117b9de7eb977741229"}, + {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e21fb44e1eff06dd6ef971d4bdc611807d6bd3691223d9c01a18cec3677939e"}, + {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93202666046d9edadfe9f2e7bf5e0782ea0d497b6d63da322e541665d65a044e"}, + {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc77086ce244453e074e445104f0ecb27530d6fd3a46698e33f6c38951d5a0f1"}, + {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dd68a92cab699a233641f5929a40f02a4ede8c009068ca8aa1fe87b8c20ae3"}, + {file = "yarl-1.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b372aad2b5f81db66ee7ec085cbad72c4da660d994e8e590c997e9b01e44901"}, + {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e6f3515aafe0209dd17fb9bdd3b4e892963370b3de781f53e1746a521fb39fc0"}, + {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dfef7350ee369197106805e193d420b75467b6cceac646ea5ed3049fcc950a05"}, + {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:728be34f70a190566d20aa13dc1f01dc44b6aa74580e10a3fb159691bc76909d"}, + {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ff205b58dc2929191f68162633d5e10e8044398d7a45265f90a0f1d51f85f72c"}, + {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baf211dcad448a87a0d9047dc8282d7de59473ade7d7fdf22150b1d23859f946"}, + {file = "yarl-1.8.2-cp38-cp38-win32.whl", hash = "sha256:272b4f1599f1b621bf2aabe4e5b54f39a933971f4e7c9aa311d6d7dc06965165"}, + {file = "yarl-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:326dd1d3caf910cd26a26ccbfb84c03b608ba32499b5d6eeb09252c920bcbe4f"}, + {file = "yarl-1.8.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f8ca8ad414c85bbc50f49c0a106f951613dfa5f948ab69c10ce9b128d368baf8"}, + {file = "yarl-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:418857f837347e8aaef682679f41e36c24250097f9e2f315d39bae3a99a34cbf"}, + {file = "yarl-1.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae0eec05ab49e91a78700761777f284c2df119376e391db42c38ab46fd662b77"}, + {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:009a028127e0a1755c38b03244c0bea9d5565630db9c4cf9572496e947137a87"}, + {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3edac5d74bb3209c418805bda77f973117836e1de7c000e9755e572c1f7850d0"}, + {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da65c3f263729e47351261351b8679c6429151ef9649bba08ef2528ff2c423b2"}, + {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef8fb25e52663a1c85d608f6dd72e19bd390e2ecaf29c17fb08f730226e3a08"}, + {file = "yarl-1.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcd7bb1e5c45274af9a1dd7494d3c52b2be5e6bd8d7e49c612705fd45420b12d"}, + {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44ceac0450e648de86da8e42674f9b7077d763ea80c8ceb9d1c3e41f0f0a9951"}, + {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:97209cc91189b48e7cfe777237c04af8e7cc51eb369004e061809bcdf4e55220"}, + {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:48dd18adcf98ea9cd721a25313aef49d70d413a999d7d89df44f469edfb38a06"}, + {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e59399dda559688461762800d7fb34d9e8a6a7444fd76ec33220a926c8be1516"}, + {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d617c241c8c3ad5c4e78a08429fa49e4b04bedfc507b34b4d8dceb83b4af3588"}, + {file = "yarl-1.8.2-cp39-cp39-win32.whl", hash = "sha256:cb6d48d80a41f68de41212f3dfd1a9d9898d7841c8f7ce6696cf2fd9cb57ef83"}, + {file = "yarl-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:6604711362f2dbf7160df21c416f81fac0de6dbcf0b5445a2ef25478ecc4c778"}, + {file = "yarl-1.8.2.tar.gz", hash = "sha256:49d43402c6e3013ad0978602bf6bf5328535c48d192304b91b97a3c6790b1562"}, ] [package.dependencies] @@ -3151,4 +3289,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "7a9538d73639f13b3fc625050cf9c99b19d642513b2500b5bf3af3e1e3642b7f" +content-hash = "43d7855f49fd8d140880dfe18ab6b6b5d35f145927ba277405c850e1c07765e1" diff --git a/pyproject.toml b/pyproject.toml index c0f3d16..197e673 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,14 +14,13 @@ packages = [{include = "bartender", from = "src"}] [tool.poetry.dependencies] python = "^3.9" fastapi = "^0.85.2" -uvicorn = { version = "^0.17.0", extras = ["standard"] } +uvicorn = { version = "^0.20.0", extras = ["standard"] } pydantic = {version = "^1.9.0", extras = ["dotenv"]} yarl = "^1.7.2" ujson = "^5.1.0" SQLAlchemy = {version = "^1.4", extras = ["mypy", "asyncio"]} alembic = "^1.7.7" asyncpg = {version = "^0.25.0", extras = ["sa"]} -httptools = "^0.3.0" python-multipart = "^0.0.5" aiofiles = "^0.8.0" fastapi-users = {extras = ["sqlalchemy", "oauth"], version = "^10.1.5"} diff --git a/src/bartender/schedulers/memory.py b/src/bartender/schedulers/memory.py index 28cd7a1..d7e7f55 100644 --- a/src/bartender/schedulers/memory.py +++ b/src/bartender/schedulers/memory.py @@ -99,6 +99,7 @@ class MemoryScheduler(AbstractScheduler): # * asking job state from instance that is not running that job # Should add warning to only use with workers=1 or reload=True # And add alternative redis scheduler using Celery, RQ or arq + # Or have single instance created in __main__.py def __init__(self, config: MemorySchedulerConfig): """In memory scheduler. diff --git a/src/bartender/web/api/monitoring/views.py b/src/bartender/web/api/monitoring/views.py index 1b36caf..6d0c5a8 100644 --- a/src/bartender/web/api/monitoring/views.py +++ b/src/bartender/web/api/monitoring/views.py @@ -10,3 +10,6 @@ def health_check() -> None: It returns 200 if the project is healthy. """ + # TODO check + # 1. Database connection is live + # 2. Schedulers and filesystems of job destinations are working. From 4d0ee0861aba3b8f7f7dab840194a43a555294ec Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Mon, 16 Jan 2023 13:17:28 +0100 Subject: [PATCH 27/36] Picker now in own module Use if TYPE_CHECKING to prevent circular imports --- src/bartender/picker.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bartender/picker.py b/src/bartender/picker.py index d946ded..f1d8111 100644 --- a/src/bartender/picker.py +++ b/src/bartender/picker.py @@ -8,7 +8,6 @@ DestinationPicker = Callable[[Path, str, "Context"], str] -# TODO move to own module, without causing a circular import def pick_first( job_dir: Path, application_name: str, From e5570da524a7dbf4c4f5c0e3fcd59b0dd455919e Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 27 Jan 2023 10:36:52 +0100 Subject: [PATCH 28/36] Improve picker docs --- src/bartender/picker.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bartender/picker.py b/src/bartender/picker.py index f1d8111..f6816ce 100644 --- a/src/bartender/picker.py +++ b/src/bartender/picker.py @@ -13,12 +13,12 @@ def pick_first( application_name: str, context: "Context", ) -> str: - """Always picks first destination to where a job should be submitted to. + """Always picks first available destination from context. :param job_dir: Location where job input files are located. :param application_name: Application name that should be run. :param context: Context with applications and destinations. - :return: Destination where job should be submitted to. + :return: Destination name. """ destination_names = list(context.destinations.keys()) return destination_names[0] @@ -36,7 +36,7 @@ def __call__( application_name: str, context: "Context", ) -> str: - """Always picks the next destination to where a job should be submitted to. + """Always picks the next destination. Takes list of destinations and each time it is called will pick the next destination in the destination list. @@ -45,7 +45,7 @@ def __call__( :param job_dir: Location where job input files are located. :param application_name: Application name that should be run. :param context: Context with applications and destinations. - :return: Destination where job should be submitted to. + :return: Destination name. """ destination_names = list(context.destinations.keys()) if self.last == "": From df7da14659408b5e8668e9110b3b526151349b73 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 27 Jan 2023 10:39:21 +0100 Subject: [PATCH 29/36] Simplify test names --- tests/schedulers/test_build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/schedulers/test_build.py b/tests/schedulers/test_build.py index a1a46ee..8611ae3 100644 --- a/tests/schedulers/test_build.py +++ b/tests/schedulers/test_build.py @@ -6,7 +6,7 @@ @pytest.mark.anyio -async def test_single_memory_scheduler() -> None: +async def test_memory_scheduler() -> None: try: config = MemorySchedulerConfig() @@ -20,7 +20,7 @@ async def test_single_memory_scheduler() -> None: @pytest.mark.anyio -async def test_single_localsimplist_slurm_scheduler() -> None: +async def test_slurm_scheduler() -> None: try: config = SlurmSchedulerConfig() From f13d4bb046ae687d5c0fb2a1377fb015534d6681 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 27 Jan 2023 10:59:32 +0100 Subject: [PATCH 30/36] Prefer BaseModel over pydantic.dataclass or dataclass.dataclass --- src/bartender/config.py | 15 ++++----------- src/bartender/context.py | 6 ++++-- src/bartender/destinations.py | 5 +---- src/bartender/filesystems/local.py | 6 +++--- src/bartender/filesystems/sftp.py | 5 ++--- src/bartender/schedulers/memory.py | 15 ++++----------- src/bartender/schedulers/slurm.py | 6 +++--- src/bartender/ssh_utils.py | 6 ++---- 8 files changed, 23 insertions(+), 41 deletions(-) diff --git a/src/bartender/config.py b/src/bartender/config.py index 0a17dc7..130c1e2 100644 --- a/src/bartender/config.py +++ b/src/bartender/config.py @@ -54,15 +54,10 @@ from pathlib import Path from string import Template from tempfile import gettempdir -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from dataclasses import dataclass -else: - from pydantic.dataclasses import dataclass # noqa: WPS440 +from typing import Any from fastapi import Request -from pydantic import Field, validator +from pydantic import BaseModel, Field, validator from pydantic.types import DirectoryPath from yaml import safe_load as load_yaml @@ -70,8 +65,7 @@ from bartender.schedulers.abstract import JobDescription -@dataclass -class ApplicatonConfiguration: +class ApplicatonConfiguration(BaseModel): """Command to run application. `$config` in command string will be replaced @@ -100,8 +94,7 @@ def description(self, job_dir: Path) -> JobDescription: return JobDescription(job_dir=job_dir, command=command) -@dataclass -class Config: +class Config(BaseModel): """Bartender configuration. The bartender.settings.Settings class is for FastAPI settings. diff --git a/src/bartender/context.py b/src/bartender/context.py index 89056ee..18ac635 100644 --- a/src/bartender/context.py +++ b/src/bartender/context.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass as pydataclass +from dataclasses import dataclass from pathlib import Path from fastapi import Request @@ -9,7 +9,9 @@ from bartender.picker import DestinationPicker, import_picker -@pydataclass +# TODO also use pydantic.BaseModel here, +# but mypy complains if BaseModel is used +@dataclass class Context: """Context for web service.""" diff --git a/src/bartender/destinations.py b/src/bartender/destinations.py index 08c1de1..d6d6fb9 100644 --- a/src/bartender/destinations.py +++ b/src/bartender/destinations.py @@ -1,5 +1,3 @@ -from dataclasses import dataclass - from pydantic import BaseModel, Field from bartender.filesystems.abstract import AbstractFileSystem @@ -39,8 +37,7 @@ def default_destinations() -> dict[str, DestinationConfig]: } -@dataclass -class Destination: +class Destination(BaseModel): """A destination is a combination of a scheduler and optional filesystem.""" scheduler: AbstractScheduler diff --git a/src/bartender/filesystems/local.py b/src/bartender/filesystems/local.py index 78ddd6b..fd1ef52 100644 --- a/src/bartender/filesystems/local.py +++ b/src/bartender/filesystems/local.py @@ -1,13 +1,13 @@ -from dataclasses import dataclass from pathlib import Path from typing import Literal +from pydantic import BaseModel + from bartender.filesystems.abstract import AbstractFileSystem from bartender.schedulers.abstract import JobDescription -@dataclass -class LocalFileSystemConfig: +class LocalFileSystemConfig(BaseModel): """Configuration for local file system.""" type: Literal["local"] = "local" diff --git a/src/bartender/filesystems/sftp.py b/src/bartender/filesystems/sftp.py index 0a5ba5c..e9d6f00 100644 --- a/src/bartender/filesystems/sftp.py +++ b/src/bartender/filesystems/sftp.py @@ -1,16 +1,15 @@ -from dataclasses import dataclass from pathlib import Path from typing import Literal, Optional from asyncssh import SSHClientConnection +from pydantic import BaseModel from bartender.filesystems.abstract import AbstractFileSystem from bartender.schedulers.abstract import JobDescription from bartender.ssh_utils import SshConnectConfig, ssh_connect -@dataclass -class SftpFileSystemConfig: +class SftpFileSystemConfig(BaseModel): """Configuration for SFTP file system. :param ssh_config: SSH connection configuration. diff --git a/src/bartender/schedulers/memory.py b/src/bartender/schedulers/memory.py index d7e7f55..db62a6f 100644 --- a/src/bartender/schedulers/memory.py +++ b/src/bartender/schedulers/memory.py @@ -6,23 +6,17 @@ create_task, gather, ) -from dataclasses import dataclass as pydataclass -from typing import TYPE_CHECKING, Literal, Optional +from typing import Literal, Optional from uuid import uuid4 -if TYPE_CHECKING: - from dataclasses import dataclass -else: - from pydantic.dataclasses import dataclass # noqa: WPS440 - +from pydantic import BaseModel from pydantic.types import PositiveInt from bartender.db.models.job_model import CompletedStates, State from bartender.schedulers.abstract import AbstractScheduler, JobDescription -@pydataclass -class _Job: +class _Job(BaseModel): description: JobDescription id: str state: State @@ -74,8 +68,7 @@ async def _worker(queue: Queue[_Job], jobs: dict[str, _Job], worker_index: int) queue.task_done() -@dataclass -class MemorySchedulerConfig: +class MemorySchedulerConfig(BaseModel): """Configuration for in memory scheduler. :param slots: Maximum number of concurrently runnning jobs. Minimum is 1. diff --git a/src/bartender/schedulers/slurm.py b/src/bartender/schedulers/slurm.py index 5d5a6c5..a778a74 100644 --- a/src/bartender/schedulers/slurm.py +++ b/src/bartender/schedulers/slurm.py @@ -1,7 +1,8 @@ -from dataclasses import dataclass from textwrap import dedent from typing import Literal, Optional +from pydantic import BaseModel + from bartender.db.models.job_model import State from bartender.schedulers.abstract import AbstractScheduler, JobDescription from bartender.schedulers.runner import ( @@ -35,8 +36,7 @@ def _map_slurm_state(slurm_state: str) -> State: return "error" -@dataclass -class SlurmSchedulerConfig: +class SlurmSchedulerConfig(BaseModel): """Configuration for Slurm scheduler. :param ssh_config: SSH connection configuration. diff --git a/src/bartender/ssh_utils.py b/src/bartender/ssh_utils.py index d0be49a..f3efcea 100644 --- a/src/bartender/ssh_utils.py +++ b/src/bartender/ssh_utils.py @@ -1,11 +1,9 @@ -from dataclasses import dataclass - from asyncssh import SSHClientConnection, connect from asyncssh.misc import DefTuple +from pydantic import BaseModel -@dataclass -class SshConnectConfig: +class SshConnectConfig(BaseModel): """Configuration for ssh connection. Attributes: From 673a18ea6ccea8dd339e079a3b1918fc58860880 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 27 Jan 2023 11:01:38 +0100 Subject: [PATCH 31/36] Right angle --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18b72bd..b973861 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ $ tree . ├── services # Package for different external services such as rabbit or redis etc. ├── settings.py # Main configuration settings for project. ├── static # Static content. - ├── web # Package contains web server. Handlers, startup config. + └── web # Package contains web server. Handlers, startup config. ├── api # Package with all handlers. │   └── router.py # Main router. ├── application.py # FastAPI application configuration. From 86e2571afa63847dbcb29fe65fd8e169e985a0d9 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 27 Jan 2023 11:24:15 +0100 Subject: [PATCH 32/36] Dont use BaseModel for class with abstract classes as attributes --- src/bartender/destinations.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bartender/destinations.py b/src/bartender/destinations.py index d6d6fb9..08c1de1 100644 --- a/src/bartender/destinations.py +++ b/src/bartender/destinations.py @@ -1,3 +1,5 @@ +from dataclasses import dataclass + from pydantic import BaseModel, Field from bartender.filesystems.abstract import AbstractFileSystem @@ -37,7 +39,8 @@ def default_destinations() -> dict[str, DestinationConfig]: } -class Destination(BaseModel): +@dataclass +class Destination: """A destination is a combination of a scheduler and optional filesystem.""" scheduler: AbstractScheduler From ba1f341d2a936f861522b2631a27329e67ff2c24 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 27 Jan 2023 11:26:38 +0100 Subject: [PATCH 33/36] Where config value must be --- src/bartender/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bartender/config.py b/src/bartender/config.py index 130c1e2..145c707 100644 --- a/src/bartender/config.py +++ b/src/bartender/config.py @@ -71,6 +71,8 @@ class ApplicatonConfiguration(BaseModel): `$config` in command string will be replaced with value of ApplicatonConfiguration.config. + The config value must be a path relative to the job directory. + .. code-block:: yaml command: wc $config From ebc4fd1a1ce0e4753688b548808834116e59e5e1 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 27 Jan 2023 11:27:59 +0100 Subject: [PATCH 34/36] Update src/bartender/destinations.py Co-authored-by: Peter Kalverla --- src/bartender/destinations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bartender/destinations.py b/src/bartender/destinations.py index 08c1de1..f0a20c8 100644 --- a/src/bartender/destinations.py +++ b/src/bartender/destinations.py @@ -49,8 +49,8 @@ class Destination: async def close(self) -> None: """Cleanup destination. - A job destination can have connections to remote systems - call this method to clean those up. + A job destination can have connections to remote systems. + Call this method to clean those up. """ await self.scheduler.close() self.filesystem.close() From c069295659462bd59a48bf32aedda5d60d7f5859 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 27 Jan 2023 12:04:38 +0100 Subject: [PATCH 35/36] Dont set default job root dir as it is not validated --- config-example.yaml | 2 +- src/bartender/config.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/config-example.yaml b/config-example.yaml index fef043f..1c0dc7b 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -1,5 +1,5 @@ # By default the files of jobs are stored in /tmp/jobs -# job_root_dir: /tmp/jobs +job_root_dir: /tmp/jobs # By default jobs are submitted to the first destination # destination_picker: bartender.picker:pick_first # To use a custom picker set `destination_picker` to a `:` diff --git a/src/bartender/config.py b/src/bartender/config.py index 145c707..92bd030 100644 --- a/src/bartender/config.py +++ b/src/bartender/config.py @@ -53,7 +53,6 @@ from pathlib import Path from string import Template -from tempfile import gettempdir from typing import Any from fastapi import Request @@ -111,7 +110,7 @@ class Config(BaseModel): destinations: dict[str, DestinationConfig] = Field( default_factory=default_destinations, ) - job_root_dir: DirectoryPath = Path(gettempdir()) / "jobs" + job_root_dir: DirectoryPath destination_picker: str = "bartender.picker:pick_first" @validator("applications") From 2cef6edc00bbec49cfdd858e1ce6495efb32960a Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 27 Jan 2023 12:15:50 +0100 Subject: [PATCH 36/36] Use pydantic to validate default value --- config-example.yaml | 2 +- src/bartender/config.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/config-example.yaml b/config-example.yaml index 1c0dc7b..fef043f 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -1,5 +1,5 @@ # By default the files of jobs are stored in /tmp/jobs -job_root_dir: /tmp/jobs +# job_root_dir: /tmp/jobs # By default jobs are submitted to the first destination # destination_picker: bartender.picker:pick_first # To use a custom picker set `destination_picker` to a `:` diff --git a/src/bartender/config.py b/src/bartender/config.py index 92bd030..c0e5c51 100644 --- a/src/bartender/config.py +++ b/src/bartender/config.py @@ -53,6 +53,7 @@ from pathlib import Path from string import Template +from tempfile import gettempdir from typing import Any from fastapi import Request @@ -110,9 +111,12 @@ class Config(BaseModel): destinations: dict[str, DestinationConfig] = Field( default_factory=default_destinations, ) - job_root_dir: DirectoryPath + job_root_dir: DirectoryPath = Path(gettempdir()) / "jobs" destination_picker: str = "bartender.picker:pick_first" + class Config: + validate_all = True + @validator("applications") def applications_non_empty( cls, # noqa: N805 following pydantic docs