diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 9bc63e0..a8fb98e 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -15,55 +15,41 @@ env:
FORCE_COLOR: "1"
jobs:
- run:
- name: Python ${{ matrix.python-version }} on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }}
- runs-on: ${{ matrix.os }}
- timeout-minutes: 30
+ test:
+ name: Python ${{ matrix.python-version }} - ${{ matrix.cdist-group }}/3
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
strategy:
fail-fast: false
matrix:
- os: [ubuntu-latest]
- # os: [ubuntu-latest, windows-latest, macos-latest]
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
+ cdist-group: [1, 2, 3]
steps:
- uses: actions/checkout@v4
- if: runner.os == 'Linux'
- name: Install Microsoft ODBC Drivers & Free additional space
- run: |
- sudo ACCEPT_EULA=Y apt-get install msodbcsql18 -y || true
- sudo docker rmi $(docker image ls -aq) >/dev/null 2>&1 || true
- sudo rm -rf \
- /usr/share/dotnet /usr/local/lib/android /opt/ghc \
- /usr/local/share/powershell /usr/share/swift /usr/local/.ghcup \
- /usr/lib/jvm || true
- sudo apt-get autoremove -y \
- && sudo apt-get clean -y \
- && sudo rm -rf /root/.cache \
- && sudo rm -rf /var/apt/lists/* \
- && sudo rm -rf /var/cache/apt/* \
- && sudo apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false
+ name: Install Microsoft ODBC Drivers
+ run: sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 libmariadb-dev
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- - name: Install Hatch
- run: pip install --upgrade hatch hatch-pip-compile
+ - name: Install uv
+ uses: astral-sh/setup-uv@v5
- - if: matrix.python-version == '3.12' && runner.os == 'Linux'
- name: Lint
- run: hatch run lint:check
+ - name: Intall dependencies
+ run: uv sync --frozen
- if: matrix.python-version == '3.12' && runner.os == 'Linux'
name: Run tests with coverage tracking
- run: hatch run +py=${{ matrix.python-version }} test:cov
+ run: uv run pytest --cdist-group=${{ matrix.cdist-group }}/3 -k "not elasticsearch"
- if: matrix.python-version != '3.12' || runner.os != 'Linux'
name: Run tests without tracking coverage
- run: hatch run +py=${{ matrix.python-version }} test:no-cov
+ run: uv run pytest --cdist-group=${{ matrix.cdist-group }}/3 -k "not elasticsearch"
- if: matrix.python-version == '3.12' && runner.os == 'Linux'
uses: actions/upload-artifact@v4
@@ -77,9 +63,31 @@ jobs:
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: litestar-org/pytest-databases
+
+ # run elasticsearch in a separate step. it's too slow
+ test_elasticsearch:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: 3.9
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v5
+
+ - name: Intall dependencies
+ run: uv sync --frozen
+
+ - name: Run tests with coverage tracking
+ run: uv run pytest -k elasticsearch
+
sonar:
needs:
- - run
+ - test
+ - test_elasticsearch
if: github.event.pull_request.head.repo.fork == false && github.repository_owner == 'litestar-org'
runs-on: ubuntu-latest
steps:
@@ -100,49 +108,3 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- codeql:
- needs:
- - run
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: Initialize CodeQL Without Dependencies
- uses: github/codeql-action/init@v3
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
-
- build-docs:
- if: github.event_name == 'pull_request'
- runs-on: ubuntu-latest
- steps:
- - name: Check out repository
- uses: actions/checkout@v4
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: "3.11"
-
- - name: Install Hatch
- run: pip install --upgrade hatch hatch-containers hatch-pip-compile
-
- - name: Build docs
- run: hatch run docs:build
-
- - name: Save PR number
- env:
- PR_NUMBER: ${{ github.event.number }}
- run: echo $PR_NUMBER > .pr_number
-
- - name: Upload artifact
- uses: actions/upload-artifact@v4
- with:
- name: docs-preview
- path: |
- docs/_build/html
- .pr_number
diff --git a/.github/workflows/docs-preview.yaml b/.github/workflows/docs-preview.yaml
deleted file mode 100644
index b1ddcee..0000000
--- a/.github/workflows/docs-preview.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-name: Documentation Preview
-
-on:
- workflow_run:
- workflows: [Tests And Linting]
- types: [completed]
-
-jobs:
- deploy:
- if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' }}
- runs-on: ubuntu-latest
- permissions:
- issues: write
- pull-requests: write
-
- steps:
- - name: Check out repository
- uses: actions/checkout@v4
-
- - name: Download artifact
- uses: dawidd6/action-download-artifact@v8
- with:
- workflow_conclusion: success
- run_id: ${{ github.event.workflow_run.id }}
- path: docs-preview
- name: docs-preview
-
- - name: Set PR number
- run: echo "PR_NUMBER=$(cat docs-preview/.pr_number)" >> $GITHUB_ENV
-
- - name: Deploy docs preview
- uses: JamesIves/github-pages-deploy-action@v4
- with:
- folder: docs-preview/docs/_build/html
- token: ${{ secrets.DOCS_PREVIEW_DEPLOY_TOKEN }}
- repository-name: litestar-org/pytest-databases-docs-preview
- clean: false
- target-folder: ${{ env.PR_NUMBER }}
- branch: gh-pages
-
- - uses: actions/github-script@v7
- env:
- PR_NUMBER: ${{ env.PR_NUMBER }}
- with:
- script: |
- const issue_number = process.env.PR_NUMBER
- const body = "Documentation preview will be available shortly at https://litestar-org.github.io/pytest-databases-docs-preview/" + issue_number
-
- const opts = github.rest.issues.listComments.endpoint.merge({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: issue_number,
- });
-
- const comments = await github.paginate(opts)
-
- for (const comment of comments) {
- if (comment.user.id === 41898282 && comment.body === body) {
- await github.rest.issues.deleteComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- comment_id: comment.id
- })
- }
- }
-
- await github.rest.issues.createComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: issue_number,
- body: body,
- })
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
deleted file mode 100644
index 2437ec4..0000000
--- a/.github/workflows/docs.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-name: Documentation
-on:
- release:
- types: [published]
- push:
- branches:
- - main
-env:
- PYTHONUNBUFFERED: "1"
- FORCE_COLOR: "1"
-jobs:
- docs:
- permissions:
- contents: write
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: Set up Python 3.12
- uses: actions/setup-python@v5
- with:
- python-version: 3.11
-
- - name: Install Hatch
- run: pip install --upgrade hatch hatch-pip-compile
-
- - name: Build release docs
- run: hatch run docs:python scripts/build_docs.py docs-build
- if: github.event_name == 'release'
-
- - name: Build dev docs
- run: hatch run docs:python scripts/build_docs.py docs-build
- if: github.event_name == 'push'
-
- - name: Deploy
- uses: JamesIves/github-pages-deploy-action@v4
- with:
- folder: docs-build
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index d4bb2cb..0000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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/__init__.py b/docs/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/_static/style.css b/docs/_static/style.css
deleted file mode 100644
index ed74eb4..0000000
--- a/docs/_static/style.css
+++ /dev/null
@@ -1,35 +0,0 @@
-#version-warning {
- top: 0;
- position: sticky;
- z-index: 60;
- width: 100%;
- height: 2.5rem;
- display: flex;
- column-gap: 0.5rem;
- justify-content: center;
- justify-items: center;
- align-items: center;
- background-color: #eee;
- border-bottom: 2px solid #ae2828;
-}
-
-@media (prefers-color-scheme: dark) {
- body:not([data-theme="light"]) #version-warning {
- background-color: black;
- }
-}
-
-.breaking-change {
- font-variant: all-small-caps;
- margin-left: 0.4rem;
- color: #f55353;
-}
-
-p {
- font-size: 1.1em;
-}
-
-html[data-theme="dark"] .mermaid svg {
- background-color: white;
- padding: 1em;
-}
diff --git a/docs/_static/versioning.js b/docs/_static/versioning.js
deleted file mode 100644
index 819a83e..0000000
--- a/docs/_static/versioning.js
+++ /dev/null
@@ -1,104 +0,0 @@
-const loadVersions = async () => {
- const res = await fetch(
- DOCUMENTATION_OPTIONS.URL_ROOT + "_static/versions.json",
- );
- if (res.status !== 200) {
- return null;
- }
- return await res.json();
-};
-
-const addVersionWarning = (currentVersion, latestVersion) => {
- if (currentVersion === latestVersion) {
- return;
- }
-
- const header = document.querySelector(".bd-header__inner")?.parentElement;
- if (!header) {
- return;
- }
-
- const container = document.createElement("div");
- container.id = "version-warning";
-
- const warningText = document.createElement("span");
- warningText.textContent = `You are viewing the documentation for ${
- currentVersion === "dev" ||
- parseInt(currentVersion) > parseInt(latestVersion)
- ? "a preview"
- : "an outdated"
- } version of Litestar.`;
- container.appendChild(warningText);
-
- const latestLink = document.createElement("a");
- latestLink.textContent = "Click here to go to the latest version";
- latestLink.href = DOCUMENTATION_OPTIONS.URL_ROOT + "../latest";
- container.appendChild(latestLink);
-
- header.before(container);
-};
-
-const formatVersionName = (version, isLatest) =>
- version + (isLatest ? " (latest)" : "");
-
-const addVersionSelect = (currentVersion, versionSpec) => {
- const navEnd = document.querySelector(".navbar-header-items__end");
-
- if (!navEnd) {
- return;
- }
-
- const container = document.createElement("div");
- container.classList.add("navbar-nav");
-
- const dropdown = document.createElement("div");
- dropdown.classList.add("dropdown");
- container.appendChild(dropdown);
-
- const dropdownToggle = document.createElement("button");
- dropdownToggle.classList.add("btn", "dropdown-toggle", "nav-item");
- dropdownToggle.setAttribute("data-bs-toggle", "dropdown");
- dropdownToggle.setAttribute("type", "button");
- dropdownToggle.textContent = `Version: ${formatVersionName(
- currentVersion,
- currentVersion === versionSpec.latest,
- )}`;
- dropdown.appendChild(dropdownToggle);
-
- const dropdownContent = document.createElement("div");
- dropdownContent.classList.add("dropdown-menu");
- dropdown.appendChild(dropdownContent);
-
- for (const version of versionSpec.versions) {
- const navItem = document.createElement("li");
- navItem.classList.add("nav-item");
-
- const navLink = document.createElement("a");
- navLink.classList.add("nav-link", "nav-internal");
- navLink.href = DOCUMENTATION_OPTIONS.URL_ROOT + `../${version}`;
- navLink.textContent = formatVersionName(
- version,
- version === versionSpec.latest,
- );
- navItem.appendChild(navLink);
-
- dropdownContent.appendChild(navItem);
- }
-
- navEnd.prepend(container);
-};
-
-const setupVersioning = (versions) => {
- if (versions === null) {
- return;
- }
-
- const currentVersion = DOCUMENTATION_OPTIONS.VERSION;
-
- addVersionWarning(currentVersion, versions.latest);
- addVersionSelect(currentVersion, versions);
-};
-
-window.addEventListener("DOMContentLoaded", () => {
- loadVersions().then(setupVersioning);
-});
diff --git a/docs/_static/versions.json b/docs/_static/versions.json
deleted file mode 100644
index 93f7327..0000000
--- a/docs/_static/versions.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "versions": ["1", "main"], "latest": "1" }
diff --git a/docs/changelog.rst b/docs/changelog.rst
deleted file mode 100644
index 070af4f..0000000
--- a/docs/changelog.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-=========
-Changelog
-=========
-
-All commits to this project will be documented in this file.
-
-PyTest Databases Changelog
diff --git a/docs/conf.py b/docs/conf.py
deleted file mode 100644
index d2cc265..0000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# Configuration file for the Sphinx documentation builder.
-from __future__ import annotations
-
-import os
-
-from pytest_databases.__metadata__ import __version__ as version
-
-# -- Environmental Data ------------------------------------------------------
-
-# -- Project information -----------------------------------------------------
-project = "pytest-databases"
-author = "Litestar Org"
-release = version
-release = os.getenv("_PYTEST-DATABASES_DOCS_BUILD_VERSION", version.rsplit(".")[0])
-copyright = "2024, Litestar Org" # noqa: A001
-
-# -- General configuration ---------------------------------------------------
-extensions = [
- "sphinx.ext.autodoc",
- "sphinx.ext.napoleon",
- "sphinx.ext.autosectionlabel",
- "sphinx.ext.githubpages",
- "sphinx.ext.viewcode",
- "sphinx.ext.intersphinx",
- "docs.fix_missing_references",
- "sphinx_copybutton",
- "sphinx.ext.todo",
- "sphinx.ext.viewcode",
- "sphinx_click",
- "sphinx_toolbox.collapse",
- "sphinx_design",
-]
-
-intersphinx_mapping = {
- "python": ("https://docs.python.org/3", None),
- "msgspec": ("https://jcristharif.com/msgspec/", None),
- "sqlalchemy": ("https://docs.sqlalchemy.org/en/20/", None),
- "litestar": ("https://docs.litestar.dev/latest/", None),
- "click": ("https://click.palletsprojects.com/en/8.1.x/", None),
- "redis": ("https://redis-py.readthedocs.io/en/stable/", None),
- "jinja2": ("https://jinja.palletsprojects.com/en/latest/", None),
-}
-PY_CLASS = "py:class"
-PY_RE = r"py:.*"
-PY_METH = "py:meth"
-PY_ATTR = "py:attr"
-PY_OBJ = "py:obj"
-
-nitpicky = True
-nitpick_ignore = [
- # type vars and aliases / intentionally undocumented
- (PY_CLASS, "T"),
-]
-nitpick_ignore_regex = [
- (PY_RE, r"pytest_databases.*\.T"),
-]
-
-napoleon_google_docstring = True
-napoleon_include_special_with_doc = True
-napoleon_use_admonition_for_examples = True
-napoleon_use_admonition_for_notes = True
-napoleon_use_admonition_for_references = False
-napoleon_attr_annotations = True
-
-autoclass_content = "class"
-autodoc_class_signature = "separated"
-autodoc_default_options = {"special-members": "__init__", "show-inheritance": True, "members": True}
-autodoc_member_order = "bysource"
-autodoc_typehints_format = "short"
-autodoc_type_aliases = {"FilterTypes": "FilterTypes"}
-
-autosectionlabel_prefix_document = True
-
-todo_include_todos = True
-
-templates_path = ["_templates"]
-exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
-
-# -- Style configuration -----------------------------------------------------
-html_theme = "litestar_sphinx_theme"
-html_static_path = ["_static"]
-html_js_files = ["versioning.js"]
-html_css_files = ["style.css"]
-html_show_sourcelink = True
-html_title = "Pytst Databases"
-
-
-html_theme_options = {
- "logo": {
- "link": "https://litestar.dev",
- },
- "use_page_nav": False,
- "github_repo_name": "pytest-databases",
- "announcement": "This documentation is currently under development.",
- "pygment_light_style": "xcode",
- "pygment_dark_style": "lightbulb",
- "navigation_with_keys": True,
- "extra_navbar_items": {
- "Documentation": "index",
- "Community": {
- "Contributing": {
- "description": "Learn how to contribute to the Litestar project",
- "link": "https://docs.litestar.dev/latest/contribution-guide.html",
- "icon": "contributing",
- },
- "Code of Conduct": {
- "description": "Review the etiquette for interacting with the Litestar community",
- "link": "https://github.com/litestar-org/.github?tab=coc-ov-file",
- "icon": "coc",
- },
- "Security": {
- "description": "Overview of Litestar's security protocols",
- "link": "https://github.com/litestar-org/.github?tab=coc-ov-file#security-ov-file",
- "icon": "coc",
- },
- },
- "About": {
- "Litestar Organization": {
- "description": "Details about the Litestar organization",
- "link": "https://litestar.dev/about/organization",
- "icon": "org",
- },
- "Releases": {
- "description": "Explore the release process, versioning, and deprecation policy for Litestar",
- "link": "https://litestar.dev/about/litestar-releases",
- "icon": "releases",
- },
- },
- "Release notes": {
- "What's new in 2.0": "release-notes/whats-new-2",
- "2.x Changelog": "https://docs.litestar.dev/2/release-notes/changelog.html",
- "1.x Changelog": "https://docs.litestar.dev/1/release-notes/changelog.html",
- },
- "Help": {
- "Discord Help Forum": {
- "description": "Dedicated Discord help forum",
- "link": "https://discord.gg/litestar-919193495116337154",
- "icon": "coc",
- },
- "GitHub Discussions": {
- "description": "GitHub Discussions ",
- "link": "https://github.com/orgs/litestar-org/discussions",
- "icon": "coc",
- },
- "Stack Overflow": {
- "description": "We monitor the litestar tag on Stack Overflow",
- "link": "https://stackoverflow.com/questions/tagged/litestar",
- "icon": "coc",
- },
- },
- },
-}
diff --git a/docs/contribution-guide.rst b/docs/contribution-guide.rst
deleted file mode 100644
index bbd440e..0000000
--- a/docs/contribution-guide.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-:orphan:
-
-.. include:: ../CONTRIBUTING.rst
diff --git a/docs/fix_missing_references.py b/docs/fix_missing_references.py
deleted file mode 100644
index f3acd9c..0000000
--- a/docs/fix_missing_references.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any
-
-if TYPE_CHECKING:
- from docutils.nodes import Element
- from sphinx.addnodes import pending_xref
- from sphinx.application import Sphinx
- from sphinx.environment import BuildEnvironment
-
-
-def on_missing_reference(app: Sphinx, env: BuildEnvironment, node: pending_xref, contnode: Element) -> Any:
- if not hasattr(node, "attributes"):
- return None
-
- attributes = node.attributes # type: ignore[attr-defined]
- target = attributes["reftarget"]
- py_domain = env.domains["py"]
-
- # autodoc sometimes incorrectly resolves these types, so we try to resolve them as py:data fist and fall back to any
- new_node = py_domain.resolve_xref(env, node["refdoc"], app.builder, "data", target, node, contnode)
- if new_node is None:
- resolved_xrefs = py_domain.resolve_any_xref(env, node["refdoc"], app.builder, target, node, contnode)
- for ref in resolved_xrefs:
- if ref:
- return ref[1]
- return new_node
-
-
-def setup(app: Sphinx) -> dict[str, bool]:
- app.connect("missing-reference", on_missing_reference)
- app.add_config_value("ignore_missing_refs", default={}, rebuild=False)
-
- return {"parallel_read_safe": True, "parallel_write_safe": True}
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 100644
index 9bfd667..0000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,72 +0,0 @@
-================
-Pytest Databases
-================
-
-Pytest Databases includes ready made fixtures for popular database backends.
-
-
-Installation
-------------
-
-Installing ``pytest-databases`` is as easy as calling your favorite Python package manager:
-
-.. tab-set::
-
- .. tab-item:: pip
- :sync: key1
-
- .. code-block:: bash
- :caption: Using pip
-
- python3 -m pip install pytest-databases
-
- .. tab-item:: uv
- :sync: key2
-
- .. code-block:: bash
- :caption: Using `uv `_
-
- uv add pytest-databases
-
- .. tab-item:: pipx
- :sync: key3
-
- .. code-block:: bash
- :caption: Using `pipx `_
-
- pipx install pytest-databases
-
- .. tab-item:: pdm
-
- .. code-block:: bash
- :caption: Using `PDM `_
-
- pdm add pytest-databases
-
- .. tab-item:: Poetry
-
- .. code-block:: bash
- :caption: Using `Poetry `_
-
- poetry add pytest-databases
-
-Usage
------
-
-.. todo:: Add usage instructions
-
-.. toctree::
- :titlesonly:
- :caption: Pytest Databases Documentation
- :hidden:
-
- usage/index
- reference/index
-
-.. toctree::
- :titlesonly:
- :caption: Development
- :hidden:
-
- changelog
- contribution-guide
diff --git a/docs/reference/docker.rst b/docs/reference/docker.rst
deleted file mode 100644
index 85eb1d7..0000000
--- a/docs/reference/docker.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-======
-docker
-======
-
-.. automodule:: pytest_databases.docker
- :members:
diff --git a/docs/reference/index.rst b/docs/reference/index.rst
deleted file mode 100644
index 0370fbc..0000000
--- a/docs/reference/index.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-=============
-API Reference
-=============
-
-The API reference is automatically generated from the docstrings in the code, and is useful for
-finding out what methods and attributes are available for a given class. The API reference is
-divided into several sections, each of which is listed below.
-
-.. note:: Private methods and attributes are not included in the API reference.
-
-Available API References
-------------------------
-
-.. toctree::
- :titlesonly:
-
- docker
diff --git a/docs/usage/alloydb_omni.rst b/docs/usage/alloydb_omni.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/alloydb_omni.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/azure-storage.rst b/docs/usage/azure-storage.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/azure-storage.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/bigquery.rst b/docs/usage/bigquery.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/bigquery.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/cockroachdb.rst b/docs/usage/cockroachdb.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/cockroachdb.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/dragonfly.rst b/docs/usage/dragonfly.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/dragonfly.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/elastic_search.rst b/docs/usage/elastic_search.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/elastic_search.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/index.rst b/docs/usage/index.rst
deleted file mode 100644
index 7bc7f50..0000000
--- a/docs/usage/index.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-=====
-Usage
-=====
-
-The usage documentation is for end users of the library. It provides an high-level
-overview of what features are available and how to use them.
-
-
-
-.. toctree::
- :titlesonly:
- :glob:
-
- *
diff --git a/docs/usage/keydb.rst b/docs/usage/keydb.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/keydb.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/mariadb.rst b/docs/usage/mariadb.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/mariadb.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/mssql.rst b/docs/usage/mssql.rst
deleted file mode 100644
index 1586868..0000000
--- a/docs/usage/mssql.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-====================
-Microsoft SQL Server
-====================
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/mysql.rst b/docs/usage/mysql.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/mysql.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/oracle.rst b/docs/usage/oracle.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/oracle.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/postgres.rst b/docs/usage/postgres.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/postgres.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/redis.rst b/docs/usage/redis.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/redis.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/docs/usage/spanner.rst b/docs/usage/spanner.rst
deleted file mode 100644
index ef19213..0000000
--- a/docs/usage/spanner.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-===========
-Placeholder
-===========
-
-.. todo:: You've stumbled upon a beautiful work in progress! Please excuse our unfinished work and check
- back soon for updates.
diff --git a/pyproject.toml b/pyproject.toml
index d040a1c..6982d2e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,7 +11,7 @@ description = 'Reusable database fixtures for any and all databases.'
license = "MIT"
name = "pytest-databases"
readme = "README.md"
-requires-python = ">=3.8"
+requires-python = ">=3.9"
version = "0.10.0"
#
authors = [{ name = "Cody Fincher", email = "cody.fincher@gmail.com" }]
@@ -36,16 +36,16 @@ keywords = [
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
# direct dependencies of this package
-dependencies = ["pytest"]
+dependencies = ["pytest", "filelock", "docker"]
[project.urls]
Documentation = "https://github.com/litestar-org/pytest-databases#readme"
@@ -60,256 +60,19 @@ dragonfly = ["redis"]
elasticsearch7 = ["elasticsearch7"]
elasticsearch8 = ["elasticsearch8"]
keydb = ["redis"]
-mssql = ["pymssql"]
-mysql = ["pymysql"]
+mariadb = ["mariadb"]
+mssql = ["pymssql<=2.3.1"]
+mysql = ["mysql-connector-python"]
oracle = ["oracledb"]
postgres = ["psycopg>=3"]
redis = ["redis"]
spanner = ["google-cloud-spanner"]
-######################
-# Build & Versioning #
-######################
-[tool.hatch.build.targets.sdist]
-exclude = ["/.github", "/docs"]
-
-[tool.hatch.metadata]
-allow-direct-references = true
-
-#####################
-# Environment Setup #
-#####################
-
-
-# Default environment with production dependencies
-[tool.hatch.envs.default]
-extra-dependencies = [
- # tests
- "coverage[toml]>=6.2",
- "coverage[toml]>=6.2",
- "pytest",
- "pytest-cov",
- "pytest-mock",
- "pytest-vcr",
- "pytest-click",
- "pytest-xdist",
- # lint
- "mypy",
- "ruff",
- "pylint",
- "pre-commit",
- "types-click",
- "types-six",
- "types-decorator",
- "types-pyyaml",
- "types-docutils",
- "types-redis",
- "types-pymysql",
- # docs
- "sphinx>=7.1.2",
- "sphinx-autobuild>=2021.3.14",
- "sphinx-copybutton>=0.5.2",
- "litestar-sphinx-theme @ git+https://github.com/litestar-org/litestar-sphinx-theme.git",
- "sphinx-click>=5.0.1",
- "sphinx-toolbox>=3.5.0",
- "sphinx-design>=0.5.0",
- "sphinxcontrib-mermaid>=0.9.2",
- "auto-pytabs[sphinx]>=0.4.0",
-]
-installer = "uv"
-python = "3.12"
-
-
-# Test environment with test-only dependencies
-[tool.hatch.envs.test]
-extra-dependencies = [
- # tests
- "coverage[toml]>=6.2",
- "coverage[toml]>=6.2",
- "pytest",
- "pytest-cov",
- "pytest-mock",
- "pytest-vcr",
- "pytest-click",
- "pytest-xdist",
- # lint
- "mypy",
- "ruff",
- "pylint",
- "pre-commit",
- "types-click",
- "types-six",
- "types-decorator",
- "types-pyyaml",
- "types-docutils",
- "types-redis",
- "types-pymysql",
- # docs
- "sphinx>=7.1.2",
- "sphinx-autobuild>=2021.3.14",
- "sphinx-copybutton>=0.5.2",
- "litestar-sphinx-theme @ git+https://github.com/litestar-org/litestar-sphinx-theme.git",
- "sphinx-click>=5.0.1",
- "sphinx-toolbox>=3.5.0",
- "sphinx-design>=0.5.0",
- "sphinxcontrib-mermaid>=0.9.2",
- "auto-pytabs[sphinx]>=0.4.0",
-]
-features = [
- "oracle",
- "mysql",
- "mssql",
- "postgres",
- "spanner",
- "cockroachdb",
- "spanner",
- "redis",
- "elasticsearch7",
- "elasticsearch8",
- "bigquery",
- "azure-storage",
-]
-template = "default"
-type = "virtual"
-
-[tool.hatch.envs.test.env-vars]
-PYTHONPATH = ".:src/"
-PYTHONUNBUFFERED = "1"
-SOURCE_DATE_EPOCH = "1580601600"
-
-# Test matrix for various Python versions replacing the functionality of tox
-[[tool.hatch.envs.test.matrix]]
-python = ["3.8", "3.9", "3.10", "3.11", "3.12"]
-
-[tool.hatch.envs.test.scripts]
-cov = "pytest --cov=pytest_databases --cov-report=xml"
-debug = "cov --no-cov -s --pdb --pdbcls=IPython.core.debugger:Pdb"
-no-cov = "cov --no-cov"
-
-
-# Docs environment
-[tool.hatch.envs.docs]
-extra-dependencies = [
- # tests
- "coverage[toml]>=6.2",
- "coverage[toml]>=6.2",
- "pytest",
- "pytest-cov",
- "pytest-mock",
- "pytest-vcr",
- "pytest-click",
- "pytest-xdist",
- # lint
- "mypy",
- "ruff",
- "pylint",
- "pre-commit",
- "types-click",
- "types-six",
- "types-decorator",
- "types-pyyaml",
- "types-docutils",
- "types-redis",
- "types-pymysql",
- # docs
- "sphinx>=7.1.2",
- "sphinx-autobuild>=2021.3.14",
- "sphinx-copybutton>=0.5.2",
- "litestar-sphinx-theme @ git+https://github.com/litestar-org/litestar-sphinx-theme.git",
- "sphinx-click>=5.0.1",
- "sphinx-toolbox>=3.5.0",
- "sphinx-design>=0.5.0",
- "sphinxcontrib-mermaid>=0.9.2",
- "auto-pytabs[sphinx]>=0.4.0",
-]
-features = [
- "oracle",
- "mysql",
- "mssql",
- "postgres",
- "spanner",
- "cockroachdb",
- "spanner",
- "redis",
- "elasticsearch7",
- "elasticsearch8",
- "bigquery",
- "azure-storage",
-]
-python = "3.11"
-template = "default"
-type = "virtual"
-
-[tool.hatch.envs.docs.env-vars]
-PYTHONPATH = "."
-PYTHONUNBUFFERED = "1"
-SOURCE_DATE_EPOCH = "1580601600"
-[tool.hatch.envs.docs.scripts]
-build = "sphinx-build -M html docs docs/_build/ -E -a -j auto --keep-going"
-serve = "sphinx-autobuild docs docs/_build/ -j auto --watch pytest_databases --watch docs --watch tests --watch CONTRIBUTING.rst --port 8002 {args}"
-# --ignore-url=None since the SUMMARY.md file leaves a None in sitemap.xml
-validate = "linkchecker --config .linkcheckerrc --ignore-url=/reference --ignore-url=None site"
-# https://github.com/linkchecker/linkchecker/issues/678
-build-check = ["build", "validate"]
-
-[tool.hatch.envs.local]
-extra-dependencies = [
- # tests
- "coverage[toml]>=6.2",
- "coverage[toml]>=6.2",
- "pytest",
- "pytest-cov",
- "pytest-mock",
- "pytest-vcr",
- "pytest-click",
- "pytest-xdist",
- # lint
- "mypy",
- "ruff",
- "pylint",
- "pre-commit",
- "types-click",
- "types-six",
- "types-decorator",
- "types-pyyaml",
- "types-docutils",
- "types-redis",
- "types-pymysql",
- # docs
- "sphinx>=7.1.2",
- "sphinx-autobuild>=2021.3.14",
- "sphinx-copybutton>=0.5.2",
- "litestar-sphinx-theme @ git+https://github.com/litestar-org/litestar-sphinx-theme.git",
- "sphinx-click>=5.0.1",
- "sphinx-toolbox>=3.5.0",
- "sphinx-design>=0.5.0",
- "sphinxcontrib-mermaid>=0.9.2",
- "auto-pytabs[sphinx]>=0.4.0",
-]
-features = [
- "oracle",
- "mysql",
- "mssql",
- "postgres",
- "cockroachdb",
- "spanner",
- "redis",
- "elasticsearch7",
- "elasticsearch8",
- "bigquery",
- "azure-storage",
-]
-path = ".venv/"
-python = "3.11"
-template = "docs"
-type = "virtual"
-
-
-# Lint environment
-[tool.hatch.envs.lint]
-extra-dependencies = [
+[dependency-groups]
+dev = [
# tests
+ "pytest-databases[azure-storage,bigquery,cockroachdb,dragonfly,elasticsearch7,elasticsearch8,keydb,mssql,mysql,mariadb,oracle,postgres,redis,spanner]",
"coverage[toml]>=6.2",
"coverage[toml]>=6.2",
"pytest",
@@ -340,21 +103,8 @@ extra-dependencies = [
"sphinx-design>=0.5.0",
"sphinxcontrib-mermaid>=0.9.2",
"auto-pytabs[sphinx]>=0.4.0",
+ "pytest-cdist>=0.2",
]
-python = "3.12"
-type = "virtual"
-
-[tool.hatch.envs.lint.scripts]
-check = ["style", "typing"]
-fix = [
- "typing",
- "ruff format {args:.}",
- "ruff check --fix {args:.}",
- "style", # feedback on what is not fixable
- "pre-commit run --all-files",
-]
-style = ["echo \"VERSION: `ruff --version`\"", "ruff check {args:.}", "ruff format --check {args:.}"]
-typing = ["echo \"VERSION: `mypy --version`\"", "mypy --install-types --non-interactive {args}"]
##################
@@ -375,69 +125,6 @@ exclude = ["scripts", "docs"]
include = ["src/pytest_databases", "tests"]
-[tool.git-cliff.changelog]
-body = """
-{% if version %}\
- `Release [v{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} `_
- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * `See All commits in v{{ version | trim_start_matches(pat="v") }} `_
-{% else %}\
- [unreleased]
- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-{% endif %}\
-{% if previous %}\
- {% if previous.commit_id %}
- `{{ previous.commit_id | truncate(length=7, end="") }} `_ ... \
- `{{ commit_id | truncate(length=7, end="") }} `_ \
- | `See diff for {{ version | trim_start_matches(pat="v") }} `_
- {% endif %}\
-{% endif %}\
-{% for group, commits in commits | group_by(attribute="group") %}
- {{ group | upper_first }}
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- {% for commit in commits %}
- * (`{{ commit.id | truncate(length=7, end="") }} `_) {% if commit.breaking %}[**breaking**] {% endif %} - {{ commit.message | upper_first }} ({{ commit.author.name }})\
- {% for footer in commit.footers -%}
- , {{ footer.token }}{{ footer.separator }}{{ footer.value }}\
- {% endfor %}\
- {% endfor %}
-{% endfor %}\n
-"""
-footer = """
-Pytest Databases Changelog
-"""
-header = """
-=========
-Changelog
-=========\n
-All commits to this project will be documented in this file.\n
-"""
-trim = true
-
-[tool.git-cliff.git]
-commit_parsers = [
- { message = "^feat", group = "Features" },
- { message = "^fix", group = "Bug Fixes" },
- { message = "^doc", group = "Documentation" },
- { message = "^perf", group = "Performance" },
- { message = "^refactor", group = "Refactor" },
- { message = "^style", group = "Styling" },
- { message = "^test", group = "Testing" },
- { message = "^chore\\(release\\): prepare for", skip = true },
- { message = "^chore", group = "Miscellaneous Tasks" },
- { body = ".*security", group = "Security" },
-]
-conventional_commits = true
-filter_commits = false
-filter_unconventional = true
-ignore_tags = ""
-protect_breaking_commits = false
-skip_tags = "v0.1.0-beta.1"
-sort_commits = "oldest"
-split_commits = false
-tag_pattern = "v[0-9]*"
-topo_order = false
-
[tool.mypy]
disallow_untyped_defs = false
files = ["src/pytest_databases", "tests"]
@@ -471,14 +158,7 @@ module = ["docutils.nodes.*"]
[[tool.mypy.overrides]]
ignore_missing_imports = true
-module = [
- "pyodbc",
- "google.auth.*",
- "google.cloud.*",
- "google.protobuf.*",
- "googleapiclient",
- "googleapiclient.*",
-]
+module = ["pyodbc", "google.auth.*", "google.cloud.*", "google.protobuf.*", "googleapiclient", "googleapiclient.*"]
[tool.ruff]
exclude = [
@@ -543,8 +223,6 @@ lint.ignore = [
"B008", # flake8-bugbear - Do not perform function call `Parameter` in argument defaultsRuff(B008)
"RUF012", # ruff - mutable class attributes should be annotated with `typing.ClassVar`
"ANN401", # ruff - Dynamically typed expressions (typing.Any) are disallowed
- "ANN102",
- "ANN101", # ruff - Missing type annotation for `self` in method
"PLR0913", # ruff - Too many arguments to function call
"PLR2004", # Magic value used in comparison
"FBT001", # Boolean typed positional argument in function definition
@@ -560,14 +238,10 @@ lint.ignore = [
"RUF029", # Ruff - Function is declared `async`, but doesn't `await` or use `async` features. # ignore
"SLF001",
"PT007",
- 'PT004',
- 'PT005',
'S603',
"E501", # pycodestyle line too long, handled by black
"PLW2901", # pylint - for loop variable overwritten by assignment target
"ANN401",
- "ANN102",
- "ANN101",
"FBT",
"PLR0913", # too many arguments
"PT",
@@ -580,6 +254,7 @@ lint.ignore = [
"DOC501", # Raised exception missing from docstring
"DOC502", # Raised exception missing from docstring
"A005", # module shadows builtin
+ "S608",
]
lint.select = ["ALL"]
# Allow unused variables when underscore-prefixed.
@@ -647,7 +322,7 @@ line-length = 120
## Testing Tools
[tool.pytest.ini_options]
-addopts = "--dist loadfile -n 2 -ra -q --doctest-glob='*.md'"
+addopts = "--doctest-glob='*.md' --dist=loadgroup"
filterwarnings = [
"ignore::DeprecationWarning:pkg_resources",
"ignore::DeprecationWarning:xdist.*",
@@ -663,6 +338,8 @@ markers = [
"elasticsearch: Elasticsearch Tests",
]
testpaths = ["tests"]
+cdist-justify-items = "file"
+cdist-group-steal = "3:10"
[tool.coverage.run]
branch = true
@@ -697,3 +374,7 @@ exclude_lines = [
'class .*\bProtocol\):',
'@(abc\.)?abstractmethod',
]
+
+
+[project.entry-points.pytest11]
+pytest_databases = "pytest_databases._service"
diff --git a/src/pytest_databases/_service.py b/src/pytest_databases/_service.py
new file mode 100644
index 0000000..2cb9b32
--- /dev/null
+++ b/src/pytest_databases/_service.py
@@ -0,0 +1,260 @@
+from __future__ import annotations
+
+import contextlib
+import json
+import os
+import subprocess # noqa: S404
+import time
+from contextlib import AbstractContextManager, contextmanager
+from typing import TYPE_CHECKING, Any, Callable, Generator
+
+import docker
+import filelock
+import pytest
+from docker.errors import ImageNotFound
+from typing_extensions import Self
+
+from pytest_databases.helpers import get_xdist_worker_id
+from pytest_databases.types import ServiceContainer
+
+if TYPE_CHECKING:
+ import pathlib
+ from types import TracebackType
+
+ from docker.models.containers import Container
+ from docker.types import Ulimit
+
+
+def get_docker_host() -> str:
+ result = subprocess.run(
+ ["docker", "context", "ls", "--format=json"], # noqa: S607
+ text=True,
+ capture_output=True,
+ check=True,
+ )
+ docker_ls = result.stdout.splitlines()
+ # if this is empty, we are not in a dockerized environment; It's probably a podman environment on linux
+ if not docker_ls or (len(docker_ls) == 1 and docker_ls[0] == "[]"):
+ uid_result = subprocess.run(
+ ["id", "-u"], # noqa: S607
+ text=True,
+ capture_output=True,
+ check=True,
+ )
+ uid = uid_result.stdout.strip()
+ return f"unix:///run/user/{uid}/podman/podman.sock"
+ contexts = (json.loads(line) for line in docker_ls)
+ return next(context["DockerEndpoint"] for context in contexts if context["Current"] is True)
+
+
+def get_docker_client() -> docker.DockerClient:
+ env = {**os.environ}
+ if "DOCKER_HOST" not in env:
+ env["DOCKER_HOST"] = get_docker_host()
+ return docker.DockerClient.from_env(environment=env)
+
+
+def _stop_all_containers(client: docker.DockerClient) -> None:
+ containers: list[Container] = client.containers.list(
+ all=True,
+ filters={"label": "pytest_databases"},
+ ignore_removed=True,
+ )
+ for container in containers:
+ if container.status == "running":
+ container.kill()
+ elif container.status in {"stopped", "dead"}:
+ container.remove()
+ elif container.status == "removing":
+ continue
+ else:
+ msg = f"Cannot handle container in state {container.status}"
+ raise RuntimeError(msg)
+
+
+class DockerService(AbstractContextManager):
+ def __init__(
+ self,
+ client: docker.DockerClient,
+ tmp_path: pathlib.Path,
+ session: pytest.Session,
+ ) -> None:
+ self._client = client
+ self._tmp_path = tmp_path
+ self._session = session
+ self._is_xdist = get_xdist_worker_id() is not None
+
+ def __enter__(self) -> Self:
+ if self._is_xdist:
+ ctrl_file = _get_ctrl_file(self._session)
+ with filelock.FileLock(ctrl_file.with_suffix(".lock")):
+ if not ctrl_file.exists():
+ ctrl_file.touch()
+ self._stop_all_containers()
+ else:
+ self._stop_all_containers()
+ return self
+
+ def __exit__(
+ self,
+ /,
+ __exc_type: type[BaseException] | None,
+ __exc_value: BaseException | None,
+ __traceback: TracebackType | None,
+ ) -> None:
+ if not self._is_xdist:
+ self._stop_all_containers()
+
+ def _get_container(self, name: str) -> Container | None:
+ containers = self._client.containers.list(
+ filters={"name": name},
+ )
+ if len(containers) > 1:
+ msg = "More than one running container found"
+ raise ValueError(msg)
+ if containers:
+ return containers[0]
+ return None
+
+ def _stop_all_containers(self) -> None:
+ _stop_all_containers(self._client)
+
+ @contextmanager
+ def run(
+ self,
+ image: str,
+ container_port: int,
+ name: str,
+ command: str | None = None,
+ env: dict[str, Any] | None = None,
+ exec_after_start: str | list[str] | None = None,
+ check: Callable[[ServiceContainer], bool] | None = None,
+ wait_for_log: str | bytes | None = None,
+ timeout: int = 10,
+ pause: float = 0.1,
+ transient: bool = False,
+ ulimits: list[Ulimit] | None = None,
+ shm_size: int | None = None,
+ mem_limit: str | None = None,
+ ) -> Generator[ServiceContainer, None, None]:
+ if check is None and wait_for_log is None:
+ msg = "Must set at least check or wait_for_log"
+ raise ValueError(msg)
+
+ name = f"pytest_databases_{name}"
+ lock = filelock.FileLock(self._tmp_path / name) if self._is_xdist else contextlib.nullcontext()
+
+ with lock:
+ container = self._get_container(name)
+ try:
+ self._client.images.get(image)
+ except ImageNotFound:
+ self._client.images.pull(*image.rsplit(":", maxsplit=1))
+
+ if container is None:
+ container = self._client.containers.run(
+ image,
+ command,
+ detach=True,
+ remove=True,
+ ports={container_port: None},
+ labels=["pytest_databases"],
+ name=name,
+ environment=env,
+ ulimits=ulimits,
+ mem_limit=mem_limit,
+ )
+ container.reload()
+
+ host_port = int(
+ container.ports[next(k for k in container.ports if k.startswith(str(container_port)))][0]["HostPort"]
+ )
+ service = ServiceContainer(
+ host="127.0.0.1",
+ port=host_port,
+ )
+
+ started = time.time()
+ if wait_for_log:
+ if isinstance(wait_for_log, str):
+ wait_for_log = wait_for_log.encode()
+ while time.time() - started < timeout:
+ if wait_for_log in container.logs():
+ break
+ time.sleep(pause)
+ else:
+ msg = f"Service {name!r} failed to come online"
+ raise ValueError(msg)
+
+ if check:
+ while time.time() - started < timeout:
+ if check(service) is True:
+ break
+ time.sleep(pause)
+ else:
+ msg = f"Service {name!r} failed to come online"
+ raise ValueError(msg)
+
+ if exec_after_start:
+ container.exec_run(exec_after_start)
+
+ yield service
+
+ if transient:
+ try:
+ container.stop()
+ container.remove(force=True)
+ except docker.errors.APIError as exc:
+ # '409 - Conflict' means removal is already in progress. this is the
+ # safest way of delaing with it, since the API is a bit borked when it
+ # comes to concurrent requests
+ if exc.status_code not in {409, 404}:
+ raise
+
+
+@pytest.fixture(scope="session")
+def docker_client() -> Generator[docker.DockerClient, None, None]:
+ client = get_docker_client()
+ try:
+ yield client
+ finally:
+ client.close()
+
+
+@pytest.fixture(scope="session")
+def docker_service(
+ docker_client: docker.DockerClient,
+ tmp_path_factory: pytest.TempPathFactory,
+ request: pytest.FixtureRequest,
+) -> Generator[DockerService, None, None]:
+ tmp_path = _get_base_tmp_path(tmp_path_factory)
+ with DockerService(
+ client=docker_client,
+ tmp_path=tmp_path,
+ session=request.session,
+ ) as service:
+ yield service
+
+
+def _get_base_tmp_path(tmp_path_factory: pytest.TempPathFactory) -> pathlib.Path:
+ tmp_path = tmp_path_factory.getbasetemp()
+ if get_xdist_worker_id() is not None:
+ tmp_path = tmp_path.parent
+ return tmp_path
+
+
+def _get_ctrl_file(session: pytest.Session) -> pathlib.Path:
+ tmp_path = _get_base_tmp_path(session.config._tmp_path_factory) # type: ignore[attr-defined]
+ return tmp_path / "ctrl"
+
+
+@pytest.hookimpl(wrapper=True)
+def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> Generator[Any, Any, Any]:
+ try:
+ return (yield)
+ finally:
+ if not hasattr(session.config, "workerinput") and _get_ctrl_file(session).exists():
+ # if we're running on xdist, delete the ctrl file, telling the deamon proc
+ # to stop all running containers.
+ # when not running on xdist, containers are stopped by the service itself
+ _stop_all_containers(get_docker_client())
diff --git a/src/pytest_databases/docker/alloydb_omni.py b/src/pytest_databases/docker/alloydb_omni.py
index f6595ff..39ddab7 100644
--- a/src/pytest_databases/docker/alloydb_omni.py
+++ b/src/pytest_databases/docker/alloydb_omni.py
@@ -1,136 +1,65 @@
from __future__ import annotations
-import os
-import sys
-from pathlib import Path
+import dataclasses
from typing import TYPE_CHECKING
import psycopg
import pytest
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases.docker.postgres import (
+ _make_connection_string,
+ _provide_postgres_service,
+)
+from pytest_databases.helpers import get_xdist_worker_num
+from pytest_databases.types import ServiceContainer
if TYPE_CHECKING:
from collections.abc import Generator
+ from pytest_databases._service import DockerService
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-alloydb-{simple_string_hash(__file__)}"
-
-def alloydb_omni_responsive(host: str, port: int, user: str, password: str, database: str) -> bool:
- try:
- conn = psycopg.connect(
- host=host,
- port=port,
- user=user,
- database=database,
- password=password,
- )
- except Exception: # noqa: BLE001
- return False
-
- try:
- db_open = conn.fetchrow("SELECT 1")
- return bool(db_open is not None and db_open[0] == 1)
- finally:
- conn.close()
-
-
-@pytest.fixture(scope="session")
-def alloydb_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(autouse=False, scope="session")
-def alloydb_docker_services(
- alloydb_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=alloydb_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def postgres_user() -> str:
- return "postgres"
-
-
-@pytest.fixture(scope="session")
-def postgres_password() -> str:
- return "super-secret"
+@dataclasses.dataclass
+class AlloyDBService(ServiceContainer):
+ database: str
+ password: str
+ user: str
@pytest.fixture(scope="session")
-def postgres_database() -> str:
- return "postgres"
+def alloydb_omni_image() -> str:
+ return "google/alloydbomni"
@pytest.fixture(scope="session")
-def alloydb_omni_port() -> int:
- return 5420
-
-
-@pytest.fixture(scope="session")
-def default_alloydb_omni_service_name() -> str:
- return "alloydb"
-
-
-@pytest.fixture(scope="session")
-def alloydb_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.alloydb-omni.yml")]
-
-
-@pytest.fixture(scope="session")
-def alloydb_docker_ip(alloydb_docker_services: DockerServiceRegistry) -> str:
- return alloydb_docker_services.docker_ip
-
-
-# alias to the latest
-@pytest.fixture(autouse=False, scope="session")
def alloydb_omni_service(
- alloydb_docker_services: DockerServiceRegistry,
- default_alloydb_omni_service_name: str,
- alloydb_docker_compose_files: list[Path],
- alloydb_omni_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> Generator[None, None, None]:
- os.environ["POSTGRES_PASSWORD"] = postgres_password
- os.environ["POSTGRES_USER"] = postgres_user
- os.environ["POSTGRES_DATABASE"] = postgres_database
- os.environ[f"{default_alloydb_omni_service_name.upper()}_PORT"] = str(alloydb_omni_port)
- alloydb_docker_services.start(
- name=default_alloydb_omni_service_name,
- docker_compose_files=alloydb_docker_compose_files,
- timeout=45,
- pause=1,
- check=alloydb_omni_responsive,
- port=alloydb_omni_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- yield
+ docker_service: DockerService,
+ alloydb_omni_image: str,
+) -> Generator[AlloyDBService, None, None]:
+ with _provide_postgres_service(
+ docker_service=docker_service,
+ image=alloydb_omni_image,
+ name=f"alloydb_{get_xdist_worker_num() or 0}",
+ xdist_postgres_isolate="server",
+ ) as service:
+ yield AlloyDBService(
+ host=service.host,
+ port=service.port,
+ password=service.password,
+ database=service.database,
+ user=service.user,
+ )
-@pytest.fixture(autouse=False, scope="session")
-def alloydb_omni_startup_connection(
- alloydb_omni_service: DockerServiceRegistry,
- alloydb_docker_ip: str,
- alloydb_omni_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> Generator[psycopg.Connection, None, None]:
+@pytest.fixture(scope="session")
+def alloydb_omni_connection(alloydb_omni_service: AlloyDBService) -> Generator[psycopg.Connection, None, None]:
with psycopg.connect(
- host=alloydb_docker_ip,
- port=alloydb_omni_port,
- user=postgres_user,
- database=postgres_database,
- password=postgres_password,
+ _make_connection_string(
+ host=alloydb_omni_service.host,
+ port=alloydb_omni_service.port,
+ user=alloydb_omni_service.user,
+ database=alloydb_omni_service.database,
+ password=alloydb_omni_service.password,
+ )
) as conn:
yield conn
diff --git a/src/pytest_databases/docker/azure_blob.py b/src/pytest_databases/docker/azure_blob.py
index 500bd66..47feae1 100644
--- a/src/pytest_databases/docker/azure_blob.py
+++ b/src/pytest_databases/docker/azure_blob.py
@@ -1,163 +1,120 @@
from __future__ import annotations
-import json
-import os
-import secrets
-import subprocess # noqa: S404
-from pathlib import Path
-from typing import AsyncGenerator, Generator
+from dataclasses import dataclass
+from typing import TYPE_CHECKING, AsyncGenerator, Generator
import pytest
from azure.storage.blob import ContainerClient
from azure.storage.blob.aio import ContainerClient as AsyncContainerClient
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
-
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-azure-blob-{simple_string_hash(__file__)}"
-
-
-def _get_container_ids(compose_file_name: str) -> list[str]:
- proc = subprocess.run(
- [ # noqa: S607
- "docker",
- "container",
- "ls",
- f"--filter=label=com.docker.compose.project.config_files={compose_file_name}",
- "--format=json",
- ],
- capture_output=True,
- text=True,
- check=True,
- )
- return [json.loads(line)["ID"] for line in proc.stdout.splitlines()]
-
-
-def _get_container_logs(compose_file_name: str) -> str:
- logs = ""
- for container_id in _get_container_ids(compose_file_name):
- stdout = subprocess.run(
- ["docker", "logs", container_id], # noqa: S607
- capture_output=True,
- text=True,
- check=True,
- ).stdout
- logs += stdout
- return logs
+from pytest_databases.helpers import get_xdist_worker_count, get_xdist_worker_num
+from pytest_databases.types import ServiceContainer, XdistIsolationLevel
-
-@pytest.fixture(scope="session")
-def azure_blob_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(scope="session")
-def azure_blob_service_startup_delay() -> int:
- return 1
-
-
-@pytest.fixture(scope="session")
-def azure_blob_docker_services(
- azure_blob_compose_project_name: str,
- azure_blob_service_startup_delay: int,
- worker_id: str = "main",
-) -> Generator[DockerServiceRegistry, None, None]:
- with DockerServiceRegistry(
- worker_id,
- compose_project_name=azure_blob_compose_project_name,
- ) as registry:
- yield registry
+if TYPE_CHECKING:
+ from pytest_databases._service import DockerService
-@pytest.fixture(scope="session")
-def azure_blob_connection_string() -> str:
- return "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"
+DEFAULT_ACCOUNT_KEY = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
+DEFAULT_ACCOUNT_NAME = "devstoreaccount1"
-@pytest.fixture(scope="session")
-def azure_blob_account_name() -> str:
- return "devstoreaccount1"
+@dataclass
+class AzureBlobService(ServiceContainer):
+ connection_string: str
+ account_url: str
+ account_key: str
+ account_name: str
@pytest.fixture(scope="session")
-def azure_blob_account_key() -> str:
- return "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
+def azure_blob_xdist_isolation_level() -> XdistIsolationLevel:
+ return "database"
@pytest.fixture(scope="session")
-def azure_blob_account_url() -> str:
- return "http://127.0.0.1:10000/devstoreaccount1"
+def azurite_in_memory() -> bool:
+ return True
-@pytest.fixture(scope="session")
-def azure_blob_port() -> int:
- return 10000
+def _create_account_options(number: int) -> list[tuple[str, str]]:
+ return [(f"test_account_{i}", DEFAULT_ACCOUNT_KEY) for i in range(number)]
@pytest.fixture(scope="session")
-def azure_blob_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.azurite.yml")]
-
-
-@pytest.fixture(scope="session")
-def default_azure_blob_redis_service_name() -> str:
- return "azurite"
-
-
-@pytest.fixture(scope="session")
-def azure_blob_docker_ip(azure_blob_docker_services: DockerServiceRegistry) -> str:
- return azure_blob_docker_services.docker_ip
+def azure_blob_service(
+ docker_service: DockerService,
+ azurite_in_memory: bool,
+ azure_blob_xdist_isolation_level: XdistIsolationLevel,
+) -> Generator[ServiceContainer, None, None]:
+ command = "azurite-blob --blobHost 0.0.0.0 --blobPort 10000"
+ if azurite_in_memory:
+ command += " --inMemoryPersistence"
+
+ name = "azurite-blob"
+ env = {}
+ account_name = DEFAULT_ACCOUNT_NAME
+ account_key = DEFAULT_ACCOUNT_KEY
+
+ worker_num = get_xdist_worker_num()
+
+ if worker_num is not None:
+ if azure_blob_xdist_isolation_level == "server":
+ name = f"{name}_{worker_num}"
+ else:
+ accounts = _create_account_options(get_xdist_worker_count())
+ env["AZURITE_ACCOUNTS"] = ";".join(f"{name}:{key}" for name, key in accounts)
+ account_name, account_key = accounts[worker_num]
+
+ with docker_service.run(
+ image="mcr.microsoft.com/azure-storage/azurite",
+ name=name,
+ command=command,
+ wait_for_log="Azurite Blob service successfully listens on",
+ container_port=10000,
+ env=env,
+ ) as service:
+ account_url = f"http://127.0.0.1:{service.port}/{account_name}"
+ connection_string = (
+ "DefaultEndpointsProtocol=http;"
+ f"AccountName={account_name};"
+ f"AccountKey={account_key};"
+ f"BlobEndpoint={account_url};"
+ )
+
+ yield AzureBlobService(
+ host=service.host,
+ port=service.port,
+ connection_string=connection_string,
+ account_url=account_url,
+ account_key=account_key,
+ account_name=account_name,
+ )
@pytest.fixture(scope="session")
def azure_blob_default_container_name() -> str:
- return secrets.token_hex(4)
+ return "pytest-databases"
@pytest.fixture(scope="session")
def azure_blob_container_client(
- azure_blob_connection_string: str,
+ azure_blob_service: AzureBlobService,
azure_blob_default_container_name: str,
- azure_blob_service: None,
) -> Generator[ContainerClient, None, None]:
with ContainerClient.from_connection_string(
- azure_blob_connection_string, container_name=azure_blob_default_container_name
+ azure_blob_service.connection_string,
+ container_name=azure_blob_default_container_name,
) as container_client:
yield container_client
@pytest.fixture(scope="session")
async def azure_blob_async_container_client(
- azure_blob_connection_string: str, azure_blob_default_container_name: str
+ azure_blob_service: AzureBlobService,
+ azure_blob_default_container_name: str,
) -> AsyncGenerator[AsyncContainerClient, None]:
async with AsyncContainerClient.from_connection_string(
- azure_blob_connection_string, container_name=azure_blob_default_container_name
+ azure_blob_service.connection_string,
+ container_name=azure_blob_default_container_name,
) as container_client:
yield container_client
-
-
-@pytest.fixture(autouse=False, scope="session")
-def azure_blob_service(
- azure_blob_docker_services: DockerServiceRegistry,
- default_azure_blob_redis_service_name: str,
- azure_blob_docker_compose_files: list[Path],
- azure_blob_connection_string: str,
- azure_blob_port: int,
- azure_blob_default_container_name: str,
-) -> Generator[None, None, None]:
- os.environ["AZURE_BLOB_PORT"] = str(azure_blob_port)
-
- def azurite_responsive(host: str, port: int) -> bool:
- # because azurite has a bug where it will hang for a long time if you make a
- # request against it before it has completed startup we can't ping it, so we're
- # inspecting the container logs instead
- logs = _get_container_logs(str(azure_blob_docker_compose_files[0].absolute()))
- return "Azurite Blob service successfully listens on" in logs
-
- azure_blob_docker_services.start(
- name=default_azure_blob_redis_service_name,
- docker_compose_files=azure_blob_docker_compose_files,
- check=azurite_responsive,
- port=azure_blob_port,
- )
- yield
diff --git a/src/pytest_databases/docker/bigquery.py b/src/pytest_databases/docker/bigquery.py
index 5fef90e..11afcb6 100644
--- a/src/pytest_databases/docker/bigquery.py
+++ b/src/pytest_databases/docker/bigquery.py
@@ -1,8 +1,6 @@
from __future__ import annotations
-import os
-import sys
-from pathlib import Path
+from dataclasses import dataclass
from typing import TYPE_CHECKING
import pytest
@@ -10,142 +8,95 @@
from google.auth.credentials import AnonymousCredentials, Credentials
from google.cloud import bigquery
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases.helpers import get_xdist_worker_num
+from pytest_databases.types import ServiceContainer, XdistIsolationLevel
if TYPE_CHECKING:
from collections.abc import Generator
-
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-bigquery-{simple_string_hash(__file__)}"
-
-
-def bigquery_responsive(
- host: str,
- bigquery_endpoint: str,
- bigquery_dataset: str,
- bigquery_client_options: ClientOptions,
- bigquery_project: str,
- bigquery_credentials: Credentials,
-) -> bool:
- try:
- client = bigquery.Client(
- project=bigquery_project, client_options=bigquery_client_options, credentials=bigquery_credentials
- )
-
- job = client.query(query="SELECT 1 as one")
-
- resp = list(job.result())
- return resp[0].one == 1
- except Exception: # noqa: BLE001
- return False
-
-
-@pytest.fixture(scope="session")
-def bigquery_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(autouse=False, scope="session")
-def bigquery_docker_services(
- bigquery_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=bigquery_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def bigquery_port() -> int:
- return 9051
-
-
-@pytest.fixture(scope="session")
-def bigquery_grpc_port() -> int:
- return 9061
+ from pytest_databases._service import DockerService
@pytest.fixture(scope="session")
-def bigquery_dataset() -> str:
- return "test-dataset"
+def xdist_bigquery_isolation_level() -> XdistIsolationLevel:
+ return "database"
@pytest.fixture(scope="session")
-def bigquery_project() -> str:
- return "emulator-test-project"
-
+def bigquery_image() -> str:
+ return "ghcr.io/goccy/bigquery-emulator:latest"
-@pytest.fixture(scope="session")
-def bigquery_client_options(bigquery_endpoint: str) -> ClientOptions:
- return ClientOptions(api_endpoint=bigquery_endpoint)
+@dataclass
+class BigQueryService(ServiceContainer):
+ project: str
+ dataset: str
+ credentials: Credentials
-@pytest.fixture(scope="session")
-def bigquery_credentials() -> Credentials:
- return AnonymousCredentials()
-
+ @property
+ def endpoint(self) -> str:
+ return f"http://{self.host}:{self.port}"
-@pytest.fixture(scope="session")
-def bigquery_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.bigquery.yml")]
-
-
-@pytest.fixture(scope="session")
-def default_bigquery_service_name() -> str:
- return "bigquery"
+ @property
+ def client_options(self) -> ClientOptions:
+ return ClientOptions(api_endpoint=self.endpoint)
@pytest.fixture(scope="session")
-def bigquery_docker_ip(bigquery_docker_services: DockerServiceRegistry) -> str:
- return bigquery_docker_services.docker_ip
-
-
-@pytest.fixture(scope="session")
-def bigquery_endpoint(bigquery_docker_ip: str, bigquery_port: int) -> str:
- return f"http://{bigquery_docker_ip}:{bigquery_port}"
-
-
-@pytest.fixture(autouse=False, scope="session")
def bigquery_service(
- bigquery_docker_services: DockerServiceRegistry,
- default_bigquery_service_name: str,
- bigquery_docker_compose_files: list[Path],
- bigquery_port: int,
- bigquery_grpc_port: int,
- bigquery_endpoint: str,
- bigquery_dataset: str,
- bigquery_project: str,
- bigquery_credentials: Credentials,
- bigquery_client_options: ClientOptions,
-) -> Generator[None, None, None]:
- os.environ["BIGQUERY_ENDPOINT"] = bigquery_endpoint
- os.environ["BIGQUERY_DATASET"] = bigquery_dataset
- os.environ["BIGQUERY_PORT"] = str(bigquery_port)
- os.environ["BIGQUERY_GRPC_PORT"] = str(bigquery_grpc_port)
- os.environ["GOOGLE_CLOUD_PROJECT"] = bigquery_project
- bigquery_docker_services.start(
- name=default_bigquery_service_name,
- docker_compose_files=bigquery_docker_compose_files,
+ docker_service: DockerService,
+ xdist_bigquery_isolation_level: XdistIsolationLevel,
+ bigquery_image: str,
+) -> Generator[BigQueryService, None, None]:
+ project = "emulator-test-project"
+ dataset = "test-dataset"
+ container_name = "bigquery"
+
+ worker_num = get_xdist_worker_num()
+ if worker_num is not None:
+ container_name += f"_{worker_num}"
+
+ def check(_service: ServiceContainer) -> bool:
+ try:
+ client = bigquery.Client(
+ project=project,
+ client_options=ClientOptions(api_endpoint=f"http://{_service.host}:{_service.port}"),
+ credentials=AnonymousCredentials(),
+ )
+
+ job = client.query(query="SELECT 1 as one")
+
+ resp = list(job.result())
+ return resp[0].one == 1
+ except Exception: # noqa: BLE001
+ return False
+
+ with docker_service.run(
+ image=bigquery_image,
+ command=f"--project={project} --dataset={dataset}",
+ name=container_name,
+ check=check,
+ env={
+ "PROJECT_ID": project,
+ "DATASET_NAME": dataset,
+ },
+ container_port=9050,
timeout=60,
- check=bigquery_responsive,
- bigquery_endpoint=bigquery_endpoint,
- bigquery_dataset=bigquery_dataset,
- bigquery_project=bigquery_project,
- bigquery_credentials=bigquery_credentials,
- bigquery_client_options=bigquery_client_options,
- )
- yield
+ transient=xdist_bigquery_isolation_level == "server",
+ ) as service:
+ yield BigQueryService(
+ host=service.host,
+ port=service.port,
+ project=project,
+ dataset=dataset,
+ credentials=AnonymousCredentials(),
+ )
-@pytest.fixture(autouse=False, scope="session")
-def bigquery_startup_connection(
- bigquery_service: DockerServiceRegistry,
- bigquery_project: str,
- bigquery_credentials: Credentials,
- bigquery_client_options: ClientOptions,
-) -> Generator[bigquery.Client, None, None]:
+@pytest.fixture(scope="session")
+def bigquery_client(bigquery_service: BigQueryService) -> Generator[bigquery.Client, None, None]:
yield bigquery.Client(
- project=bigquery_project, client_options=bigquery_client_options, credentials=bigquery_credentials
+ project=bigquery_service.project,
+ client_options=bigquery_service.client_options,
+ credentials=bigquery_service.credentials,
)
diff --git a/src/pytest_databases/docker/cockroachdb.py b/src/pytest_databases/docker/cockroachdb.py
index 10c3677..26f9098 100644
--- a/src/pytest_databases/docker/cockroachdb.py
+++ b/src/pytest_databases/docker/cockroachdb.py
@@ -1,61 +1,29 @@
from __future__ import annotations
-import os
-import sys
-from pathlib import Path
+from dataclasses import dataclass
from typing import TYPE_CHECKING
import psycopg
import pytest
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases._service import DockerService, ServiceContainer
+from pytest_databases.helpers import get_xdist_worker_num
if TYPE_CHECKING:
from collections.abc import Generator
-
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-cockroachdb-{simple_string_hash(__file__)}"
-
-
-def cockroachdb_responsive(host: str, port: int, database: str, driver_opts: dict[str, str]) -> bool:
- opts = "&".join(f"{k}={v}" for k, v in driver_opts.items()) if driver_opts else ""
- try:
- conn = psycopg.connect(f"postgresql://root@{host}:{port}/{database}?{opts}")
- except Exception: # noqa: BLE001
- return False
-
- try:
- db_open = conn.execute("SELECT 1").fetchone()
- return bool(db_open is not None and db_open[0] == 1)
- finally:
- conn.close()
+ from pytest_databases.types import XdistIsolationLevel
@pytest.fixture(scope="session")
-def cockroachdb_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
+def xdist_cockroachdb_isolation_level() -> XdistIsolationLevel:
+ return "database"
-@pytest.fixture(autouse=False, scope="session")
-def cockroachdb_docker_services(
- cockroachdb_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
- with DockerServiceRegistry(worker_id, compose_project_name=cockroachdb_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def cockroachdb_port() -> int:
- return 26257
-
-
-@pytest.fixture(scope="session")
-def cockroachdb_database() -> str:
- return "defaultdb"
+@dataclass
+class CockroachDBService(ServiceContainer):
+ database: str
+ driver_opts: dict[str, str]
@pytest.fixture(scope="session")
@@ -64,54 +32,64 @@ def cockroachdb_driver_opts() -> dict[str, str]:
@pytest.fixture(scope="session")
-def cockroachdb_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.cockroachdb.yml")]
+def cockroachdb_image() -> str:
+ return "cockroachdb/cockroach:latest"
@pytest.fixture(scope="session")
-def default_cockroachdb_service_name() -> str:
- return "cockroachdb"
-
-
-@pytest.fixture(scope="session")
-def cockroachdb_docker_ip(cockroachdb_docker_services: DockerServiceRegistry) -> str:
- return cockroachdb_docker_services.docker_ip
-
-
-@pytest.fixture(autouse=False, scope="session")
def cockroachdb_service(
- cockroachdb_docker_services: DockerServiceRegistry,
- default_cockroachdb_service_name: str,
- cockroachdb_docker_compose_files: list[Path],
- cockroachdb_port: int,
- cockroachdb_database: str,
+ docker_service: DockerService,
+ xdist_cockroachdb_isolation_level: XdistIsolationLevel,
cockroachdb_driver_opts: dict[str, str],
-) -> Generator[None, None, None]:
- os.environ["COCKROACHDB_DATABASE"] = cockroachdb_database
- os.environ["COCKROACHDB_PORT"] = str(cockroachdb_port)
- cockroachdb_docker_services.start(
- name=default_cockroachdb_service_name,
- docker_compose_files=cockroachdb_docker_compose_files,
- timeout=60,
- pause=1,
+ cockroachdb_image: str,
+) -> Generator[CockroachDBService, None, None]:
+ def cockroachdb_responsive(_service: ServiceContainer) -> bool:
+ opts = "&".join(f"{k}={v}" for k, v in cockroachdb_driver_opts.items()) if cockroachdb_driver_opts else ""
+ try:
+ conn = psycopg.connect(f"postgresql://root@{_service.host}:{_service.port}/defaultdb?{opts}")
+ except Exception: # noqa: BLE001
+ return False
+
+ try:
+ db_open = conn.execute("SELECT 1").fetchone()
+ return bool(db_open is not None and db_open[0] == 1)
+ finally:
+ conn.close()
+
+ container_name = "cockroachdb"
+ db_name = "pytest_databases"
+ worker_num = get_xdist_worker_num()
+ if worker_num is not None:
+ suffix = f"_{worker_num}"
+ if xdist_cockroachdb_isolation_level == "server":
+ container_name += suffix
+ else:
+ db_name += suffix
+
+ with docker_service.run(
+ image=cockroachdb_image,
+ container_port=26257,
check=cockroachdb_responsive,
- port=cockroachdb_port,
- database=cockroachdb_database,
- driver_opts=cockroachdb_driver_opts,
- )
- yield
-
-
-@pytest.fixture(autouse=False, scope="session")
-def cockroachdb_startup_connection(
- cockroachdb_service: DockerServiceRegistry,
- cockroachdb_docker_ip: str,
- cockroachdb_port: int,
- cockroachdb_database: str,
+ name=container_name,
+ command="start-single-node --insecure",
+ exec_after_start=f'cockroach sql --insecure -e "CREATE DATABASE {db_name}";',
+ transient=xdist_cockroachdb_isolation_level == "server",
+ ) as service:
+ yield CockroachDBService(
+ host=service.host,
+ port=service.port,
+ database=db_name,
+ driver_opts=cockroachdb_driver_opts,
+ )
+
+
+@pytest.fixture(scope="session")
+def cockroachdb_connection(
+ cockroachdb_service: CockroachDBService,
cockroachdb_driver_opts: dict[str, str],
) -> Generator[psycopg.Connection, None, None]:
opts = "&".join(f"{k}={v}" for k, v in cockroachdb_driver_opts.items()) if cockroachdb_driver_opts else ""
with psycopg.connect(
- f"postgresql://root@{cockroachdb_docker_ip}:{cockroachdb_port}/{cockroachdb_database}?{opts}"
+ f"postgresql://root@{cockroachdb_service.host}:{cockroachdb_service.port}/{cockroachdb_service.database}?{opts}"
) as conn:
yield conn
diff --git a/src/pytest_databases/docker/docker-compose.alloydb-omni.yml b/src/pytest_databases/docker/docker-compose.alloydb-omni.yml
deleted file mode 100644
index fe85b6b..0000000
--- a/src/pytest_databases/docker/docker-compose.alloydb-omni.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-services:
- alloydb:
- networks:
- - default
- image: google/alloydbomni
- ulimits:
- memlock: -1
- ports:
- - "${ALLOYDB_PORT:-5420}:5432" # use a non-standard port here
- # For better performance, consider `host` mode instead `port` to avoid docker NAT.
- # `host` mode is NOT currently supported in Swarm Mode.
- # https://docs.docker.com/compose/compose-file/compose-file-v3/#network_mode
- # network_mode: "host"
- environment:
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-super-secret}
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/docker-compose.azurite.yml b/src/pytest_databases/docker/docker-compose.azurite.yml
deleted file mode 100644
index abfb1fb..0000000
--- a/src/pytest_databases/docker/docker-compose.azurite.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-version: "3.9"
-services:
- azurite:
- image: mcr.microsoft.com/azure-storage/azurite
- hostname: azurite
- restart: always
- command: "azurite-blob --blobHost 0.0.0.0 --blobPort 10000"
- ports:
- - "10000:10000"
diff --git a/src/pytest_databases/docker/docker-compose.bigquery.yml b/src/pytest_databases/docker/docker-compose.bigquery.yml
deleted file mode 100644
index 16a6239..0000000
--- a/src/pytest_databases/docker/docker-compose.bigquery.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-services:
- bigquery:
- image: ghcr.io/goccy/bigquery-emulator:latest
- ports:
- - "${BIGQUERY_PORT:-9050}:9050"
- - "${BIGQUERY_GRPC_PORT:-9060}:9060"
- entrypoint: /bin/bigquery-emulator
- command: --project=${GOOGLE_CLOUD_PROJECT:-emulator-test-project} --dataset=${BIGQUERY_DATASET:-test-dataset}
- environment:
- PROJECT_ID: ${GOOGLE_CLOUD_PROJECT:-emulator-test-project}
- DATASET_NAME: ${BIGQUERY_DATASET:-test-dataset}
- volumes:
- - bigquerydata:/work
-networks:
- default:
- driver: bridge
-volumes:
- bigquerydata:
diff --git a/src/pytest_databases/docker/docker-compose.cockroachdb.yml b/src/pytest_databases/docker/docker-compose.cockroachdb.yml
deleted file mode 100644
index 4239806..0000000
--- a/src/pytest_databases/docker/docker-compose.cockroachdb.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-services:
- cockroachdb:
- image: cockroachdb/cockroach:latest
- command: start-single-node --insecure --http-addr=cockroachdb:8080
- restart: "no"
- expose:
- - "8080"
- - "${COCKROACHDB_PORT:-26257}"
- ports:
- - "${COCKROACHDB_PORT:-26257}:26257"
- - "${COCKROACHDB_WEB_PORT:-8880}:8080"
- volumes:
- - cockroach-data:/cockroach/cockroach-data/
- healthcheck:
- test: ["CMD", "curl", "-f", "http://localhost:8080/health?ready=1"]
- interval: 3s
- timeout: 3s
- retries: 5
-networks:
- default:
- driver: bridge
-volumes:
- cockroach-data:
diff --git a/src/pytest_databases/docker/docker-compose.dragonfly.yml b/src/pytest_databases/docker/docker-compose.dragonfly.yml
deleted file mode 100644
index 27a85f7..0000000
--- a/src/pytest_databases/docker/docker-compose.dragonfly.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-services:
- dragonfly:
- image: "docker.dragonflydb.io/dragonflydb/dragonfly"
- ulimits:
- memlock: -1
- ports:
- - "${DRAGONFLY_PORT:-6398}:6379"
- # For better performance, consider `host` mode instead `port` to avoid docker NAT.
- # `host` mode is NOT currently supported in Swarm Mode.
- # https://docs.docker.com/compose/compose-file/compose-file-v3/#network_mode
- # network_mode: "host"
- volumes:
- - dragonflydata:/data
-networks:
- default:
- driver: bridge
-volumes:
- dragonflydata:
diff --git a/src/pytest_databases/docker/docker-compose.elasticsearch.yml b/src/pytest_databases/docker/docker-compose.elasticsearch.yml
deleted file mode 100644
index 9a9738c..0000000
--- a/src/pytest_databases/docker/docker-compose.elasticsearch.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-services:
- elasticsearch7:
- image: elasticsearch:7.17.19
- ports:
- - 9200:9200
- - 9301:9300
- environment:
- - discovery.type=single-node
- - xpack.security.enabled=false
- healthcheck:
- test: curl -s http://localhost:9200 >/dev/null || exit 1
- interval: 30s
- timeout: 10s
- retries: 50
- elasticsearch8:
- image: elasticsearch:8.13.0
- ports:
- - 9201:9200
- - 9300:9300
- environment:
- - discovery.type=single-node
- - xpack.security.enabled=false
- healthcheck:
- test: curl -s http://localhost:9200 >/dev/null || exit 1
- interval: 30s
- timeout: 10s
- retries: 50
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/docker-compose.keydb.yml b/src/pytest_databases/docker/docker-compose.keydb.yml
deleted file mode 100644
index f3312d1..0000000
--- a/src/pytest_databases/docker/docker-compose.keydb.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-services:
- keydb:
- image: eqalpha/keydb
- ports:
- - "${KEYDB_PORT:-6396}:6379"
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/docker-compose.mariadb.yml b/src/pytest_databases/docker/docker-compose.mariadb.yml
deleted file mode 100644
index 9a1dd48..0000000
--- a/src/pytest_databases/docker/docker-compose.mariadb.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-services:
- mariadb113:
- networks:
- - default
- image: mariadb:11.3
- ports:
- - "${MARIADB113_PORT:-3359}:3306" # use a non-standard port here
- environment:
- MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD:-super-secret}
- MARIADB_PASSWORD: ${MARIADB_PASSWORD:-super-secret}
- MARIADB_USER: ${MARIADB_USER:-app}
- MARIADB_DATABASE: ${MARIADB_DATABASE:-db}
- MARIADB_ROOT_HOST: "%"
- LANG: C.UTF-8
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/docker-compose.mssql.yml b/src/pytest_databases/docker/docker-compose.mssql.yml
deleted file mode 100644
index 5b90fba..0000000
--- a/src/pytest_databases/docker/docker-compose.mssql.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-services:
- mssql2022:
- networks:
- - default
- image: mcr.microsoft.com/mssql/server:2022-latest
- ports:
- - "${MSSQL2022_PORT:-4133}:1433" # use a non-standard port here
- environment:
- SA_PASSWORD: ${MSSQL_PASSWORD:-Super-secret1}
- MSSQL_PID: Developer
- ACCEPT_EULA: Accepted
- MSSQL_TCP_PORT: 1433
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/docker-compose.mysql.yml b/src/pytest_databases/docker/docker-compose.mysql.yml
deleted file mode 100644
index ec33884..0000000
--- a/src/pytest_databases/docker/docker-compose.mysql.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-services:
- mysql8:
- networks:
- - default
- image: mysql:latest
- ports:
- - "${MYSQL8_PORT:-3360}:3306" # use a non-standard port here
- environment:
- MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-super-secret}
- MYSQL_PASSWORD: ${MYSQL_PASSWORD:-super-secret}
- MYSQL_USER: ${MYSQL_USER:-app}
- MYSQL_DATABASE: ${MYSQL_DATABASE:-db}
- MYSQL_ROOT_HOST: "%"
- LANG: C.UTF-8
- mysql57:
- networks:
- - default
- image: mysql:5.7
- ports:
- - "${MYSQL57_PORT:-3362}:3306" # use a non-standard port here
- environment:
- MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-super-secret}
- MYSQL_PASSWORD: ${MYSQL_PASSWORD:-super-secret}
- MYSQL_USER: ${MYSQL_USER:-app}
- MYSQL_DATABASE: ${MYSQL_DATABASE:-db}
- MYSQL_ROOT_HOST: "%"
- LANG: C.UTF-8
- mysql56:
- networks:
- - default
- image: mysql:5.6
- ports:
- - "${MYSQL56_PORT:-3363}:3306" # use a non-standard port here
- environment:
- MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-super-secret}
- MYSQL_PASSWORD: ${MYSQL_PASSWORD:-super-secret}
- MYSQL_USER: ${MYSQL_USER:-app}
- MYSQL_DATABASE: ${MYSQL_DATABASE:-db}
- MYSQL_ROOT_HOST: "%"
- LANG: C.UTF-8
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/docker-compose.oracle.yml b/src/pytest_databases/docker/docker-compose.oracle.yml
deleted file mode 100644
index c601f81..0000000
--- a/src/pytest_databases/docker/docker-compose.oracle.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-services:
- oracle18c:
- networks:
- - default
- image: gvenzl/oracle-xe:18-slim-faststart
- ports:
- - "${ORACLE18C_PORT:-1514}:1521" # use a non-standard port here
- environment:
- ORACLE_PASSWORD: ${ORACLE_SYSTEM_PASSWORD:-super-secret}
- APP_USER_PASSWORD: ${ORACLE_PASSWORD:-super-secret}
- APP_USER: ${ORACLE_USER:-app}
- oracle23ai:
- networks:
- - default
- image: gvenzl/oracle-free:23-slim-faststart
- ports:
- - "${ORACLE23AI_PORT:-1513}:1521" # use a non-standard port here
- environment:
- ORACLE_PASSWORD: ${ORACLE_SYSTEM_PASSWORD:-super-secret}
- APP_USER_PASSWORD: ${ORACLE_PASSWORD:-super-secret}
- APP_USER: ${ORACLE_USER:-app}
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/docker-compose.postgres.yml b/src/pytest_databases/docker/docker-compose.postgres.yml
deleted file mode 100644
index edab03e..0000000
--- a/src/pytest_databases/docker/docker-compose.postgres.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-services:
- postgres12:
- networks:
- - default
- image: postgres:12
- ports:
- - "${POSTGRES12_PORT:-5423}:5432" # use a non-standard port here
- environment:
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-super-secret}
- postgres13:
- networks:
- - default
- image: postgres:13
- ports:
- - "${POSTGRES13_PORT:-5424}:5432" # use a non-standard port here
- environment:
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-super-secret}
- postgres14:
- networks:
- - default
- image: postgres:14
- ports:
- - "${POSTGRES14_PORT:-5425}:5432" # use a non-standard port here
- environment:
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-super-secret}
- postgres15:
- networks:
- - default
- image: postgres:15
- ports:
- - "${POSTGRES15_PORT:-5426}:5432" # use a non-standard port here
- environment:
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-super-secret}
- postgres16:
- networks:
- - default
- image: postgres:16
- ports:
- - "${POSTGRES16_PORT:-5427}:5432" # use a non-standard port here
- environment:
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-super-secret}
- postgres17:
- networks:
- - default
- image: postgres:17
- ports:
- - "${POSTGRES17_PORT:-5428}:5432" # use a non-standard port here
- environment:
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-super-secret}
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/docker-compose.redis.yml b/src/pytest_databases/docker/docker-compose.redis.yml
deleted file mode 100644
index 07768c1..0000000
--- a/src/pytest_databases/docker/docker-compose.redis.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-services:
- redis:
- image: redis
- ports:
- - "${REDIS_PORT:-6397}:6379"
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/docker-compose.spanner.yml b/src/pytest_databases/docker/docker-compose.spanner.yml
deleted file mode 100644
index 73ae5c1..0000000
--- a/src/pytest_databases/docker/docker-compose.spanner.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-services:
- spanner:
- image: gcr.io/cloud-spanner-emulator/emulator:latest
- ports:
- - "${SPANNER_PORT:-9010}:9010"
- # Init (Create Instance)
- spanner_init:
- image: gcr.io/google.com/cloudsdktool/cloud-sdk:332.0.0-slim
- command: >
- bash -c 'gcloud config configurations create emulator &&
- gcloud config set auth/disable_credentials true &&
- gcloud config set project $${PROJECT_ID} &&
- gcloud config set auth/disable_credentials true &&
- gcloud spanner instances create $${INSTANCE_NAME} --config=emulator-config --description=Emulator --nodes=1'
- environment:
- PROJECT_ID: ${GOOGLE_CLOUD_PROJECT:-emulator-test-project}
- INSTANCE_NAME: ${SPANNER_INSTANCE:-test-instance}
- DATABASE_NAME: ${SPANNER_DATABASE:-test-database}
- depends_on:
- - spanner
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/docker-compose.valkey.yml b/src/pytest_databases/docker/docker-compose.valkey.yml
deleted file mode 100644
index 61a5531..0000000
--- a/src/pytest_databases/docker/docker-compose.valkey.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-services:
- valkey:
- image: valkey/valkey:latest
- ports:
- - "${VALKEY_PORT:-6308}:6379"
-networks:
- default:
- driver: bridge
diff --git a/src/pytest_databases/docker/dragonfly.py b/src/pytest_databases/docker/dragonfly.py
deleted file mode 100644
index aef5889..0000000
--- a/src/pytest_databases/docker/dragonfly.py
+++ /dev/null
@@ -1,70 +0,0 @@
-from __future__ import annotations
-
-import os
-import sys
-from pathlib import Path
-from typing import TYPE_CHECKING
-
-import pytest
-
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.docker.redis import redis_responsive
-from pytest_databases.helpers import simple_string_hash
-
-if TYPE_CHECKING:
- from collections.abc import Generator
-
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-dragonfly-{simple_string_hash(__file__)}"
-
-
-@pytest.fixture(scope="session")
-def dragonfly_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(autouse=False, scope="session")
-def dragonfly_docker_services(
- dragonfly_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=dragonfly_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def dragonfly_port() -> int:
- return 6398
-
-
-@pytest.fixture(scope="session")
-def dragonfly_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.dragonfly.yml")]
-
-
-@pytest.fixture(scope="session")
-def default_dragonfly_service_name() -> str:
- return "dragonfly"
-
-
-@pytest.fixture(scope="session")
-def dragonfly_docker_ip(dragonfly_docker_services: DockerServiceRegistry) -> str:
- return dragonfly_docker_services.docker_ip
-
-
-@pytest.fixture(autouse=False, scope="session")
-def dragonfly_service(
- dragonfly_docker_services: DockerServiceRegistry,
- default_dragonfly_service_name: str,
- dragonfly_docker_compose_files: list[Path],
- dragonfly_port: int,
-) -> Generator[None, None, None]:
- os.environ["DRAGONFLY_PORT"] = str(dragonfly_port)
- dragonfly_docker_services.start(
- name=default_dragonfly_service_name,
- docker_compose_files=dragonfly_docker_compose_files,
- check=redis_responsive,
- port=dragonfly_port,
- )
- yield
diff --git a/src/pytest_databases/docker/elastic_search.py b/src/pytest_databases/docker/elastic_search.py
index 8f0c63d..7eb7146 100644
--- a/src/pytest_databases/docker/elastic_search.py
+++ b/src/pytest_databases/docker/elastic_search.py
@@ -1,22 +1,28 @@
from __future__ import annotations
-import os
-import sys
-from pathlib import Path
+import contextlib
+import dataclasses
+import traceback
from typing import TYPE_CHECKING
import pytest
from elasticsearch7 import Elasticsearch as Elasticsearch7
from elasticsearch7 import Elasticsearch as Elasticsearch8
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases.types import ServiceContainer
if TYPE_CHECKING:
from collections.abc import Generator
+ from pytest_databases._service import DockerService
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-elasticsearch-{simple_string_hash(__file__)}"
+
+@dataclasses.dataclass
+class ElasticsearchService(ServiceContainer):
+ scheme: str
+ user: str
+ password: str
+ database: str
def elasticsearch7_responsive(scheme: str, host: str, port: int, user: str, password: str, database: str) -> bool:
@@ -36,141 +42,93 @@ def elasticsearch8_responsive(scheme: str, host: str, port: int, user: str, pass
) as client:
return client.ping()
except Exception: # noqa: BLE001
+ traceback.print_exc()
return False
@pytest.fixture(scope="session")
-def elasticsearch_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(autouse=False, scope="session")
-def elasticsearch_docker_services(
- elasticsearch_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=elasticsearch_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def elasticsearch_user() -> str:
- return "elastic"
-
-
-@pytest.fixture(scope="session")
-def elasticsearch_password() -> str:
- return "changeme"
-
-
-@pytest.fixture(scope="session")
-def elasticsearch_database() -> str:
- return "db"
-
-
-@pytest.fixture(scope="session")
-def elasticsearch_scheme() -> str:
- return "http"
-
-
-@pytest.fixture(scope="session")
-def elasticsearch7_port() -> int:
- return 9200
-
-
-@pytest.fixture(scope="session")
-def elasticsearch8_port() -> int:
- return 9201
-
-
-@pytest.fixture(scope="session")
-def elasticsearch_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.elasticsearch.yml")]
-
-
-@pytest.fixture(scope="session")
-def default_elasticsearch_service_name() -> str:
- return "elasticsearch8"
-
-
-@pytest.fixture(scope="session")
-def elasticsearch_docker_ip(elasticsearch_docker_services: DockerServiceRegistry) -> str:
- return elasticsearch_docker_services.docker_ip
+def elasticsearch_service_memory_limit() -> str:
+ return "500m"
+
+
+@contextlib.contextmanager
+def _provide_elasticsearch_service(
+ docker_service: DockerService,
+ image: str,
+ name: str,
+ client_cls: type[Elasticsearch7 | Elasticsearch8],
+ memory_limit: str,
+) -> Generator[ElasticsearchService, None, None]:
+ user = "elastic"
+ password = "changeme"
+ database = "db"
+ scheme = "http"
+
+ def check(_service: ServiceContainer) -> bool:
+ try:
+ with client_cls(
+ hosts=[{"host": _service.host, "port": _service.port, "scheme": scheme}],
+ verify_certs=False,
+ http_auth=(user, password),
+ ) as client:
+ return client.ping()
+ except Exception: # noqa: BLE001
+ return False
+
+ with docker_service.run(
+ image=image,
+ name=name,
+ container_port=9200,
+ env={
+ "discovery.type": "single-node",
+ "xpack.security.enabled": "false",
+ },
+ check=check,
+ timeout=120,
+ pause=1,
+ transient=True,
+ mem_limit="1g",
+ ) as service:
+ yield ElasticsearchService(
+ host=service.host,
+ port=service.port,
+ user=user,
+ password=password,
+ scheme=scheme,
+ database=database,
+ )
@pytest.fixture(autouse=False, scope="session")
-def elasticsearch7_service(
- elasticsearch_docker_services: DockerServiceRegistry,
- elasticsearch_docker_compose_files: list[Path],
- elasticsearch7_port: int,
- elasticsearch_database: str,
- elasticsearch_user: str,
- elasticsearch_password: str,
- elasticsearch_scheme: str,
-) -> Generator[None, None, None]:
- elasticsearch_docker_services.start(
- "elasticsearch7",
- docker_compose_files=elasticsearch_docker_compose_files,
- timeout=45,
- pause=1,
- check=elasticsearch7_responsive,
- port=elasticsearch7_port,
- database=elasticsearch_database,
- user=elasticsearch_user,
- password=elasticsearch_password,
- scheme=elasticsearch_scheme,
- )
- yield
+def elasticsearch_7_service(
+ docker_service: DockerService,
+ elasticsearch_service_memory_limit: str,
+) -> Generator[ElasticsearchService, None, None]:
+ with _provide_elasticsearch_service(
+ docker_service=docker_service,
+ image="elasticsearch:7.17.19",
+ name="elasticsearch-7",
+ client_cls=Elasticsearch7,
+ memory_limit=elasticsearch_service_memory_limit,
+ ) as service:
+ yield service
@pytest.fixture(autouse=False, scope="session")
-def elasticsearch8_service(
- elasticsearch_docker_services: DockerServiceRegistry,
- elasticsearch_docker_compose_files: list[Path],
- elasticsearch8_port: int,
- elasticsearch_database: str,
- elasticsearch_user: str,
- elasticsearch_password: str,
- elasticsearch_scheme: str,
-) -> Generator[None, None, None]:
- elasticsearch_docker_services.start(
- "elasticsearch8",
- docker_compose_files=elasticsearch_docker_compose_files,
- timeout=45,
- pause=1,
- check=elasticsearch8_responsive,
- port=elasticsearch8_port,
- database=elasticsearch_database,
- user=elasticsearch_user,
- password=elasticsearch_password,
- scheme=elasticsearch_scheme,
- )
- yield
+def elasticsearch_8_service(
+ docker_service: DockerService,
+ elasticsearch_service_memory_limit: str,
+) -> Generator[ElasticsearchService, None, None]:
+ with _provide_elasticsearch_service(
+ docker_service=docker_service,
+ image="elasticsearch:8.13.0",
+ name="elasticsearch-8",
+ client_cls=Elasticsearch8,
+ memory_limit=elasticsearch_service_memory_limit,
+ ) as service:
+ yield service
@pytest.fixture(autouse=False, scope="session")
-def elasticsearch_service(
- elasticsearch_docker_services: DockerServiceRegistry,
- default_elasticsearch_service_name: str,
- elasticsearch_docker_compose_files: list[Path],
- elasticsearch8_port: int,
- elasticsearch_database: str,
- elasticsearch_user: str,
- elasticsearch_password: str,
- elasticsearch_scheme: str,
-) -> Generator[None, None, None]:
- elasticsearch_docker_services.start(
- name=default_elasticsearch_service_name,
- docker_compose_files=elasticsearch_docker_compose_files,
- timeout=45,
- pause=1,
- check=elasticsearch8_responsive,
- port=elasticsearch8_port,
- database=elasticsearch_database,
- user=elasticsearch_user,
- password=elasticsearch_password,
- scheme=elasticsearch_scheme,
- )
- yield
+def elasticsearch_service(elasticsearch8_service: ElasticsearchService) -> ElasticsearchService:
+ return elasticsearch8_service
diff --git a/src/pytest_databases/docker/keydb.py b/src/pytest_databases/docker/keydb.py
deleted file mode 100644
index cc26443..0000000
--- a/src/pytest_databases/docker/keydb.py
+++ /dev/null
@@ -1,71 +0,0 @@
-from __future__ import annotations
-
-import os
-import sys
-from pathlib import Path
-from typing import TYPE_CHECKING
-
-import pytest
-
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.docker.redis import redis_responsive
-from pytest_databases.helpers import simple_string_hash
-
-if TYPE_CHECKING:
- from collections.abc import Generator
-
-
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-keydb-{simple_string_hash(__file__)}"
-
-
-@pytest.fixture(autouse=False, scope="session")
-def keydb_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(scope="session")
-def keydb_docker_services(
- keydb_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=keydb_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def keydb_port() -> int:
- return 6396
-
-
-@pytest.fixture(scope="session")
-def keydb_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.keydb.yml")]
-
-
-@pytest.fixture(scope="session")
-def default_keydb_service_name() -> str:
- return "keydb"
-
-
-@pytest.fixture(scope="session")
-def keydb_docker_ip(keydb_docker_services: DockerServiceRegistry) -> str:
- return keydb_docker_services.docker_ip
-
-
-@pytest.fixture(autouse=False, scope="session")
-def keydb_service(
- keydb_docker_services: DockerServiceRegistry,
- default_keydb_service_name: str,
- keydb_docker_compose_files: list[Path],
- keydb_port: int,
-) -> Generator[None, None, None]:
- os.environ["KEYDB_PORT"] = str(keydb_port)
- keydb_docker_services.start(
- name=default_keydb_service_name,
- docker_compose_files=keydb_docker_compose_files,
- check=redis_responsive,
- port=keydb_port,
- )
- yield
diff --git a/src/pytest_databases/docker/mariadb.py b/src/pytest_databases/docker/mariadb.py
index 6678746..484b4d0 100644
--- a/src/pytest_databases/docker/mariadb.py
+++ b/src/pytest_databases/docker/mariadb.py
@@ -1,177 +1,139 @@
from __future__ import annotations
-import os
-import sys
-from pathlib import Path
-from typing import TYPE_CHECKING, Any, Generator
+import contextlib
+import traceback
+from dataclasses import dataclass
+from typing import TYPE_CHECKING, Generator
-import pymysql
+import mariadb
import pytest
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.docker.mysql import mysql_responsive
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases.helpers import get_xdist_worker_num
+from pytest_databases.types import ServiceContainer, XdistIsolationLevel
if TYPE_CHECKING:
from collections.abc import Generator
+ from pytest_databases._service import DockerService
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-mariadb-{simple_string_hash(__file__)}"
-
-@pytest.fixture(scope="session")
-def mariadb_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(autouse=False, scope="session")
-def mariadb_docker_services(
- mariadb_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=mariadb_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def mariadb_user() -> str:
- return "app"
-
-
-@pytest.fixture(scope="session")
-def mariadb_password() -> str:
- return "super-secret"
-
-
-@pytest.fixture(scope="session")
-def mariadb_root_password() -> str:
- return "super-secret"
-
-
-@pytest.fixture(scope="session")
-def mariadb_database() -> str:
- return "db"
-
-
-@pytest.fixture(scope="session")
-def mariadb113_port() -> int:
- return 3359
-
-
-@pytest.fixture(scope="session")
-def default_mariadb_service_name() -> str:
- return "mariadb113"
+@dataclass
+class MariaDBService(ServiceContainer):
+ db: str
+ user: str
+ password: str
@pytest.fixture(scope="session")
-def mariadb_port(mariadb113_port: int) -> int:
- return mariadb113_port
-
-
-@pytest.fixture(scope="session")
-def mariadb_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.mariadb.yml")]
-
-
-@pytest.fixture(scope="session")
-def mariadb_docker_ip(mariadb_docker_services: DockerServiceRegistry) -> str:
- return mariadb_docker_services.docker_ip
+def xdist_mariadb_isolation_level() -> XdistIsolationLevel:
+ return "database"
+
+
+@contextlib.contextmanager
+def _provide_mysql_service(
+ docker_service: DockerService,
+ image: str,
+ name: str,
+ isolation_level: XdistIsolationLevel,
+) -> Generator[MariaDBService, None, None]:
+ user = "app"
+ password = "super-secret"
+ root_password = "super-secret"
+ database = "db"
+
+ def check(_service: ServiceContainer) -> bool:
+ try:
+ conn = mariadb.connect(
+ host=_service.host,
+ port=_service.port,
+ user=user,
+ database=database,
+ password=password,
+ )
+ except Exception: # noqa: BLE001
+ traceback.print_exc()
+ return False
+
+ try:
+ with conn.cursor() as cursor:
+ cursor.execute("select 1 as is_available")
+ resp = cursor.fetchone()
+ return resp is not None and resp[0] == 1
+ finally:
+ with contextlib.suppress(Exception):
+ conn.close()
+
+ worker_num = get_xdist_worker_num()
+ db_name = "pytest_databases"
+ if worker_num is not None:
+ suffix = f"_{worker_num}"
+ if isolation_level == "server":
+ name += suffix
+ else:
+ db_name += suffix
+
+ with docker_service.run(
+ image=image,
+ check=check,
+ container_port=3306,
+ name=name,
+ env={
+ "MARIADB_ROOT_PASSWORD": root_password,
+ "MARIADB_PASSWORD": password,
+ "MARIADB_USER": user,
+ "MARIADB_DATABASE": database,
+ "MARIADB_ROOT_HOST": "%",
+ "LANG": "C.UTF-8",
+ },
+ timeout=60,
+ pause=0.5,
+ exec_after_start=(
+ f'mariadb --user=root --password={root_password} -e "CREATE DATABASE {db_name};'
+ f"GRANT ALL PRIVILEGES ON *.* TO '{user}'@'%'; "
+ 'FLUSH PRIVILEGES;"'
+ ),
+ transient=isolation_level == "server",
+ ) as service:
+ yield MariaDBService(
+ db=db_name,
+ host=service.host,
+ port=service.port,
+ user=user,
+ password=password,
+ )
@pytest.fixture(autouse=False, scope="session")
-def mariadb113_service(
- mariadb_docker_services: DockerServiceRegistry,
- mariadb_docker_compose_files: list[Path],
- mariadb113_port: int,
- mariadb_database: str,
- mariadb_user: str,
- mariadb_password: str,
- mariadb_root_password: str,
-) -> Generator[None, None, None]:
- os.environ["MARIADB_ROOT_PASSWORD"] = mariadb_root_password
- os.environ["MARIADB_PASSWORD"] = mariadb_password
- os.environ["MARIADB_USER"] = mariadb_user
- os.environ["MARIADB_DATABASE"] = mariadb_database
- os.environ["MARIADB113_PORT"] = str(mariadb113_port)
- mariadb_docker_services.start(
- "mariadb113",
- docker_compose_files=mariadb_docker_compose_files,
- timeout=45,
- pause=1,
- check=mysql_responsive,
- port=mariadb113_port,
- database=mariadb_database,
- user=mariadb_user,
- password=mariadb_password,
- )
- yield
+def mariadb_113_service(
+ docker_service: DockerService,
+ xdist_mariadb_isolation_level: XdistIsolationLevel,
+) -> Generator[MariaDBService, None, None]:
+ with _provide_mysql_service(
+ docker_service=docker_service,
+ image="mariadb:11.3",
+ name="mariadb-11.3",
+ isolation_level=xdist_mariadb_isolation_level,
+ ) as service:
+ yield service
@pytest.fixture(autouse=False, scope="session")
-def mariadb_service(
- mariadb_docker_services: DockerServiceRegistry,
- default_mariadb_service_name: str,
- mariadb_docker_compose_files: list[Path],
- mariadb_port: int,
- mariadb_database: str,
- mariadb_user: str,
- mariadb_password: str,
- mariadb_root_password: str,
-) -> Generator[None, None, None]:
- os.environ["MARIADB_ROOT_PASSWORD"] = mariadb_root_password
- os.environ["MARIADB_PASSWORD"] = mariadb_password
- os.environ["MARIADB_USER"] = mariadb_user
- os.environ["MARIADB_DATABASE"] = mariadb_database
- os.environ[f"{default_mariadb_service_name.upper()}_PORT"] = str(mariadb_port)
- mariadb_docker_services.start(
- name=default_mariadb_service_name,
- docker_compose_files=mariadb_docker_compose_files,
- timeout=45,
- pause=1,
- check=mysql_responsive,
- port=mariadb_port,
- database=mariadb_database,
- user=mariadb_user,
- password=mariadb_password,
- )
- yield
+def mariadb_service(mariadb_113_service: MariaDBService) -> MariaDBService:
+ return mariadb_113_service
@pytest.fixture(autouse=False, scope="session")
-def mariadb_startup_connection(
- mariadb_service: DockerServiceRegistry,
- mariadb_docker_ip: str,
- mariadb_port: int,
- mariadb_database: str,
- mariadb_user: str,
- mariadb_password: str,
-) -> Generator[Any, None, None]:
- with pymysql.connect(
- host=mariadb_docker_ip,
- port=mariadb_port,
- user=mariadb_user,
- database=mariadb_database,
- password=mariadb_password,
+def mariadb_113_connection(mariadb_113_service: MariaDBService) -> Generator[mariadb.Connection, None, None]:
+ with mariadb.connect(
+ host=mariadb_113_service.host,
+ port=mariadb_113_service.port,
+ user=mariadb_113_service.user,
+ database=mariadb_113_service.db,
+ password=mariadb_113_service.password,
) as conn:
yield conn
@pytest.fixture(autouse=False, scope="session")
-def mariadb113_startup_connection(
- mariadb113_service: DockerServiceRegistry,
- mariadb_docker_ip: str,
- mariadb113_port: int,
- mariadb_database: str,
- mariadb_user: str,
- mariadb_password: str,
-) -> Generator[Any, None, None]:
- with pymysql.connect(
- host=mariadb_docker_ip,
- port=mariadb113_port,
- user=mariadb_user,
- database=mariadb_database,
- password=mariadb_password,
- ) as conn:
- yield conn
+def mariadb_connection(mariadb_113_connection: mariadb.Connection) -> mariadb.Connection:
+ return mariadb_113_connection
diff --git a/src/pytest_databases/docker/mssql.py b/src/pytest_databases/docker/mssql.py
index d01e989..35ac49e 100644
--- a/src/pytest_databases/docker/mssql.py
+++ b/src/pytest_databases/docker/mssql.py
@@ -1,202 +1,126 @@
from __future__ import annotations
-import os
-import sys
-from pathlib import Path
+import dataclasses
from typing import TYPE_CHECKING
import pymssql
import pytest
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases.helpers import get_xdist_worker_num
+from pytest_databases.types import ServiceContainer, XdistIsolationLevel
if TYPE_CHECKING:
from collections.abc import Generator
-
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-mssql-{simple_string_hash(__file__)}"
-
-
-def mssql_responsive(host: str, user: str, password: str, database: str, port: int) -> bool:
- try:
- conn = pymssql.connect(
- user=user,
- password=password,
- database=database,
- host=host,
- port=str(port),
- timeout=2,
- )
- with conn.cursor() as cursor:
- cursor.execute("select 1 as is_available")
- resp = cursor.fetchone()
- return resp[0] == 1 if resp is not None else False
- except Exception: # noqa: BLE001
- return False
-
-
-@pytest.fixture(scope="session")
-def mssql_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(autouse=False, scope="session")
-def mssql_docker_services(
- mssql_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=mssql_compose_project_name) as registry:
- yield registry
+ from pytest_databases._service import DockerService
@pytest.fixture(scope="session")
-def mssql_user() -> str:
- return "sa"
+def xdist_mssql_isolation_level() -> XdistIsolationLevel:
+ return "database"
@pytest.fixture(scope="session")
-def mssql_password() -> str:
- return "Super-secret1"
-
-
-@pytest.fixture(scope="session")
-def mssql_database() -> str:
- return "master"
-
-
-@pytest.fixture(scope="session")
-def mssql2022_port() -> int:
- return 4133
-
-
-@pytest.fixture(scope="session")
-def mssql_port(mssql2022_port: int) -> int:
- return mssql2022_port
-
-
-@pytest.fixture(scope="session")
-def mssql_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.mssql.yml")]
-
-
-@pytest.fixture(scope="session")
-def default_mssql_service_name() -> str:
- return "mssql2022"
-
-
-@pytest.fixture(scope="session")
-def mssql_docker_ip(mssql_docker_services: DockerServiceRegistry) -> str:
- return mssql_docker_services.docker_ip
-
-
-@pytest.fixture(scope="session")
-def mssql_connection_string(
- mssql_docker_ip: str, mssql_port: int, mssql_database: str, mssql_user: str, mssql_password: str
-) -> str:
- return f"encrypt=no; TrustServerCertificate=yes; driver={{ODBC Driver 18 for SQL Server}}; server={mssql_docker_ip},{mssql_port}; database={mssql_database}; UID={mssql_user}; PWD={mssql_password}"
-
-
-@pytest.fixture(scope="session")
-def mssql2022_connection_string(
- mssql_docker_ip: str, mssql2022_port: int, mssql_database: str, mssql_user: str, mssql_password: str
-) -> str:
- return f"encrypt=no; TrustServerCertificate=yes; driver={{ODBC Driver 18 for SQL Server}}; server={mssql_docker_ip},{mssql2022_port}; database={mssql_database}; UID={mssql_user}; PWD={mssql_password}"
-
-
-@pytest.fixture(autouse=False, scope="session")
-def mssql2022_service(
- mssql_docker_services: DockerServiceRegistry,
- mssql_docker_compose_files: list[Path],
- mssql_docker_ip: str,
- mssql2022_port: int,
- mssql_database: str,
- mssql_user: str,
- mssql_password: str,
-) -> Generator[None, None, None]:
- os.environ["MSSQL_PASSWORD"] = mssql_password
- os.environ["MSSQL_USER"] = mssql_user
- os.environ["MSSQL_DATABASE"] = mssql_database
- os.environ["MSSQL2022_PORT"] = str(mssql2022_port)
- mssql_docker_services.start(
- "mssql2022",
- docker_compose_files=mssql_docker_compose_files,
- timeout=120,
- pause=1,
- check=mssql_responsive,
- port=mssql2022_port,
- database=mssql_database,
- user=mssql_user,
- password=mssql_password,
- )
- yield
+def mssql_image() -> str:
+ return "mcr.microsoft.com/mssql/server:2022-latest"
+
+
+@dataclasses.dataclass
+class MSSQLService(ServiceContainer):
+ user: str
+ password: str
+ database: str
+
+ @property
+ def connection_string(self) -> str:
+ return (
+ "encrypt=no; "
+ "TrustServerCertificate=yes; "
+ f"driver={{ODBC Driver 18 for SQL Server}}; "
+ f"server={self.host},{self.port}; "
+ f"database={self.database}; "
+ f"UID={self.user}; "
+ f"PWD={self.password}"
+ )
@pytest.fixture(autouse=False, scope="session")
def mssql_service(
- mssql_docker_services: DockerServiceRegistry,
- default_mssql_service_name: str,
- mssql_docker_compose_files: list[Path],
- mssql_docker_ip: str,
- mssql_port: int,
- mssql_database: str,
- mssql_user: str,
- mssql_password: str,
- mssql_connection_string: str,
-) -> Generator[None, None, None]:
- os.environ["MSSQL_PASSWORD"] = mssql_password
- os.environ["MSSQL_USER"] = mssql_user
- os.environ["MSSQL_DATABASE"] = mssql_database
- os.environ[f"{default_mssql_service_name.upper()}_PORT"] = str(mssql_port)
- mssql_docker_services.start(
- name=default_mssql_service_name,
- docker_compose_files=mssql_docker_compose_files,
- timeout=120,
+ docker_service: DockerService,
+ xdist_mssql_isolation_level: XdistIsolationLevel,
+ mssql_image: str,
+) -> Generator[MSSQLService, None, None]:
+ password = "Super-secret1"
+
+ def check(_service: ServiceContainer) -> bool:
+ try:
+ with pymssql.connect(
+ user="sa",
+ password=password,
+ database="master",
+ host=_service.host,
+ port=str(_service.port),
+ timeout=2,
+ ) as conn, conn.cursor() as cursor:
+ cursor.execute("select 1 as is_available")
+ resp = cursor.fetchone()
+ return resp[0] == 1 if resp is not None else False
+ except Exception: # noqa: BLE001
+ return False
+
+ worker_num = get_xdist_worker_num()
+ db_name = "pytest_databases"
+ name = "mssql"
+ if worker_num is not None:
+ suffix = f"_{worker_num}"
+ if xdist_mssql_isolation_level == "server":
+ name += suffix
+ else:
+ db_name += suffix
+
+ with docker_service.run(
+ image=mssql_image,
+ check=check,
+ container_port=1433,
+ name=name,
+ env={
+ "SA_PASSWORD": password,
+ "MSSQL_PID": "Developer",
+ "ACCEPT_EULA": "Y",
+ "MSSQL_TCP_PORT": "1433",
+ },
+ timeout=100,
pause=1,
- check=mssql_responsive,
- port=mssql_port,
- database=mssql_database,
- user=mssql_user,
- password=mssql_password,
- )
- yield
-
-
-@pytest.fixture(autouse=False, scope="session")
-def mssql_startup_connection(
- mssql_service: DockerServiceRegistry,
- mssql_docker_ip: str,
- mssql_port: int,
- mssql_database: str,
- mssql_user: str,
- mssql_password: str,
-) -> Generator[pymssql.Connection, None, None]:
- with pymssql.connect(
- host=mssql_docker_ip,
- port=mssql_port,
- database=mssql_database,
- user=mssql_user,
- password=mssql_password,
- timeout=2,
- ) as db_connection:
- yield db_connection
+ transient=xdist_mssql_isolation_level == "server",
+ ) as service:
+ with pymssql.connect(
+ user="sa",
+ password=password,
+ database="master",
+ host=service.host,
+ port=str(service.port),
+ timeout=2,
+ autocommit=True,
+ ) as conn, conn.cursor() as cursor:
+ cursor.execute(f"CREATE DATABASE {db_name}")
+
+ yield MSSQLService(
+ database=db_name,
+ host=service.host,
+ port=service.port,
+ user="sa",
+ password=password,
+ )
@pytest.fixture(autouse=False, scope="session")
-def mssql2022_startup_connection(
- mssql2022_service: DockerServiceRegistry,
- mssql2022_port: int,
- mssql_database: str,
- mssql_user: str,
- mssql_password: str,
-) -> Generator[pymssql.Connection, None, None]:
+def mssql_connection(mssql_service: MSSQLService) -> Generator[pymssql.Connection, None, None]:
with pymssql.connect(
- port=mssql2022_port,
- database=mssql_database,
- user=mssql_user,
- password=mssql_password,
+ host=mssql_service.host,
+ port=str(mssql_service.port),
+ database=mssql_service.database,
+ user=mssql_service.user,
+ password=mssql_service.password,
timeout=2,
) as db_connection:
yield db_connection
diff --git a/src/pytest_databases/docker/mysql.py b/src/pytest_databases/docker/mysql.py
index 9aaeebd..b5b546a 100644
--- a/src/pytest_databases/docker/mysql.py
+++ b/src/pytest_databases/docker/mysql.py
@@ -1,305 +1,193 @@
from __future__ import annotations
import contextlib
-import os
-import sys
-from pathlib import Path
-from typing import TYPE_CHECKING, Any
+from dataclasses import dataclass
+from typing import TYPE_CHECKING
-import pymysql
+import mysql.connector
import pytest
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases._service import DockerService, ServiceContainer
+from pytest_databases.helpers import get_xdist_worker_num
if TYPE_CHECKING:
from collections.abc import Generator
+ from mysql.connector.abstracts import MySQLConnectionAbstract
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-mysql-{simple_string_hash(__file__)}"
+ from pytest_databases.types import XdistIsolationLevel
-def mysql_responsive(host: str, port: int, user: str, password: str, database: str) -> bool:
- try:
- conn = pymysql.connect(
- host=host,
- port=port,
- user=user,
- database=database,
- password=password,
- )
- except Exception: # noqa: BLE001
- return False
-
- try:
- with conn.cursor() as cursor:
- cursor.execute("select 1 as is_available")
- resp = cursor.fetchone()
- return resp is not None and resp[0] == 1
- finally:
- with contextlib.suppress(Exception):
- conn.close()
+@dataclass
+class MySQLService(ServiceContainer):
+ db: str
+ user: str
+ password: str
@pytest.fixture(scope="session")
-def mysql_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(autouse=False, scope="session")
-def mysql_docker_services(
- mysql_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=mysql_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def mysql_user() -> str:
- return "app"
-
-
-@pytest.fixture(scope="session")
-def mysql_password() -> str:
- return "super-secret"
-
-
-@pytest.fixture(scope="session")
-def mysql_root_password() -> str:
- return "super-secret"
-
-
-@pytest.fixture(scope="session")
-def mysql_database() -> str:
- return "db"
-
-
-@pytest.fixture(scope="session")
-def mysql56_port() -> int:
- return 3362
-
-
-@pytest.fixture(scope="session")
-def mysql57_port() -> int:
- return 3361
+def xdist_mysql_isolation_level() -> XdistIsolationLevel:
+ return "database"
+
+
+@contextlib.contextmanager
+def _provide_mysql_service(
+ docker_service: DockerService,
+ image: str,
+ name: str,
+ isolation_level: XdistIsolationLevel,
+) -> Generator[MySQLService, None, None]:
+ user = "app"
+ password = "super-secret"
+ root_password = "super-secret"
+ database = "db"
+
+ def check(_service: ServiceContainer) -> bool:
+ try:
+ conn = mysql.connector.connect(
+ host=_service.host,
+ port=_service.port,
+ user=user,
+ database=database,
+ password=password,
+ )
+ except mysql.connector.errors.OperationalError as exc:
+ if "Lost connection" in exc.msg:
+ return False
+ raise
+
+ try:
+ with conn.cursor() as cursor:
+ cursor.execute("select 1 as is_available")
+ resp = cursor.fetchone()
+ return resp is not None and resp[0] == 1
+ finally:
+ with contextlib.suppress(Exception):
+ conn.close()
+
+ worker_num = get_xdist_worker_num()
+ db_name = "pytest_databases"
+ if worker_num is not None:
+ suffix = f"_{worker_num}"
+ if isolation_level == "server":
+ name += suffix
+ else:
+ db_name += suffix
+
+ with docker_service.run(
+ image=image,
+ check=check,
+ container_port=3306,
+ name=name,
+ env={
+ "MYSQL_ROOT_PASSWORD": root_password,
+ "MYSQL_PASSWORD": password,
+ "MYSQL_USER": user,
+ "MYSQL_DATABASE": database,
+ "MYSQL_ROOT_HOST": "%",
+ "LANG": "C.UTF-8",
+ },
+ timeout=60,
+ pause=0.5,
+ exec_after_start=(
+ f'mysql --user=root --password={root_password} -e "CREATE DATABASE {db_name};'
+ f"GRANT ALL PRIVILEGES ON *.* TO '{user}'@'%'; "
+ 'FLUSH PRIVILEGES;"'
+ ),
+ transient=isolation_level == "server",
+ ) as service:
+ yield MySQLService(
+ db=db_name,
+ host=service.host,
+ port=service.port,
+ user=user,
+ password=password,
+ )
@pytest.fixture(scope="session")
-def default_mysql_service_name() -> str:
- return "mysql8"
+def mysql_service(mysql_8_service: MySQLService) -> MySQLService:
+ return mysql_8_service
@pytest.fixture(scope="session")
-def mysql8_port() -> int:
- return 3360
+def mysql_56_service(
+ docker_service: DockerService,
+ xdist_mysql_isolation_level: XdistIsolationLevel,
+) -> Generator[MySQLService, None, None]:
+ with _provide_mysql_service(
+ image="mysql:5.6",
+ name="mysql-56",
+ docker_service=docker_service,
+ isolation_level=xdist_mysql_isolation_level,
+ ) as service:
+ yield service
@pytest.fixture(scope="session")
-def mysql_port(mysql8_port: int) -> int:
- return mysql8_port
+def mysql_57_service(
+ docker_service: DockerService,
+ xdist_mysql_isolation_level: XdistIsolationLevel,
+) -> Generator[MySQLService, None, None]:
+ with _provide_mysql_service(
+ image="mysql:5.7",
+ name="mysql-57",
+ docker_service=docker_service,
+ isolation_level=xdist_mysql_isolation_level,
+ ) as service:
+ yield service
@pytest.fixture(scope="session")
-def mysql_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.mysql.yml")]
+def mysql_8_service(
+ docker_service: DockerService,
+ xdist_mysql_isolation_level: XdistIsolationLevel,
+) -> Generator[MySQLService, None, None]:
+ with _provide_mysql_service(
+ image="mysql:8",
+ name="mysql-8",
+ docker_service=docker_service,
+ isolation_level=xdist_mysql_isolation_level,
+ ) as service:
+ yield service
@pytest.fixture(scope="session")
-def mysql_docker_ip(mysql_docker_services: DockerServiceRegistry) -> str:
- return mysql_docker_services.docker_ip
-
-
-@pytest.fixture(autouse=False, scope="session")
-def mysql8_service(
- mysql_docker_services: DockerServiceRegistry,
- mysql_docker_compose_files: list[Path],
- mysql8_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
- mysql_root_password: str,
-) -> Generator[None, None, None]:
- os.environ["MYSQL_ROOT_PASSWORD"] = mysql_root_password
- os.environ["MYSQL_PASSWORD"] = mysql_password
- os.environ["MYSQL_USER"] = mysql_user
- os.environ["MYSQL_DATABASE"] = mysql_database
- os.environ["MYSQL8_PORT"] = str(mysql8_port)
- mysql_docker_services.start(
- "mysql8",
- docker_compose_files=mysql_docker_compose_files,
- timeout=45,
- pause=1,
- check=mysql_responsive,
- port=mysql8_port,
- database=mysql_database,
- user=mysql_user,
- password=mysql_password,
- )
- yield
-
-
-@pytest.fixture(autouse=False, scope="session")
-def mysql57_service(
- mysql_docker_services: DockerServiceRegistry,
- mysql_docker_compose_files: list[Path],
- mysql57_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
- mysql_root_password: str,
-) -> Generator[None, None, None]:
- os.environ["MYSQL_ROOT_PASSWORD"] = mysql_root_password
- os.environ["MYSQL_PASSWORD"] = mysql_password
- os.environ["MYSQL_USER"] = mysql_user
- os.environ["MYSQL_DATABASE"] = mysql_database
- os.environ["MYSQL57_PORT"] = str(mysql57_port)
- mysql_docker_services.start(
- "mysql57",
- docker_compose_files=mysql_docker_compose_files,
- timeout=45,
- pause=1,
- check=mysql_responsive,
- port=mysql57_port,
- database=mysql_database,
- user=mysql_user,
- password=mysql_password,
- )
- yield
-
-
-@pytest.fixture(autouse=False, scope="session")
-def mysql56_service(
- mysql_docker_services: DockerServiceRegistry,
- mysql_docker_compose_files: list[Path],
- mysql56_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
- mysql_root_password: str,
-) -> Generator[None, None, None]:
- os.environ["MYSQL_ROOT_PASSWORD"] = mysql_root_password
- os.environ["MYSQL_PASSWORD"] = mysql_password
- os.environ["MYSQL_USER"] = mysql_user
- os.environ["MYSQL_DATABASE"] = mysql_database
- os.environ["MYSQL56_PORT"] = str(mysql56_port)
- mysql_docker_services.start(
- "mysql56",
- docker_compose_files=mysql_docker_compose_files,
- timeout=45,
- pause=1,
- check=mysql_responsive,
- port=mysql56_port,
- database=mysql_database,
- user=mysql_user,
- password=mysql_password,
- )
- yield
-
-
-@pytest.fixture(autouse=False, scope="session")
-def mysql_service(
- mysql_docker_services: DockerServiceRegistry,
- default_mysql_service_name: str,
- mysql_docker_compose_files: list[Path],
- mysql_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
- mysql_root_password: str,
-) -> Generator[None, None, None]:
- os.environ["MYSQL_ROOT_PASSWORD"] = mysql_root_password
- os.environ["MYSQL_PASSWORD"] = mysql_password
- os.environ["MYSQL_USER"] = mysql_user
- os.environ["MYSQL_DATABASE"] = mysql_database
- os.environ[f"{default_mysql_service_name.upper()}_PORT"] = str(mysql_port)
- mysql_docker_services.start(
- name=default_mysql_service_name,
- docker_compose_files=mysql_docker_compose_files,
- timeout=45,
- pause=1,
- check=mysql_responsive,
- port=mysql_port,
- database=mysql_database,
- user=mysql_user,
- password=mysql_password,
- )
- yield
-
-
-@pytest.fixture(autouse=False, scope="session")
-def mysql_startup_connection(
- mysql_service: DockerServiceRegistry,
- mysql_docker_ip: str,
- mysql_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> Generator[Any, None, None]:
- with pymysql.connect(
- host=mysql_docker_ip,
- port=mysql_port,
- user=mysql_user,
- database=mysql_database,
- password=mysql_password,
+def mysql_56_connection(mysql_56_service: MySQLService) -> Generator[MySQLConnectionAbstract, None, None]:
+ with mysql.connector.connect(
+ host=mysql_56_service.host,
+ port=mysql_56_service.port,
+ user=mysql_56_service.user,
+ database=mysql_56_service.db,
+ password=mysql_56_service.password,
) as conn:
yield conn
-@pytest.fixture(autouse=False, scope="session")
-def mysql56_startup_connection(
- mysql56_service: DockerServiceRegistry,
- mysql_docker_ip: str,
- mysql56_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> Generator[Any, None, None]:
- with pymysql.connect(
- host=mysql_docker_ip,
- port=mysql56_port,
- user=mysql_user,
- database=mysql_database,
- password=mysql_password,
+@pytest.fixture(scope="session")
+def mysql_57_connection(mysql_57_service: MySQLService) -> Generator[MySQLConnectionAbstract, None, None]:
+ with mysql.connector.connect(
+ host=mysql_57_service.host,
+ port=mysql_57_service.port,
+ user=mysql_57_service.user,
+ database=mysql_57_service.db,
+ password=mysql_57_service.password,
) as conn:
yield conn
-@pytest.fixture(autouse=False, scope="session")
-def mysql57_startup_connection(
- mysql57_service: DockerServiceRegistry,
- mysql_docker_ip: str,
- mysql57_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> Generator[Any, None, None]:
- with pymysql.connect(
- host=mysql_docker_ip,
- port=mysql57_port,
- user=mysql_user,
- database=mysql_database,
- password=mysql_password,
- ) as conn:
- yield conn
+@pytest.fixture(scope="session")
+def mysql_connection(mysql_8_connection: MySQLConnectionAbstract) -> MySQLConnectionAbstract:
+ return mysql_8_connection
-@pytest.fixture(autouse=False, scope="session")
-def mysql8_startup_connection(
- mysql8_service: DockerServiceRegistry,
- mysql_docker_ip: str,
- mysql8_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> Generator[Any, None, None]:
- with pymysql.connect(
- host=mysql_docker_ip,
- port=mysql8_port,
- user=mysql_user,
- database=mysql_database,
- password=mysql_password,
+@pytest.fixture(scope="session")
+def mysql_8_connection(mysql_8_service: MySQLService) -> Generator[MySQLConnectionAbstract, None, None]:
+ with mysql.connector.connect(
+ host=mysql_8_service.host,
+ port=mysql_8_service.port,
+ user=mysql_8_service.user,
+ database=mysql_8_service.db,
+ password=mysql_8_service.password,
) as conn:
yield conn
diff --git a/src/pytest_databases/docker/oracle.py b/src/pytest_databases/docker/oracle.py
index 41b1455..3df576d 100644
--- a/src/pytest_databases/docker/oracle.py
+++ b/src/pytest_databases/docker/oracle.py
@@ -1,21 +1,19 @@
from __future__ import annotations
-import os
-import sys
-from pathlib import Path
+import contextlib
+from dataclasses import dataclass
from typing import TYPE_CHECKING
import oracledb
import pytest
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases.helpers import get_xdist_worker_num
+from pytest_databases.types import ServiceContainer
if TYPE_CHECKING:
from collections.abc import Generator
-
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-oracle-{simple_string_hash(__file__)}"
+ from pytest_databases._service import DockerService
def oracle_responsive(host: str, port: int, service_name: str, user: str, password: str) -> bool:
@@ -35,223 +33,123 @@ def oracle_responsive(host: str, port: int, service_name: str, user: str, passwo
return False
-@pytest.fixture(scope="session")
-def oracle_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(autouse=False, scope="session")
-def oracle_docker_services(
- oracle_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=oracle_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def oracle_user() -> str:
- return "app"
-
-
-@pytest.fixture(scope="session")
-def oracle_password() -> str:
- return "super-secret"
-
-
-@pytest.fixture(scope="session")
-def oracle_system_password() -> str:
- return "super-secret"
-
-
-@pytest.fixture(scope="session")
-def oracle18c_service_name() -> str:
- return "xepdb1"
-
-
-@pytest.fixture(scope="session")
-def oracle23ai_service_name() -> str:
- return "FREEPDB1"
-
-
-@pytest.fixture(scope="session")
-def oracle_service_name(oracle23ai_service_name: str) -> str:
- return oracle23ai_service_name
-
-
-@pytest.fixture(scope="session")
-def oracle18c_port() -> int:
- return 1514
-
-
-@pytest.fixture(scope="session")
-def oracle23ai_port() -> int:
- return 1513
-
-
-@pytest.fixture(scope="session")
-def default_oracle_service_name() -> str:
- return "oracle23ai"
-
-
-@pytest.fixture(scope="session")
-def oracle_port(oracle23ai_port: int) -> int:
- return oracle23ai_port
-
-
-@pytest.fixture(scope="session")
-def oracle_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.oracle.yml")]
-
-
-@pytest.fixture(scope="session")
-def oracle_docker_ip(oracle_docker_services: DockerServiceRegistry) -> str:
- return oracle_docker_services.docker_ip
+@dataclass
+class OracleService(ServiceContainer):
+ user: str
+ password: str
+ system_password: str
+ service_name: str
+
+
+@contextlib.contextmanager
+def _provide_oracle_service(
+ docker_service: DockerService,
+ image: str,
+ name: str,
+ service_name: str,
+) -> Generator[OracleService, None, None]:
+ user = "app"
+ password = "super-secret"
+ system_password = "super-secret"
+
+ def check(_service: ServiceContainer) -> bool:
+ try:
+ conn = oracledb.connect(
+ host=_service.host,
+ port=_service.port,
+ user=user,
+ password=password,
+ service_name=service_name,
+ )
+ with conn.cursor() as cursor:
+ cursor.execute("SELECT 1 FROM dual")
+ resp = cursor.fetchone()
+ return resp[0] == 1 if resp is not None else False
+ except Exception: # noqa: BLE001
+ return False
+
+ worker_num = get_xdist_worker_num()
+ if worker_num is not None:
+ name = f"{name}_{worker_num}"
+
+ with docker_service.run(
+ image=image,
+ name=name,
+ check=check,
+ container_port=1521,
+ timeout=60,
+ env={
+ "ORACLE_PASSWORD": system_password,
+ "APP_USER_PASSWORD": password,
+ "APP_USER": user,
+ },
+ ) as service:
+ yield OracleService(
+ host=service.host,
+ port=service.port,
+ system_password=system_password,
+ user=user,
+ password=password,
+ service_name=service_name,
+ )
@pytest.fixture(autouse=False, scope="session")
-def oracle23ai_service(
- oracle_docker_services: DockerServiceRegistry,
- oracle_docker_compose_files: list[Path],
- oracle23ai_port: int,
- oracle23ai_service_name: str,
- oracle_system_password: str,
- oracle_user: str,
- oracle_password: str,
-) -> Generator[None, None, None]:
- os.environ["ORACLE_PASSWORD"] = oracle_password
- os.environ["ORACLE_SYSTEM_PASSWORD"] = oracle_system_password
- os.environ["ORACLE_USER"] = oracle_user
- os.environ["ORACLE23AI_SERVICE_NAME"] = oracle23ai_service_name
- os.environ["ORACLE23AI_PORT"] = str(oracle23ai_port)
- oracle_docker_services.start(
- "oracle23ai",
- docker_compose_files=oracle_docker_compose_files,
- timeout=90,
- pause=1,
- check=oracle_responsive,
- port=oracle23ai_port,
- service_name=oracle23ai_service_name,
- user=oracle_user,
- password=oracle_password,
- )
- yield
+def oracle_23ai_service(docker_service: DockerService) -> Generator[OracleService, None, None]:
+ with _provide_oracle_service(
+ image="gvenzl/oracle-free:23-slim-faststart",
+ name="oracle23ai",
+ service_name="FREEPDB1",
+ docker_service=docker_service,
+ ) as service:
+ yield service
@pytest.fixture(autouse=False, scope="session")
-def oracle18c_service(
- oracle_docker_services: DockerServiceRegistry,
- oracle_docker_compose_files: list[Path],
- oracle18c_port: int,
- oracle18c_service_name: str,
- oracle_system_password: str,
- oracle_user: str,
- oracle_password: str,
-) -> Generator[None, None, None]:
- os.environ["ORACLE_PASSWORD"] = oracle_password
- os.environ["ORACLE_SYSTEM_PASSWORD"] = oracle_system_password
- os.environ["ORACLE_USER"] = oracle_user
- os.environ["ORACLE18C_SERVICE_NAME"] = oracle18c_service_name
- os.environ["ORACLE18C_PORT"] = str(oracle18c_port)
- oracle_docker_services.start(
- "oracle18c",
- docker_compose_files=oracle_docker_compose_files,
- timeout=90,
- pause=1,
- check=oracle_responsive,
- port=oracle18c_port,
- service_name=oracle18c_service_name,
- user=oracle_user,
- password=oracle_password,
- )
- yield
+def oracle_18c_service(docker_service: DockerService) -> Generator[OracleService, None, None]:
+ with _provide_oracle_service(
+ image="gvenzl/oracle-free:23-slim-faststart",
+ name="oracle18c",
+ service_name="xepdb1",
+ docker_service=docker_service,
+ ) as service:
+ yield service
# alias to the latest
@pytest.fixture(autouse=False, scope="session")
-def oracle_service(
- oracle_docker_services: DockerServiceRegistry,
- default_oracle_service_name: str,
- oracle_docker_compose_files: list[Path],
- oracle_port: int,
- oracle_service_name: str,
- oracle_system_password: str,
- oracle_user: str,
- oracle_password: str,
-) -> Generator[None, None]:
- os.environ["ORACLE_PASSWORD"] = oracle_password
- os.environ["ORACLE_SYSTEM_PASSWORD"] = oracle_system_password
- os.environ["ORACLE_USER"] = oracle_user
- os.environ["ORACLE_SERVICE_NAME"] = oracle_service_name
- os.environ[f"{default_oracle_service_name.upper()}_PORT"] = str(oracle_port)
- oracle_docker_services.start(
- name=default_oracle_service_name,
- docker_compose_files=oracle_docker_compose_files,
- timeout=90,
- pause=1,
- check=oracle_responsive,
- port=oracle_port,
- service_name=oracle_service_name,
- user=oracle_user,
- password=oracle_password,
- )
- yield
+def oracle_service(oracle23ai_service: OracleService) -> OracleService:
+ return oracle23ai_service
@pytest.fixture(autouse=False, scope="session")
-def oracle_startup_connection(
- oracle_service: DockerServiceRegistry,
- oracle_docker_ip: str,
- oracle_port: int,
- oracle_service_name: str,
- oracle_user: str,
- oracle_password: str,
+def oracle_18c_connection(
+ oracle18c_service: OracleService,
) -> Generator[oracledb.Connection, None, None]:
with oracledb.connect(
- host=oracle_docker_ip,
- port=oracle_port,
- user=oracle_user,
- service_name=oracle_service_name,
- password=oracle_password,
+ host=oracle18c_service.host,
+ port=oracle18c_service.port,
+ user=oracle18c_service.user,
+ service_name=oracle18c_service.service_name,
+ password=oracle18c_service.password,
) as db_connection:
yield db_connection
@pytest.fixture(autouse=False, scope="session")
-def oracle18c_startup_connection(
- oracle18c_service: DockerServiceRegistry,
- oracle_docker_ip: str,
- oracle18c_port: int,
- oracle18c_service_name: str,
- oracle_user: str,
- oracle_password: str,
+def oracle_23ai_connection(
+ oracle23ai_service: OracleService,
) -> Generator[oracledb.Connection, None, None]:
with oracledb.connect(
- host=oracle_docker_ip,
- port=oracle18c_port,
- user=oracle_user,
- service_name=oracle18c_service_name,
- password=oracle_password,
+ host=oracle23ai_service.host,
+ port=oracle23ai_service.port,
+ user=oracle23ai_service.user,
+ service_name=oracle23ai_service.service_name,
+ password=oracle23ai_service.password,
) as db_connection:
yield db_connection
@pytest.fixture(autouse=False, scope="session")
-def oracle23ai_startup_connection(
- oracle23ai_service: DockerServiceRegistry,
- oracle_docker_ip: str,
- oracle23ai_port: int,
- oracle23ai_service_name: str,
- oracle_user: str,
- oracle_password: str,
-) -> Generator[oracledb.Connection, None, None]:
- with oracledb.connect(
- host=oracle_docker_ip,
- port=oracle23ai_port,
- user=oracle_user,
- service_name=oracle23ai_service_name,
- password=oracle_password,
- ) as db_connection:
- yield db_connection
+def oracle_startup_connection(oracle23ai_startup_connection: oracledb.Connection) -> oracledb.Connection:
+ return oracle23ai_startup_connection
diff --git a/src/pytest_databases/docker/postgres.py b/src/pytest_databases/docker/postgres.py
index 7469964..aa3a4e5 100644
--- a/src/pytest_databases/docker/postgres.py
+++ b/src/pytest_databases/docker/postgres.py
@@ -1,454 +1,315 @@
from __future__ import annotations
-import os
-import sys
-from pathlib import Path
+import dataclasses
+from contextlib import contextmanager
from typing import TYPE_CHECKING
import psycopg
import pytest
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases.helpers import get_xdist_worker_num
+from pytest_databases.types import ServiceContainer, XdistIsolationLevel
if TYPE_CHECKING:
from collections.abc import Generator
-
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-postgres-{simple_string_hash(__file__)}"
+ from pytest_databases._service import DockerService
def _make_connection_string(host: str, port: int, user: str, password: str, database: str) -> str:
return f"dbname={database} user={user} host={host} port={port} password={password}"
-def postgres_responsive(host: str, port: int, user: str, password: str, database: str) -> bool:
- try:
- with psycopg.connect(
- _make_connection_string(host=host, port=port, user=user, password=password, database=database)
- ) as conn:
- db_open = conn.execute("SELECT 1").fetchone()
- return bool(db_open is not None and db_open[0] == 1)
- except Exception: # noqa: BLE001
- return False
-
-
@pytest.fixture(scope="session")
-def postgres_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
+def xdist_postgres_isolation_level() -> XdistIsolationLevel:
+ return "database"
+
+
+@dataclasses.dataclass
+class PostgresService(ServiceContainer):
+ database: str
+ password: str
+ user: str
+
+
+@contextmanager
+def _provide_postgres_service(
+ docker_service: DockerService,
+ image: str,
+ name: str,
+ xdist_postgres_isolate: XdistIsolationLevel,
+) -> Generator[PostgresService, None, None]:
+ def check(_service: ServiceContainer) -> bool:
+ try:
+ with psycopg.connect(
+ _make_connection_string(
+ host=_service.host,
+ port=_service.port,
+ user="postgres",
+ password="super-secret",
+ database="postgres",
+ )
+ ) as conn:
+ db_open = conn.execute("SELECT 1").fetchone()
+ return bool(db_open is not None and db_open[0] == 1)
+ except Exception: # noqa: BLE001
+ return False
+
+ worker_num = get_xdist_worker_num()
+ db_name = "pytest_databases"
+ if worker_num is not None:
+ suffix = f"_{worker_num}"
+ if xdist_postgres_isolate == "server":
+ name += suffix
+ else:
+ db_name += suffix
+
+ with docker_service.run(
+ image=image,
+ check=check,
+ container_port=5432,
+ name=name,
+ env={
+ "POSTGRES_PASSWORD": "super-secret",
+ },
+ exec_after_start=f"psql -U postgres -d postgres -c 'CREATE DATABASE {db_name};'",
+ transient=xdist_postgres_isolate == "server",
+ ) as service:
+ yield PostgresService(
+ database=db_name,
+ host=service.host,
+ port=service.port,
+ user="postgres",
+ password="super-secret",
+ )
@pytest.fixture(autouse=False, scope="session")
-def postgres_docker_services(
- postgres_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=postgres_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def postgres_user() -> str:
- return "postgres"
-
-
-@pytest.fixture(scope="session")
-def postgres_password() -> str:
- return "super-secret"
-
-
-@pytest.fixture(scope="session")
-def postgres_database() -> str:
- return "postgres"
-
-
-@pytest.fixture(scope="session")
-def postgres11_port() -> int:
- return 5422
-
-
-@pytest.fixture(scope="session")
-def postgres12_port() -> int:
- return 5423
-
-
-@pytest.fixture(scope="session")
-def postgres13_port() -> int:
- return 5424
-
-
-@pytest.fixture(scope="session")
-def postgres14_port() -> int:
- return 5425
-
-
-@pytest.fixture(scope="session")
-def postgres15_port() -> int:
- return 5426
-
-
-@pytest.fixture(scope="session")
-def postgres16_port() -> int:
- return 5427
-
-@pytest.fixture(scope="session")
-def postgres17_port() -> int:
- return 5428
-
-
-@pytest.fixture(scope="session")
-def default_postgres_service_name() -> str:
- return "postgres17"
-
-
-@pytest.fixture(scope="session")
-def postgres_port(postgres17_port: int) -> int:
- return postgres17_port
+def postgres_11_service(
+ docker_service: DockerService,
+ xdist_postgres_isolation_level: XdistIsolationLevel,
+) -> Generator[PostgresService, None, None]:
+ with _provide_postgres_service(
+ docker_service,
+ image="postgres:11",
+ name="postgres-11",
+ xdist_postgres_isolate=xdist_postgres_isolation_level,
+ ) as service:
+ yield service
-@pytest.fixture(scope="session")
-def postgres_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.postgres.yml")]
+@pytest.fixture(autouse=False, scope="session")
+def postgres_12_service(
+ docker_service: DockerService,
+ xdist_postgres_isolation_level: XdistIsolationLevel,
+) -> Generator[PostgresService, None, None]:
+ with _provide_postgres_service(
+ docker_service,
+ image="postgres:12",
+ name="postgres-12",
+ xdist_postgres_isolate=xdist_postgres_isolation_level,
+ ) as service:
+ yield service
-@pytest.fixture(scope="session")
-def postgres_docker_ip(postgres_docker_services: DockerServiceRegistry) -> str:
- return postgres_docker_services.docker_ip
+@pytest.fixture(autouse=False, scope="session")
+def postgres_13_service(
+ docker_service: DockerService,
+ xdist_postgres_isolation_level: XdistIsolationLevel,
+) -> Generator[PostgresService, None, None]:
+ with _provide_postgres_service(
+ docker_service,
+ image="postgres:13",
+ name="postgres-13",
+ xdist_postgres_isolate=xdist_postgres_isolation_level,
+ ) as service:
+ yield service
@pytest.fixture(autouse=False, scope="session")
-def postgres12_service(
- postgres_docker_services: DockerServiceRegistry,
- postgres_docker_compose_files: list[Path],
- postgres12_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> DockerServiceRegistry:
- os.environ["POSTGRES_PASSWORD"] = postgres_password
- os.environ["POSTGRES_USER"] = postgres_user
- os.environ["POSTGRES_DATABASE"] = postgres_database
- os.environ["POSTGRES12_PORT"] = str(postgres12_port)
- postgres_docker_services.start(
- "postgres12",
- docker_compose_files=postgres_docker_compose_files,
- timeout=45,
- pause=1,
- check=postgres_responsive,
- port=postgres12_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- return postgres_docker_services
+def postgres_14_service(
+ docker_service: DockerService,
+ xdist_postgres_isolation_level: XdistIsolationLevel,
+) -> Generator[PostgresService, None, None]:
+ with _provide_postgres_service(
+ docker_service,
+ image="postgres:14",
+ name="postgres-14",
+ xdist_postgres_isolate=xdist_postgres_isolation_level,
+ ) as service:
+ yield service
@pytest.fixture(autouse=False, scope="session")
-def postgres13_service(
- postgres_docker_services: DockerServiceRegistry,
- postgres_docker_compose_files: list[Path],
- postgres13_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> DockerServiceRegistry:
- os.environ["POSTGRES_PASSWORD"] = postgres_password
- os.environ["POSTGRES_USER"] = postgres_user
- os.environ["POSTGRES_DATABASE"] = postgres_database
- os.environ["POSTGRES13_PORT"] = str(postgres13_port)
- postgres_docker_services.start(
- "postgres13",
- docker_compose_files=postgres_docker_compose_files,
- timeout=45,
- pause=1,
- check=postgres_responsive,
- port=postgres13_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- return postgres_docker_services
+def postgres_15_service(
+ docker_service: DockerService,
+ xdist_postgres_isolation_level: XdistIsolationLevel,
+) -> Generator[PostgresService, None, None]:
+ with _provide_postgres_service(
+ docker_service,
+ image="postgres:15",
+ name="postgres-15",
+ xdist_postgres_isolate=xdist_postgres_isolation_level,
+ ) as service:
+ yield service
@pytest.fixture(autouse=False, scope="session")
-def postgres14_service(
- postgres_docker_services: DockerServiceRegistry,
- postgres_docker_compose_files: list[Path],
- postgres14_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> DockerServiceRegistry:
- os.environ["POSTGRES_PASSWORD"] = postgres_password
- os.environ["POSTGRES_USER"] = postgres_user
- os.environ["POSTGRES_DATABASE"] = postgres_database
- os.environ["POSTGRES14_PORT"] = str(postgres14_port)
- postgres_docker_services.start(
- "postgres14",
- docker_compose_files=postgres_docker_compose_files,
- timeout=45,
- pause=1,
- check=postgres_responsive,
- port=postgres14_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- return postgres_docker_services
+def postgres_16_service(
+ docker_service: DockerService,
+ xdist_postgres_isolation_level: XdistIsolationLevel,
+) -> Generator[PostgresService, None, None]:
+ with _provide_postgres_service(
+ docker_service,
+ image="postgres:16",
+ name="postgres-16",
+ xdist_postgres_isolate=xdist_postgres_isolation_level,
+ ) as service:
+ yield service
@pytest.fixture(autouse=False, scope="session")
-def postgres15_service(
- postgres_docker_services: DockerServiceRegistry,
- postgres_docker_compose_files: list[Path],
- postgres15_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> DockerServiceRegistry:
- os.environ["POSTGRES_PASSWORD"] = postgres_password
- os.environ["POSTGRES_USER"] = postgres_user
- os.environ["POSTGRES_DATABASE"] = postgres_database
- os.environ["POSTGRES15_PORT"] = str(postgres15_port)
- postgres_docker_services.start(
- "postgres15",
- docker_compose_files=postgres_docker_compose_files,
- timeout=45,
- pause=1,
- check=postgres_responsive,
- port=postgres15_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- return postgres_docker_services
+def postgres_17_service(
+ docker_service: DockerService,
+ xdist_postgres_isolation_level: XdistIsolationLevel,
+) -> Generator[PostgresService, None, None]:
+ with _provide_postgres_service(
+ docker_service,
+ image="postgres:17",
+ name="postgres-17",
+ xdist_postgres_isolate=xdist_postgres_isolation_level,
+ ) as service:
+ yield service
@pytest.fixture(autouse=False, scope="session")
-def postgres16_service(
- postgres_docker_services: DockerServiceRegistry,
- postgres_docker_compose_files: list[Path],
- postgres16_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> DockerServiceRegistry:
- os.environ["POSTGRES_PASSWORD"] = postgres_password
- os.environ["POSTGRES_USER"] = postgres_user
- os.environ["POSTGRES_DATABASE"] = postgres_database
- os.environ["POSTGRES16_PORT"] = str(postgres16_port)
- postgres_docker_services.start(
- "postgres16",
- docker_compose_files=postgres_docker_compose_files,
- timeout=45,
- pause=1,
- check=postgres_responsive,
- port=postgres16_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- return postgres_docker_services
+def postgres_service(postgres_17_service: PostgresService) -> PostgresService:
+ return postgres_17_service
@pytest.fixture(autouse=False, scope="session")
-def postgres17_service(
- postgres_docker_services: DockerServiceRegistry,
- postgres_docker_compose_files: list[Path],
- postgres17_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> Generator[DockerServiceRegistry, None, None]:
- os.environ["POSTGRES_PASSWORD"] = postgres_password
- os.environ["POSTGRES_USER"] = postgres_user
- os.environ["POSTGRES_DATABASE"] = postgres_database
- os.environ["POSTGRES17_PORT"] = str(postgres17_port)
- postgres_docker_services.start(
- "postgres17",
- docker_compose_files=postgres_docker_compose_files,
- timeout=45,
- pause=1,
- check=postgres_responsive,
- port=postgres17_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- yield postgres_docker_services
-
-
-# alias to the latest
-@pytest.fixture(autouse=False, scope="session")
-def postgres_service(
- postgres_docker_services: DockerServiceRegistry,
- default_postgres_service_name: str,
- postgres_docker_compose_files: list[Path],
- postgres_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> DockerServiceRegistry:
- os.environ["POSTGRES_PASSWORD"] = postgres_password
- os.environ["POSTGRES_USER"] = postgres_user
- os.environ["POSTGRES_DATABASE"] = postgres_database
- os.environ[f"{default_postgres_service_name.upper()}_PORT"] = str(postgres_port)
- postgres_docker_services.start(
- name=default_postgres_service_name,
- docker_compose_files=postgres_docker_compose_files,
- timeout=45,
- pause=1,
- check=postgres_responsive,
- port=postgres_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- return postgres_docker_services
+def postgres_connection(
+ postgres_service: PostgresService,
+) -> Generator[psycopg.Connection, None, None]:
+ with psycopg.connect(
+ _make_connection_string(
+ host=postgres_service.host,
+ port=postgres_service.port,
+ user=postgres_service.user,
+ password=postgres_service.password,
+ database=postgres_service.database,
+ ),
+ ) as conn:
+ yield conn
@pytest.fixture(autouse=False, scope="session")
-def postgres_startup_connection(
- postgres_service: DockerServiceRegistry,
- postgres_docker_ip: str,
- postgres_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
+def postgres_11_connection(
+ postgres_11_service: PostgresService,
) -> Generator[psycopg.Connection, None, None]:
with psycopg.connect(
_make_connection_string(
- host=postgres_docker_ip,
- port=postgres_port,
- user=postgres_user,
- password=postgres_password,
- database=postgres_database,
+ host=postgres_11_service.host,
+ port=postgres_11_service.port,
+ user=postgres_11_service.user,
+ password=postgres_11_service.password,
+ database=postgres_11_service.database,
),
) as conn:
yield conn
+
@pytest.fixture(autouse=False, scope="session")
-def postgres17_startup_connection(
- postgres17_service: DockerServiceRegistry,
- postgres_docker_ip: str,
- postgres17_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
+def postgres_12_connection(
+ postgres_12_service: PostgresService,
) -> Generator[psycopg.Connection, None, None]:
with psycopg.connect(
_make_connection_string(
- host=postgres_docker_ip,
- port=postgres17_port,
- user=postgres_user,
- password=postgres_password,
- database=postgres_database,
+ host=postgres_12_service.host,
+ port=postgres_12_service.port,
+ user=postgres_12_service.user,
+ password=postgres_12_service.password,
+ database=postgres_12_service.database,
),
) as conn:
yield conn
+
@pytest.fixture(autouse=False, scope="session")
-def postgres16_startup_connection(
- postgres16_service: DockerServiceRegistry,
- postgres_docker_ip: str,
- postgres16_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
+def postgres_13_connection(
+ postgres_13_service: PostgresService,
) -> Generator[psycopg.Connection, None, None]:
with psycopg.connect(
_make_connection_string(
- host=postgres_docker_ip,
- port=postgres16_port,
- user=postgres_user,
- password=postgres_password,
- database=postgres_database,
+ host=postgres_13_service.host,
+ port=postgres_13_service.port,
+ user=postgres_13_service.user,
+ password=postgres_13_service.password,
+ database=postgres_13_service.database,
),
) as conn:
yield conn
@pytest.fixture(autouse=False, scope="session")
-def postgres15_startup_connection(
- postgres15_service: DockerServiceRegistry,
- postgres_docker_ip: str,
- postgres15_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
+def postgres_14_connection(
+ postgres_14_service: PostgresService,
) -> Generator[psycopg.Connection, None, None]:
with psycopg.connect(
_make_connection_string(
- host=postgres_docker_ip,
- port=postgres15_port,
- user=postgres_user,
- password=postgres_password,
- database=postgres_database,
+ host=postgres_14_service.host,
+ port=postgres_14_service.port,
+ user=postgres_14_service.user,
+ password=postgres_14_service.password,
+ database=postgres_14_service.database,
),
) as conn:
yield conn
@pytest.fixture(autouse=False, scope="session")
-def postgres14_startup_connection(
- postgres14_service: DockerServiceRegistry,
- postgres_docker_ip: str,
- postgres14_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
+def postgres_15_connection(
+ postgres_15_service: PostgresService,
) -> Generator[psycopg.Connection, None, None]:
with psycopg.connect(
_make_connection_string(
- host=postgres_docker_ip,
- port=postgres14_port,
- user=postgres_user,
- password=postgres_password,
- database=postgres_database,
+ host=postgres_15_service.host,
+ port=postgres_15_service.port,
+ user=postgres_15_service.user,
+ password=postgres_15_service.password,
+ database=postgres_15_service.database,
),
) as conn:
yield conn
@pytest.fixture(autouse=False, scope="session")
-def postgres13_startup_connection(
- postgres13_service: DockerServiceRegistry,
- postgres_docker_ip: str,
- postgres13_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
+def postgres_16_connection(
+ postgres_16_service: PostgresService,
) -> Generator[psycopg.Connection, None, None]:
with psycopg.connect(
_make_connection_string(
- host=postgres_docker_ip,
- port=postgres13_port,
- user=postgres_user,
- password=postgres_password,
- database=postgres_database,
+ host=postgres_16_service.host,
+ port=postgres_16_service.port,
+ user=postgres_16_service.user,
+ password=postgres_16_service.password,
+ database=postgres_16_service.database,
),
) as conn:
yield conn
@pytest.fixture(autouse=False, scope="session")
-def postgres12_startup_connection(
- postgres12_service: DockerServiceRegistry,
- postgres_docker_ip: str,
- postgres12_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
+def postgres_17_connection(
+ postgres_17_service: PostgresService,
) -> Generator[psycopg.Connection, None, None]:
with psycopg.connect(
_make_connection_string(
- host=postgres_docker_ip,
- port=postgres12_port,
- user=postgres_user,
- password=postgres_password,
- database=postgres_database,
+ host=postgres_17_service.host,
+ port=postgres_17_service.port,
+ user=postgres_17_service.user,
+ password=postgres_17_service.password,
+ database=postgres_17_service.database,
),
) as conn:
yield conn
diff --git a/src/pytest_databases/docker/redis.py b/src/pytest_databases/docker/redis.py
index 82436f8..865af77 100644
--- a/src/pytest_databases/docker/redis.py
+++ b/src/pytest_databases/docker/redis.py
@@ -1,26 +1,31 @@
from __future__ import annotations
-import os
-import sys
-from pathlib import Path
-from typing import TYPE_CHECKING
+import dataclasses
+from typing import TYPE_CHECKING, Generator
import pytest
from redis import Redis
from redis.exceptions import ConnectionError as RedisConnectionError
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases.helpers import get_xdist_worker_num
+from pytest_databases.types import ServiceContainer, XdistIsolationLevel
if TYPE_CHECKING:
- from collections.abc import Generator
+ from pytest_databases._service import DockerService
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-redis-{simple_string_hash(__file__)}"
+@dataclasses.dataclass
+class RedisService(ServiceContainer):
+ db: int
-def redis_responsive(host: str, port: int) -> bool:
- client = Redis(host=host, port=port)
+@pytest.fixture(scope="session")
+def xdist_redis_isolation_level() -> XdistIsolationLevel:
+ return "database"
+
+
+def redis_responsive(service_container: ServiceContainer) -> bool:
+ client = Redis(host=service_container.host, port=service_container.port)
try:
return client.ping()
except (ConnectionError, RedisConnectionError):
@@ -30,53 +35,42 @@ def redis_responsive(host: str, port: int) -> bool:
@pytest.fixture(scope="session")
-def redis_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(autouse=False, scope="session")
-def redis_docker_services(
- redis_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=redis_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def redis_port() -> int:
- return 6397
-
-
-@pytest.fixture(scope="session")
-def redis_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.redis.yml")]
+def redis_port(redis_service: RedisService) -> int:
+ return redis_service.port
@pytest.fixture(scope="session")
-def default_redis_service_name() -> str:
- return "redis"
+def redis_host(redis_service: RedisService) -> str:
+ return redis_service.host
@pytest.fixture(scope="session")
-def redis_docker_ip(redis_docker_services: DockerServiceRegistry) -> str:
- return redis_docker_services.docker_ip
+def redis_image() -> str:
+ return "redis:latest"
@pytest.fixture(autouse=False, scope="session")
def redis_service(
- redis_docker_services: DockerServiceRegistry,
- default_redis_service_name: str,
- redis_docker_compose_files: list[Path],
- redis_port: int,
-) -> Generator[None, None, None]:
- os.environ["REDIS_PORT"] = str(redis_port)
- redis_docker_services.start(
- name=default_redis_service_name,
- docker_compose_files=redis_docker_compose_files,
+ docker_service: DockerService,
+ redis_image: str,
+ xdist_redis_isolation_level: XdistIsolationLevel,
+) -> Generator[RedisService, None, None]:
+ worker_num = get_xdist_worker_num()
+ name = "redis"
+ db = 0
+ if worker_num is not None:
+ if xdist_redis_isolation_level == "database":
+ container_num = worker_num // 1
+ name += f"_{container_num + 1}"
+ db = worker_num
+ else:
+ name += f"_{worker_num + 1}"
+
+ with docker_service.run(
+ redis_image,
check=redis_responsive,
- port=redis_port,
- )
- yield
+ container_port=6379,
+ name=name,
+ transient=xdist_redis_isolation_level == "server",
+ ) as service:
+ yield RedisService(host=service.host, port=service.port, db=db)
diff --git a/src/pytest_databases/docker/spanner.py b/src/pytest_databases/docker/spanner.py
index a6ed3f2..8ef98c3 100644
--- a/src/pytest_databases/docker/spanner.py
+++ b/src/pytest_databases/docker/spanner.py
@@ -1,142 +1,72 @@
from __future__ import annotations
-import contextlib
-import os
-import sys
-from pathlib import Path
+from dataclasses import dataclass
from typing import TYPE_CHECKING
import pytest
+from google.api_core.client_options import ClientOptions
from google.auth.credentials import AnonymousCredentials, Credentials
from google.cloud import spanner
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.helpers import simple_string_hash
+from pytest_databases.helpers import get_xdist_worker_num
+from pytest_databases.types import ServiceContainer
if TYPE_CHECKING:
from collections.abc import Generator
-
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-spanner-{simple_string_hash(__file__)}"
-
-
-def spanner_responsive(
- host: str,
- spanner_port: int,
- spanner_instance: str,
- spanner_database: str,
- spanner_project: str,
- spanner_credentials: Credentials,
-) -> bool:
- try:
- spanner_client = spanner.Client(project=spanner_project, credentials=spanner_credentials)
- instance = spanner_client.instance(spanner_instance)
- with contextlib.suppress(Exception):
- instance.create()
-
- database = instance.database(spanner_database)
- with contextlib.suppress(Exception):
- database.create()
-
- with database.snapshot() as snapshot:
- resp = next(iter(snapshot.execute_sql("SELECT 1")))
- return resp[0] == 1
- except Exception: # noqa: BLE001
- return False
+ from pytest_databases._service import DockerService
@pytest.fixture(scope="session")
-def spanner_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
+def spanner_image() -> str:
+ return "gcr.io/cloud-spanner-emulator/emulator:latest"
-@pytest.fixture(autouse=False, scope="session")
-def spanner_docker_services(
- spanner_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
- with DockerServiceRegistry(worker_id, compose_project_name=spanner_compose_project_name) as registry:
- yield registry
+@dataclass
+class SpannerService(ServiceContainer):
+ credentials: Credentials
+ project: str
+ database_name: str
+ instance_name: str
+ @property
+ def endpoint(self) -> str:
+ return f"{self.host}:{self.port}"
-@pytest.fixture(scope="session")
-def spanner_port() -> int:
- return 9010
-
-
-@pytest.fixture(scope="session")
-def spanner_instance() -> str:
- return "test-instance"
-
-
-@pytest.fixture(scope="session")
-def spanner_database() -> str:
- return "test-database"
-
-
-@pytest.fixture(scope="session")
-def spanner_project() -> str:
- return "emulator-test-project"
-
-
-@pytest.fixture(scope="session")
-def spanner_credentials() -> Credentials:
- return AnonymousCredentials()
-
-
-@pytest.fixture(scope="session")
-def spanner_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.spanner.yml")]
-
-
-@pytest.fixture(scope="session")
-def default_spanner_service_name() -> str:
- return "spanner"
-
-
-@pytest.fixture(scope="session")
-def spanner_docker_ip(spanner_docker_services: DockerServiceRegistry) -> str:
- return spanner_docker_services.docker_ip
+ @property
+ def client_options(self) -> ClientOptions:
+ return ClientOptions(api_endpoint=self.endpoint)
@pytest.fixture(autouse=False, scope="session")
-def spanner_service(
- spanner_docker_services: DockerServiceRegistry,
- default_spanner_service_name: str,
- spanner_docker_compose_files: list[Path],
- spanner_docker_ip: str,
- spanner_port: int,
- spanner_instance: str,
- spanner_database: str,
- spanner_project: str,
- spanner_credentials: Credentials,
-) -> Generator[None, None, None]:
- os.environ["SPANNER_EMULATOR_HOST"] = f"{spanner_docker_ip}:{spanner_port}"
- os.environ["SPANNER_DATABASE"] = spanner_database
- os.environ["SPANNER_INSTANCE"] = spanner_instance
- os.environ["SPANNER_PORT"] = str(spanner_port)
- os.environ["GOOGLE_CLOUD_PROJECT"] = spanner_project
- spanner_docker_services.start(
- name=default_spanner_service_name,
- docker_compose_files=spanner_docker_compose_files,
- timeout=60,
- check=spanner_responsive,
- spanner_port=spanner_port,
- spanner_instance=spanner_instance,
- spanner_database=spanner_database,
- spanner_project=spanner_project,
- spanner_credentials=spanner_credentials,
- )
- yield
+def spanner_service(docker_service: DockerService, spanner_image: str) -> Generator[SpannerService, None, None]:
+ with docker_service.run(
+ image=spanner_image,
+ name=f"pytest_databases_spanner_{get_xdist_worker_num() or 0}",
+ container_port=9010,
+ wait_for_log="gRPC server listening at",
+ transient=True,
+ ) as service:
+ yield SpannerService(
+ host=service.host,
+ port=service.port,
+ credentials=AnonymousCredentials(),
+ project="emulator-test-project",
+ instance_name="emulator-test-instance",
+ database_name="emulator-test-database",
+ )
@pytest.fixture(autouse=False, scope="session")
-def spanner_startup_connection(
- spanner_service: DockerServiceRegistry,
- spanner_project: str,
- spanner_credentials: Credentials,
+def spanner_connection(
+ spanner_service: SpannerService,
) -> Generator[spanner.Client, None, None]:
- c = spanner.Client(project=spanner_project, credentials=spanner_credentials)
- yield c
+ client = spanner.Client(
+ project=spanner_service.project,
+ credentials=spanner_service.credentials,
+ client_options=spanner_service.client_options,
+ )
+ try:
+ yield client
+ finally:
+ client.close()
diff --git a/src/pytest_databases/docker/valkey.py b/src/pytest_databases/docker/valkey.py
deleted file mode 100644
index fd208d3..0000000
--- a/src/pytest_databases/docker/valkey.py
+++ /dev/null
@@ -1,71 +0,0 @@
-from __future__ import annotations
-
-import os
-import sys
-from pathlib import Path
-from typing import TYPE_CHECKING
-
-import pytest
-
-from pytest_databases.docker import DockerServiceRegistry
-from pytest_databases.docker.redis import redis_responsive
-from pytest_databases.helpers import simple_string_hash
-
-if TYPE_CHECKING:
- from collections.abc import Generator
-
-
-COMPOSE_PROJECT_NAME: str = f"pytest-databases-valkey-{simple_string_hash(__file__)}"
-
-
-@pytest.fixture(scope="session")
-def valkey_compose_project_name() -> str:
- return os.environ.get("COMPOSE_PROJECT_NAME", COMPOSE_PROJECT_NAME)
-
-
-@pytest.fixture(autouse=False, scope="session")
-def valkey_docker_services(
- valkey_compose_project_name: str, worker_id: str = "main"
-) -> Generator[DockerServiceRegistry, None, None]:
- if os.getenv("GITHUB_ACTIONS") == "true" and sys.platform != "linux":
- pytest.skip("Docker not available on this platform")
-
- with DockerServiceRegistry(worker_id, compose_project_name=valkey_compose_project_name) as registry:
- yield registry
-
-
-@pytest.fixture(scope="session")
-def valkey_port() -> int:
- return 6308
-
-
-@pytest.fixture(scope="session")
-def valkey_docker_compose_files() -> list[Path]:
- return [Path(Path(__file__).parent / "docker-compose.valkey.yml")]
-
-
-@pytest.fixture(scope="session")
-def default_valkey_service_name() -> str:
- return "valkey"
-
-
-@pytest.fixture(scope="session")
-def valkey_docker_ip(valkey_docker_services: DockerServiceRegistry) -> str:
- return valkey_docker_services.docker_ip
-
-
-@pytest.fixture(autouse=False, scope="session")
-def valkey_service(
- valkey_docker_services: DockerServiceRegistry,
- default_valkey_service_name: str,
- valkey_docker_compose_files: list[Path],
- valkey_port: int,
-) -> Generator[None, None, None]:
- os.environ["REDIS_PORT"] = str(valkey_port)
- valkey_docker_services.start(
- name=default_valkey_service_name,
- docker_compose_files=valkey_docker_compose_files,
- check=redis_responsive,
- port=valkey_port,
- )
- yield
diff --git a/src/pytest_databases/helpers.py b/src/pytest_databases/helpers.py
index bfc73c2..7cd517e 100644
--- a/src/pytest_databases/helpers.py
+++ b/src/pytest_databases/helpers.py
@@ -1,6 +1,7 @@
from __future__ import annotations
import hashlib
+import os
def simple_string_hash(string_to_hash: str) -> str:
@@ -19,3 +20,18 @@ def simple_string_hash(string_to_hash: str) -> str:
digest = hasher.digest()
hex_string = digest.hex()
return hex_string[:12]
+
+
+def get_xdist_worker_id() -> str | None:
+ return os.getenv("PYTEST_XDIST_WORKER")
+
+
+def get_xdist_worker_num() -> int | None:
+ worker_id = get_xdist_worker_id()
+ if worker_id is None or worker_id == "master":
+ return None
+ return int(worker_id.replace("gw", ""))
+
+
+def get_xdist_worker_count() -> int:
+ return int(os.getenv("PYTEST_XDIST_WORKER_COUNT", "0"))
diff --git a/src/pytest_databases/types.py b/src/pytest_databases/types.py
new file mode 100644
index 0000000..6889393
--- /dev/null
+++ b/src/pytest_databases/types.py
@@ -0,0 +1,13 @@
+from __future__ import annotations
+
+import dataclasses
+from typing import Literal
+
+
+@dataclasses.dataclass
+class ServiceContainer:
+ host: str
+ port: int
+
+
+XdistIsolationLevel = Literal["database", "server"]
diff --git a/tests/conftest.py b/tests/conftest.py
index 0ea4e4f..d7b558c 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,12 +1,11 @@
from __future__ import annotations
-from pathlib import Path
-
import pytest
pytestmark = pytest.mark.anyio
-here = Path(__file__).parent
-root_path = here.parent
+
+
pytest_plugins = [
"pytest_databases.docker",
+ "pytester",
]
diff --git a/tests/docker/__init__.py b/tests/docker/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/docker/test_alloydb_omni.py b/tests/docker/test_alloydb_omni.py
deleted file mode 100644
index 01f398e..0000000
--- a/tests/docker/test_alloydb_omni.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-import pytest
-
-from pytest_databases.docker.alloydb_omni import alloydb_omni_responsive
-
-if TYPE_CHECKING:
- import psycopg
-
- from pytest_databases.docker import DockerServiceRegistry
-
-
-pytestmark = pytest.mark.skip
-
-pytest_plugins = [
- "pytest_databases.docker.alloydb_omni",
-]
-
-
-def test_alloydb_omni_default_config(
- default_alloydb_omni_service_name: str,
- alloydb_omni_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- assert default_alloydb_omni_service_name == "alloydb"
- assert alloydb_omni_port == 5420
- assert postgres_database == "postgres"
- assert postgres_user == "postgres"
- assert postgres_password == "super-secret"
-
-
-def test_alloydb_omni_services(
- alloydb_docker_ip: str,
- alloydb_omni_service: DockerServiceRegistry,
- alloydb_omni_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- ping = alloydb_omni_responsive(
- alloydb_docker_ip,
- port=alloydb_omni_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- assert ping
-
-
-def test_alloydb_omni_service_after_start(
- postgres_startup_connection: psycopg.Connection,
-) -> None:
- postgres_startup_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1")
- result = postgres_startup_connection.execute("select * from simple_table").fetchone()
- assert bool(result is not None and result[0] == 1)
diff --git a/tests/docker/test_azure_blob.py b/tests/docker/test_azure_blob.py
deleted file mode 100644
index b0d3c71..0000000
--- a/tests/docker/test_azure_blob.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from azure.storage.blob import ContainerClient
-
-pytest_plugins = [
- "pytest_databases.docker.azure_blob",
-]
-
-
-def test_azure_blob_service(azure_blob_container_client: ContainerClient) -> None:
- assert not azure_blob_container_client.exists()
diff --git a/tests/docker/test_bigquery.py b/tests/docker/test_bigquery.py
deleted file mode 100644
index 4f3bf95..0000000
--- a/tests/docker/test_bigquery.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from google.cloud import bigquery
-
-from pytest_databases.docker.bigquery import bigquery_responsive
-
-if TYPE_CHECKING:
- from google.api_core.client_options import ClientOptions
- from google.auth.credentials import Credentials
-
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.bigquery",
-]
-
-
-def test_bigquery_default_config(
- bigquery_docker_ip: str,
- bigquery_port: int,
- bigquery_grpc_port: int,
- bigquery_dataset: str,
- bigquery_endpoint: str,
- bigquery_project: str,
-) -> None:
- assert bigquery_port == 9051
- assert bigquery_grpc_port == 9061
- assert bigquery_dataset == "test-dataset"
- assert bigquery_endpoint == f"http://{bigquery_docker_ip}:9051"
- assert bigquery_project == "emulator-test-project"
-
-
-def test_bigquery_services(
- bigquery_docker_ip: str,
- bigquery_service: DockerServiceRegistry,
- bigquery_endpoint: str,
- bigquery_dataset: str,
- bigquery_client_options: ClientOptions,
- bigquery_project: str,
- bigquery_credentials: Credentials,
-) -> None:
- ping = bigquery_responsive(
- bigquery_docker_ip,
- bigquery_endpoint=bigquery_endpoint,
- bigquery_dataset=bigquery_dataset,
- bigquery_project=bigquery_project,
- bigquery_credentials=bigquery_credentials,
- bigquery_client_options=bigquery_client_options,
- )
- assert ping
-
-
-def test_bigquery_service_after_start(
- bigquery_startup_connection: bigquery.Client,
-) -> None:
- assert isinstance(bigquery_startup_connection, bigquery.Client)
diff --git a/tests/docker/test_cockroachdb.py b/tests/docker/test_cockroachdb.py
deleted file mode 100644
index 145d9ba..0000000
--- a/tests/docker/test_cockroachdb.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from pytest_databases.docker.cockroachdb import cockroachdb_responsive
-
-if TYPE_CHECKING:
- import psycopg
-
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.cockroachdb",
-]
-
-
-def test_cockroachdb_default_config(
- cockroachdb_port: int, cockroachdb_database: str, cockroachdb_driver_opts: dict[str, str]
-) -> None:
- assert cockroachdb_port == 26257
- assert cockroachdb_database == "defaultdb"
- assert cockroachdb_driver_opts == {"sslmode": "disable"}
-
-
-def test_cockroachdb_service(
- cockroachdb_docker_ip: str,
- cockroachdb_service: DockerServiceRegistry,
- cockroachdb_database: str,
- cockroachdb_port: int,
- cockroachdb_driver_opts: dict[str, str],
-) -> None:
- ping = cockroachdb_responsive(
- cockroachdb_docker_ip, cockroachdb_port, cockroachdb_database, cockroachdb_driver_opts
- )
- assert ping
-
-
-def test_cockroachdb_services_after_start(
- cockroachdb_startup_connection: psycopg.Connection,
-) -> None:
- cockroachdb_startup_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
- result = cockroachdb_startup_connection.execute("select * from simple_table").fetchone()
- assert bool(result is not None and result[0] == 1)
diff --git a/tests/docker/test_dragonfly.py b/tests/docker/test_dragonfly.py
deleted file mode 100644
index ccfcdde..0000000
--- a/tests/docker/test_dragonfly.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from pytest_databases.docker.redis import redis_responsive
-
-if TYPE_CHECKING:
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.dragonfly",
-]
-
-
-def test_dragonfly_default_config(dragonfly_port: int) -> None:
- assert dragonfly_port == 6398
-
-
-def test_dragonfly_service(
- dragonfly_docker_ip: str,
- dragonfly_service: DockerServiceRegistry,
- dragonfly_port: int,
-) -> None:
- ping = redis_responsive(dragonfly_docker_ip, dragonfly_port)
- assert ping
diff --git a/tests/docker/test_elasticsearch.py b/tests/docker/test_elasticsearch.py
deleted file mode 100644
index 62dcf3c..0000000
--- a/tests/docker/test_elasticsearch.py
+++ /dev/null
@@ -1,143 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Generator
-from unittest import mock
-
-import pytest
-from elasticsearch7 import Elasticsearch as Elasticsearch7
-from elasticsearch8 import Elasticsearch as Elasticsearch8
-
-from pytest_databases.docker.elastic_search import elasticsearch7_responsive, elasticsearch8_responsive
-
-if TYPE_CHECKING:
- from pathlib import Path
-
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.elastic_search",
-]
-
-
-@pytest.fixture
-def elasticsearch7_service(
- elasticsearch_docker_services: DockerServiceRegistry,
- elasticsearch_docker_compose_files: list[Path],
- elasticsearch7_port: int,
- elasticsearch_database: str,
- elasticsearch_user: str,
- elasticsearch_password: str,
- elasticsearch_scheme: str,
-) -> Generator[None, None, None]:
- """Overwrites fixture to stop container after the test."""
- try:
- elasticsearch_docker_services.start(
- "elasticsearch7",
- docker_compose_files=elasticsearch_docker_compose_files,
- timeout=45,
- pause=1,
- check=elasticsearch7_responsive,
- port=elasticsearch7_port,
- database=elasticsearch_database,
- user=elasticsearch_user,
- password=elasticsearch_password,
- scheme=elasticsearch_scheme,
- )
- yield
- finally:
- elasticsearch_docker_services.stop("elasticsearch7")
-
-
-@pytest.fixture
-def elasticsearch8_service(
- elasticsearch_docker_services: DockerServiceRegistry,
- elasticsearch_docker_compose_files: list[Path],
- elasticsearch8_port: int,
- elasticsearch_database: str,
- elasticsearch_user: str,
- elasticsearch_password: str,
- elasticsearch_scheme: str,
-) -> Generator[None, None, None]:
- """Overwrites fixture to stop container after the test."""
- try:
- elasticsearch_docker_services.start(
- "elasticsearch8",
- docker_compose_files=elasticsearch_docker_compose_files,
- timeout=45,
- pause=1,
- check=elasticsearch8_responsive,
- port=elasticsearch8_port,
- database=elasticsearch_database,
- user=elasticsearch_user,
- password=elasticsearch_password,
- scheme=elasticsearch_scheme,
- )
- yield
- finally:
- elasticsearch_docker_services.stop("elasticsearch8")
-
-
-def test_elasticsearch7_default_config(
- elasticsearch7_port: str, elasticsearch_user: str, elasticsearch_password: str, elasticsearch_scheme: str
-) -> None:
- assert elasticsearch7_port == 9200
- assert elasticsearch_user == "elastic"
- assert elasticsearch_password == "changeme"
- assert elasticsearch_scheme == "http"
-
-
-def test_elasticsearch8_default_config(
- elasticsearch8_port: str, elasticsearch_user: str, elasticsearch_password: str, elasticsearch_scheme: str
-) -> None:
- assert elasticsearch8_port == 9201
- assert elasticsearch_user == "elastic"
- assert elasticsearch_password == "changeme"
- assert elasticsearch_scheme == "http"
-
-
-def test_elasticsearch7_service(
- elasticsearch_docker_ip: str,
- elasticsearch7_service: DockerServiceRegistry,
- elasticsearch7_port: str,
- elasticsearch_user: str,
- elasticsearch_password: str,
- elasticsearch_scheme: str,
-) -> None:
- with Elasticsearch7(
- hosts=[{"host": elasticsearch_docker_ip, "port": elasticsearch7_port, "scheme": elasticsearch_scheme}],
- verify_certs=False,
- http_auth=(elasticsearch_user, elasticsearch_password),
- ) as client:
- info = client.info()
-
- assert info["version"]["number"] == "7.17.19"
-
-
-def test_elasticsearch8_service(
- elasticsearch_docker_ip: str,
- elasticsearch8_service: DockerServiceRegistry,
- elasticsearch8_port: str,
- elasticsearch_user: str,
- elasticsearch_password: str,
- elasticsearch_scheme: str,
-) -> None:
- with Elasticsearch8(
- hosts=[{"host": elasticsearch_docker_ip, "port": elasticsearch8_port, "scheme": elasticsearch_scheme}],
- verify_certs=False,
- basic_auth=(elasticsearch_user, elasticsearch_password),
- ) as client:
- info = client.info()
-
- assert info["version"]["number"] == "8.13.0"
-
-
-@pytest.mark.parametrize(
- "responsive, path_to_mock",
- (
- (elasticsearch7_responsive, "pytest_databases.docker.elastic_search.Elasticsearch7.ping"),
- (elasticsearch8_responsive, "pytest_databases.docker.elastic_search.Elasticsearch8.ping"),
- ),
-)
-def test_elasticsearch_responsive(responsive: Callable, path_to_mock: str) -> None:
- with mock.patch(path_to_mock, mock.Mock(side_effect=Exception)):
- assert not responsive(scheme="", host="", port="", user="", password="", database="")
diff --git a/tests/docker/test_keydb.py b/tests/docker/test_keydb.py
deleted file mode 100644
index 3d568ec..0000000
--- a/tests/docker/test_keydb.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from pytest_databases.docker.redis import redis_responsive
-
-if TYPE_CHECKING:
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.keydb",
-]
-
-
-def test_keydb_default_config(keydb_port: int) -> None:
- assert keydb_port == 6396
-
-
-def test_keydb_service(
- keydb_docker_ip: str,
- keydb_service: DockerServiceRegistry,
- keydb_port: int,
-) -> None:
- ping = redis_responsive(keydb_docker_ip, keydb_port)
- assert ping
diff --git a/tests/docker/test_mariadb.py b/tests/docker/test_mariadb.py
deleted file mode 100644
index 8015638..0000000
--- a/tests/docker/test_mariadb.py
+++ /dev/null
@@ -1,95 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any
-
-from pytest_databases.docker.mysql import mysql_responsive
-
-if TYPE_CHECKING:
- from pytest_databases.docker import DockerServiceRegistry
-
-
-pytest_plugins = [
- "pytest_databases.docker.mariadb",
-]
-
-
-def test_mariadb_default_config(
- default_mariadb_service_name: str,
- mariadb_port: int,
- mariadb_database: str,
- mariadb_user: str,
- mariadb_password: str,
-) -> None:
- assert default_mariadb_service_name == "mariadb113"
- assert mariadb_port == 3359
- assert mariadb_database == "db"
- assert mariadb_user == "app"
- assert mariadb_password == "super-secret"
-
-
-def test_mariadb_113_config(
- mariadb113_port: int,
- mariadb_database: str,
- mariadb_user: str,
- mariadb_password: str,
-) -> None:
- assert mariadb113_port == 3359
- assert mariadb_database == "db"
- assert mariadb_user == "app"
- assert mariadb_password == "super-secret"
-
-
-def test_mariadb_services(
- mariadb_docker_ip: str,
- mariadb_service: DockerServiceRegistry,
- mariadb_port: int,
- mariadb_database: str,
- mariadb_user: str,
- mariadb_password: str,
-) -> None:
- ping = mysql_responsive(
- mariadb_docker_ip,
- port=mariadb_port,
- database=mariadb_database,
- user=mariadb_user,
- password=mariadb_password,
- )
- assert ping
-
-
-def test_mariadb_113_services(
- mariadb_docker_ip: str,
- mariadb113_service: DockerServiceRegistry,
- mariadb113_port: int,
- mariadb_database: str,
- mariadb_user: str,
- mariadb_password: str,
-) -> None:
- ping = mysql_responsive(
- mariadb_docker_ip,
- port=mariadb113_port,
- database=mariadb_database,
- user=mariadb_user,
- password=mariadb_password,
- )
- assert ping
-
-
-def test_mariadb_services_after_start(
- mariadb_startup_connection: Any,
-) -> None:
- with mariadb_startup_connection.cursor() as cursor:
- cursor.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
-
-
-def test_mariadb113_services_after_start(
- mariadb113_startup_connection: Any,
-) -> None:
- with mariadb113_startup_connection.cursor() as cursor:
- cursor.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
diff --git a/tests/docker/test_mssql.py b/tests/docker/test_mssql.py
deleted file mode 100644
index d622cce..0000000
--- a/tests/docker/test_mssql.py
+++ /dev/null
@@ -1,98 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from pytest_databases.docker.mssql import mssql_responsive
-
-if TYPE_CHECKING:
- import pyodbc
-
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.mssql",
-]
-
-
-def test_mssql_default_config(
- default_mssql_service_name: str,
- mssql_port: int,
- mssql_database: str,
- mssql_user: str,
- mssql_password: str,
-) -> None:
- assert default_mssql_service_name == "mssql2022"
- assert mssql_port == 4133
- assert mssql_database == "master"
- assert mssql_user == "sa"
- assert mssql_password == "Super-secret1"
-
-
-def test_mssql_2022_config(
- mssql2022_port: int,
- mssql_database: str,
- mssql_user: str,
- mssql_password: str,
-) -> None:
- assert mssql2022_port == 4133
- assert mssql_database == "master"
- assert mssql_user == "sa"
- assert mssql_password == "Super-secret1"
-
-
-def test_mssql_services(
- mssql_docker_ip: str,
- mssql_service: DockerServiceRegistry,
- mssql_port: int,
- mssql_database: str,
- mssql_user: str,
- mssql_password: str,
-) -> None:
- ping = mssql_responsive(
- mssql_docker_ip,
- port=mssql_port,
- database=mssql_database,
- user=mssql_user,
- password=mssql_password,
- )
- assert ping
-
-
-def test_mssql_2022_services(
- mssql_docker_ip: str,
- mssql2022_service: DockerServiceRegistry,
- mssql2022_port: int,
- mssql_database: str,
- mssql_user: str,
- mssql_password: str,
-) -> None:
- ping = mssql_responsive(
- mssql_docker_ip,
- port=mssql2022_port,
- database=mssql_database,
- user=mssql_user,
- password=mssql_password,
- )
- assert ping
-
-
-def test_mssql_services_after_start(
- mssql_startup_connection: pyodbc.Connection,
-) -> None:
- with mssql_startup_connection.cursor() as cursor:
- cursor.execute("CREATE view simple_table as SELECT 1 as the_value")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
- cursor.execute("drop view simple_table")
-
-
-async def test_mssql2022_services_after_start(
- mssql2022_startup_connection: pyodbc.Connection,
-) -> None:
- with mssql2022_startup_connection.cursor() as cursor:
- cursor.execute("CREATE view simple_table as SELECT 1 as the_value")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
- cursor.execute("drop view simple_table")
diff --git a/tests/docker/test_mysql.py b/tests/docker/test_mysql.py
deleted file mode 100644
index d4d457c..0000000
--- a/tests/docker/test_mysql.py
+++ /dev/null
@@ -1,156 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any
-
-from pytest_databases.docker.mysql import mysql_responsive
-
-if TYPE_CHECKING:
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.mysql",
-]
-
-
-def test_mysql_default_config(
- default_mysql_service_name: str,
- mysql_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> None:
- assert default_mysql_service_name == "mysql8"
- assert mysql_port == 3360
- assert mysql_database == "db"
- assert mysql_user == "app"
- assert mysql_password == "super-secret"
-
-
-def test_mysql_8_config(
- mysql8_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> None:
- assert mysql8_port == 3360
- assert mysql_database == "db"
- assert mysql_user == "app"
- assert mysql_password == "super-secret"
-
-
-def test_mysql_57_config(
- mysql57_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> None:
- assert mysql57_port == 3361
- assert mysql_database == "db"
- assert mysql_user == "app"
- assert mysql_password == "super-secret"
-
-
-def test_mysql_56_config(
- mysql56_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> None:
- assert mysql56_port == 3362
- assert mysql_database == "db"
- assert mysql_user == "app"
- assert mysql_password == "super-secret"
-
-
-def test_mysql_57_services(
- mysql_docker_ip: str,
- mysql57_service: DockerServiceRegistry,
- mysql57_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> None:
- ping = mysql_responsive(
- mysql_docker_ip,
- port=mysql57_port,
- database=mysql_database,
- user=mysql_user,
- password=mysql_password,
- )
- assert ping
-
-
-def test_mysql_56_services(
- mysql_docker_ip: str,
- mysql56_service: DockerServiceRegistry,
- mysql56_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> None:
- ping = mysql_responsive(
- mysql_docker_ip,
- port=mysql56_port,
- database=mysql_database,
- user=mysql_user,
- password=mysql_password,
- )
- assert ping
-
-
-def test_mysql_8_services(
- mysql_docker_ip: str,
- mysql8_service: DockerServiceRegistry,
- mysql8_port: int,
- mysql_database: str,
- mysql_user: str,
- mysql_password: str,
-) -> None:
- ping = mysql_responsive(
- mysql_docker_ip,
- port=mysql8_port,
- database=mysql_database,
- user=mysql_user,
- password=mysql_password,
- )
- assert ping
-
-
-def test_mysql_services_after_start(
- mysql_startup_connection: Any,
-) -> None:
- with mysql_startup_connection.cursor() as cursor:
- cursor.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
-
-
-def test_mysql56_services_after_start(
- mysql56_startup_connection: Any,
-) -> None:
- with mysql56_startup_connection.cursor() as cursor:
- cursor.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
-
-
-def test_mysql57_services_after_start(
- mysql57_startup_connection: Any,
-) -> None:
- with mysql57_startup_connection.cursor() as cursor:
- cursor.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
-
-
-def test_mysql8_services_after_start(
- mysql8_startup_connection: Any,
-) -> None:
- with mysql8_startup_connection.cursor() as cursor:
- cursor.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
diff --git a/tests/docker/test_oracle.py b/tests/docker/test_oracle.py
deleted file mode 100644
index 4f28da1..0000000
--- a/tests/docker/test_oracle.py
+++ /dev/null
@@ -1,114 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-import oracledb
-
-if TYPE_CHECKING:
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.oracle",
-]
-
-
-def test_oracle18c_default_config(
- oracle_user: str, oracle_password: str, oracle18c_service_name: str, oracle18c_port: int
-) -> None:
- assert oracle_user == "app"
- assert oracle_password == "super-secret"
- assert oracle18c_service_name == "xepdb1"
- assert oracle18c_port == 1514
-
-
-def test_oracle23ai_default_config(
- oracle_user: str, oracle_password: str, oracle23ai_service_name: str, oracle23ai_port: int
-) -> None:
- assert oracle_user == "app"
- assert oracle_password == "super-secret"
- assert oracle23ai_service_name == "FREEPDB1"
- assert oracle23ai_port == 1513
-
-
-def test_oracle_default_config(
- oracle_user: str,
- oracle_password: str,
- oracle_service_name: str,
- oracle_port: int,
- oracle23ai_port: int,
- default_oracle_service_name: str,
-) -> None:
- assert default_oracle_service_name == "oracle23ai"
- assert oracle_user == "app"
- assert oracle_password == "super-secret"
- assert oracle_service_name == "FREEPDB1"
- assert oracle_port == 1513
- assert oracle_port == oracle23ai_port
-
-
-def test_oracle18c_service(
- oracle_docker_ip: str,
- oracle18c_service: DockerServiceRegistry,
- oracle18c_service_name: str,
- oracle18c_port: int,
- oracle_user: str,
- oracle_password: str,
-) -> None:
- conn = oracledb.connect(
- user=oracle_user,
- password=oracle_password,
- dsn=f"{oracle_docker_ip}:{oracle18c_port!s}/{oracle18c_service_name}",
- )
- with conn.cursor() as cur:
- cur.execute("SELECT 'Hello World!' FROM dual")
- res = cur.fetchall()[0][0]
- assert "Hello World!" in res
-
-
-def test_oracle23ai_service(
- oracle_docker_ip: str,
- oracle23ai_service: DockerServiceRegistry,
- oracle23ai_service_name: str,
- oracle23ai_port: int,
- oracle_user: str,
- oracle_password: str,
-) -> None:
- conn = oracledb.connect(
- user=oracle_user,
- password=oracle_password,
- dsn=f"{oracle_docker_ip}:{oracle23ai_port!s}/{oracle23ai_service_name}",
- )
- with conn.cursor() as cur:
- cur.execute("SELECT 'Hello World!' FROM dual")
- res = cur.fetchall()[0][0]
- assert "Hello World!" in res
-
-
-def test_oracle_services_after_start(
- oracle_startup_connection: oracledb.Connection,
-) -> None:
- with oracle_startup_connection.cursor() as cursor:
- cursor.execute("CREATE or replace view simple_table as SELECT 1 as the_value from dual")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
-
-
-def test_oracle18c_services_after_start(
- oracle18c_startup_connection: oracledb.Connection,
-) -> None:
- with oracle18c_startup_connection.cursor() as cursor:
- cursor.execute("CREATE or replace view simple_table as SELECT 1 as the_value from dual")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
-
-
-def test_oracle23ai_services_after_start(
- oracle23ai_startup_connection: oracledb.Connection,
-) -> None:
- with oracle23ai_startup_connection.cursor() as cursor:
- cursor.execute("CREATE or replace view simple_table as SELECT 1 as the_value from dual")
- cursor.execute("select * from simple_table")
- result = cursor.fetchall()
- assert bool(result is not None and result[0][0] == 1)
diff --git a/tests/docker/test_postgres.py b/tests/docker/test_postgres.py
deleted file mode 100644
index be983e9..0000000
--- a/tests/docker/test_postgres.py
+++ /dev/null
@@ -1,269 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from pytest_databases.docker.postgres import postgres_responsive
-
-if TYPE_CHECKING:
- import psycopg
-
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.postgres",
-]
-
-
-def test_postgres_default_config(
- default_postgres_service_name: str,
- postgres_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- assert default_postgres_service_name == "postgres17"
- assert postgres_port == 5428
- assert postgres_database == "postgres"
- assert postgres_user == "postgres"
- assert postgres_password == "super-secret"
-
-
-def test_postgres_12_config(
- postgres12_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- assert postgres12_port == 5423
- assert postgres_database == "postgres"
- assert postgres_user == "postgres"
- assert postgres_password == "super-secret"
-
-
-def test_postgres_13_config(
- postgres13_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- assert postgres13_port == 5424
- assert postgres_database == "postgres"
- assert postgres_user == "postgres"
- assert postgres_password == "super-secret"
-
-
-def test_postgres_14_config(
- postgres14_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- assert postgres14_port == 5425
- assert postgres_database == "postgres"
- assert postgres_user == "postgres"
- assert postgres_password == "super-secret"
-
-
-def test_postgres_15_config(
- postgres15_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- assert postgres15_port == 5426
- assert postgres_database == "postgres"
- assert postgres_user == "postgres"
- assert postgres_password == "super-secret"
-
-
-def test_postgres_16_config(
- postgres16_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- assert postgres16_port == 5427
- assert postgres_database == "postgres"
- assert postgres_user == "postgres"
- assert postgres_password == "super-secret"
-
-def test_postgres_17_config(
- postgres17_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- assert postgres17_port == 5428
- assert postgres_database == "postgres"
- assert postgres_user == "postgres"
- assert postgres_password == "super-secret"
-
-def test_postgres_services(
- postgres_docker_ip: str,
- postgres_service: DockerServiceRegistry,
- postgres_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- ping = postgres_responsive(
- postgres_docker_ip,
- port=postgres_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- assert ping
-
-
-def test_postgres_12_services(
- postgres_docker_ip: str,
- postgres12_service: DockerServiceRegistry,
- postgres12_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- ping = postgres_responsive(
- postgres_docker_ip,
- port=postgres12_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- assert ping
-
-
-def test_postgres_13_services(
- postgres_docker_ip: str,
- postgres13_service: DockerServiceRegistry,
- postgres13_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- ping = postgres_responsive(
- postgres_docker_ip,
- port=postgres13_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- assert ping
-
-
-def test_postgres_14_services(
- postgres_docker_ip: str,
- postgres14_service: DockerServiceRegistry,
- postgres14_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- ping = postgres_responsive(
- postgres_docker_ip,
- port=postgres14_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- assert ping
-
-
-def test_postgres_15_services(
- postgres_docker_ip: str,
- postgres15_service: DockerServiceRegistry,
- postgres15_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- ping = postgres_responsive(
- postgres_docker_ip,
- port=postgres15_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- assert ping
-
-
-def test_postgres_16_services(
- postgres_docker_ip: str,
- postgres16_service: DockerServiceRegistry,
- postgres16_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- ping = postgres_responsive(
- postgres_docker_ip,
- port=postgres16_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- assert ping
-
-def test_postgres_17_services(
- postgres_docker_ip: str,
- postgres17_service: DockerServiceRegistry,
- postgres17_port: int,
- postgres_database: str,
- postgres_user: str,
- postgres_password: str,
-) -> None:
- ping = postgres_responsive(
- postgres_docker_ip,
- port=postgres17_port,
- database=postgres_database,
- user=postgres_user,
- password=postgres_password,
- )
- assert ping
-
-def test_postgres_17_services_after_start(
- postgres17_startup_connection: psycopg.Connection,
-) -> None:
- postgres17_startup_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1")
- result = postgres17_startup_connection.execute("select * from simple_table").fetchone()
- assert bool(result is not None and result[0] == 1)
-
-def test_postgres_16_services_after_start(
- postgres16_startup_connection: psycopg.Connection,
-) -> None:
- postgres16_startup_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1")
- result = postgres16_startup_connection.execute("select * from simple_table").fetchone()
- assert bool(result is not None and result[0] == 1)
-
-
-def test_postgres_15_services_after_start(
- postgres15_startup_connection: psycopg.Connection,
-) -> None:
- postgres15_startup_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1")
- result = postgres15_startup_connection.execute("select * from simple_table").fetchone()
- assert bool(result is not None and result[0] == 1)
-
-
-def test_postgres_14_services_after_start(
- postgres14_startup_connection: psycopg.Connection,
-) -> None:
- postgres14_startup_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1")
- result = postgres14_startup_connection.execute("select * from simple_table").fetchone()
- assert bool(result is not None and result[0] == 1)
-
-
-def test_postgres_13_services_after_start(
- postgres13_startup_connection: psycopg.Connection,
-) -> None:
- postgres13_startup_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1")
- result = postgres13_startup_connection.execute("select * from simple_table").fetchone()
- assert bool(result is not None and result[0] == 1)
-
-
-def test_postgres_12_services_after_start(
- postgres12_startup_connection: psycopg.Connection,
-) -> None:
- postgres12_startup_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1")
- result = postgres12_startup_connection.execute("select * from simple_table").fetchone()
- assert bool(result is not None and result[0] == 1)
diff --git a/tests/docker/test_redis.py b/tests/docker/test_redis.py
deleted file mode 100644
index 91416e4..0000000
--- a/tests/docker/test_redis.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from pytest_databases.docker.redis import redis_responsive
-
-if TYPE_CHECKING:
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.redis",
-]
-
-
-def test_redis_default_config(redis_port: int) -> None:
- assert redis_port == 6397
-
-
-def test_redis_service(
- redis_docker_ip: str,
- redis_service: DockerServiceRegistry,
- redis_port: int,
-) -> None:
- ping = redis_responsive(redis_docker_ip, redis_port)
- assert ping
diff --git a/tests/docker/test_spanner.py b/tests/docker/test_spanner.py
deleted file mode 100644
index 8e02b91..0000000
--- a/tests/docker/test_spanner.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from google.cloud import spanner
-
-from pytest_databases.docker.spanner import spanner_responsive
-
-if TYPE_CHECKING:
- from google.auth.credentials import Credentials
-
- from pytest_databases.docker import DockerServiceRegistry
-
-pytest_plugins = [
- "pytest_databases.docker.spanner",
-]
-
-
-def test_spanner_default_config(
- spanner_port: int, spanner_instance: str, spanner_database: str, spanner_project: str
-) -> None:
- assert spanner_port == 9010
- assert spanner_instance == "test-instance"
- assert spanner_database == "test-database"
- assert spanner_project == "emulator-test-project"
-
-
-def test_spanner_services(
- spanner_docker_ip: str,
- spanner_service: DockerServiceRegistry,
- spanner_port: int,
- spanner_instance: str,
- spanner_database: str,
- spanner_project: str,
- spanner_credentials: Credentials,
-) -> None:
- ping = spanner_responsive(
- spanner_docker_ip,
- spanner_port=spanner_port,
- spanner_instance=spanner_instance,
- spanner_database=spanner_database,
- spanner_project=spanner_project,
- spanner_credentials=spanner_credentials,
- )
- assert ping
-
-
-def test_spanner_service_after_start(
- spanner_startup_connection: spanner.Client,
-) -> None:
- assert isinstance(spanner_startup_connection, spanner.Client)
diff --git a/tests/docker/test_valkey.py b/tests/docker/test_valkey.py
deleted file mode 100644
index 0dd3683..0000000
--- a/tests/docker/test_valkey.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from pytest_databases.docker.valkey import redis_responsive
-
-if TYPE_CHECKING:
- from pytest_databases.docker import DockerServiceRegistry
-
-
-pytest_plugins = [
- "pytest_databases.docker.valkey",
-]
-
-
-def test_valkey_default_config(valkey_port: int) -> None:
- assert valkey_port == 6308
-
-
-def test_valkey_service(
- valkey_docker_ip: str,
- valkey_service: DockerServiceRegistry,
- valkey_port: int,
-) -> None:
- ping = redis_responsive(valkey_docker_ip, valkey_port)
- assert ping
diff --git a/tests/test_alloydb_omni.py b/tests/test_alloydb_omni.py
new file mode 100644
index 0000000..1cc101d
--- /dev/null
+++ b/tests/test_alloydb_omni.py
@@ -0,0 +1,90 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ import pytest
+
+
+def test_service_fixture(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string # noqa: PLC2701
+
+ pytest_plugins = ["pytest_databases.docker.alloydb_omni"]
+
+ def test(alloydb_omni_service) -> None:
+ with psycopg.connect(
+ _make_connection_string(
+ host=alloydb_omni_service.host,
+ port=alloydb_omni_service.port,
+ user=alloydb_omni_service.user,
+ password=alloydb_omni_service.password,
+ database=alloydb_omni_service.database,
+ )
+ ) as conn:
+ db_open = conn.execute("SELECT 1").fetchone()
+ assert db_open is not None and db_open[0] == 1
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def test_startup_connection_fixture(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string # noqa: PLC2701
+
+ pytest_plugins = ["pytest_databases.docker.alloydb_omni"]
+
+ def test(alloydb_omni_connection) -> None:
+ alloydb_omni_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1")
+ result = alloydb_omni_connection.execute("select * from simple_table").fetchone()
+ assert result is not None and result[0] == 1
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def test_xdist_isolate(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string
+
+ pytest_plugins = ["pytest_databases.docker.alloydb_omni"]
+
+
+ def test_one(alloydb_omni_service) -> None:
+ with psycopg.connect(
+ _make_connection_string(
+ host=alloydb_omni_service.host,
+ port=alloydb_omni_service.port,
+ user=alloydb_omni_service.user,
+ password=alloydb_omni_service.password,
+ database=alloydb_omni_service.database,
+ ),
+ autocommit=True,
+ ) as conn:
+ conn.execute("CREATE DATABASE foo")
+
+ def test_two(alloydb_omni_service) -> None:
+ with psycopg.connect(
+ _make_connection_string(
+ host=alloydb_omni_service.host,
+ port=alloydb_omni_service.port,
+ user=alloydb_omni_service.user,
+ password=alloydb_omni_service.password,
+ database=alloydb_omni_service.database,
+ ),
+ autocommit=True,
+ ) as conn:
+ conn.execute("CREATE DATABASE foo")
+ """)
+
+ result = pytester.runpytest_subprocess("-n", "2")
+ result.assert_outcomes(passed=2)
diff --git a/tests/test_azure_blob.py b/tests/test_azure_blob.py
new file mode 100644
index 0000000..d3c7ee7
--- /dev/null
+++ b/tests/test_azure_blob.py
@@ -0,0 +1,82 @@
+import pytest
+
+pytest_plugins = [
+ "pytest_databases.docker.azure_blob",
+]
+
+
+def test_default_no_xdist(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+import pytest
+from azure.storage.blob import ContainerClient
+
+pytest_plugins = [
+ "pytest_databases.docker.azure_blob",
+]
+
+
+def test_one(azure_blob_container_client: ContainerClient) -> None:
+ azure_blob_container_client.create_container()
+
+
+def test_two(azure_blob_container_client: ContainerClient) -> None:
+ assert azure_blob_container_client.exists()
+""")
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=2)
+
+
+def test_xdist_isolate_server(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+import pytest
+from azure.storage.blob import ContainerClient
+
+pytest_plugins = [
+ "pytest_databases.docker.azure_blob",
+]
+
+
+@pytest.fixture(scope="session")
+def azure_blob_xdist_isolation_level():
+ return "server"
+
+
+def test_one(azure_blob_container_client: ContainerClient) -> None:
+ assert not azure_blob_container_client.exists()
+ azure_blob_container_client.create_container()
+
+
+def test_two(azure_blob_container_client: ContainerClient) -> None:
+ assert not azure_blob_container_client.exists()
+ azure_blob_container_client.create_container()
+""")
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=2)
+
+
+def test_xdist_isolate_database(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+from azure.storage.blob import ContainerClient
+from pytest_databases.helpers import get_xdist_worker_num
+
+pytest_plugins = [
+ "pytest_databases.docker.azure_blob",
+]
+
+
+def test_one(azure_blob_container_client: ContainerClient, azure_blob_default_container_name: str) -> None:
+ assert not azure_blob_container_client.exists()
+ azure_blob_container_client.create_container()
+ assert azure_blob_container_client.container_name == azure_blob_default_container_name
+ assert azure_blob_container_client.account_name == f"test_account_{get_xdist_worker_num()}"
+
+
+def test_two(azure_blob_container_client: ContainerClient, azure_blob_default_container_name: str) -> None:
+ assert not azure_blob_container_client.exists()
+ azure_blob_container_client.create_container()
+ assert azure_blob_container_client.container_name == azure_blob_default_container_name
+ assert azure_blob_container_client.account_name == f"test_account_{get_xdist_worker_num()}"
+
+""")
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=2)
diff --git a/tests/test_bigquery.py b/tests/test_bigquery.py
new file mode 100644
index 0000000..3058fc6
--- /dev/null
+++ b/tests/test_bigquery.py
@@ -0,0 +1,60 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ import pytest
+
+
+def test_service_fixture(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ from google.cloud import bigquery
+
+ pytest_plugins = ["pytest_databases.docker.bigquery"]
+
+ def test(bigquery_service) -> None:
+ client = bigquery.Client(
+ project=bigquery_service.project,
+ client_options=bigquery_service.client_options,
+ credentials=bigquery_service.credentials,
+ )
+
+ job = client.query(query="SELECT 1 as one")
+
+ resp = list(job.result())
+ assert resp[0].one == 1
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def test_client_fixture(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ from google.cloud import bigquery
+
+ pytest_plugins = ["pytest_databases.docker.bigquery"]
+
+ def test(bigquery_client) -> None:
+ assert isinstance(bigquery_client, bigquery.Client)
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def test_xdist(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ from google.cloud import bigquery
+
+ pytest_plugins = ["pytest_databases.docker.bigquery"]
+
+ def test_one(bigquery_client, bigquery_service) -> None:
+ bigquery_client.query(f"CREATE TABLE `{bigquery_service.dataset}.test` AS select 1 as the_value")
+
+ def test_two(bigquery_client, bigquery_service) -> None:
+ bigquery_client.query(f"CREATE TABLE `{bigquery_service.dataset}.test` AS select 1 as the_value")
+ """)
+
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=2)
diff --git a/tests/test_cockroachdb.py b/tests/test_cockroachdb.py
new file mode 100644
index 0000000..041e728
--- /dev/null
+++ b/tests/test_cockroachdb.py
@@ -0,0 +1,142 @@
+# from __future__ import annotations
+#
+# from typing import TYPE_CHECKING
+#
+# import psycopg
+#
+# if TYPE_CHECKING:
+# from pytest_databases.docker.cockroachdb import CockroachDBService
+#
+# pytest_plugins = [
+# "pytest_databases.docker.cockroachdb",
+# ]
+#
+#
+# def test_cockroachdb_default_config(cockroachdb_driver_opts: dict[str, str]) -> None:
+# assert cockroachdb_driver_opts == {"sslmode": "disable"}
+#
+#
+# def test_cockroachdb_service(
+# cockroachdb_service: CockroachDBService,
+# ) -> None:
+# opts = "&".join(f"{k}={v}" for k, v in cockroachdb_service.driver_opts.items())
+# with psycopg.connect(
+# f"postgresql://root@{cockroachdb_service.host}:{cockroachdb_service.port}/{cockroachdb_service.database}?{opts}"
+# ) as conn:
+# conn.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
+# result = conn.execute("select * from simple_table").fetchone()
+# assert result is not None and result[0] == 1
+#
+#
+# def test_cockroachdb_services_after_start(
+# cockroachdb_startup_connection: psycopg.Connection,
+# ) -> None:
+# cockroachdb_startup_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
+# result = cockroachdb_startup_connection.execute("select * from simple_table").fetchone()
+# assert result is not None and result[0] == 1
+
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ import pytest
+
+
+def test_service_fixture(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string # noqa: PLC2701
+
+ pytest_plugins = ["pytest_databases.docker.cockroachdb"]
+
+ def test(cockroachdb_service) -> None:
+ opts = "&".join(f"{k}={v}" for k, v in cockroachdb_service.driver_opts.items())
+ with psycopg.connect(
+ f"postgresql://root@{cockroachdb_service.host}:{cockroachdb_service.port}/{cockroachdb_service.database}?{opts}"
+ ) as conn:
+ db_open = conn.execute("SELECT 1").fetchone()
+ assert db_open is not None and db_open[0] == 1
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def test_startup_connection_fixture(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string # noqa: PLC2701
+
+
+ pytest_plugins = ["pytest_databases.docker.cockroachdb"]
+
+ def test(cockroachdb_connection) -> None:
+ cockroachdb_connection.execute("CREATE TABLE if not exists simple_table as SELECT 1")
+ result = cockroachdb_connection.execute("select * from simple_table").fetchone()
+ assert result is not None and result[0] == 1
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def test_xdist_isolate_database(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string
+
+ pytest_plugins = ["pytest_databases.docker.cockroachdb"]
+
+ def test_one(cockroachdb_service) -> None:
+ opts = "&".join(f"{k}={v}" for k, v in cockroachdb_service.driver_opts.items())
+ with psycopg.connect(
+ f"postgresql://root@{cockroachdb_service.host}:{cockroachdb_service.port}/{cockroachdb_service.database}?{opts}"
+ ) as conn:
+ conn.execute("CREATE TABLE foo AS SELECT 1")
+
+ def test_two(cockroachdb_service) -> None:
+ opts = "&".join(f"{k}={v}" for k, v in cockroachdb_service.driver_opts.items())
+ with psycopg.connect(
+ f"postgresql://root@{cockroachdb_service.host}:{cockroachdb_service.port}/{cockroachdb_service.database}?{opts}"
+ ) as conn:
+ conn.execute("CREATE TABLE foo AS SELECT 1")
+ """)
+
+ result = pytester.runpytest_subprocess("-n", "2")
+ result.assert_outcomes(passed=2)
+
+
+def test_xdist_isolate_server(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string
+
+ pytest_plugins = ["pytest_databases.docker.cockroachdb"]
+
+ @pytest.fixture(scope="session")
+ def xdist_cockroachdb_isolation_level():
+ return "server"
+
+ def test_one(cockroachdb_service) -> None:
+ opts = "&".join(f"{k}={v}" for k, v in cockroachdb_service.driver_opts.items())
+ with psycopg.connect(
+ f"postgresql://root@{cockroachdb_service.host}:{cockroachdb_service.port}/{cockroachdb_service.database}?{opts}"
+ ) as conn:
+ conn.execute("CREATE DATABASE foo")
+
+ def test_two(cockroachdb_service) -> None:
+ opts = "&".join(f"{k}={v}" for k, v in cockroachdb_service.driver_opts.items())
+ with psycopg.connect(
+ f"postgresql://root@{cockroachdb_service.host}:{cockroachdb_service.port}/{cockroachdb_service.database}?{opts}"
+ ) as conn:
+ conn.execute("CREATE DATABASE foo")
+ """)
+
+ result = pytester.runpytest_subprocess("-n", "2")
+ result.assert_outcomes(passed=2)
diff --git a/tests/test_elasticsearch.py b/tests/test_elasticsearch.py
new file mode 100644
index 0000000..c30bf58
--- /dev/null
+++ b/tests/test_elasticsearch.py
@@ -0,0 +1,60 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ import pytest
+
+
+def test_elasticsearch_7(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ from elasticsearch7 import Elasticsearch
+
+ pytest_plugins = ["pytest_databases.docker.elastic_search"]
+
+ def test(elasticsearch_7_service) -> None:
+ with Elasticsearch(
+ hosts=[
+ {
+ "host": elasticsearch_7_service.host,
+ "port": elasticsearch_7_service.port,
+ "scheme": elasticsearch_7_service.scheme,
+ }
+ ],
+ verify_certs=False,
+ http_auth=(elasticsearch_7_service.user, elasticsearch_7_service.password),
+ ) as client:
+ info = client.info()
+
+ assert info["version"]["number"] == "7.17.19"
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def test_elasticsearch_8(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ from elasticsearch7 import Elasticsearch
+
+ pytest_plugins = ["pytest_databases.docker.elastic_search"]
+
+ def test(elasticsearch_8_service) -> None:
+ with Elasticsearch(
+ hosts=[
+ {
+ "host": elasticsearch_8_service.host,
+ "port": elasticsearch_8_service.port,
+ "scheme": elasticsearch_8_service.scheme,
+ }
+ ],
+ verify_certs=False,
+ basic_auth=(elasticsearch_8_service.user, elasticsearch_8_service.password),
+ ) as client:
+ info = client.info()
+
+ assert info["version"]["number"] == "8.13.0"
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
diff --git a/tests/test_mariadb.py b/tests/test_mariadb.py
new file mode 100644
index 0000000..5fd62fb
--- /dev/null
+++ b/tests/test_mariadb.py
@@ -0,0 +1,95 @@
+from __future__ import annotations
+
+import pytest
+
+
+@pytest.mark.parametrize(
+ "service_fixture",
+ [
+ "mariadb_service",
+ "mariadb_113_service",
+ ],
+)
+def test_service_fixture(pytester: pytest.Pytester, service_fixture: str) -> None:
+ pytester.makepyfile(f"""
+ import mariadb
+
+ pytest_plugins = ["pytest_databases.docker.mariadb"]
+
+ def test({service_fixture}):
+ with mariadb.connect(
+ host={service_fixture}.host,
+ port={service_fixture}.port,
+ user={service_fixture}.user,
+ database={service_fixture}.db,
+ password={service_fixture}.password,
+ ) as conn, conn.cursor() as cursor:
+ cursor.execute("select 1 as is_available")
+ resp = cursor.fetchone()
+ assert resp is not None and resp[0] == 1
+ """)
+
+ result = pytester.runpytest("-vv")
+ result.assert_outcomes(passed=1)
+
+
+@pytest.mark.parametrize(
+ "connection_fixture",
+ [
+ "mariadb_connection",
+ "mariadb_113_connection",
+ ],
+)
+def test_connection_fixture(pytester: pytest.Pytester, connection_fixture: str) -> None:
+ pytester.makepyfile(f"""
+ pytest_plugins = ["pytest_databases.docker.mariadb"]
+
+ def test({connection_fixture}):
+ with {connection_fixture}.cursor() as cursor:
+ cursor.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
+ cursor.execute("select * from simple_table")
+ result = cursor.fetchall()
+ assert result is not None and result[0][0] == 1
+ """)
+
+ result = pytester.runpytest("-vv")
+ result.assert_outcomes(passed=1)
+
+
+def test_xdist_isolate_database(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ pytest_plugins = ["pytest_databases.docker.mariadb"]
+
+ def test_1(mariadb_113_connection):
+ with mariadb_113_connection.cursor() as cursor:
+ cursor.execute("CREATE TABLE simple_table as SELECT 1 as the_value;")
+
+ def test_2(mariadb_113_connection):
+ with mariadb_113_connection.cursor() as cursor:
+ cursor.execute("CREATE TABLE simple_table as SELECT 1 as the_value;")
+ """)
+
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=2)
+
+
+def test_xdist_isolate_server(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ pytest_plugins = ["pytest_databases.docker.mariadb"]
+
+ @pytest.fixture(scope="session")
+ def xdist_mariadb_isolation_level():
+ return "server"
+
+ def test_1(mariadb_113_connection):
+ with mariadb_113_connection.cursor() as cursor:
+ cursor.execute("CREATE DATABASE db_test")
+
+ def test_2(mariadb_113_connection):
+ with mariadb_113_connection.cursor() as cursor:
+ cursor.execute("CREATE DATABASE db_test")
+ """)
+
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=2)
diff --git a/tests/test_mssql.py b/tests/test_mssql.py
new file mode 100644
index 0000000..0b4a42d
--- /dev/null
+++ b/tests/test_mssql.py
@@ -0,0 +1,115 @@
+from __future__ import annotations
+
+import pytest
+
+
+@pytest.mark.parametrize(
+ "service_fixture",
+ [
+ "mssql_service",
+ ],
+)
+def test_service_fixture(pytester: pytest.Pytester, service_fixture: str) -> None:
+ pytester.makepyfile(f"""
+ import pymssql
+ pytest_plugins = ["pytest_databases.docker.mssql"]
+
+ def test({service_fixture}):
+ conn = pymssql.connect(
+ host={service_fixture}.host,
+ port=str({service_fixture}.port),
+ database={service_fixture}.database,
+ user={service_fixture}.user,
+ password={service_fixture}.password,
+ timeout=2,
+ )
+ with conn.cursor() as cursor:
+ cursor.execute("select 1 as is_available")
+ resp = cursor.fetchone()
+ return resp[0] == 1 if resp is not None else False
+ """)
+
+ result = pytester.runpytest("-vv")
+ result.assert_outcomes(passed=1)
+
+
+@pytest.mark.parametrize(
+ "connection_fixture",
+ [
+ "mssql_connection",
+ ],
+)
+def test_connection_fixture(pytester: pytest.Pytester, connection_fixture: str) -> None:
+ pytester.makepyfile(f"""
+ import pymssql
+ pytest_plugins = ["pytest_databases.docker.mssql"]
+
+ def test({connection_fixture}):
+ with {connection_fixture}.cursor() as cursor:
+ cursor.execute("CREATE view simple_table as SELECT 1 as the_value")
+ cursor.execute("select * from simple_table")
+ result = cursor.fetchall()
+ assert bool(result is not None and result[0][0] == 1)
+ cursor.execute("drop view simple_table")
+
+ """)
+
+ result = pytester.runpytest("-vv")
+ result.assert_outcomes(passed=1)
+
+
+def test_xdist_isolate_database(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pymssql
+ pytest_plugins = ["pytest_databases.docker.mssql"]
+
+ def test_1(mssql_connection):
+ with mssql_connection.cursor() as cursor:
+ cursor.execute("CREATE view simple_table as SELECT 1 as the_value;")
+
+ def test_2(mssql_connection):
+ with mssql_connection.cursor() as cursor:
+ cursor.execute("CREATE view simple_table as SELECT 1 as the_value;")
+ """)
+
+ result = pytester.runpytest("-n", "2", "-vv")
+ result.assert_outcomes(passed=2)
+
+
+def test_xdist_isolate_server(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pymssql
+ import pytest
+ pytest_plugins = ["pytest_databases.docker.mssql"]
+
+ @pytest.fixture(scope="session")
+ def xdist_mssql_isolation_level():
+ return "server"
+
+ def test_1(mssql_service):
+ with pymssql.connect(
+ host=mssql_service.host,
+ port=str(mssql_service.port),
+ database=mssql_service.database,
+ user=mssql_service.user,
+ password=mssql_service.password,
+ timeout=2,
+ autocommit=True,
+ ) as conn, conn.cursor() as cursor:
+ cursor.execute("CREATE DATABASE db_test")
+
+ def test_2(mssql_service):
+ with pymssql.connect(
+ host=mssql_service.host,
+ port=str(mssql_service.port),
+ database=mssql_service.database,
+ user=mssql_service.user,
+ password=mssql_service.password,
+ timeout=2,
+ autocommit=True,
+ ) as conn, conn.cursor() as cursor:
+ cursor.execute("CREATE DATABASE db_test")
+ """)
+
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=2)
diff --git a/tests/test_mysql.py b/tests/test_mysql.py
new file mode 100644
index 0000000..bc32689
--- /dev/null
+++ b/tests/test_mysql.py
@@ -0,0 +1,95 @@
+from __future__ import annotations
+
+import pytest
+
+
+@pytest.mark.parametrize(
+ "service_fixture",
+ [
+ "mysql_8_service",
+ "mysql_56_service",
+ "mysql_57_service",
+ ],
+)
+def test_service_fixture(pytester: pytest.Pytester, service_fixture: str) -> None:
+ pytester.makepyfile(f"""
+ import mysql.connector
+ pytest_plugins = ["pytest_databases.docker.mysql"]
+
+ def test({service_fixture}):
+ with mysql.connector.connect(
+ host={service_fixture}.host,
+ port={service_fixture}.port,
+ user={service_fixture}.user,
+ database={service_fixture}.db,
+ password={service_fixture}.password,
+ ) as conn, conn.cursor() as cursor:
+ cursor.execute("select 1 as is_available")
+ resp = cursor.fetchone()
+ assert resp is not None and resp[0] == 1
+ """)
+
+ result = pytester.runpytest("-vv")
+ result.assert_outcomes(passed=1)
+
+
+@pytest.mark.parametrize(
+ "connection_fixture",
+ [
+ "mysql_56_connection",
+ "mysql_57_connection",
+ ],
+)
+def test_connection_fixture(pytester: pytest.Pytester, connection_fixture: str) -> None:
+ pytester.makepyfile(f"""
+ pytest_plugins = ["pytest_databases.docker.mysql"]
+
+ def test({connection_fixture}):
+ with {connection_fixture}.cursor() as cursor:
+ cursor.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
+ cursor.execute("select * from simple_table")
+ result = cursor.fetchall()
+ assert result is not None and result[0][0] == 1
+ """)
+
+ result = pytester.runpytest("-vv")
+ result.assert_outcomes(passed=1)
+
+
+def test_xdist_isolate_database(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ pytest_plugins = ["pytest_databases.docker.mysql"]
+
+ def test_1(mysql_56_connection):
+ with mysql_56_connection.cursor() as cursor:
+ cursor.execute("CREATE TABLE simple_table as SELECT 1 as the_value;")
+
+ def test_2(mysql_56_connection):
+ with mysql_56_connection.cursor() as cursor:
+ cursor.execute("CREATE TABLE simple_table as SELECT 1 as the_value;")
+ """)
+
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=2)
+
+
+def test_xdist_isolate_server(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ pytest_plugins = ["pytest_databases.docker.mysql"]
+
+ @pytest.fixture(scope="session")
+ def xdist_mysql_isolation_level():
+ return "server"
+
+ def test_1(mysql_56_connection):
+ with mysql_56_connection.cursor() as cursor:
+ cursor.execute("CREATE DATABASE db_test")
+
+ def test_2(mysql_56_connection):
+ with mysql_56_connection.cursor() as cursor:
+ cursor.execute("CREATE DATABASE db_test")
+ """)
+
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=2)
diff --git a/tests/test_oracle.py b/tests/test_oracle.py
new file mode 100644
index 0000000..bd725ed
--- /dev/null
+++ b/tests/test_oracle.py
@@ -0,0 +1,49 @@
+from __future__ import annotations
+
+import pytest
+
+pytestmark = pytest.mark.skip()
+
+
+@pytest.mark.parametrize(
+ "service_fixture",
+ [
+ "oracle_18c_service",
+ "oracle_23ai_service",
+ "oracle_23ai_service",
+ ],
+)
+def test_service_fixture(pytester: pytest.Pytester, service_fixture: str) -> None:
+ pytester.makepyfile(f"""
+ import oracledb
+ pytest_plugins = ["pytest_databases.docker.oracle"]
+
+ def test({service_fixture}):
+ conn = oracledb.connect(
+ user=service_fixture.user,
+ password=service_fixture.password,
+ dsn=f"{{{service_fixture}.host}}:{{{service_fixture}.port!s}}/{{{service_fixture}.service_name}}",
+ )
+ with conn.cursor() as cur:
+ cur.execute("SELECT 'Hello World!' FROM dual")
+ res = cur.fetchall()[0][0]
+ assert "Hello World!" in res
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+@pytest.mark.parametrize("connection_fixture", ["oracle_18c_connection"])
+def test_connection_fixture(pytester: pytest.Pytester, connection_fixture: str) -> None:
+ pytester.makepyfile(f"""
+ import oracledb
+ pytest_plugins = ["pytest_databases.docker.oracle"]
+
+ def test({connection_fixture}):
+ with {connection_fixture}.cursor() as cursor:
+ cursor.execute("CREATE or replace view simple_table as SELECT 1 as the_value from dual")
+ cursor.execute("select * from simple_table")
+ result = cursor.fetchall()
+ assert bool(result is not None and result[0][0] == 1)
+ """)
diff --git a/tests/test_postgres.py b/tests/test_postgres.py
new file mode 100644
index 0000000..8c34437
--- /dev/null
+++ b/tests/test_postgres.py
@@ -0,0 +1,143 @@
+from __future__ import annotations
+
+import pytest
+
+
+@pytest.mark.parametrize(
+ "service_fixture",
+ [
+ "postgres_service",
+ "postgres_12_service",
+ "postgres_13_service",
+ "postgres_14_service",
+ "postgres_15_service",
+ "postgres_16_service",
+ "postgres_17_service",
+ ],
+)
+def test_service_fixture(pytester: pytest.Pytester, service_fixture: str) -> None:
+ pytester.makepyfile(f"""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string # noqa: PLC2701
+
+
+ pytest_plugins = [
+ "pytest_databases.docker.postgres",
+ ]
+
+ def test({service_fixture}) -> None:
+ with psycopg.connect(
+ _make_connection_string(
+ host={service_fixture}.host,
+ port={service_fixture}.port,
+ user={service_fixture}.user,
+ password={service_fixture}.password,
+ database={service_fixture}.database,
+ )
+ ) as conn:
+ db_open = conn.execute("SELECT 1").fetchone()
+ assert db_open is not None and db_open[0] == 1
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+@pytest.mark.parametrize(
+ "connection_fixture",
+ [
+ "postgres_connection",
+ "postgres_11_connection",
+ "postgres_12_connection",
+ "postgres_13_connection",
+ "postgres_14_connection",
+ "postgres_15_connection",
+ "postgres_16_connection",
+ "postgres_17_connection",
+ ],
+)
+def test_startup_connection_fixture(pytester: pytest.Pytester, connection_fixture: str) -> None:
+ pytester.makepyfile(f"""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string # noqa: PLC2701
+
+
+ pytest_plugins = [
+ "pytest_databases.docker.postgres",
+ ]
+
+ def test({connection_fixture}) -> None:
+ {connection_fixture}.execute("CREATE TABLE if not exists simple_table as SELECT 1")
+ result = {connection_fixture}.execute("select * from simple_table").fetchone()
+ assert result is not None and result[0] == 1
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def test_xdist_isolate_db(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string # noqa: PLC2701
+
+
+ pytest_plugins = ["pytest_databases.docker.postgres"]
+
+ def test_two(postgres_connection) -> None:
+ postgres_connection.execute("CREATE TABLE foo AS SELECT 1")
+
+ def test_two(postgres_connection) -> None:
+ postgres_connection.execute("CREATE TABLE foo AS SELECT 1")
+ """)
+
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=1)
+
+
+def test_xdist_isolate_server(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import pytest
+ import psycopg
+ from pytest_databases.docker.postgres import _make_connection_string
+
+ pytest_plugins = [
+ "pytest_databases.docker.postgres",
+ ]
+
+ @pytest.fixture(scope="session")
+ def xdist_postgres_isolation_level():
+ return "server"
+
+ def test_one(postgres_service) -> None:
+ with psycopg.connect(
+ _make_connection_string(
+ host=postgres_service.host,
+ port=postgres_service.port,
+ user=postgres_service.user,
+ password=postgres_service.password,
+ database=postgres_service.database,
+ ),
+ autocommit=True,
+ ) as conn:
+ conn.execute("CREATE DATABASE foo")
+
+ def test_two(postgres_service) -> None:
+ with psycopg.connect(
+ _make_connection_string(
+ host=postgres_service.host,
+ port=postgres_service.port,
+ user=postgres_service.user,
+ password=postgres_service.password,
+ database=postgres_service.database,
+ ),
+ autocommit=True,
+ ) as conn:
+ conn.execute("CREATE DATABASE foo")
+ """)
+
+ result = pytester.runpytest_subprocess("-n", "2")
+ result.assert_outcomes(passed=2)
diff --git a/tests/test_redis.py b/tests/test_redis.py
new file mode 100644
index 0000000..8eff7f2
--- /dev/null
+++ b/tests/test_redis.py
@@ -0,0 +1,112 @@
+from __future__ import annotations
+
+import pytest
+
+pytest_plugins = [
+ "pytest_databases.docker.redis",
+]
+
+
+@pytest.fixture(
+ params=[
+ pytest.param("redis:latest", id="redis"),
+ pytest.param("valkey/valkey", id="valkey"),
+ pytest.param("eqalpha/keydb", id="keydb"),
+ pytest.param("docker.dragonflydb.io/dragonflydb/dragonfly", id="dragonflydb"),
+ ]
+)
+def redis_image_name(request: pytest.FixtureRequest) -> str:
+ return request.param
+
+
+def test_default_no_xdist(pytester: pytest.Pytester, redis_image_name: str) -> None:
+ pytester.makepyfile(f"""
+import pytest
+import redis
+from pytest_databases.docker.redis import RedisService
+from pytest_databases.helpers import get_xdist_worker_num
+
+pytest_plugins = [
+ "pytest_databases.docker.redis",
+]
+
+@pytest.fixture(scope="session")
+def redis_image():
+ return "{redis_image_name}"
+
+
+def test_redis_service(redis_service: RedisService) -> None:
+ assert redis.Redis.from_url("redis://", host=redis_service.host, port=redis_service.port).ping()
+""")
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def test_xdist_isolate_database(pytester: pytest.Pytester, redis_image_name: str) -> None:
+ pytester.makepyfile(f"""
+import pytest
+import redis
+from pytest_databases.helpers import get_xdist_worker_num
+
+pytest_plugins = [
+ "pytest_databases.docker.redis",
+]
+
+
+@pytest.fixture(scope="session")
+def redis_image():
+ return "{redis_image_name}"
+
+
+def test_one(redis_service):
+ client = redis.Redis.from_url("redis://", host=redis_service.host, port=redis_service.port, db=redis_service.db)
+ assert not client.get("one")
+ client.set("one", "1")
+ assert redis_service.db == get_xdist_worker_num()
+
+
+def test_two(redis_service):
+ client = redis.Redis.from_url("redis://", host=redis_service.host, port=redis_service.port, db=redis_service.db)
+ assert not client.get("one")
+ client.set("one", "1")
+ assert redis_service.db == get_xdist_worker_num()
+""")
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=2)
+
+
+def test_xdist_isolate_server(pytester: pytest.Pytester, redis_image_name: str) -> None:
+ pytester.makepyfile(f"""
+import pytest
+import redis
+from pytest_databases.helpers import get_xdist_worker_num
+
+pytest_plugins = [
+ "pytest_databases.docker.redis",
+]
+
+@pytest.fixture(scope="session")
+def redis_image():
+ return "{redis_image_name}"
+
+
+@pytest.fixture(scope="session")
+def xdist_redis_isolation_level():
+ return "server"
+
+
+def test_one(redis_service):
+ client = redis.Redis.from_url("redis://", host=redis_service.host, port=redis_service.port, db=redis_service.db)
+ assert not client.get("one")
+ client.set("one", "1")
+ assert redis_service.db == 0
+
+
+def test_two(redis_service):
+ client = redis.Redis.from_url("redis://", host=redis_service.host, port=redis_service.port, db=redis_service.db)
+ assert not client.get("one")
+ client.set("one", "1")
+ assert redis_service.db == 0
+""")
+ result = pytester.runpytest("-n", "2")
+ result.assert_outcomes(passed=2)
diff --git a/tests/test_spanner.py b/tests/test_spanner.py
new file mode 100644
index 0000000..98582d8
--- /dev/null
+++ b/tests/test_spanner.py
@@ -0,0 +1,49 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ import pytest
+
+
+def test_service_fixture(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ from google.cloud import spanner
+ import contextlib
+
+ pytest_plugins = ["pytest_databases.docker.spanner"]
+
+ def test_spanner_service(spanner_service) -> None:
+ spanner_client = spanner.Client(
+ project=spanner_service.project,
+ credentials=spanner_service.credentials,
+ client_options=spanner_service.client_options,
+ )
+ instance = spanner_client.instance(spanner_service.instance_name)
+ with contextlib.suppress(Exception):
+ instance.create()
+
+ database = instance.database(spanner_service.database_name)
+ with contextlib.suppress(Exception):
+ database.create()
+
+ with database.snapshot() as snapshot:
+ resp = next(iter(snapshot.execute_sql("SELECT 1")))
+ assert resp[0] == 1
+ """)
+
+ result = pytester.runpytest("-vv")
+ result.assert_outcomes(passed=1)
+
+
+def test_spanner_connection(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ from google.cloud import spanner
+ pytest_plugins = ["pytest_databases.docker.spanner"]
+
+ def test(spanner_connection) -> None:
+ assert isinstance(spanner_connection, spanner.Client)
+ """)
+
+ result = pytester.runpytest()
+ result.assert_outcomes(passed=1)
diff --git a/uv.lock b/uv.lock
new file mode 100644
index 0000000..dda6b9d
--- /dev/null
+++ b/uv.lock
@@ -0,0 +1,3157 @@
+version = 1
+requires-python = ">=3.9"
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'",
+ "python_full_version >= '3.13' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.10.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.10.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version < '3.10' and platform_python_implementation == 'PyPy'",
+ "python_full_version < '3.10' and platform_python_implementation != 'PyPy'",
+]
+
+[[package]]
+name = "accessible-pygments"
+version = "0.0.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903 },
+]
+
+[[package]]
+name = "alabaster"
+version = "0.7.16"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.10' and platform_python_implementation == 'PyPy'",
+ "python_full_version < '3.10' and platform_python_implementation != 'PyPy'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511 },
+]
+
+[[package]]
+name = "alabaster"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'",
+ "python_full_version >= '3.13' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.10.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.10.*' and platform_python_implementation != 'PyPy'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 },
+]
+
+[[package]]
+name = "anyio"
+version = "4.8.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "idna" },
+ { name = "sniffio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 },
+]
+
+[[package]]
+name = "apeye"
+version = "1.4.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "apeye-core" },
+ { name = "domdf-python-tools" },
+ { name = "platformdirs" },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4f/6b/cc65e31843d7bfda8313a9dc0c77a21e8580b782adca53c7cb3e511fe023/apeye-1.4.1.tar.gz", hash = "sha256:14ea542fad689e3bfdbda2189a354a4908e90aee4bf84c15ab75d68453d76a36", size = 99219 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/89/7b/2d63664777b3e831ac1b1d8df5bbf0b7c8bee48e57115896080890527b1b/apeye-1.4.1-py3-none-any.whl", hash = "sha256:44e58a9104ec189bf42e76b3a7fe91e2b2879d96d48e9a77e5e32ff699c9204e", size = 107989 },
+]
+
+[[package]]
+name = "apeye-core"
+version = "1.1.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "domdf-python-tools" },
+ { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e5/4c/4f108cfd06923bd897bf992a6ecb6fb122646ee7af94d7f9a64abd071d4c/apeye_core-1.1.5.tar.gz", hash = "sha256:5de72ed3d00cc9b20fea55e54b7ab8f5ef8500eb33a5368bc162a5585e238a55", size = 96511 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/77/9f/fa9971d2a0c6fef64c87ba362a493a4f230eff4ea8dfb9f4c7cbdf71892e/apeye_core-1.1.5-py3-none-any.whl", hash = "sha256:dc27a93f8c9e246b3b238c5ea51edf6115ab2618ef029b9f2d9a190ec8228fbf", size = 99286 },
+]
+
+[[package]]
+name = "astroid"
+version = "3.3.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/80/c5/5c83c48bbf547f3dd8b587529db7cf5a265a3368b33e85e76af8ff6061d3/astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b", size = 398196 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/07/28/0bc8a17d6cd4cc3c79ae41b7105a2b9a327c110e5ddd37a8a27b29a5c8a2/astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c", size = 275153 },
+]
+
+[[package]]
+name = "async-timeout"
+version = "5.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 },
+]
+
+[[package]]
+name = "auto-pytabs"
+version = "0.5.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "ruff" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f9/ff/f5752f43f659ee62dd563af5bb0fe0a63111c3ff4708e9596279385f52bb/auto_pytabs-0.5.0.tar.gz", hash = "sha256:30087831c8be5b2314e663efd06c96b84c096572a060a492540f586362cc4326", size = 15362 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6e/df/e76dc1261882283f7ae93ebbf75438e85d8bb713a51dbbd5d17fef29e607/auto_pytabs-0.5.0-py3-none-any.whl", hash = "sha256:e59fb6d2f8b41b05d0906a322dd4bb1a86749d429483ec10036587de3657dcc8", size = 13748 },
+]
+
+[package.optional-dependencies]
+sphinx = [
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+]
+
+[[package]]
+name = "autodocsumm"
+version = "0.2.14"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/03/96/92afe8a7912b327c01f0a8b6408c9556ee13b1aba5b98d587ac7327ff32d/autodocsumm-0.2.14.tar.gz", hash = "sha256:2839a9d4facc3c4eccd306c08695540911042b46eeafcdc3203e6d0bab40bc77", size = 46357 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/bc/3f66af9beb683728e06ca08797e4e9d3e44f432f339718cae3ba856a9cad/autodocsumm-0.2.14-py3-none-any.whl", hash = "sha256:3bad8717fc5190802c60392a7ab04b9f3c97aa9efa8b3780b3d81d615bfe5dc0", size = 14640 },
+]
+
+[[package]]
+name = "azure-core"
+version = "1.32.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "requests" },
+ { name = "six" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cc/ee/668328306a9e963a5ad9f152cd98c7adad86c822729fd1d2a01613ad1e67/azure_core-1.32.0.tar.gz", hash = "sha256:22b3c35d6b2dae14990f6c1be2912bf23ffe50b220e708a28ab1bb92b1c730e5", size = 279128 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/39/83/325bf5e02504dbd8b4faa98197a44cdf8a325ef259b48326a2b6f17f8383/azure_core-1.32.0-py3-none-any.whl", hash = "sha256:eac191a0efb23bfa83fddf321b27b122b4ec847befa3091fa736a5c32c50d7b4", size = 198855 },
+]
+
+[[package]]
+name = "azure-storage-blob"
+version = "12.24.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "azure-core" },
+ { name = "cryptography" },
+ { name = "isodate" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/aa/ff/f6e81d15687510d83a06cafba9ac38d17df71a2bb18f35a0fb169aee3af3/azure_storage_blob-12.24.1.tar.gz", hash = "sha256:052b2a1ea41725ba12e2f4f17be85a54df1129e13ea0321f5a2fcc851cbf47d4", size = 570523 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/74/3c/3814aba90a63e84c7de0eb6fdf67bd1a9115ac5f99ec5b7a817a5d5278ec/azure_storage_blob-12.24.1-py3-none-any.whl", hash = "sha256:77fb823fdbac7f3c11f7d86a5892e2f85e161e8440a7489babe2195bf248f09e", size = 408432 },
+]
+
+[[package]]
+name = "babel"
+version = "2.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 },
+]
+
+[[package]]
+name = "beautifulsoup4"
+version = "4.12.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "soupsieve" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 },
+]
+
+[[package]]
+name = "cachecontrol"
+version = "0.14.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "msgpack" },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b7/a4/3390ac4dfa1773f661c8780368018230e8207ec4fd3800d2c0c3adee4456/cachecontrol-0.14.2.tar.gz", hash = "sha256:7d47d19f866409b98ff6025b6a0fca8e4c791fb31abbd95f622093894ce903a2", size = 28832 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/63/baffb44ca6876e7b5fc8fe17b24a7c07bf479d604a592182db9af26ea366/cachecontrol-0.14.2-py3-none-any.whl", hash = "sha256:ebad2091bf12d0d200dfc2464330db638c5deb41d546f6d7aca079e87290f3b0", size = 21780 },
+]
+
+[package.optional-dependencies]
+filecache = [
+ { name = "filelock" },
+]
+
+[[package]]
+name = "cachetools"
+version = "5.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d9/74/57df1ab0ce6bc5f6fa868e08de20df8ac58f9c44330c7671ad922d2bbeae/cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95", size = 28044 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/4e/de4ff18bcf55857ba18d3a4bd48c8a9fde6bb0980c9d20b263f05387fd88/cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb", size = 9530 },
+]
+
+[[package]]
+name = "certifi"
+version = "2024.12.14"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 },
+]
+
+[[package]]
+name = "cffi"
+version = "1.17.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser", marker = "platform_python_implementation != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 },
+ { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 },
+ { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 },
+ { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 },
+ { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 },
+ { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 },
+ { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 },
+ { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 },
+ { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 },
+ { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 },
+ { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 },
+ { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 },
+ { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 },
+ { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 },
+ { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 },
+ { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 },
+ { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 },
+ { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 },
+ { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 },
+ { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 },
+ { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 },
+ { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 },
+ { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 },
+ { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 },
+ { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 },
+ { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 },
+ { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 },
+ { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 },
+ { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 },
+ { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 },
+ { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 },
+ { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 },
+ { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 },
+ { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 },
+ { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 },
+ { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 },
+ { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 },
+ { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 },
+ { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 },
+ { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 },
+ { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 },
+ { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 },
+ { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 },
+ { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 },
+ { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 },
+ { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 },
+ { url = "https://files.pythonhosted.org/packages/b9/ea/8bb50596b8ffbc49ddd7a1ad305035daa770202a6b782fc164647c2673ad/cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", size = 182220 },
+ { url = "https://files.pythonhosted.org/packages/ae/11/e77c8cd24f58285a82c23af484cf5b124a376b32644e445960d1a4654c3a/cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", size = 178605 },
+ { url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910 },
+ { url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200 },
+ { url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565 },
+ { url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635 },
+ { url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218 },
+ { url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486 },
+ { url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911 },
+ { url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632 },
+ { url = "https://files.pythonhosted.org/packages/cb/b5/fd9f8b5a84010ca169ee49f4e4ad6f8c05f4e3545b72ee041dbbcb159882/cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", size = 171820 },
+ { url = "https://files.pythonhosted.org/packages/8c/52/b08750ce0bce45c143e1b5d7357ee8c55341b52bdef4b0f081af1eb248c2/cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", size = 181290 },
+]
+
+[[package]]
+name = "cfgv"
+version = "3.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 },
+ { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 },
+ { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 },
+ { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 },
+ { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 },
+ { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 },
+ { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 },
+ { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 },
+ { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 },
+ { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 },
+ { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 },
+ { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 },
+ { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 },
+ { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 },
+ { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 },
+ { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 },
+ { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 },
+ { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 },
+ { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 },
+ { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 },
+ { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 },
+ { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 },
+ { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 },
+ { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 },
+ { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 },
+ { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 },
+ { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
+ { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
+ { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
+ { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
+ { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
+ { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
+ { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
+ { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
+ { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
+ { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
+ { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
+ { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
+ { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
+ { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
+ { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
+ { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
+ { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
+ { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
+ { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
+ { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
+ { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
+ { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
+ { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
+ { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
+ { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
+ { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
+ { url = "https://files.pythonhosted.org/packages/7f/c0/b913f8f02836ed9ab32ea643c6fe4d3325c3d8627cf6e78098671cafff86/charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", size = 197867 },
+ { url = "https://files.pythonhosted.org/packages/0f/6c/2bee440303d705b6fb1e2ec789543edec83d32d258299b16eed28aad48e0/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", size = 141385 },
+ { url = "https://files.pythonhosted.org/packages/3d/04/cb42585f07f6f9fd3219ffb6f37d5a39b4fd2db2355b23683060029c35f7/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", size = 151367 },
+ { url = "https://files.pythonhosted.org/packages/54/54/2412a5b093acb17f0222de007cc129ec0e0df198b5ad2ce5699355269dfe/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", size = 143928 },
+ { url = "https://files.pythonhosted.org/packages/5a/6d/e2773862b043dcf8a221342954f375392bb2ce6487bcd9f2c1b34e1d6781/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", size = 146203 },
+ { url = "https://files.pythonhosted.org/packages/b9/f8/ca440ef60d8f8916022859885f231abb07ada3c347c03d63f283bec32ef5/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", size = 148082 },
+ { url = "https://files.pythonhosted.org/packages/04/d2/42fd330901aaa4b805a1097856c2edf5095e260a597f65def493f4b8c833/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", size = 142053 },
+ { url = "https://files.pythonhosted.org/packages/9e/af/3a97a4fa3c53586f1910dadfc916e9c4f35eeada36de4108f5096cb7215f/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", size = 150625 },
+ { url = "https://files.pythonhosted.org/packages/26/ae/23d6041322a3556e4da139663d02fb1b3c59a23ab2e2b56432bd2ad63ded/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", size = 153549 },
+ { url = "https://files.pythonhosted.org/packages/94/22/b8f2081c6a77cb20d97e57e0b385b481887aa08019d2459dc2858ed64871/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", size = 150945 },
+ { url = "https://files.pythonhosted.org/packages/c7/0b/c5ec5092747f801b8b093cdf5610e732b809d6cb11f4c51e35fc28d1d389/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", size = 146595 },
+ { url = "https://files.pythonhosted.org/packages/0c/5a/0b59704c38470df6768aa154cc87b1ac7c9bb687990a1559dc8765e8627e/charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", size = 95453 },
+ { url = "https://files.pythonhosted.org/packages/85/2d/a9790237cb4d01a6d57afadc8573c8b73c609ade20b80f4cda30802009ee/charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", size = 102811 },
+ { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
+]
+
+[[package]]
+name = "click"
+version = "8.1.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
+]
+
+[[package]]
+name = "coverage"
+version = "7.6.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/84/ba/ac14d281f80aab516275012e8875991bb06203957aa1e19950139238d658/coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23", size = 803868 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c5/12/2a2a923edf4ddabdffed7ad6da50d96a5c126dae7b80a33df7310e329a1e/coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78", size = 207982 },
+ { url = "https://files.pythonhosted.org/packages/ca/49/6985dbca9c7be3f3cb62a2e6e492a0c88b65bf40579e16c71ae9c33c6b23/coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c", size = 208414 },
+ { url = "https://files.pythonhosted.org/packages/35/93/287e8f1d1ed2646f4e0b2605d14616c9a8a2697d0d1b453815eb5c6cebdb/coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a", size = 236860 },
+ { url = "https://files.pythonhosted.org/packages/de/e1/cfdb5627a03567a10031acc629b75d45a4ca1616e54f7133ca1fa366050a/coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165", size = 234758 },
+ { url = "https://files.pythonhosted.org/packages/6d/85/fc0de2bcda3f97c2ee9fe8568f7d48f7279e91068958e5b2cc19e0e5f600/coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988", size = 235920 },
+ { url = "https://files.pythonhosted.org/packages/79/73/ef4ea0105531506a6f4cf4ba571a214b14a884630b567ed65b3d9c1975e1/coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5", size = 234986 },
+ { url = "https://files.pythonhosted.org/packages/c6/4d/75afcfe4432e2ad0405c6f27adeb109ff8976c5e636af8604f94f29fa3fc/coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3", size = 233446 },
+ { url = "https://files.pythonhosted.org/packages/86/5b/efee56a89c16171288cafff022e8af44f8f94075c2d8da563c3935212871/coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5", size = 234566 },
+ { url = "https://files.pythonhosted.org/packages/f2/db/67770cceb4a64d3198bf2aa49946f411b85ec6b0a9b489e61c8467a4253b/coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244", size = 210675 },
+ { url = "https://files.pythonhosted.org/packages/8d/27/e8bfc43f5345ec2c27bc8a1fa77cdc5ce9dcf954445e11f14bb70b889d14/coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e", size = 211518 },
+ { url = "https://files.pythonhosted.org/packages/85/d2/5e175fcf6766cf7501a8541d81778fd2f52f4870100e791f5327fd23270b/coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3", size = 208088 },
+ { url = "https://files.pythonhosted.org/packages/4b/6f/06db4dc8fca33c13b673986e20e466fd936235a6ec1f0045c3853ac1b593/coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43", size = 208536 },
+ { url = "https://files.pythonhosted.org/packages/0d/62/c6a0cf80318c1c1af376d52df444da3608eafc913b82c84a4600d8349472/coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132", size = 240474 },
+ { url = "https://files.pythonhosted.org/packages/a3/59/750adafc2e57786d2e8739a46b680d4fb0fbc2d57fbcb161290a9f1ecf23/coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f", size = 237880 },
+ { url = "https://files.pythonhosted.org/packages/2c/f8/ef009b3b98e9f7033c19deb40d629354aab1d8b2d7f9cfec284dbedf5096/coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994", size = 239750 },
+ { url = "https://files.pythonhosted.org/packages/a6/e2/6622f3b70f5f5b59f705e680dae6db64421af05a5d1e389afd24dae62e5b/coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99", size = 238642 },
+ { url = "https://files.pythonhosted.org/packages/2d/10/57ac3f191a3c95c67844099514ff44e6e19b2915cd1c22269fb27f9b17b6/coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd", size = 237266 },
+ { url = "https://files.pythonhosted.org/packages/ee/2d/7016f4ad9d553cabcb7333ed78ff9d27248ec4eba8dd21fa488254dff894/coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377", size = 238045 },
+ { url = "https://files.pythonhosted.org/packages/a7/fe/45af5c82389a71e0cae4546413266d2195c3744849669b0bab4b5f2c75da/coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8", size = 210647 },
+ { url = "https://files.pythonhosted.org/packages/db/11/3f8e803a43b79bc534c6a506674da9d614e990e37118b4506faf70d46ed6/coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609", size = 211508 },
+ { url = "https://files.pythonhosted.org/packages/86/77/19d09ea06f92fdf0487499283b1b7af06bc422ea94534c8fe3a4cd023641/coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853", size = 208281 },
+ { url = "https://files.pythonhosted.org/packages/b6/67/5479b9f2f99fcfb49c0d5cf61912a5255ef80b6e80a3cddba39c38146cf4/coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078", size = 208514 },
+ { url = "https://files.pythonhosted.org/packages/15/d1/febf59030ce1c83b7331c3546d7317e5120c5966471727aa7ac157729c4b/coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0", size = 241537 },
+ { url = "https://files.pythonhosted.org/packages/4b/7e/5ac4c90192130e7cf8b63153fe620c8bfd9068f89a6d9b5f26f1550f7a26/coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50", size = 238572 },
+ { url = "https://files.pythonhosted.org/packages/dc/03/0334a79b26ecf59958f2fe9dd1f5ab3e2f88db876f5071933de39af09647/coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022", size = 240639 },
+ { url = "https://files.pythonhosted.org/packages/d7/45/8a707f23c202208d7b286d78ad6233f50dcf929319b664b6cc18a03c1aae/coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b", size = 240072 },
+ { url = "https://files.pythonhosted.org/packages/66/02/603ce0ac2d02bc7b393279ef618940b4a0535b0868ee791140bda9ecfa40/coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0", size = 238386 },
+ { url = "https://files.pythonhosted.org/packages/04/62/4e6887e9be060f5d18f1dd58c2838b2d9646faf353232dec4e2d4b1c8644/coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852", size = 240054 },
+ { url = "https://files.pythonhosted.org/packages/5c/74/83ae4151c170d8bd071924f212add22a0e62a7fe2b149edf016aeecad17c/coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359", size = 210904 },
+ { url = "https://files.pythonhosted.org/packages/c3/54/de0893186a221478f5880283119fc40483bc460b27c4c71d1b8bba3474b9/coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247", size = 211692 },
+ { url = "https://files.pythonhosted.org/packages/25/6d/31883d78865529257bf847df5789e2ae80e99de8a460c3453dbfbe0db069/coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9", size = 208308 },
+ { url = "https://files.pythonhosted.org/packages/70/22/3f2b129cc08de00c83b0ad6252e034320946abfc3e4235c009e57cfeee05/coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b", size = 208565 },
+ { url = "https://files.pythonhosted.org/packages/97/0a/d89bc2d1cc61d3a8dfe9e9d75217b2be85f6c73ebf1b9e3c2f4e797f4531/coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690", size = 241083 },
+ { url = "https://files.pythonhosted.org/packages/4c/81/6d64b88a00c7a7aaed3a657b8eaa0931f37a6395fcef61e53ff742b49c97/coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18", size = 238235 },
+ { url = "https://files.pythonhosted.org/packages/9a/0b/7797d4193f5adb4b837207ed87fecf5fc38f7cc612b369a8e8e12d9fa114/coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c", size = 240220 },
+ { url = "https://files.pythonhosted.org/packages/65/4d/6f83ca1bddcf8e51bf8ff71572f39a1c73c34cf50e752a952c34f24d0a60/coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd", size = 239847 },
+ { url = "https://files.pythonhosted.org/packages/30/9d/2470df6aa146aff4c65fee0f87f58d2164a67533c771c9cc12ffcdb865d5/coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e", size = 237922 },
+ { url = "https://files.pythonhosted.org/packages/08/dd/723fef5d901e6a89f2507094db66c091449c8ba03272861eaefa773ad95c/coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694", size = 239783 },
+ { url = "https://files.pythonhosted.org/packages/3d/f7/64d3298b2baf261cb35466000628706ce20a82d42faf9b771af447cd2b76/coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6", size = 210965 },
+ { url = "https://files.pythonhosted.org/packages/d5/58/ec43499a7fc681212fe7742fe90b2bc361cdb72e3181ace1604247a5b24d/coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e", size = 211719 },
+ { url = "https://files.pythonhosted.org/packages/ab/c9/f2857a135bcff4330c1e90e7d03446b036b2363d4ad37eb5e3a47bbac8a6/coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe", size = 209050 },
+ { url = "https://files.pythonhosted.org/packages/aa/b3/f840e5bd777d8433caa9e4a1eb20503495709f697341ac1a8ee6a3c906ad/coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273", size = 209321 },
+ { url = "https://files.pythonhosted.org/packages/85/7d/125a5362180fcc1c03d91850fc020f3831d5cda09319522bcfa6b2b70be7/coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8", size = 252039 },
+ { url = "https://files.pythonhosted.org/packages/a9/9c/4358bf3c74baf1f9bddd2baf3756b54c07f2cfd2535f0a47f1e7757e54b3/coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098", size = 247758 },
+ { url = "https://files.pythonhosted.org/packages/cf/c7/de3eb6fc5263b26fab5cda3de7a0f80e317597a4bad4781859f72885f300/coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb", size = 250119 },
+ { url = "https://files.pythonhosted.org/packages/3e/e6/43de91f8ba2ec9140c6a4af1102141712949903dc732cf739167cfa7a3bc/coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0", size = 249597 },
+ { url = "https://files.pythonhosted.org/packages/08/40/61158b5499aa2adf9e37bc6d0117e8f6788625b283d51e7e0c53cf340530/coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf", size = 247473 },
+ { url = "https://files.pythonhosted.org/packages/50/69/b3f2416725621e9f112e74e8470793d5b5995f146f596f133678a633b77e/coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2", size = 248737 },
+ { url = "https://files.pythonhosted.org/packages/3c/6e/fe899fb937657db6df31cc3e61c6968cb56d36d7326361847440a430152e/coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", size = 211611 },
+ { url = "https://files.pythonhosted.org/packages/1c/55/52f5e66142a9d7bc93a15192eba7a78513d2abf6b3558d77b4ca32f5f424/coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", size = 212781 },
+ { url = "https://files.pythonhosted.org/packages/40/41/473617aadf9a1c15bc2d56be65d90d7c29bfa50a957a67ef96462f7ebf8e/coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a", size = 207978 },
+ { url = "https://files.pythonhosted.org/packages/10/f6/480586607768b39a30e6910a3c4522139094ac0f1677028e1f4823688957/coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27", size = 208415 },
+ { url = "https://files.pythonhosted.org/packages/f1/af/439bb760f817deff6f4d38fe7da08d9dd7874a560241f1945bc3b4446550/coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4", size = 236452 },
+ { url = "https://files.pythonhosted.org/packages/d0/13/481f4ceffcabe29ee2332e60efb52e4694f54a402f3ada2bcec10bb32e43/coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f", size = 234374 },
+ { url = "https://files.pythonhosted.org/packages/c5/59/4607ea9d6b1b73e905c7656da08d0b00cdf6e59f2293ec259e8914160025/coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25", size = 235505 },
+ { url = "https://files.pythonhosted.org/packages/85/60/d66365723b9b7f29464b11d024248ed3523ce5aab958e4ad8c43f3f4148b/coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315", size = 234616 },
+ { url = "https://files.pythonhosted.org/packages/74/f8/2cf7a38e7d81b266f47dfcf137fecd8fa66c7bdbd4228d611628d8ca3437/coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90", size = 233099 },
+ { url = "https://files.pythonhosted.org/packages/50/2b/bff6c1c6b63c4396ea7ecdbf8db1788b46046c681b8fcc6ec77db9f4ea49/coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d", size = 234089 },
+ { url = "https://files.pythonhosted.org/packages/bf/b5/baace1c754d546a67779358341aa8d2f7118baf58cac235db457e1001d1b/coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18", size = 210701 },
+ { url = "https://files.pythonhosted.org/packages/b1/bf/9e1e95b8b20817398ecc5a1e8d3e05ff404e1b9fb2185cd71561698fe2a2/coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59", size = 211482 },
+ { url = "https://files.pythonhosted.org/packages/a1/70/de81bfec9ed38a64fc44a77c7665e20ca507fc3265597c28b0d989e4082e/coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f", size = 200223 },
+]
+
+[package.optional-dependencies]
+toml = [
+ { name = "tomli", marker = "python_full_version <= '3.11'" },
+]
+
+[[package]]
+name = "cryptography"
+version = "44.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/91/4c/45dfa6829acffa344e3967d6006ee4ae8be57af746ae2eba1c431949b32c/cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", size = 710657 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/55/09/8cc67f9b84730ad330b3b72cf867150744bf07ff113cda21a15a1c6d2c7c/cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", size = 6541833 },
+ { url = "https://files.pythonhosted.org/packages/7e/5b/3759e30a103144e29632e7cb72aec28cedc79e514b2ea8896bb17163c19b/cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", size = 3922710 },
+ { url = "https://files.pythonhosted.org/packages/5f/58/3b14bf39f1a0cfd679e753e8647ada56cddbf5acebffe7db90e184c76168/cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", size = 4137546 },
+ { url = "https://files.pythonhosted.org/packages/98/65/13d9e76ca19b0ba5603d71ac8424b5694415b348e719db277b5edc985ff5/cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", size = 3915420 },
+ { url = "https://files.pythonhosted.org/packages/b1/07/40fe09ce96b91fc9276a9ad272832ead0fddedcba87f1190372af8e3039c/cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", size = 4154498 },
+ { url = "https://files.pythonhosted.org/packages/75/ea/af65619c800ec0a7e4034207aec543acdf248d9bffba0533342d1bd435e1/cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", size = 3932569 },
+ { url = "https://files.pythonhosted.org/packages/c7/af/d1deb0c04d59612e3d5e54203159e284d3e7a6921e565bb0eeb6269bdd8a/cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", size = 4016721 },
+ { url = "https://files.pythonhosted.org/packages/bd/69/7ca326c55698d0688db867795134bdfac87136b80ef373aaa42b225d6dd5/cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", size = 4240915 },
+ { url = "https://files.pythonhosted.org/packages/ef/d4/cae11bf68c0f981e0413906c6dd03ae7fa864347ed5fac40021df1ef467c/cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", size = 2757925 },
+ { url = "https://files.pythonhosted.org/packages/64/b1/50d7739254d2002acae64eed4fc43b24ac0cc44bf0a0d388d1ca06ec5bb1/cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", size = 3202055 },
+ { url = "https://files.pythonhosted.org/packages/11/18/61e52a3d28fc1514a43b0ac291177acd1b4de00e9301aaf7ef867076ff8a/cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", size = 6542801 },
+ { url = "https://files.pythonhosted.org/packages/1a/07/5f165b6c65696ef75601b781a280fc3b33f1e0cd6aa5a92d9fb96c410e97/cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", size = 3922613 },
+ { url = "https://files.pythonhosted.org/packages/28/34/6b3ac1d80fc174812486561cf25194338151780f27e438526f9c64e16869/cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", size = 4137925 },
+ { url = "https://files.pythonhosted.org/packages/d0/c7/c656eb08fd22255d21bc3129625ed9cd5ee305f33752ef2278711b3fa98b/cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", size = 3915417 },
+ { url = "https://files.pythonhosted.org/packages/ef/82/72403624f197af0db6bac4e58153bc9ac0e6020e57234115db9596eee85d/cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", size = 4155160 },
+ { url = "https://files.pythonhosted.org/packages/a2/cd/2f3c440913d4329ade49b146d74f2e9766422e1732613f57097fea61f344/cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", size = 3932331 },
+ { url = "https://files.pythonhosted.org/packages/7f/df/8be88797f0a1cca6e255189a57bb49237402b1880d6e8721690c5603ac23/cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", size = 4017372 },
+ { url = "https://files.pythonhosted.org/packages/af/36/5ccc376f025a834e72b8e52e18746b927f34e4520487098e283a719c205e/cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", size = 4239657 },
+ { url = "https://files.pythonhosted.org/packages/46/b0/f4f7d0d0bcfbc8dd6296c1449be326d04217c57afb8b2594f017eed95533/cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", size = 2758672 },
+ { url = "https://files.pythonhosted.org/packages/97/9b/443270b9210f13f6ef240eff73fd32e02d381e7103969dc66ce8e89ee901/cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", size = 3202071 },
+ { url = "https://files.pythonhosted.org/packages/77/d4/fea74422326388bbac0c37b7489a0fcb1681a698c3b875959430ba550daa/cryptography-44.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", size = 3338857 },
+ { url = "https://files.pythonhosted.org/packages/1a/aa/ba8a7467c206cb7b62f09b4168da541b5109838627f582843bbbe0235e8e/cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4", size = 3850615 },
+ { url = "https://files.pythonhosted.org/packages/89/fa/b160e10a64cc395d090105be14f399b94e617c879efd401188ce0fea39ee/cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", size = 4081622 },
+ { url = "https://files.pythonhosted.org/packages/47/8f/20ff0656bb0cf7af26ec1d01f780c5cfbaa7666736063378c5f48558b515/cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", size = 3867546 },
+ { url = "https://files.pythonhosted.org/packages/38/d9/28edf32ee2fcdca587146bcde90102a7319b2f2c690edfa627e46d586050/cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", size = 4090937 },
+ { url = "https://files.pythonhosted.org/packages/cc/9d/37e5da7519de7b0b070a3fedd4230fe76d50d2a21403e0f2153d70ac4163/cryptography-44.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", size = 3128774 },
+]
+
+[[package]]
+name = "cssutils"
+version = "2.11.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "more-itertools" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/33/9f/329d26121fe165be44b1dfff21aa0dc348f04633931f1d20ed6cf448a236/cssutils-2.11.1.tar.gz", hash = "sha256:0563a76513b6af6eebbe788c3bf3d01c920e46b3f90c8416738c5cfc773ff8e2", size = 711657 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/ec/bb273b7208c606890dc36540fe667d06ce840a6f62f9fae7e658fcdc90fb/cssutils-2.11.1-py3-none-any.whl", hash = "sha256:a67bfdfdff4f3867fab43698ec4897c1a828eca5973f4073321b3bccaf1199b1", size = 385747 },
+]
+
+[[package]]
+name = "dict2css"
+version = "0.3.0.post1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cssutils" },
+ { name = "domdf-python-tools" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/24/eb/776eef1f1aa0188c0fc165c3a60b71027539f71f2eedc43ad21b060e9c39/dict2css-0.3.0.post1.tar.gz", hash = "sha256:89c544c21c4ca7472c3fffb9d37d3d926f606329afdb751dc1de67a411b70719", size = 7845 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fe/47/290daabcf91628f4fc0e17c75a1690b354ba067066cd14407712600e609f/dict2css-0.3.0.post1-py3-none-any.whl", hash = "sha256:f006a6b774c3e31869015122ae82c491fd25e7de4a75607a62aa3e798f837e0d", size = 25647 },
+]
+
+[[package]]
+name = "dill"
+version = "0.3.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/70/43/86fe3f9e130c4137b0f1b50784dd70a5087b911fe07fa81e53e0c4c47fea/dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c", size = 187000 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/d1/e73b6ad76f0b1fb7f23c35c6d95dbc506a9c8804f43dda8cb5b0fa6331fd/dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", size = 119418 },
+]
+
+[[package]]
+name = "distlib"
+version = "0.3.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 },
+]
+
+[[package]]
+name = "docker"
+version = "7.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pywin32", marker = "sys_platform == 'win32'" },
+ { name = "requests" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774 },
+]
+
+[[package]]
+name = "docutils"
+version = "0.21.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 },
+]
+
+[[package]]
+name = "domdf-python-tools"
+version = "3.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "natsort" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6b/78/974e10c583ba9d2302e748c9585313a7f2c7ba00e4f600324f432e38fe68/domdf_python_tools-3.9.0.tar.gz", hash = "sha256:1f8a96971178333a55e083e35610d7688cd7620ad2b99790164e1fc1a3614c18", size = 103792 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/de/e9/7447a88b217650a74927d3444a89507986479a69b83741900eddd34167fe/domdf_python_tools-3.9.0-py3-none-any.whl", hash = "sha256:4e1ef365cbc24627d6d1e90cf7d46d8ab8df967e1237f4a26885f6986c78872e", size = 127106 },
+]
+
+[[package]]
+name = "elastic-transport"
+version = "8.17.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d7/82/2a544ac3d9c4ae19acc7f53117251bee20dd65dc3dff01fe55ea45ae9bd9/elastic_transport-8.17.0.tar.gz", hash = "sha256:e755f38f99fa6ec5456e236b8e58f0eb18873ac8fe710f74b91a16dd562de2a5", size = 73304 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/0d/2dd25c06078070973164b661e0d79868e434998391f9aed74d4070aab270/elastic_transport-8.17.0-py3-none-any.whl", hash = "sha256:59f553300866750e67a38828fede000576562a0e66930c641adb75249e0c95af", size = 64523 },
+]
+
+[[package]]
+name = "elasticsearch7"
+version = "7.17.12"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fc/50/58fab3e70c3d1ee75c782eff8186d9de423978f5a7c7fe91a6e5e2db84cd/elasticsearch7-7.17.12.tar.gz", hash = "sha256:6d67c788ef6b0e64e8134679636258044da185395a343a1b0bd60bd164b613c5", size = 248092 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bf/cd/9a9bb6776f80547f18fceef2a6a9cfc069d7e10512938b501f74704b409e/elasticsearch7-7.17.12-py2.py3-none-any.whl", hash = "sha256:d23a6d7cccc33f3a60a5295149b961334763ee144fca7db2e794129dad43184d", size = 386381 },
+]
+
+[[package]]
+name = "elasticsearch8"
+version = "8.17.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "elastic-transport" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ad/ac/d3f6a356f773d5db93d44f5938b55e2cd5f5d7dd051bc96aa86bf6a272ae/elasticsearch8-8.17.1.tar.gz", hash = "sha256:bb414b7e3d8387697946208f0f0b6e5e79bcf165cd021234b2ec7e5200d33690", size = 541074 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/12/e602c3d591a0f1faa2124f0f08822e82e8882243c23d35d67d1a81d560f1/elasticsearch8-8.17.1-py3-none-any.whl", hash = "sha256:c3e12138736c5614d9803016618a573effcf40bbb93838df14ae281680fe2260", size = 654211 },
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
+]
+
+[[package]]
+name = "execnet"
+version = "2.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612 },
+]
+
+[[package]]
+name = "filelock"
+version = "3.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 },
+]
+
+[[package]]
+name = "google-api-core"
+version = "2.24.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "google-auth" },
+ { name = "googleapis-common-protos" },
+ { name = "proto-plus" },
+ { name = "protobuf" },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/81/56/d70d66ed1b5ab5f6c27bf80ec889585ad8f865ff32acbafd3b2ef0bfb5d0/google_api_core-2.24.0.tar.gz", hash = "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf", size = 162647 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a1/76/65b8b94e74bf1b6d1cc38d916089670c4da5029d25762441d8c5c19e51dd/google_api_core-2.24.0-py3-none-any.whl", hash = "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9", size = 158576 },
+]
+
+[package.optional-dependencies]
+grpc = [
+ { name = "grpcio" },
+ { name = "grpcio-status" },
+]
+
+[[package]]
+name = "google-auth"
+version = "2.38.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cachetools" },
+ { name = "pyasn1-modules" },
+ { name = "rsa" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c6/eb/d504ba1daf190af6b204a9d4714d457462b486043744901a6eeea711f913/google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4", size = 270866 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9d/47/603554949a37bca5b7f894d51896a9c534b9eab808e2520a748e081669d0/google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a", size = 210770 },
+]
+
+[[package]]
+name = "google-cloud-bigquery"
+version = "3.29.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "google-api-core", extra = ["grpc"] },
+ { name = "google-auth" },
+ { name = "google-cloud-core" },
+ { name = "google-resumable-media" },
+ { name = "packaging" },
+ { name = "python-dateutil" },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/21/36/87875a9775985849f18d4b3e320e4acdeb5232db3d49cfa6269e7c7867b8/google_cloud_bigquery-3.29.0.tar.gz", hash = "sha256:fafc2b455ffce3bcc6ce0e884184ef50b6a11350a83b91e327fadda4d5566e72", size = 467180 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/60/9e1430f0fe17f8e8e931eff468021516f74f2573f261221529767dd59591/google_cloud_bigquery-3.29.0-py2.py3-none-any.whl", hash = "sha256:5453a4eabe50118254eda9778f3d7dad413490de5f7046b5e66c98f5a1580308", size = 244605 },
+]
+
+[[package]]
+name = "google-cloud-core"
+version = "2.4.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "google-api-core" },
+ { name = "google-auth" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b8/1f/9d1e0ba6919668608570418a9a51e47070ac15aeff64261fb092d8be94c0/google-cloud-core-2.4.1.tar.gz", hash = "sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073", size = 35587 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5e/0f/2e2061e3fbcb9d535d5da3f58cc8de4947df1786fe6a1355960feb05a681/google_cloud_core-2.4.1-py2.py3-none-any.whl", hash = "sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61", size = 29233 },
+]
+
+[[package]]
+name = "google-cloud-spanner"
+version = "3.51.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "google-api-core", extra = ["grpc"] },
+ { name = "google-cloud-core" },
+ { name = "grpc-google-iam-v1" },
+ { name = "grpc-interceptor" },
+ { name = "proto-plus" },
+ { name = "protobuf" },
+ { name = "sqlparse" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/90/f9/b30df85501ee1200328dd3bc69424dccc8a0b3389cd52f70d740a90fac87/google_cloud_spanner-3.51.0.tar.gz", hash = "sha256:346c2c20f64847883464fb0de5a6f9b48ecc6f79b032a2fb3a0aa088d9a9863f", size = 593310 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cc/b0/b0328d320d80d6963e7c4eb1e07a40d791f2c2847cda6af033141b02852a/google_cloud_spanner-3.51.0-py2.py3-none-any.whl", hash = "sha256:2d01f33582526ebe7fab62034e92e722e512c21f6bc4abe27e03d86ef7ea576a", size = 432632 },
+]
+
+[[package]]
+name = "google-crc32c"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/67/72/c3298da1a3773102359c5a78f20dae8925f5ea876e37354415f68594a6fb/google_crc32c-1.6.0.tar.gz", hash = "sha256:6eceb6ad197656a1ff49ebfbbfa870678c75be4344feb35ac1edf694309413dc", size = 14472 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1a/be/d7846cb50e17bf72a70ea2d8159478ac5de0f1170b10cac279f50079e78d/google_crc32c-1.6.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5bcc90b34df28a4b38653c36bb5ada35671ad105c99cfe915fb5bed7ad6924aa", size = 30267 },
+ { url = "https://files.pythonhosted.org/packages/84/3b/29cadae166132e4991087a49dc88906a1d3d5ec22b80f63bc4bc7b6e0431/google_crc32c-1.6.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d9e9913f7bd69e093b81da4535ce27af842e7bf371cde42d1ae9e9bd382dc0e9", size = 30113 },
+ { url = "https://files.pythonhosted.org/packages/18/a9/49a7b2c4b7cc69d15778a820734f9beb647b1b4cf1a629ca43e3d3a54c70/google_crc32c-1.6.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a184243544811e4a50d345838a883733461e67578959ac59964e43cca2c791e7", size = 37702 },
+ { url = "https://files.pythonhosted.org/packages/4b/aa/52538cceddefc7c2d66c6bd59dfe67a50f65a4952f441f91049e4188eb57/google_crc32c-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:236c87a46cdf06384f614e9092b82c05f81bd34b80248021f729396a78e55d7e", size = 32847 },
+ { url = "https://files.pythonhosted.org/packages/b1/2c/1928413d3faae74ae0d7bdba648cf36ed6b03328c562b47046af016b7249/google_crc32c-1.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebab974b1687509e5c973b5c4b8b146683e101e102e17a86bd196ecaa4d099fc", size = 37844 },
+ { url = "https://files.pythonhosted.org/packages/d6/f4/f62fa405e442b37c5676973b759dd6e56cd8d58a5c78662912456526f716/google_crc32c-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:50cf2a96da226dcbff8671233ecf37bf6e95de98b2a2ebadbfdf455e6d05df42", size = 33444 },
+ { url = "https://files.pythonhosted.org/packages/7d/14/ab47972ac79b6e7b03c8be3a7ef44b530a60e69555668dbbf08fc5692a98/google_crc32c-1.6.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f7a1fc29803712f80879b0806cb83ab24ce62fc8daf0569f2204a0cfd7f68ed4", size = 30267 },
+ { url = "https://files.pythonhosted.org/packages/54/7d/738cb0d25ee55629e7d07da686decf03864a366e5e863091a97b7bd2b8aa/google_crc32c-1.6.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:40b05ab32a5067525670880eb5d169529089a26fe35dce8891127aeddc1950e8", size = 30112 },
+ { url = "https://files.pythonhosted.org/packages/3e/6d/33ca50cbdeec09c31bb5dac277c90994edee975662a4c890bda7ffac90ef/google_crc32c-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e4b426c3702f3cd23b933436487eb34e01e00327fac20c9aebb68ccf34117d", size = 32861 },
+ { url = "https://files.pythonhosted.org/packages/67/1e/4870896fc81ec77b1b5ebae7fdd680d5a4d40e19a4b6d724032f996ca77a/google_crc32c-1.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51c4f54dd8c6dfeb58d1df5e4f7f97df8abf17a36626a217f169893d1d7f3e9f", size = 32490 },
+ { url = "https://files.pythonhosted.org/packages/00/9c/f5f5af3ddaa7a639d915f8f58b09bbb8d1db90ecd0459b62cd430eb9a4b6/google_crc32c-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:bb8b3c75bd157010459b15222c3fd30577042a7060e29d42dabce449c087f2b3", size = 33446 },
+ { url = "https://files.pythonhosted.org/packages/cf/41/65a91657d6a8123c6c12f9aac72127b6ac76dda9e2ba1834026a842eb77c/google_crc32c-1.6.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ed767bf4ba90104c1216b68111613f0d5926fb3780660ea1198fc469af410e9d", size = 30268 },
+ { url = "https://files.pythonhosted.org/packages/59/d0/ee743a267c7d5c4bb8bd865f7d4c039505f1c8a4b439df047fdc17be9769/google_crc32c-1.6.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:62f6d4a29fea082ac4a3c9be5e415218255cf11684ac6ef5488eea0c9132689b", size = 30113 },
+ { url = "https://files.pythonhosted.org/packages/25/53/e5e449c368dd26ade5fb2bb209e046d4309ed0623be65b13f0ce026cb520/google_crc32c-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c87d98c7c4a69066fd31701c4e10d178a648c2cac3452e62c6b24dc51f9fcc00", size = 32995 },
+ { url = "https://files.pythonhosted.org/packages/52/12/9bf6042d5b0ac8c25afed562fb78e51b0641474097e4139e858b45de40a5/google_crc32c-1.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd5e7d2445d1a958c266bfa5d04c39932dc54093fa391736dbfdb0f1929c1fb3", size = 32614 },
+ { url = "https://files.pythonhosted.org/packages/76/29/fc20f5ec36eac1eea0d0b2de4118c774c5f59c513f2a8630d4db6991f3e0/google_crc32c-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:7aec8e88a3583515f9e0957fe4f5f6d8d4997e36d0f61624e70469771584c760", size = 33445 },
+ { url = "https://files.pythonhosted.org/packages/3d/72/e7ac76dfd77dac46b0de63f0f117522e309f1bf79b29fc024b3570aa6f70/google_crc32c-1.6.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:e2806553238cd076f0a55bddab37a532b53580e699ed8e5606d0de1f856b5205", size = 30267 },
+ { url = "https://files.pythonhosted.org/packages/75/d0/8ca5b4b7982b6671cb5caccef230deb52c24f80e022f1d4b85b704d83a6e/google_crc32c-1.6.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:bb0966e1c50d0ef5bc743312cc730b533491d60585a9a08f897274e57c3f70e0", size = 30107 },
+ { url = "https://files.pythonhosted.org/packages/04/b2/42487d0bfc032f4b35f0675efa0a2cf89ae6a46a5ae5b01786d225c37211/google_crc32c-1.6.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:386122eeaaa76951a8196310432c5b0ef3b53590ef4c317ec7588ec554fec5d2", size = 37547 },
+ { url = "https://files.pythonhosted.org/packages/0f/fc/f8b5ae0273d0ecd8773944a5204e744adbb5ef2e471caaec6d220c95c478/google_crc32c-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2952396dc604544ea7476b33fe87faedc24d666fb0c2d5ac971a2b9576ab871", size = 32686 },
+ { url = "https://files.pythonhosted.org/packages/38/27/d9370090b5e399e04a92d6c45d1f66f35cf87c6799c7777a3c250a36a9f1/google_crc32c-1.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35834855408429cecf495cac67ccbab802de269e948e27478b1e47dfb6465e57", size = 37690 },
+ { url = "https://files.pythonhosted.org/packages/64/64/e83a0c71e380af513ea9b3a23ecd8c84b055fb806e2d8ecea8453eb72eda/google_crc32c-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:d8797406499f28b5ef791f339594b0b5fdedf54e203b5066675c406ba69d705c", size = 33442 },
+ { url = "https://files.pythonhosted.org/packages/e7/ff/ed48d136b65ddc61f5aef6261c58cd817c8cd60640b16680e5419fb17018/google_crc32c-1.6.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48abd62ca76a2cbe034542ed1b6aee851b6f28aaca4e6551b5599b6f3ef175cc", size = 28057 },
+ { url = "https://files.pythonhosted.org/packages/14/fb/54deefe679b7d1c1cc81d83396fcf28ad1a66d213bddeb275a8d28665918/google_crc32c-1.6.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e311c64008f1f1379158158bb3f0c8d72635b9eb4f9545f8cf990c5668e59d", size = 27866 },
+ { url = "https://files.pythonhosted.org/packages/b0/9e/5c01e8032d359fc78db914f32b7609ef64e63b894669536cd8b0d20409e1/google_crc32c-1.6.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05e2d8c9a2f853ff116db9706b4a27350587f341eda835f46db3c0a8c8ce2f24", size = 28051 },
+ { url = "https://files.pythonhosted.org/packages/50/1f/3b6c645c2d1d35e577404d25551c889a34b70de9ffc4ebd97141b16cedec/google_crc32c-1.6.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ca8145b060679ec9176e6de4f89b07363d6805bd4760631ef254905503598d", size = 27860 },
+]
+
+[[package]]
+name = "google-resumable-media"
+version = "2.7.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "google-crc32c" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/58/5a/0efdc02665dca14e0837b62c8a1a93132c264bd02054a15abb2218afe0ae/google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0", size = 2163099 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/82/35/b8d3baf8c46695858cb9d8835a53baa1eeb9906ddaf2f728a5f5b640fd1e/google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa", size = 81251 },
+]
+
+[[package]]
+name = "googleapis-common-protos"
+version = "1.66.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "protobuf" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ff/a7/8e9cccdb1c49870de6faea2a2764fa23f627dd290633103540209f03524c/googleapis_common_protos-1.66.0.tar.gz", hash = "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c", size = 114376 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/0f/c0713fb2b3d28af4b2fded3291df1c4d4f79a00d15c2374a9e010870016c/googleapis_common_protos-1.66.0-py2.py3-none-any.whl", hash = "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed", size = 221682 },
+]
+
+[package.optional-dependencies]
+grpc = [
+ { name = "grpcio" },
+]
+
+[[package]]
+name = "grpc-google-iam-v1"
+version = "0.14.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "googleapis-common-protos", extra = ["grpc"] },
+ { name = "grpcio" },
+ { name = "protobuf" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/47/2f/68e43b0e551974fa7dd18798a5974710586a72dc484ecaa2fc023d961342/grpc_google_iam_v1-0.14.0.tar.gz", hash = "sha256:c66e07aa642e39bb37950f9e7f491f70dad150ac9801263b42b2814307c2df99", size = 18327 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/66/b4/ab54f7fda4af43ca5c094bc1d6341780fd669c44ae18952b5337029b1d98/grpc_google_iam_v1-0.14.0-py2.py3-none-any.whl", hash = "sha256:fb4a084b30099ba3ab07d61d620a0d4429570b13ff53bd37bac75235f98b7da4", size = 27276 },
+]
+
+[[package]]
+name = "grpc-interceptor"
+version = "0.15.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "grpcio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9f/28/57449d5567adf4c1d3e216aaca545913fbc21a915f2da6790d6734aac76e/grpc-interceptor-0.15.4.tar.gz", hash = "sha256:1f45c0bcb58b6f332f37c637632247c9b02bc6af0fdceb7ba7ce8d2ebbfb0926", size = 19322 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/15/ac/8d53f230a7443401ce81791ec50a3b0e54924bf615ad287654fa4a2f5cdc/grpc_interceptor-0.15.4-py3-none-any.whl", hash = "sha256:0035f33228693ed3767ee49d937bac424318db173fef4d2d0170b3215f254d9d", size = 20848 },
+]
+
+[[package]]
+name = "grpcio"
+version = "1.70.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/69/e1/4b21b5017c33f3600dcc32b802bb48fe44a4d36d6c066f52650c7c2690fa/grpcio-1.70.0.tar.gz", hash = "sha256:8d1584a68d5922330025881e63a6c1b54cc8117291d382e4fa69339b6d914c56", size = 12788932 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/10/e9/f72408bac1f7b05b25e4df569b02d6b200c8e7857193aa9f1df7a3744add/grpcio-1.70.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:95469d1977429f45fe7df441f586521361e235982a0b39e33841549143ae2851", size = 5229736 },
+ { url = "https://files.pythonhosted.org/packages/b3/17/e65139ea76dac7bcd8a3f17cbd37e3d1a070c44db3098d0be5e14c5bd6a1/grpcio-1.70.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:ed9718f17fbdb472e33b869c77a16d0b55e166b100ec57b016dc7de9c8d236bf", size = 11432751 },
+ { url = "https://files.pythonhosted.org/packages/a0/12/42de6082b4ab14a59d30b2fc7786882fdaa75813a4a4f3d4a8c4acd6ed59/grpcio-1.70.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:374d014f29f9dfdb40510b041792e0e2828a1389281eb590df066e1cc2b404e5", size = 5711439 },
+ { url = "https://files.pythonhosted.org/packages/34/f8/b5a19524d273cbd119274a387bb72d6fbb74578e13927a473bc34369f079/grpcio-1.70.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2af68a6f5c8f78d56c145161544ad0febbd7479524a59c16b3e25053f39c87f", size = 6330777 },
+ { url = "https://files.pythonhosted.org/packages/1a/67/3d6c0ad786238aac7fa93b79246fc452978fbfe9e5f86f70da8e8a2797d0/grpcio-1.70.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7df14b2dcd1102a2ec32f621cc9fab6695effef516efbc6b063ad749867295", size = 5944639 },
+ { url = "https://files.pythonhosted.org/packages/76/0d/d9f7cbc41c2743cf18236a29b6a582f41bd65572a7144d92b80bc1e68479/grpcio-1.70.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c78b339869f4dbf89881e0b6fbf376313e4f845a42840a7bdf42ee6caed4b11f", size = 6643543 },
+ { url = "https://files.pythonhosted.org/packages/fc/24/bdd7e606b3400c14330e33a4698fa3a49e38a28c9e0a831441adbd3380d2/grpcio-1.70.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58ad9ba575b39edef71f4798fdb5c7b6d02ad36d47949cd381d4392a5c9cbcd3", size = 6199897 },
+ { url = "https://files.pythonhosted.org/packages/d1/33/8132eb370087960c82d01b89faeb28f3e58f5619ffe19889f57c58a19c18/grpcio-1.70.0-cp310-cp310-win32.whl", hash = "sha256:2b0d02e4b25a5c1f9b6c7745d4fa06efc9fd6a611af0fb38d3ba956786b95199", size = 3617513 },
+ { url = "https://files.pythonhosted.org/packages/99/bc/0fce5cfc0ca969df66f5dca6cf8d2258abb88146bf9ab89d8cf48e970137/grpcio-1.70.0-cp310-cp310-win_amd64.whl", hash = "sha256:0de706c0a5bb9d841e353f6343a9defc9fc35ec61d6eb6111802f3aa9fef29e1", size = 4303342 },
+ { url = "https://files.pythonhosted.org/packages/65/c4/1f67d23d6bcadd2fd61fb460e5969c52b3390b4a4e254b5e04a6d1009e5e/grpcio-1.70.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:17325b0be0c068f35770f944124e8839ea3185d6d54862800fc28cc2ffad205a", size = 5229017 },
+ { url = "https://files.pythonhosted.org/packages/e4/bd/cc36811c582d663a740fb45edf9f99ddbd99a10b6ba38267dc925e1e193a/grpcio-1.70.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:dbe41ad140df911e796d4463168e33ef80a24f5d21ef4d1e310553fcd2c4a386", size = 11472027 },
+ { url = "https://files.pythonhosted.org/packages/7e/32/8538bb2ace5cd72da7126d1c9804bf80b4fe3be70e53e2d55675c24961a8/grpcio-1.70.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5ea67c72101d687d44d9c56068328da39c9ccba634cabb336075fae2eab0d04b", size = 5707785 },
+ { url = "https://files.pythonhosted.org/packages/ce/5c/a45f85f2a0dfe4a6429dee98717e0e8bd7bd3f604315493c39d9679ca065/grpcio-1.70.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb5277db254ab7586769e490b7b22f4ddab3876c490da0a1a9d7c695ccf0bf77", size = 6331599 },
+ { url = "https://files.pythonhosted.org/packages/9f/e5/5316b239380b8b2ad30373eb5bb25d9fd36c0375e94a98a0a60ea357d254/grpcio-1.70.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7831a0fc1beeeb7759f737f5acd9fdcda520e955049512d68fda03d91186eea", size = 5940834 },
+ { url = "https://files.pythonhosted.org/packages/05/33/dbf035bc6d167068b4a9f2929dfe0b03fb763f0f861ecb3bb1709a14cb65/grpcio-1.70.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:27cc75e22c5dba1fbaf5a66c778e36ca9b8ce850bf58a9db887754593080d839", size = 6641191 },
+ { url = "https://files.pythonhosted.org/packages/4c/c4/684d877517e5bfd6232d79107e5a1151b835e9f99051faef51fed3359ec4/grpcio-1.70.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d63764963412e22f0491d0d32833d71087288f4e24cbcddbae82476bfa1d81fd", size = 6198744 },
+ { url = "https://files.pythonhosted.org/packages/e9/43/92fe5eeaf340650a7020cfb037402c7b9209e7a0f3011ea1626402219034/grpcio-1.70.0-cp311-cp311-win32.whl", hash = "sha256:bb491125103c800ec209d84c9b51f1c60ea456038e4734688004f377cfacc113", size = 3617111 },
+ { url = "https://files.pythonhosted.org/packages/55/15/b6cf2c9515c028aff9da6984761a3ab484a472b0dc6435fcd07ced42127d/grpcio-1.70.0-cp311-cp311-win_amd64.whl", hash = "sha256:d24035d49e026353eb042bf7b058fb831db3e06d52bee75c5f2f3ab453e71aca", size = 4304604 },
+ { url = "https://files.pythonhosted.org/packages/4c/a4/ddbda79dd176211b518f0f3795af78b38727a31ad32bc149d6a7b910a731/grpcio-1.70.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:ef4c14508299b1406c32bdbb9fb7b47612ab979b04cf2b27686ea31882387cff", size = 5198135 },
+ { url = "https://files.pythonhosted.org/packages/30/5c/60eb8a063ea4cb8d7670af8fac3f2033230fc4b75f62669d67c66ac4e4b0/grpcio-1.70.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:aa47688a65643afd8b166928a1da6247d3f46a2784d301e48ca1cc394d2ffb40", size = 11447529 },
+ { url = "https://files.pythonhosted.org/packages/fb/b9/1bf8ab66729f13b44e8f42c9de56417d3ee6ab2929591cfee78dce749b57/grpcio-1.70.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:880bfb43b1bb8905701b926274eafce5c70a105bc6b99e25f62e98ad59cb278e", size = 5664484 },
+ { url = "https://files.pythonhosted.org/packages/d1/06/2f377d6906289bee066d96e9bdb91e5e96d605d173df9bb9856095cccb57/grpcio-1.70.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e654c4b17d07eab259d392e12b149c3a134ec52b11ecdc6a515b39aceeec898", size = 6303739 },
+ { url = "https://files.pythonhosted.org/packages/ae/50/64c94cfc4db8d9ed07da71427a936b5a2bd2b27c66269b42fbda82c7c7a4/grpcio-1.70.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2394e3381071045a706ee2eeb6e08962dd87e8999b90ac15c55f56fa5a8c9597", size = 5910417 },
+ { url = "https://files.pythonhosted.org/packages/53/89/8795dfc3db4389c15554eb1765e14cba8b4c88cc80ff828d02f5572965af/grpcio-1.70.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b3c76701428d2df01964bc6479422f20e62fcbc0a37d82ebd58050b86926ef8c", size = 6626797 },
+ { url = "https://files.pythonhosted.org/packages/9c/b2/6a97ac91042a2c59d18244c479ee3894e7fb6f8c3a90619bb5a7757fa30c/grpcio-1.70.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac073fe1c4cd856ebcf49e9ed6240f4f84d7a4e6ee95baa5d66ea05d3dd0df7f", size = 6190055 },
+ { url = "https://files.pythonhosted.org/packages/86/2b/28db55c8c4d156053a8c6f4683e559cd0a6636f55a860f87afba1ac49a51/grpcio-1.70.0-cp312-cp312-win32.whl", hash = "sha256:cd24d2d9d380fbbee7a5ac86afe9787813f285e684b0271599f95a51bce33528", size = 3600214 },
+ { url = "https://files.pythonhosted.org/packages/17/c3/a7a225645a965029ed432e5b5e9ed959a574e62100afab553eef58be0e37/grpcio-1.70.0-cp312-cp312-win_amd64.whl", hash = "sha256:0495c86a55a04a874c7627fd33e5beaee771917d92c0e6d9d797628ac40e7655", size = 4292538 },
+ { url = "https://files.pythonhosted.org/packages/68/38/66d0f32f88feaf7d83f8559cd87d899c970f91b1b8a8819b58226de0a496/grpcio-1.70.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa573896aeb7d7ce10b1fa425ba263e8dddd83d71530d1322fd3a16f31257b4a", size = 5199218 },
+ { url = "https://files.pythonhosted.org/packages/c1/96/947df763a0b18efb5cc6c2ae348e56d97ca520dc5300c01617b234410173/grpcio-1.70.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:d405b005018fd516c9ac529f4b4122342f60ec1cee181788249372524e6db429", size = 11445983 },
+ { url = "https://files.pythonhosted.org/packages/fd/5b/f3d4b063e51b2454bedb828e41f3485800889a3609c49e60f2296cc8b8e5/grpcio-1.70.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f32090238b720eb585248654db8e3afc87b48d26ac423c8dde8334a232ff53c9", size = 5663954 },
+ { url = "https://files.pythonhosted.org/packages/bd/0b/dab54365fcedf63e9f358c1431885478e77d6f190d65668936b12dd38057/grpcio-1.70.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfa089a734f24ee5f6880c83d043e4f46bf812fcea5181dcb3a572db1e79e01c", size = 6304323 },
+ { url = "https://files.pythonhosted.org/packages/76/a8/8f965a7171ddd336ce32946e22954aa1bbc6f23f095e15dadaa70604ba20/grpcio-1.70.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f19375f0300b96c0117aca118d400e76fede6db6e91f3c34b7b035822e06c35f", size = 5910939 },
+ { url = "https://files.pythonhosted.org/packages/1b/05/0bbf68be8b17d1ed6f178435a3c0c12e665a1e6054470a64ce3cb7896596/grpcio-1.70.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7c73c42102e4a5ec76608d9b60227d917cea46dff4d11d372f64cbeb56d259d0", size = 6631405 },
+ { url = "https://files.pythonhosted.org/packages/79/6a/5df64b6df405a1ed1482cb6c10044b06ec47fd28e87c2232dbcf435ecb33/grpcio-1.70.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0a5c78d5198a1f0aa60006cd6eb1c912b4a1520b6a3968e677dbcba215fabb40", size = 6190982 },
+ { url = "https://files.pythonhosted.org/packages/42/aa/aeaac87737e6d25d1048c53b8ec408c056d3ed0c922e7c5efad65384250c/grpcio-1.70.0-cp313-cp313-win32.whl", hash = "sha256:fe9dbd916df3b60e865258a8c72ac98f3ac9e2a9542dcb72b7a34d236242a5ce", size = 3598359 },
+ { url = "https://files.pythonhosted.org/packages/1f/79/8edd2442d2de1431b4a3de84ef91c37002f12de0f9b577fb07b452989dbc/grpcio-1.70.0-cp313-cp313-win_amd64.whl", hash = "sha256:4119fed8abb7ff6c32e3d2255301e59c316c22d31ab812b3fbcbaf3d0d87cc68", size = 4293938 },
+ { url = "https://files.pythonhosted.org/packages/9d/0e/64061c9746a2dd6e07cb0a0f3829f0a431344add77ec36397cc452541ff6/grpcio-1.70.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:4f1937f47c77392ccd555728f564a49128b6a197a05a5cd527b796d36f3387d0", size = 5231123 },
+ { url = "https://files.pythonhosted.org/packages/72/9f/c93501d5f361aecee0146ab19300d5acb1c2747b00217c641f06fffbcd62/grpcio-1.70.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:0cd430b9215a15c10b0e7d78f51e8a39d6cf2ea819fd635a7214fae600b1da27", size = 11467217 },
+ { url = "https://files.pythonhosted.org/packages/0a/1a/980d115b701023450a304881bf3f6309f6fb15787f9b78d2728074f3bf86/grpcio-1.70.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:e27585831aa6b57b9250abaf147003e126cd3a6c6ca0c531a01996f31709bed1", size = 5710913 },
+ { url = "https://files.pythonhosted.org/packages/a0/84/af420067029808f9790e98143b3dd0f943bebba434a4706755051a520c91/grpcio-1.70.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1af8e15b0f0fe0eac75195992a63df17579553b0c4af9f8362cc7cc99ccddf4", size = 6330947 },
+ { url = "https://files.pythonhosted.org/packages/24/1c/e1f06a7d29a1fa5053dcaf5352a50f8e1f04855fd194a65422a9d685d375/grpcio-1.70.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbce24409beaee911c574a3d75d12ffb8c3e3dd1b813321b1d7a96bbcac46bf4", size = 5943913 },
+ { url = "https://files.pythonhosted.org/packages/41/8f/de13838e4467519a50cd0693e98b0b2bcc81d656013c38a1dd7dcb801526/grpcio-1.70.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ff4a8112a79464919bb21c18e956c54add43ec9a4850e3949da54f61c241a4a6", size = 6643236 },
+ { url = "https://files.pythonhosted.org/packages/ac/73/d68c745d34e43a80440da4f3d79fa02c56cb118c2a26ba949f3cfd8316d7/grpcio-1.70.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5413549fdf0b14046c545e19cfc4eb1e37e9e1ebba0ca390a8d4e9963cab44d2", size = 6199038 },
+ { url = "https://files.pythonhosted.org/packages/7e/dd/991f100b8c31636b4bb2a941dbbf54dbcc55d69c722cfa038c3d017eaa0c/grpcio-1.70.0-cp39-cp39-win32.whl", hash = "sha256:b745d2c41b27650095e81dea7091668c040457483c9bdb5d0d9de8f8eb25e59f", size = 3617512 },
+ { url = "https://files.pythonhosted.org/packages/4d/80/1aa2ba791207a13e314067209b48e1a0893ed8d1f43ef012e194aaa6c2de/grpcio-1.70.0-cp39-cp39-win_amd64.whl", hash = "sha256:a31d7e3b529c94e930a117b2175b2efd179d96eb3c7a21ccb0289a8ab05b645c", size = 4303506 },
+]
+
+[[package]]
+name = "grpcio-status"
+version = "1.70.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "googleapis-common-protos" },
+ { name = "grpcio" },
+ { name = "protobuf" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4c/d1/2397797c810020eac424e1aac10fbdc5edb6b9b4ad6617e0ed53ca907653/grpcio_status-1.70.0.tar.gz", hash = "sha256:0e7b42816512433b18b9d764285ff029bde059e9d41f8fe10a60631bd8348101", size = 13681 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e6/34/49e558040e069feebac70cdd1b605f38738c0277ac5d38e2ce3d03e1b1ec/grpcio_status-1.70.0-py3-none-any.whl", hash = "sha256:fc5a2ae2b9b1c1969cc49f3262676e6854aa2398ec69cb5bd6c47cd501904a85", size = 14429 },
+]
+
+[[package]]
+name = "h11"
+version = "0.14.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
+]
+
+[[package]]
+name = "html5lib"
+version = "1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six" },
+ { name = "webencodings" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ac/b6/b55c3f49042f1df3dcd422b7f224f939892ee94f22abcf503a9b7339eaf2/html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f", size = 272215 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6c/dd/a834df6482147d48e225a49515aabc28974ad5a4ca3215c18a882565b028/html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d", size = 112173 },
+]
+
+[[package]]
+name = "identify"
+version = "2.6.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/82/bf/c68c46601bacd4c6fb4dd751a42b6e7087240eaabc6487f2ef7a48e0e8fc/identify-2.6.6.tar.gz", hash = "sha256:7bec12768ed44ea4761efb47806f0a41f86e7c0a5fdf5950d4648c90eca7e251", size = 99217 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/74/a1/68a395c17eeefb04917034bd0a1bfa765e7654fa150cca473d669aa3afb5/identify-2.6.6-py2.py3-none-any.whl", hash = "sha256:cbd1810bce79f8b671ecb20f53ee0ae8e86ae84b557de31d89709dc2a48ba881", size = 99083 },
+]
+
+[[package]]
+name = "idna"
+version = "3.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
+]
+
+[[package]]
+name = "imagesize"
+version = "1.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 },
+]
+
+[[package]]
+name = "importlib-metadata"
+version = "8.6.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "zipp", marker = "python_full_version < '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+]
+
+[[package]]
+name = "isodate"
+version = "0.7.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320 },
+]
+
+[[package]]
+name = "isort"
+version = "5.13.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 },
+]
+
+[[package]]
+name = "litestar-sphinx-theme"
+version = "0.2.0"
+source = { git = "https://github.com/litestar-org/litestar-sphinx-theme.git#76b1d0e4c8afff1ad135b1917fe09cf6c1cc6c9b" }
+dependencies = [
+ { name = "pydata-sphinx-theme" },
+ { name = "sphinx-design" },
+]
+
+[[package]]
+name = "mariadb"
+version = "1.1.11"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a1/8b/4e263dd4e631083ffaac4f3f7b03ad6fd52a3736967aea06ef946a824ff6/mariadb-1.1.11.tar.gz", hash = "sha256:cf6647cee081e21d0994b409ba8c8fa2077f3972f1de3627c5502fb31d14f806", size = 85604 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/75/440570ee3893d9d2bfbc0f88c83b2ea18452e304ac1a3296276b70072677/mariadb-1.1.11-cp310-cp310-win32.whl", hash = "sha256:dbc4cf0e302ca82d46f9431a0b04f048e9c21ee56d6f3162c29605f84d63b40c", size = 183311 },
+ { url = "https://files.pythonhosted.org/packages/6b/1f/5295e8496aea3854ea9481538de406245b8e7f06c3e7592308ef9ed08402/mariadb-1.1.11-cp310-cp310-win_amd64.whl", hash = "sha256:579420293fa790d5ae0a6cb4bdb7e8be8facc2ceefb6123c2b0e8042b3fa725d", size = 199877 },
+ { url = "https://files.pythonhosted.org/packages/fc/0b/e4557b46d6ed5b9cadba0f580c790040c2f796e40b19115638b641e7418d/mariadb-1.1.11-cp311-cp311-win32.whl", hash = "sha256:0f8de8d66ca71bd102f34a970a331b7d75bdf7f8050d80e37cdcc6ff3c85cf7a", size = 183310 },
+ { url = "https://files.pythonhosted.org/packages/90/4f/35dc6c4b7e17157cd51cb1f6e1a1476fd0a083cc3e34f6551100216cb8df/mariadb-1.1.11-cp311-cp311-win_amd64.whl", hash = "sha256:3f64b520089cb60c4f8302f365ed0ae057c4c859ab70fc8b1c4358192c3c8f27", size = 199867 },
+ { url = "https://files.pythonhosted.org/packages/1b/5b/3ff578d7fb61196b9a61c8e78b0fcfe4a9c06ecd1a53293014fe0900c702/mariadb-1.1.11-cp312-cp312-win32.whl", hash = "sha256:f6dfdc954edf02b6519419a054798cda6034dc459d1d482e3329e37aa27d34f0", size = 183447 },
+ { url = "https://files.pythonhosted.org/packages/72/0e/ad5ec8b2d2b71fe2ce7604320d35d13a96334b2788168f2d3859a5844f93/mariadb-1.1.11-cp312-cp312-win_amd64.whl", hash = "sha256:2e72ea65f1d7d8563ee84e172f2a583193092bdb6ff83c470ca9722873273ecc", size = 199946 },
+ { url = "https://files.pythonhosted.org/packages/1e/17/e5f8f971c17ceaf9485b81e4986388f383740b27bd287b952bb72f320e0a/mariadb-1.1.11-cp313-cp313-win32.whl", hash = "sha256:d7302ccd15f0beee7b286885cbf6ac71ddc240374691d669784d99f89ba34d79", size = 183424 },
+ { url = "https://files.pythonhosted.org/packages/7f/35/93a261e7dbc6f335e89ccea02b62306d361945d9689c0470615d1c4a41a5/mariadb-1.1.11-cp313-cp313-win_amd64.whl", hash = "sha256:c1992ebf9c6f012ac158e33fef9f2c4ba899f721064c4ae3a3489233793296c0", size = 200000 },
+ { url = "https://files.pythonhosted.org/packages/7d/99/f22ad81a61bd264667d6fe1c813ccc756033750b68b18e0ae229647316dc/mariadb-1.1.11-cp39-cp39-win32.whl", hash = "sha256:6f28d8ccc597a3a1368be14078110f743900dbb3b0c7f1cce3072d83bec59c8a", size = 185711 },
+ { url = "https://files.pythonhosted.org/packages/9a/72/7677617511a8c8a9ecf4d4adc6beb46910a5b69b72372fdbb6324c057aec/mariadb-1.1.11-cp39-cp39-win_amd64.whl", hash = "sha256:e94f1738bec09c97b601ddbb1908eb24524ba4630f507a775d82ffdb6c5794b3", size = 202235 },
+]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 },
+ { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 },
+ { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 },
+ { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 },
+ { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 },
+ { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 },
+ { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 },
+ { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 },
+ { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 },
+ { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 },
+ { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 },
+ { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 },
+ { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 },
+ { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 },
+ { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 },
+ { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 },
+ { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 },
+ { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 },
+ { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 },
+ { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 },
+ { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 },
+ { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 },
+ { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 },
+ { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 },
+ { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 },
+ { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 },
+ { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 },
+ { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 },
+ { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 },
+ { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 },
+ { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 },
+ { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 },
+ { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 },
+ { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 },
+ { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 },
+ { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 },
+ { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 },
+ { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 },
+ { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 },
+ { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 },
+ { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 },
+ { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 },
+ { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 },
+ { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 },
+ { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 },
+ { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 },
+ { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 },
+ { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 },
+ { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 },
+ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
+ { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344 },
+ { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389 },
+ { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607 },
+ { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728 },
+ { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826 },
+ { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843 },
+ { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219 },
+ { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946 },
+ { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063 },
+ { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506 },
+]
+
+[[package]]
+name = "mccabe"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 },
+]
+
+[[package]]
+name = "more-itertools"
+version = "10.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/88/3b/7fa1fe835e2e93fd6d7b52b2f95ae810cf5ba133e1845f726f5a992d62c2/more-itertools-10.6.0.tar.gz", hash = "sha256:2cd7fad1009c31cc9fb6a035108509e6547547a7a738374f10bd49a09eb3ee3b", size = 125009 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/23/62/0fe302c6d1be1c777cab0616e6302478251dfbf9055ad426f5d0def75c89/more_itertools-10.6.0-py3-none-any.whl", hash = "sha256:6eb054cb4b6db1473f6e15fcc676a08e4732548acd47c708f0e179c2c7c01e89", size = 63038 },
+]
+
+[[package]]
+name = "msgpack"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cb/d0/7555686ae7ff5731205df1012ede15dd9d927f6227ea151e901c7406af4f/msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", size = 167260 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4b/f9/a892a6038c861fa849b11a2bb0502c07bc698ab6ea53359e5771397d883b/msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd", size = 150428 },
+ { url = "https://files.pythonhosted.org/packages/df/7a/d174cc6a3b6bb85556e6a046d3193294a92f9a8e583cdbd46dc8a1d7e7f4/msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d", size = 84131 },
+ { url = "https://files.pythonhosted.org/packages/08/52/bf4fbf72f897a23a56b822997a72c16de07d8d56d7bf273242f884055682/msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5", size = 81215 },
+ { url = "https://files.pythonhosted.org/packages/02/95/dc0044b439b518236aaf012da4677c1b8183ce388411ad1b1e63c32d8979/msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5", size = 371229 },
+ { url = "https://files.pythonhosted.org/packages/ff/75/09081792db60470bef19d9c2be89f024d366b1e1973c197bb59e6aabc647/msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e", size = 378034 },
+ { url = "https://files.pythonhosted.org/packages/32/d3/c152e0c55fead87dd948d4b29879b0f14feeeec92ef1fd2ec21b107c3f49/msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b", size = 363070 },
+ { url = "https://files.pythonhosted.org/packages/d9/2c/82e73506dd55f9e43ac8aa007c9dd088c6f0de2aa19e8f7330e6a65879fc/msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f", size = 359863 },
+ { url = "https://files.pythonhosted.org/packages/cb/a0/3d093b248837094220e1edc9ec4337de3443b1cfeeb6e0896af8ccc4cc7a/msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68", size = 368166 },
+ { url = "https://files.pythonhosted.org/packages/e4/13/7646f14f06838b406cf5a6ddbb7e8dc78b4996d891ab3b93c33d1ccc8678/msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b", size = 370105 },
+ { url = "https://files.pythonhosted.org/packages/67/fa/dbbd2443e4578e165192dabbc6a22c0812cda2649261b1264ff515f19f15/msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044", size = 68513 },
+ { url = "https://files.pythonhosted.org/packages/24/ce/c2c8fbf0ded750cb63cbcbb61bc1f2dfd69e16dca30a8af8ba80ec182dcd/msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f", size = 74687 },
+ { url = "https://files.pythonhosted.org/packages/b7/5e/a4c7154ba65d93be91f2f1e55f90e76c5f91ccadc7efc4341e6f04c8647f/msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", size = 150803 },
+ { url = "https://files.pythonhosted.org/packages/60/c2/687684164698f1d51c41778c838d854965dd284a4b9d3a44beba9265c931/msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", size = 84343 },
+ { url = "https://files.pythonhosted.org/packages/42/ae/d3adea9bb4a1342763556078b5765e666f8fdf242e00f3f6657380920972/msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", size = 81408 },
+ { url = "https://files.pythonhosted.org/packages/dc/17/6313325a6ff40ce9c3207293aee3ba50104aed6c2c1559d20d09e5c1ff54/msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6", size = 396096 },
+ { url = "https://files.pythonhosted.org/packages/a8/a1/ad7b84b91ab5a324e707f4c9761633e357820b011a01e34ce658c1dda7cc/msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59", size = 403671 },
+ { url = "https://files.pythonhosted.org/packages/bb/0b/fd5b7c0b308bbf1831df0ca04ec76fe2f5bf6319833646b0a4bd5e9dc76d/msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0", size = 387414 },
+ { url = "https://files.pythonhosted.org/packages/f0/03/ff8233b7c6e9929a1f5da3c7860eccd847e2523ca2de0d8ef4878d354cfa/msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e", size = 383759 },
+ { url = "https://files.pythonhosted.org/packages/1f/1b/eb82e1fed5a16dddd9bc75f0854b6e2fe86c0259c4353666d7fab37d39f4/msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6", size = 394405 },
+ { url = "https://files.pythonhosted.org/packages/90/2e/962c6004e373d54ecf33d695fb1402f99b51832631e37c49273cc564ffc5/msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", size = 396041 },
+ { url = "https://files.pythonhosted.org/packages/f8/20/6e03342f629474414860c48aeffcc2f7f50ddaf351d95f20c3f1c67399a8/msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", size = 68538 },
+ { url = "https://files.pythonhosted.org/packages/aa/c4/5a582fc9a87991a3e6f6800e9bb2f3c82972912235eb9539954f3e9997c7/msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788", size = 74871 },
+ { url = "https://files.pythonhosted.org/packages/e1/d6/716b7ca1dbde63290d2973d22bbef1b5032ca634c3ff4384a958ec3f093a/msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", size = 152421 },
+ { url = "https://files.pythonhosted.org/packages/70/da/5312b067f6773429cec2f8f08b021c06af416bba340c912c2ec778539ed6/msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", size = 85277 },
+ { url = "https://files.pythonhosted.org/packages/28/51/da7f3ae4462e8bb98af0d5bdf2707f1b8c65a0d4f496e46b6afb06cbc286/msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", size = 82222 },
+ { url = "https://files.pythonhosted.org/packages/33/af/dc95c4b2a49cff17ce47611ca9ba218198806cad7796c0b01d1e332c86bb/msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", size = 392971 },
+ { url = "https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", size = 401403 },
+ { url = "https://files.pythonhosted.org/packages/97/8c/e333690777bd33919ab7024269dc3c41c76ef5137b211d776fbb404bfead/msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", size = 385356 },
+ { url = "https://files.pythonhosted.org/packages/57/52/406795ba478dc1c890559dd4e89280fa86506608a28ccf3a72fbf45df9f5/msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", size = 383028 },
+ { url = "https://files.pythonhosted.org/packages/e7/69/053b6549bf90a3acadcd8232eae03e2fefc87f066a5b9fbb37e2e608859f/msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", size = 391100 },
+ { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 },
+ { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 },
+ { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 },
+ { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 },
+ { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 },
+ { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 },
+ { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 },
+ { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 },
+ { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 },
+ { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 },
+ { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 },
+ { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 },
+ { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 },
+ { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 },
+ { url = "https://files.pythonhosted.org/packages/f7/3b/544a5c5886042b80e1f4847a4757af3430f60d106d8d43bb7be72c9e9650/msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1", size = 150713 },
+ { url = "https://files.pythonhosted.org/packages/93/af/d63f25bcccd3d6f06fd518ba4a321f34a4370c67b579ca5c70b4a37721b4/msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48", size = 84277 },
+ { url = "https://files.pythonhosted.org/packages/92/9b/5c0dfb0009b9f96328664fecb9f8e4e9c8a1ae919e6d53986c1b813cb493/msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c", size = 81357 },
+ { url = "https://files.pythonhosted.org/packages/d1/7c/3a9ee6ec9fc3e47681ad39b4d344ee04ff20a776b594fba92d88d8b68356/msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468", size = 371256 },
+ { url = "https://files.pythonhosted.org/packages/f7/0a/8a213cecea7b731c540f25212ba5f9a818f358237ac51a44d448bd753690/msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74", size = 377868 },
+ { url = "https://files.pythonhosted.org/packages/1b/94/a82b0db0981e9586ed5af77d6cfb343da05d7437dceaae3b35d346498110/msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846", size = 363370 },
+ { url = "https://files.pythonhosted.org/packages/93/fc/6c7f0dcc1c913e14861e16eaf494c07fc1dde454ec726ff8cebcf348ae53/msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346", size = 358970 },
+ { url = "https://files.pythonhosted.org/packages/1f/c6/e4a04c0089deace870dabcdef5c9f12798f958e2e81d5012501edaff342f/msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b", size = 366358 },
+ { url = "https://files.pythonhosted.org/packages/b6/54/7d8317dac590cf16b3e08e3fb74d2081e5af44eb396f0effa13f17777f30/msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8", size = 370336 },
+ { url = "https://files.pythonhosted.org/packages/dc/6f/a5a1f43b6566831e9630e5bc5d86034a8884386297302be128402555dde1/msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd", size = 68683 },
+ { url = "https://files.pythonhosted.org/packages/5f/e8/2162621e18dbc36e2bc8492fd0e97b3975f5d89fe0472ae6d5f7fbdd8cf7/msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325", size = 74787 },
+]
+
+[[package]]
+name = "multidict"
+version = "6.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/29/68/259dee7fd14cf56a17c554125e534f6274c2860159692a414d0b402b9a6d/multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60", size = 48628 },
+ { url = "https://files.pythonhosted.org/packages/50/79/53ba256069fe5386a4a9e80d4e12857ced9de295baf3e20c68cdda746e04/multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1", size = 29327 },
+ { url = "https://files.pythonhosted.org/packages/ff/10/71f1379b05b196dae749b5ac062e87273e3f11634f447ebac12a571d90ae/multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53", size = 29689 },
+ { url = "https://files.pythonhosted.org/packages/71/45/70bac4f87438ded36ad4793793c0095de6572d433d98575a5752629ef549/multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5", size = 126639 },
+ { url = "https://files.pythonhosted.org/packages/80/cf/17f35b3b9509b4959303c05379c4bfb0d7dd05c3306039fc79cf035bbac0/multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581", size = 134315 },
+ { url = "https://files.pythonhosted.org/packages/ef/1f/652d70ab5effb33c031510a3503d4d6efc5ec93153562f1ee0acdc895a57/multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56", size = 129471 },
+ { url = "https://files.pythonhosted.org/packages/a6/64/2dd6c4c681688c0165dea3975a6a4eab4944ea30f35000f8b8af1df3148c/multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429", size = 124585 },
+ { url = "https://files.pythonhosted.org/packages/87/56/e6ee5459894c7e554b57ba88f7257dc3c3d2d379cb15baaa1e265b8c6165/multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748", size = 116957 },
+ { url = "https://files.pythonhosted.org/packages/36/9e/616ce5e8d375c24b84f14fc263c7ef1d8d5e8ef529dbc0f1df8ce71bb5b8/multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db", size = 128609 },
+ { url = "https://files.pythonhosted.org/packages/8c/4f/4783e48a38495d000f2124020dc96bacc806a4340345211b1ab6175a6cb4/multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056", size = 123016 },
+ { url = "https://files.pythonhosted.org/packages/3e/b3/4950551ab8fc39862ba5e9907dc821f896aa829b4524b4deefd3e12945ab/multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76", size = 133542 },
+ { url = "https://files.pythonhosted.org/packages/96/4d/f0ce6ac9914168a2a71df117935bb1f1781916acdecbb43285e225b484b8/multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160", size = 130163 },
+ { url = "https://files.pythonhosted.org/packages/be/72/17c9f67e7542a49dd252c5ae50248607dfb780bcc03035907dafefb067e3/multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7", size = 126832 },
+ { url = "https://files.pythonhosted.org/packages/71/9f/72d719e248cbd755c8736c6d14780533a1606ffb3fbb0fbd77da9f0372da/multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0", size = 26402 },
+ { url = "https://files.pythonhosted.org/packages/04/5a/d88cd5d00a184e1ddffc82aa2e6e915164a6d2641ed3606e766b5d2f275a/multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d", size = 28800 },
+ { url = "https://files.pythonhosted.org/packages/93/13/df3505a46d0cd08428e4c8169a196131d1b0c4b515c3649829258843dde6/multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6", size = 48570 },
+ { url = "https://files.pythonhosted.org/packages/f0/e1/a215908bfae1343cdb72f805366592bdd60487b4232d039c437fe8f5013d/multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156", size = 29316 },
+ { url = "https://files.pythonhosted.org/packages/70/0f/6dc70ddf5d442702ed74f298d69977f904960b82368532c88e854b79f72b/multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb", size = 29640 },
+ { url = "https://files.pythonhosted.org/packages/d8/6d/9c87b73a13d1cdea30b321ef4b3824449866bd7f7127eceed066ccb9b9ff/multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b", size = 131067 },
+ { url = "https://files.pythonhosted.org/packages/cc/1e/1b34154fef373371fd6c65125b3d42ff5f56c7ccc6bfff91b9b3c60ae9e0/multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72", size = 138507 },
+ { url = "https://files.pythonhosted.org/packages/fb/e0/0bc6b2bac6e461822b5f575eae85da6aae76d0e2a79b6665d6206b8e2e48/multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304", size = 133905 },
+ { url = "https://files.pythonhosted.org/packages/ba/af/73d13b918071ff9b2205fcf773d316e0f8fefb4ec65354bbcf0b10908cc6/multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351", size = 129004 },
+ { url = "https://files.pythonhosted.org/packages/74/21/23960627b00ed39643302d81bcda44c9444ebcdc04ee5bedd0757513f259/multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb", size = 121308 },
+ { url = "https://files.pythonhosted.org/packages/8b/5c/cf282263ffce4a596ed0bb2aa1a1dddfe1996d6a62d08842a8d4b33dca13/multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3", size = 132608 },
+ { url = "https://files.pythonhosted.org/packages/d7/3e/97e778c041c72063f42b290888daff008d3ab1427f5b09b714f5a8eff294/multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399", size = 127029 },
+ { url = "https://files.pythonhosted.org/packages/47/ac/3efb7bfe2f3aefcf8d103e9a7162572f01936155ab2f7ebcc7c255a23212/multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423", size = 137594 },
+ { url = "https://files.pythonhosted.org/packages/42/9b/6c6e9e8dc4f915fc90a9b7798c44a30773dea2995fdcb619870e705afe2b/multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3", size = 134556 },
+ { url = "https://files.pythonhosted.org/packages/1d/10/8e881743b26aaf718379a14ac58572a240e8293a1c9d68e1418fb11c0f90/multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753", size = 130993 },
+ { url = "https://files.pythonhosted.org/packages/45/84/3eb91b4b557442802d058a7579e864b329968c8d0ea57d907e7023c677f2/multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80", size = 26405 },
+ { url = "https://files.pythonhosted.org/packages/9f/0b/ad879847ecbf6d27e90a6eabb7eff6b62c129eefe617ea45eae7c1f0aead/multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926", size = 28795 },
+ { url = "https://files.pythonhosted.org/packages/fd/16/92057c74ba3b96d5e211b553895cd6dc7cc4d1e43d9ab8fafc727681ef71/multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", size = 48713 },
+ { url = "https://files.pythonhosted.org/packages/94/3d/37d1b8893ae79716179540b89fc6a0ee56b4a65fcc0d63535c6f5d96f217/multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", size = 29516 },
+ { url = "https://files.pythonhosted.org/packages/a2/12/adb6b3200c363062f805275b4c1e656be2b3681aada66c80129932ff0bae/multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", size = 29557 },
+ { url = "https://files.pythonhosted.org/packages/47/e9/604bb05e6e5bce1e6a5cf80a474e0f072e80d8ac105f1b994a53e0b28c42/multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", size = 130170 },
+ { url = "https://files.pythonhosted.org/packages/7e/13/9efa50801785eccbf7086b3c83b71a4fb501a4d43549c2f2f80b8787d69f/multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", size = 134836 },
+ { url = "https://files.pythonhosted.org/packages/bf/0f/93808b765192780d117814a6dfcc2e75de6dcc610009ad408b8814dca3ba/multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", size = 133475 },
+ { url = "https://files.pythonhosted.org/packages/d3/c8/529101d7176fe7dfe1d99604e48d69c5dfdcadb4f06561f465c8ef12b4df/multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", size = 131049 },
+ { url = "https://files.pythonhosted.org/packages/ca/0c/fc85b439014d5a58063e19c3a158a889deec399d47b5269a0f3b6a2e28bc/multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", size = 120370 },
+ { url = "https://files.pythonhosted.org/packages/db/46/d4416eb20176492d2258fbd47b4abe729ff3b6e9c829ea4236f93c865089/multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", size = 125178 },
+ { url = "https://files.pythonhosted.org/packages/5b/46/73697ad7ec521df7de5531a32780bbfd908ded0643cbe457f981a701457c/multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", size = 119567 },
+ { url = "https://files.pythonhosted.org/packages/cd/ed/51f060e2cb0e7635329fa6ff930aa5cffa17f4c7f5c6c3ddc3500708e2f2/multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", size = 129822 },
+ { url = "https://files.pythonhosted.org/packages/df/9e/ee7d1954b1331da3eddea0c4e08d9142da5f14b1321c7301f5014f49d492/multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", size = 128656 },
+ { url = "https://files.pythonhosted.org/packages/77/00/8538f11e3356b5d95fa4b024aa566cde7a38aa7a5f08f4912b32a037c5dc/multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", size = 125360 },
+ { url = "https://files.pythonhosted.org/packages/be/05/5d334c1f2462d43fec2363cd00b1c44c93a78c3925d952e9a71caf662e96/multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", size = 26382 },
+ { url = "https://files.pythonhosted.org/packages/a3/bf/f332a13486b1ed0496d624bcc7e8357bb8053823e8cd4b9a18edc1d97e73/multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", size = 28529 },
+ { url = "https://files.pythonhosted.org/packages/22/67/1c7c0f39fe069aa4e5d794f323be24bf4d33d62d2a348acdb7991f8f30db/multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", size = 48771 },
+ { url = "https://files.pythonhosted.org/packages/3c/25/c186ee7b212bdf0df2519eacfb1981a017bda34392c67542c274651daf23/multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", size = 29533 },
+ { url = "https://files.pythonhosted.org/packages/67/5e/04575fd837e0958e324ca035b339cea174554f6f641d3fb2b4f2e7ff44a2/multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", size = 29595 },
+ { url = "https://files.pythonhosted.org/packages/d3/b2/e56388f86663810c07cfe4a3c3d87227f3811eeb2d08450b9e5d19d78876/multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", size = 130094 },
+ { url = "https://files.pythonhosted.org/packages/6c/ee/30ae9b4186a644d284543d55d491fbd4239b015d36b23fea43b4c94f7052/multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", size = 134876 },
+ { url = "https://files.pythonhosted.org/packages/84/c7/70461c13ba8ce3c779503c70ec9d0345ae84de04521c1f45a04d5f48943d/multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", size = 133500 },
+ { url = "https://files.pythonhosted.org/packages/4a/9f/002af221253f10f99959561123fae676148dd730e2daa2cd053846a58507/multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", size = 131099 },
+ { url = "https://files.pythonhosted.org/packages/82/42/d1c7a7301d52af79d88548a97e297f9d99c961ad76bbe6f67442bb77f097/multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", size = 120403 },
+ { url = "https://files.pythonhosted.org/packages/68/f3/471985c2c7ac707547553e8f37cff5158030d36bdec4414cb825fbaa5327/multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", size = 125348 },
+ { url = "https://files.pythonhosted.org/packages/67/2c/e6df05c77e0e433c214ec1d21ddd203d9a4770a1f2866a8ca40a545869a0/multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", size = 119673 },
+ { url = "https://files.pythonhosted.org/packages/c5/cd/bc8608fff06239c9fb333f9db7743a1b2eafe98c2666c9a196e867a3a0a4/multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", size = 129927 },
+ { url = "https://files.pythonhosted.org/packages/44/8e/281b69b7bc84fc963a44dc6e0bbcc7150e517b91df368a27834299a526ac/multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", size = 128711 },
+ { url = "https://files.pythonhosted.org/packages/12/a4/63e7cd38ed29dd9f1881d5119f272c898ca92536cdb53ffe0843197f6c85/multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", size = 125519 },
+ { url = "https://files.pythonhosted.org/packages/38/e0/4f5855037a72cd8a7a2f60a3952d9aa45feedb37ae7831642102604e8a37/multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", size = 26426 },
+ { url = "https://files.pythonhosted.org/packages/7e/a5/17ee3a4db1e310b7405f5d25834460073a8ccd86198ce044dfaf69eac073/multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", size = 28531 },
+ { url = "https://files.pythonhosted.org/packages/e7/c9/9e153a6572b38ac5ff4434113af38acf8d5e9957897cdb1f513b3d6614ed/multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c", size = 48550 },
+ { url = "https://files.pythonhosted.org/packages/76/f5/79565ddb629eba6c7f704f09a09df085c8dc04643b12506f10f718cee37a/multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1", size = 29298 },
+ { url = "https://files.pythonhosted.org/packages/60/1b/9851878b704bc98e641a3e0bce49382ae9e05743dac6d97748feb5b7baba/multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c", size = 29641 },
+ { url = "https://files.pythonhosted.org/packages/89/87/d451d45aab9e422cb0fb2f7720c31a4c1d3012c740483c37f642eba568fb/multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c", size = 126202 },
+ { url = "https://files.pythonhosted.org/packages/fa/b4/27cbe9f3e2e469359887653f2e45470272eef7295139916cc21107c6b48c/multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f", size = 133925 },
+ { url = "https://files.pythonhosted.org/packages/4d/a3/afc841899face8adfd004235ce759a37619f6ec99eafd959650c5ce4df57/multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875", size = 129039 },
+ { url = "https://files.pythonhosted.org/packages/5e/41/0d0fb18c1ad574f807196f5f3d99164edf9de3e169a58c6dc2d6ed5742b9/multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255", size = 124072 },
+ { url = "https://files.pythonhosted.org/packages/00/22/defd7a2e71a44e6e5b9a5428f972e5b572e7fe28e404dfa6519bbf057c93/multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30", size = 116532 },
+ { url = "https://files.pythonhosted.org/packages/91/25/f7545102def0b1d456ab6449388eed2dfd822debba1d65af60194904a23a/multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057", size = 128173 },
+ { url = "https://files.pythonhosted.org/packages/45/79/3dbe8d35fc99f5ea610813a72ab55f426cb9cf482f860fa8496e5409be11/multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657", size = 122654 },
+ { url = "https://files.pythonhosted.org/packages/97/cb/209e735eeab96e1b160825b5d0b36c56d3862abff828fc43999bb957dcad/multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28", size = 133197 },
+ { url = "https://files.pythonhosted.org/packages/e4/3a/a13808a7ada62808afccea67837a79d00ad6581440015ef00f726d064c2d/multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972", size = 129754 },
+ { url = "https://files.pythonhosted.org/packages/77/dd/8540e139eafb240079242da8f8ffdf9d3f4b4ad1aac5a786cd4050923783/multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43", size = 126402 },
+ { url = "https://files.pythonhosted.org/packages/86/99/e82e1a275d8b1ea16d3a251474262258dbbe41c05cce0c01bceda1fc8ea5/multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada", size = 26421 },
+ { url = "https://files.pythonhosted.org/packages/86/1c/9fa630272355af7e4446a2c7550c259f11ee422ab2d30ff90a0a71cf3d9e/multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a", size = 28791 },
+ { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 },
+]
+
+[[package]]
+name = "mypy"
+version = "1.14.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mypy-extensions" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/7a/87ae2adb31d68402da6da1e5f30c07ea6063e9f09b5e7cfc9dfa44075e74/mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", size = 11211002 },
+ { url = "https://files.pythonhosted.org/packages/e1/23/eada4c38608b444618a132be0d199b280049ded278b24cbb9d3fc59658e4/mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", size = 10358400 },
+ { url = "https://files.pythonhosted.org/packages/43/c9/d6785c6f66241c62fd2992b05057f404237deaad1566545e9f144ced07f5/mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", size = 12095172 },
+ { url = "https://files.pythonhosted.org/packages/c3/62/daa7e787770c83c52ce2aaf1a111eae5893de9e004743f51bfcad9e487ec/mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", size = 12828732 },
+ { url = "https://files.pythonhosted.org/packages/1b/a2/5fb18318a3637f29f16f4e41340b795da14f4751ef4f51c99ff39ab62e52/mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", size = 13012197 },
+ { url = "https://files.pythonhosted.org/packages/28/99/e153ce39105d164b5f02c06c35c7ba958aaff50a2babba7d080988b03fe7/mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", size = 9780836 },
+ { url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432 },
+ { url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515 },
+ { url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791 },
+ { url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203 },
+ { url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900 },
+ { url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869 },
+ { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 },
+ { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 },
+ { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 },
+ { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 },
+ { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 },
+ { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 },
+ { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 },
+ { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 },
+ { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 },
+ { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 },
+ { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 },
+ { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 },
+ { url = "https://files.pythonhosted.org/packages/ca/1f/186d133ae2514633f8558e78cd658070ba686c0e9275c5a5c24a1e1f0d67/mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35", size = 11200493 },
+ { url = "https://files.pythonhosted.org/packages/af/fc/4842485d034e38a4646cccd1369f6b1ccd7bc86989c52770d75d719a9941/mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc", size = 10357702 },
+ { url = "https://files.pythonhosted.org/packages/b4/e6/457b83f2d701e23869cfec013a48a12638f75b9d37612a9ddf99072c1051/mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9", size = 12091104 },
+ { url = "https://files.pythonhosted.org/packages/f1/bf/76a569158db678fee59f4fd30b8e7a0d75bcbaeef49edd882a0d63af6d66/mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb", size = 12830167 },
+ { url = "https://files.pythonhosted.org/packages/43/bc/0bc6b694b3103de9fed61867f1c8bd33336b913d16831431e7cb48ef1c92/mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60", size = 13013834 },
+ { url = "https://files.pythonhosted.org/packages/b0/79/5f5ec47849b6df1e6943d5fd8e6632fbfc04b4fd4acfa5a5a9535d11b4e2/mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c", size = 9781231 },
+ { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 },
+]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
+]
+
+[[package]]
+name = "mysql-connector-python"
+version = "9.2.0"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0f/fc/85abcabdcf6775277511af66bb90ab6e46e74c2245cbbd686333d52e0e8f/mysql_connector_python-9.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:65808b0a9998150416ee0b5781fdff456faea247e24d505f606aea2acaf21374", size = 15149287 },
+ { url = "https://files.pythonhosted.org/packages/bd/87/6a11dd43e9fdc5ad285a966c9afa7ac92d7fea20371896ff4a3feb159d79/mysql_connector_python-9.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:877552ff04800be4a53604bfee95b4078d10fcc072d54b9ea8dcd159c692742c", size = 15967956 },
+ { url = "https://files.pythonhosted.org/packages/3c/2d/cefc46a2760d1086d02d7e7f50aac74d0091ac39bf25b1d8c2b4cfbf20ec/mysql_connector_python-9.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:8a6cfb611bdfea41c67c5305e8fc0e30fdacd258c489dc619f566213cce8bba9", size = 33547693 },
+ { url = "https://files.pythonhosted.org/packages/81/80/5c3286fe2da2ca1a361483a2c20f17c77b543563f3a4fc8d7c18c07923a5/mysql_connector_python-9.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7c25dc2cb4fca82242d71a0bda2087bdfa3638e0c8175a747a6e765242763b4a", size = 33947593 },
+ { url = "https://files.pythonhosted.org/packages/c0/ad/d4c10fbb2b842768e667b6d2a4f1b7550521c3da8c5ad260ffb09fd58c3d/mysql_connector_python-9.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:5c72ecad9007bc52d4f213e47d3c4cf17b3a3cabfeb2d36317fd4493adf5ed7d", size = 16097120 },
+ { url = "https://files.pythonhosted.org/packages/6c/52/4a368c7db6a579e472e1e0a42e72e1b716828adb7aadad7b547c316c2791/mysql_connector_python-9.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bddecd023c0e92182eb3159abee7385a1d38b395a58ff4d94d3d6f1abeca6334", size = 15149325 },
+ { url = "https://files.pythonhosted.org/packages/e1/7d/681f02e0d70f371c8a5479d7339316ff89c791dcd7743aeca0102e733be4/mysql_connector_python-9.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cc09ca64db620f7c898e1fb7c61a3d6f8f21694fcd276ab2635650ce216f8556", size = 15967956 },
+ { url = "https://files.pythonhosted.org/packages/94/30/b2d3a73b913cd83ce3dce4880abca83cf4a4aa275b5a27a740f83aa84b2d/mysql_connector_python-9.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4142a0a4e997210558136ad2b623188c102996eda2d951a3933d6338b210f15e", size = 33555392 },
+ { url = "https://files.pythonhosted.org/packages/a3/a3/31b5ccb491199ce4d05d9f867d07ad2ccc6487b412df94b036aad41b9b92/mysql_connector_python-9.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e1afa12519671e024d80fbbf74606cc4fb96786b005d79ed959acf3584ba6af4", size = 33953375 },
+ { url = "https://files.pythonhosted.org/packages/e2/18/42761250be6ad43a23edb7af85b9720ba8aa1a652036ab4854724d21f628/mysql_connector_python-9.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:cf2684641084abc47be2dbba7bd42ce22c682bad4228a10bb12c343e1ecfc484", size = 16096251 },
+ { url = "https://files.pythonhosted.org/packages/74/b9/c29682db58fb08a22e623f1eeed12fe2743459d4928ed9d248a4f6fcda7f/mysql_connector_python-9.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:03fe54ca19c2dffa8f04ca0601a1217b126ff8306c76a9b2b4e555f8fbbd2178", size = 15149615 },
+ { url = "https://files.pythonhosted.org/packages/d4/b9/3ae12be2fbf9650658e2db429245dd35514933b2972320eff311478fe234/mysql_connector_python-9.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b13b136c188363e18f9b3f6237a0e6eba8601df259ab0f8687cd689f433a391b", size = 15968207 },
+ { url = "https://files.pythonhosted.org/packages/a3/26/ee7792df84193341755115e11ad02252b15bcc1f2c991ae06e04454e3b28/mysql_connector_python-9.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e8338a9babe6c67287080ec9f6a0fc245de24841bc532ef97dfd1f93caf4668b", size = 33555687 },
+ { url = "https://files.pythonhosted.org/packages/a3/4f/33afe9e1fd556d935986aad17c74416555607a6686b0bb3d20a9be192828/mysql_connector_python-9.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:92ef9e5a37b25978a261b5a14fcfa0cf51cd96168dec4a52657bbfc1e1cb7d9a", size = 33954061 },
+ { url = "https://files.pythonhosted.org/packages/5c/48/aa1d3e1dd2fa0fece0581d49c6d10717e18fc86dde493114f3894a3371bd/mysql_connector_python-9.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:b7d149bcc455cf606a4aa604d45267fba16c8abf36056b804f8d16e8f5e753c1", size = 16096342 },
+ { url = "https://files.pythonhosted.org/packages/2c/3c/ac29cab6a7bcabcda392a3b4762d19e1e7a1656ee78d0b8bd307a57471a6/mysql_connector_python-9.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:417c538dfd64e6d415280067f4d7ce3f901ce85c548e609247a794d205769276", size = 15149659 },
+ { url = "https://files.pythonhosted.org/packages/22/c0/77d7418d78e8017b720a2f610fe9113668d7e75922686c36b35b7ace7780/mysql_connector_python-9.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4d97485eb3f63ab26f6438570c4a392bb1f53c3ad9dca1bfd8b61b5ec1bba690", size = 15968172 },
+ { url = "https://files.pythonhosted.org/packages/65/05/ff9b5c4cd58d1a2b64f55b4ca5a7ac3fca23a3adcac7e692fded4991f8d9/mysql_connector_python-9.2.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c2e973bcc6eab68e41e01438b706788e3f47b0b4d6a17fcb74583f7e0b990c5f", size = 33556035 },
+ { url = "https://files.pythonhosted.org/packages/11/fe/53f358b947997c6352642011d7f256e7fe8fec6f0badbda4aefad37d30e5/mysql_connector_python-9.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:2238ba121a30dd3a23d33ce3437642e38a1d4b86afaf7a487cdb0d7727db2010", size = 33954322 },
+ { url = "https://files.pythonhosted.org/packages/22/4b/4f5462a81d046bb54bbb62ffbcea654e78f3ae2a64eb24a14c2872c4d75e/mysql_connector_python-9.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:6557942f6c6be3b41d2965456b53a244a7ce3e6fb81cb195c243549be72a6a24", size = 16096367 },
+ { url = "https://files.pythonhosted.org/packages/52/66/f43d868b32bf8320e50c91320f514240232ce424ac2dd03eae92fbb5f083/mysql_connector_python-9.2.0-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:0aee4e3a053672ded2134eda6fff3bc64796df88c9a9e5cab130b576830e14a8", size = 15149405 },
+ { url = "https://files.pythonhosted.org/packages/1a/2c/bd132fb76300f84893fa7973c0c6c92c378803df7387355feb4ceb59a59d/mysql_connector_python-9.2.0-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:86c3217849857113912e8fc628e13182f00955ed1257cb7bfdc3d5ac3c5ff371", size = 15968011 },
+ { url = "https://files.pythonhosted.org/packages/bb/38/9d1d23fabf0a36ceb107268ada250b534f33eb93fc980b6e3dde0f957bfe/mysql_connector_python-9.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:2a96bed643e901e91c05f421a37844939f5e2f65cc54a406ec5d95e704bda068", size = 33546001 },
+ { url = "https://files.pythonhosted.org/packages/50/44/5d5d2ff4c8668d4a532366c7d571cfe1f6aa8b1bef838e51d3dff3671313/mysql_connector_python-9.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:292974916c6908e62bb0eb864c1c5679aa5c827209a200b5658aa4b4468c19a0", size = 33946028 },
+ { url = "https://files.pythonhosted.org/packages/74/20/8d220c349a1a7d35b7dae92b3b32935a8c7a0d2a4246c7f59438f7fce0f5/mysql_connector_python-9.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:2c358b5732666043683445e88405d58232d5bb0977d24ec55e02a315b33fecbc", size = 16097155 },
+ { url = "https://files.pythonhosted.org/packages/55/aa/0ff1b80fcce28abb7ce53c1d3d14ebb28fb4206ca561480cb8b7d54163ad/mysql_connector_python-9.2.0-py2.py3-none-any.whl", hash = "sha256:d007c05a48fc076e5ac7ad3d76e332fa5b0e885853c2da80071ade2d6e01aba5", size = 398150 },
+]
+
+[[package]]
+name = "natsort"
+version = "8.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268 },
+]
+
+[[package]]
+name = "nodeenv"
+version = "1.9.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 },
+]
+
+[[package]]
+name = "oracledb"
+version = "2.5.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cryptography" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9f/60/c7ea963536a46833f3c951e0d6a84f8f3db06fc47b0bba4edf22d3be9127/oracledb-2.5.1.tar.gz", hash = "sha256:63d17ebb95f9129d0ab9386cb632c9e667e3be2c767278cc11a8e4585468de33", size = 629297 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1d/92/3eabd488271f20d04e1431d73cabd7a296108250f4eddfa79258e07a94af/oracledb-2.5.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:54ea7b4da179eb3fefad338685b44fed657a9cd733fb0bfc09d344cfb266355e", size = 3757547 },
+ { url = "https://files.pythonhosted.org/packages/ab/2a/036491526d862d8a600e7b9bf4d1fd3269fac87e88de5ce99a62e1bf4c39/oracledb-2.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05df7a5a61f4d26c986e235fae6f64a81afaac8f1dbef60e2e9ecf9236218e58", size = 2233703 },
+ { url = "https://files.pythonhosted.org/packages/c6/10/d33cb384db5783565a39b0307a767348a0819d0d49baec88f0b785ea155b/oracledb-2.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d17c80063375a5d87a7ab57c8343e5434a16ea74f7be3b56f9100300ef0b69d6", size = 2417161 },
+ { url = "https://files.pythonhosted.org/packages/99/3e/e6dd5afcf79fad5eec3bc41fb9b0e8a59b3cf89ff3d4c7e4f1aabdd2b2a0/oracledb-2.5.1-cp310-cp310-win32.whl", hash = "sha256:51b3911ee822319e20f2e19d816351aac747591a59a0a96cf891c62c2a5c0c0d", size = 1469257 },
+ { url = "https://files.pythonhosted.org/packages/8e/05/48a0d7ff9aa8509f721da35e5422904bfc1e72b01d5a4995de43c94b3c28/oracledb-2.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:e4e884625117e50b619c93828affbcffa594029ef8c8b40205394990e6af65a8", size = 1790076 },
+ { url = "https://files.pythonhosted.org/packages/5d/f4/70f0e52a79c215b1fae0a229cf10b572bf9eff07c5373320b2e25a7d4414/oracledb-2.5.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:85318350fa4837b7b637e436fa5f99c17919d6329065e64d1e18e5a7cae52457", size = 3792081 },
+ { url = "https://files.pythonhosted.org/packages/35/40/0d7ddabeebb9ea11e520d24880e511eb92a3e421a88c0559656b196a8714/oracledb-2.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:676c221227159d9cee25030c56ff9782f330115cb86164d92d3360f55b07654b", size = 2239512 },
+ { url = "https://files.pythonhosted.org/packages/13/73/b33a8b4ba58ddce0d83f86e93acb3158d9b59595c8b0232ec7cdf4f8175f/oracledb-2.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e78c6de57b4b5df7f932337c57e59b62e34fc4527d2460c0cab10c2ab01825f8", size = 2410818 },
+ { url = "https://files.pythonhosted.org/packages/0f/92/2d3aa9934ddc3d5988567b5dde69fd6ee4e635fcd25e65cfcf6e3f89f3bd/oracledb-2.5.1-cp311-cp311-win32.whl", hash = "sha256:0d5974327a1957538a144b073367104cdf8bb39cf056940995b75cb099535589", size = 1472481 },
+ { url = "https://files.pythonhosted.org/packages/15/35/9d12555d43d5bcb09498de4b9da1cd8bcac40e3f3a9c16e056a04f7452e7/oracledb-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:541bb5a107917b9d9eba1346318b42f8b6024e7dd3bef1451f0745364f03399c", size = 1799457 },
+ { url = "https://files.pythonhosted.org/packages/89/bc/eef07b9a4fd0eda9da07cb9c8c9c4ef695b28034c179a138c5267a22f52f/oracledb-2.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:970a9420cc351d650cc6716122e9aa50cfb8c27f425ffc9d83651fd3edff6090", size = 3829099 },
+ { url = "https://files.pythonhosted.org/packages/bf/78/42a86c7e45bac215b9d93b5d69fb3c95c695f338ca435647536de1fd0642/oracledb-2.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6788c128af5a3a45689453fc4832f32b4a0dae2696d9917c7631a2e02865148", size = 2116133 },
+ { url = "https://files.pythonhosted.org/packages/85/ba/ad70a08361fd2285b6942d898748876ab9918f3f45100c4fba98bd8a9037/oracledb-2.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8778daa3f08639232341d802b95ca6da4c0c798c8530e4df331b3286d32e49d5", size = 2286969 },
+ { url = "https://files.pythonhosted.org/packages/a7/d6/f5181943b27fb14b13303e65072ba4861577cf523cf9dca90e22139de867/oracledb-2.5.1-cp312-cp312-win32.whl", hash = "sha256:a44613f3dfacb2b9462c3871ee333fa535fbd0ec21942e14019fcfd572487db0", size = 1433894 },
+ { url = "https://files.pythonhosted.org/packages/ee/d3/12bd235547387f44c8d920ee35d24647699097e69424b6549961626bfeaf/oracledb-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:934d02da80bfc030c644c5c43fbe58119dc170f15b4dfdb6fe04c220a1f8730d", size = 1757662 },
+ { url = "https://files.pythonhosted.org/packages/f2/26/de027f5e2ce04d9aea6d728eb6934d9cf3e6ad41f754622b26a734b0d823/oracledb-2.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0374481329fa873a2af24eb12de4fd597c6c111e148065200562eb75ea0c6be7", size = 3791626 },
+ { url = "https://files.pythonhosted.org/packages/3d/39/4100808acad8b106c3790bc0e49ea913387a687d1e4e10adbbff33b27709/oracledb-2.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66e885de106701d1f2a630d19e183e491e4f1ccb8d78855f60396ba15856fb66", size = 2128230 },
+ { url = "https://files.pythonhosted.org/packages/db/65/1ab401bca79959812782046a5050a70d2ae741d2171ca3eb5ee1b4e99138/oracledb-2.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcf446f6250d8edad5367ff03ad73dbbe672a2e4b060c51a774821dd723b0283", size = 2299016 },
+ { url = "https://files.pythonhosted.org/packages/3e/b8/269ea48150122094968ce3761ddc2962d63d9b9c95d0464f7a919cf58af1/oracledb-2.5.1-cp313-cp313-win32.whl", hash = "sha256:b02b93199a7073e9b5687fe2dfa83d25ea102ab261c577f9d55820d5ef193dda", size = 1432810 },
+ { url = "https://files.pythonhosted.org/packages/69/27/55132d27ee64b5f851f52f8ea170e18d27a063aa5d17cff508ecad9d3cc2/oracledb-2.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:173b6d132b230f0617380272181e14fc53aec65aaffe68b557a9b6040716a267", size = 1754893 },
+ { url = "https://files.pythonhosted.org/packages/6e/59/080d3b5b39819b1d35f1704faad3ae48bd4862520c6cf15b7d0aa3162e3e/oracledb-2.5.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8a2627a0d29390aaef7211c5b3f7182dfd8e76c969b39d57ee3e43c1057c6fe7", size = 3760546 },
+ { url = "https://files.pythonhosted.org/packages/d7/05/288e0f254ddb27dd547c148944cd0d04aee70b21aae7c85b7a7007d669ac/oracledb-2.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:730cd03e7fbf05acd32a221ead2a43020b3b91391597eaf728d724548f418b1b", size = 2233938 },
+ { url = "https://files.pythonhosted.org/packages/bb/94/027c0d3aece2f948afca1ab61c0c4eca419d250e0b39101cc838d6f49d9d/oracledb-2.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42524b586733daa896f675acad8b9f2fc2f4380656d60a22a109a573861fc93", size = 2409189 },
+ { url = "https://files.pythonhosted.org/packages/6d/ce/4e80bbb3d9f3f854dd4f467edb2ef327c06b82c99a483b3e4cce26897220/oracledb-2.5.1-cp39-cp39-win32.whl", hash = "sha256:7958c7796df9f8c97484768c88817dec5c6d49220fc4cccdfde12a1a883f3d46", size = 1470887 },
+ { url = "https://files.pythonhosted.org/packages/4b/65/3477eb0d0258d0e8bcdb2d4bb7c60ce254408d6a8f66e6f682b23f8dc7d5/oracledb-2.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:92e0d176e3c76a1916f4e34fc3d84994ad74cce6b8664656c4dbecb8fa7e8c37", size = 1792151 },
+]
+
+[[package]]
+name = "packaging"
+version = "24.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
+]
+
+[[package]]
+name = "platformdirs"
+version = "4.3.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
+]
+
+[[package]]
+name = "pre-commit"
+version = "4.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cfgv" },
+ { name = "identify" },
+ { name = "nodeenv" },
+ { name = "pyyaml" },
+ { name = "virtualenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2a/13/b62d075317d8686071eb843f0bb1f195eb332f48869d3c31a4c6f1e063ac/pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4", size = 193330 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/b3/df14c580d82b9627d173ceea305ba898dca135feb360b6d84019d0803d3b/pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b", size = 220560 },
+]
+
+[[package]]
+name = "propcache"
+version = "0.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/20/c8/2a13f78d82211490855b2fb303b6721348d0787fdd9a12ac46d99d3acde1/propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64", size = 41735 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/a5/0ea64c9426959ef145a938e38c832fc551843481d356713ececa9a8a64e8/propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6", size = 79296 },
+ { url = "https://files.pythonhosted.org/packages/76/5a/916db1aba735f55e5eca4733eea4d1973845cf77dfe67c2381a2ca3ce52d/propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2", size = 45622 },
+ { url = "https://files.pythonhosted.org/packages/2d/62/685d3cf268b8401ec12b250b925b21d152b9d193b7bffa5fdc4815c392c2/propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea", size = 45133 },
+ { url = "https://files.pythonhosted.org/packages/4d/3d/31c9c29ee7192defc05aa4d01624fd85a41cf98e5922aaed206017329944/propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212", size = 204809 },
+ { url = "https://files.pythonhosted.org/packages/10/a1/e4050776f4797fc86140ac9a480d5dc069fbfa9d499fe5c5d2fa1ae71f07/propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3", size = 219109 },
+ { url = "https://files.pythonhosted.org/packages/c9/c0/e7ae0df76343d5e107d81e59acc085cea5fd36a48aa53ef09add7503e888/propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d", size = 217368 },
+ { url = "https://files.pythonhosted.org/packages/fc/e1/e0a2ed6394b5772508868a977d3238f4afb2eebaf9976f0b44a8d347ad63/propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634", size = 205124 },
+ { url = "https://files.pythonhosted.org/packages/50/c1/e388c232d15ca10f233c778bbdc1034ba53ede14c207a72008de45b2db2e/propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2", size = 195463 },
+ { url = "https://files.pythonhosted.org/packages/0a/fd/71b349b9def426cc73813dbd0f33e266de77305e337c8c12bfb0a2a82bfb/propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958", size = 198358 },
+ { url = "https://files.pythonhosted.org/packages/02/f2/d7c497cd148ebfc5b0ae32808e6c1af5922215fe38c7a06e4e722fe937c8/propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c", size = 195560 },
+ { url = "https://files.pythonhosted.org/packages/bb/57/f37041bbe5e0dfed80a3f6be2612a3a75b9cfe2652abf2c99bef3455bbad/propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583", size = 196895 },
+ { url = "https://files.pythonhosted.org/packages/83/36/ae3cc3e4f310bff2f064e3d2ed5558935cc7778d6f827dce74dcfa125304/propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf", size = 207124 },
+ { url = "https://files.pythonhosted.org/packages/8c/c4/811b9f311f10ce9d31a32ff14ce58500458443627e4df4ae9c264defba7f/propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034", size = 210442 },
+ { url = "https://files.pythonhosted.org/packages/18/dd/a1670d483a61ecac0d7fc4305d91caaac7a8fc1b200ea3965a01cf03bced/propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b", size = 203219 },
+ { url = "https://files.pythonhosted.org/packages/f9/2d/30ced5afde41b099b2dc0c6573b66b45d16d73090e85655f1a30c5a24e07/propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4", size = 40313 },
+ { url = "https://files.pythonhosted.org/packages/23/84/bd9b207ac80da237af77aa6e153b08ffa83264b1c7882495984fcbfcf85c/propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba", size = 44428 },
+ { url = "https://files.pythonhosted.org/packages/bc/0f/2913b6791ebefb2b25b4efd4bb2299c985e09786b9f5b19184a88e5778dd/propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16", size = 79297 },
+ { url = "https://files.pythonhosted.org/packages/cf/73/af2053aeccd40b05d6e19058419ac77674daecdd32478088b79375b9ab54/propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717", size = 45611 },
+ { url = "https://files.pythonhosted.org/packages/3c/09/8386115ba7775ea3b9537730e8cf718d83bbf95bffe30757ccf37ec4e5da/propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3", size = 45146 },
+ { url = "https://files.pythonhosted.org/packages/03/7a/793aa12f0537b2e520bf09f4c6833706b63170a211ad042ca71cbf79d9cb/propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9", size = 232136 },
+ { url = "https://files.pythonhosted.org/packages/f1/38/b921b3168d72111769f648314100558c2ea1d52eb3d1ba7ea5c4aa6f9848/propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787", size = 239706 },
+ { url = "https://files.pythonhosted.org/packages/14/29/4636f500c69b5edea7786db3c34eb6166f3384b905665ce312a6e42c720c/propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465", size = 238531 },
+ { url = "https://files.pythonhosted.org/packages/85/14/01fe53580a8e1734ebb704a3482b7829a0ef4ea68d356141cf0994d9659b/propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af", size = 231063 },
+ { url = "https://files.pythonhosted.org/packages/33/5c/1d961299f3c3b8438301ccfbff0143b69afcc30c05fa28673cface692305/propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7", size = 220134 },
+ { url = "https://files.pythonhosted.org/packages/00/d0/ed735e76db279ba67a7d3b45ba4c654e7b02bc2f8050671ec365d8665e21/propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f", size = 220009 },
+ { url = "https://files.pythonhosted.org/packages/75/90/ee8fab7304ad6533872fee982cfff5a53b63d095d78140827d93de22e2d4/propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54", size = 212199 },
+ { url = "https://files.pythonhosted.org/packages/eb/ec/977ffaf1664f82e90737275873461695d4c9407d52abc2f3c3e24716da13/propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505", size = 214827 },
+ { url = "https://files.pythonhosted.org/packages/57/48/031fb87ab6081764054821a71b71942161619549396224cbb242922525e8/propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82", size = 228009 },
+ { url = "https://files.pythonhosted.org/packages/1a/06/ef1390f2524850838f2390421b23a8b298f6ce3396a7cc6d39dedd4047b0/propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca", size = 231638 },
+ { url = "https://files.pythonhosted.org/packages/38/2a/101e6386d5a93358395da1d41642b79c1ee0f3b12e31727932b069282b1d/propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e", size = 222788 },
+ { url = "https://files.pythonhosted.org/packages/db/81/786f687951d0979007e05ad9346cd357e50e3d0b0f1a1d6074df334b1bbb/propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034", size = 40170 },
+ { url = "https://files.pythonhosted.org/packages/cf/59/7cc7037b295d5772eceb426358bb1b86e6cab4616d971bd74275395d100d/propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3", size = 44404 },
+ { url = "https://files.pythonhosted.org/packages/4c/28/1d205fe49be8b1b4df4c50024e62480a442b1a7b818e734308bb0d17e7fb/propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a", size = 79588 },
+ { url = "https://files.pythonhosted.org/packages/21/ee/fc4d893f8d81cd4971affef2a6cb542b36617cd1d8ce56b406112cb80bf7/propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0", size = 45825 },
+ { url = "https://files.pythonhosted.org/packages/4a/de/bbe712f94d088da1d237c35d735f675e494a816fd6f54e9db2f61ef4d03f/propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d", size = 45357 },
+ { url = "https://files.pythonhosted.org/packages/7f/14/7ae06a6cf2a2f1cb382586d5a99efe66b0b3d0c6f9ac2f759e6f7af9d7cf/propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4", size = 241869 },
+ { url = "https://files.pythonhosted.org/packages/cc/59/227a78be960b54a41124e639e2c39e8807ac0c751c735a900e21315f8c2b/propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d", size = 247884 },
+ { url = "https://files.pythonhosted.org/packages/84/58/f62b4ffaedf88dc1b17f04d57d8536601e4e030feb26617228ef930c3279/propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5", size = 248486 },
+ { url = "https://files.pythonhosted.org/packages/1c/07/ebe102777a830bca91bbb93e3479cd34c2ca5d0361b83be9dbd93104865e/propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24", size = 243649 },
+ { url = "https://files.pythonhosted.org/packages/ed/bc/4f7aba7f08f520376c4bb6a20b9a981a581b7f2e385fa0ec9f789bb2d362/propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff", size = 229103 },
+ { url = "https://files.pythonhosted.org/packages/fe/d5/04ac9cd4e51a57a96f78795e03c5a0ddb8f23ec098b86f92de028d7f2a6b/propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f", size = 226607 },
+ { url = "https://files.pythonhosted.org/packages/e3/f0/24060d959ea41d7a7cc7fdbf68b31852331aabda914a0c63bdb0e22e96d6/propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec", size = 221153 },
+ { url = "https://files.pythonhosted.org/packages/77/a7/3ac76045a077b3e4de4859a0753010765e45749bdf53bd02bc4d372da1a0/propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348", size = 222151 },
+ { url = "https://files.pythonhosted.org/packages/e7/af/5e29da6f80cebab3f5a4dcd2a3240e7f56f2c4abf51cbfcc99be34e17f0b/propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6", size = 233812 },
+ { url = "https://files.pythonhosted.org/packages/8c/89/ebe3ad52642cc5509eaa453e9f4b94b374d81bae3265c59d5c2d98efa1b4/propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6", size = 238829 },
+ { url = "https://files.pythonhosted.org/packages/e9/2f/6b32f273fa02e978b7577159eae7471b3cfb88b48563b1c2578b2d7ca0bb/propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518", size = 230704 },
+ { url = "https://files.pythonhosted.org/packages/5c/2e/f40ae6ff5624a5f77edd7b8359b208b5455ea113f68309e2b00a2e1426b6/propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246", size = 40050 },
+ { url = "https://files.pythonhosted.org/packages/3b/77/a92c3ef994e47180862b9d7d11e37624fb1c00a16d61faf55115d970628b/propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1", size = 44117 },
+ { url = "https://files.pythonhosted.org/packages/0f/2a/329e0547cf2def8857157f9477669043e75524cc3e6251cef332b3ff256f/propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc", size = 77002 },
+ { url = "https://files.pythonhosted.org/packages/12/2d/c4df5415e2382f840dc2ecbca0eeb2293024bc28e57a80392f2012b4708c/propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9", size = 44639 },
+ { url = "https://files.pythonhosted.org/packages/d0/5a/21aaa4ea2f326edaa4e240959ac8b8386ea31dedfdaa636a3544d9e7a408/propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439", size = 44049 },
+ { url = "https://files.pythonhosted.org/packages/4e/3e/021b6cd86c0acc90d74784ccbb66808b0bd36067a1bf3e2deb0f3845f618/propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536", size = 224819 },
+ { url = "https://files.pythonhosted.org/packages/3c/57/c2fdeed1b3b8918b1770a133ba5c43ad3d78e18285b0c06364861ef5cc38/propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629", size = 229625 },
+ { url = "https://files.pythonhosted.org/packages/9d/81/70d4ff57bf2877b5780b466471bebf5892f851a7e2ca0ae7ffd728220281/propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b", size = 232934 },
+ { url = "https://files.pythonhosted.org/packages/3c/b9/bb51ea95d73b3fb4100cb95adbd4e1acaf2cbb1fd1083f5468eeb4a099a8/propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052", size = 227361 },
+ { url = "https://files.pythonhosted.org/packages/f1/20/3c6d696cd6fd70b29445960cc803b1851a1131e7a2e4ee261ee48e002bcd/propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce", size = 213904 },
+ { url = "https://files.pythonhosted.org/packages/a1/cb/1593bfc5ac6d40c010fa823f128056d6bc25b667f5393781e37d62f12005/propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d", size = 212632 },
+ { url = "https://files.pythonhosted.org/packages/6d/5c/e95617e222be14a34c709442a0ec179f3207f8a2b900273720501a70ec5e/propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce", size = 207897 },
+ { url = "https://files.pythonhosted.org/packages/8e/3b/56c5ab3dc00f6375fbcdeefdede5adf9bee94f1fab04adc8db118f0f9e25/propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95", size = 208118 },
+ { url = "https://files.pythonhosted.org/packages/86/25/d7ef738323fbc6ebcbce33eb2a19c5e07a89a3df2fded206065bd5e868a9/propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf", size = 217851 },
+ { url = "https://files.pythonhosted.org/packages/b3/77/763e6cef1852cf1ba740590364ec50309b89d1c818e3256d3929eb92fabf/propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f", size = 222630 },
+ { url = "https://files.pythonhosted.org/packages/4f/e9/0f86be33602089c701696fbed8d8c4c07b6ee9605c5b7536fd27ed540c5b/propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30", size = 216269 },
+ { url = "https://files.pythonhosted.org/packages/cc/02/5ac83217d522394b6a2e81a2e888167e7ca629ef6569a3f09852d6dcb01a/propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6", size = 39472 },
+ { url = "https://files.pythonhosted.org/packages/f4/33/d6f5420252a36034bc8a3a01171bc55b4bff5df50d1c63d9caa50693662f/propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1", size = 43363 },
+ { url = "https://files.pythonhosted.org/packages/0a/08/6ab7f65240a16fa01023125e65258acf7e4884f483f267cdd6fcc48f37db/propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541", size = 80403 },
+ { url = "https://files.pythonhosted.org/packages/34/fe/e7180285e21b4e6dff7d311fdf22490c9146a09a02834b5232d6248c6004/propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e", size = 46152 },
+ { url = "https://files.pythonhosted.org/packages/9c/36/aa74d884af826030ba9cee2ac109b0664beb7e9449c315c9c44db99efbb3/propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4", size = 45674 },
+ { url = "https://files.pythonhosted.org/packages/22/59/6fe80a3fe7720f715f2c0f6df250dacbd7cad42832410dbd84c719c52f78/propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097", size = 207792 },
+ { url = "https://files.pythonhosted.org/packages/4a/68/584cd51dd8f4d0f5fff5b128ce0cdb257cde903898eecfb92156bbc2c780/propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd", size = 223280 },
+ { url = "https://files.pythonhosted.org/packages/85/cb/4c3528460c41e61b06ec3f970c0f89f87fa21f63acac8642ed81a886c164/propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681", size = 221293 },
+ { url = "https://files.pythonhosted.org/packages/69/c0/560e050aa6d31eeece3490d1174da508f05ab27536dfc8474af88b97160a/propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16", size = 208259 },
+ { url = "https://files.pythonhosted.org/packages/0c/87/d6c86a77632eb1ba86a328e3313159f246e7564cb5951e05ed77555826a0/propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d", size = 198632 },
+ { url = "https://files.pythonhosted.org/packages/3a/2b/3690ea7b662dc762ab7af5f3ef0e2d7513c823d193d7b2a1b4cda472c2be/propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae", size = 203516 },
+ { url = "https://files.pythonhosted.org/packages/4d/b5/afe716c16c23c77657185c257a41918b83e03993b6ccdfa748e5e7d328e9/propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b", size = 199402 },
+ { url = "https://files.pythonhosted.org/packages/a4/c0/2d2df3aa7f8660d0d4cc4f1e00490c48d5958da57082e70dea7af366f876/propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347", size = 200528 },
+ { url = "https://files.pythonhosted.org/packages/21/c8/65ac9142f5e40c8497f7176e71d18826b09e06dd4eb401c9a4ee41aa9c74/propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf", size = 211254 },
+ { url = "https://files.pythonhosted.org/packages/09/e4/edb70b447a1d8142df51ec7511e84aa64d7f6ce0a0fdf5eb55363cdd0935/propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04", size = 214589 },
+ { url = "https://files.pythonhosted.org/packages/cb/02/817f309ec8d8883287781d6d9390f80b14db6e6de08bc659dfe798a825c2/propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587", size = 207283 },
+ { url = "https://files.pythonhosted.org/packages/d7/fe/2d18612096ed2212cfef821b6fccdba5d52efc1d64511c206c5c16be28fd/propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb", size = 40866 },
+ { url = "https://files.pythonhosted.org/packages/24/2e/b5134802e7b57c403c7b73c7a39374e7a6b7f128d1968b4a4b4c0b700250/propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1", size = 44975 },
+ { url = "https://files.pythonhosted.org/packages/41/b6/c5319caea262f4821995dca2107483b94a3345d4607ad797c76cb9c36bcc/propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", size = 11818 },
+]
+
+[[package]]
+name = "proto-plus"
+version = "1.25.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "protobuf" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7e/05/74417b2061e1bf1b82776037cad97094228fa1c1b6e82d08a78d3fb6ddb6/proto_plus-1.25.0.tar.gz", hash = "sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91", size = 56124 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dd/25/0b7cc838ae3d76d46539020ec39fc92bfc9acc29367e58fe912702c2a79e/proto_plus-1.25.0-py3-none-any.whl", hash = "sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961", size = 50126 },
+]
+
+[[package]]
+name = "protobuf"
+version = "5.29.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f7/d1/e0a911544ca9993e0f17ce6d3cc0932752356c1b0a834397f28e63479344/protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620", size = 424945 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/7a/1e38f3cafa022f477ca0f57a1f49962f21ad25850c3ca0acd3b9d0091518/protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888", size = 422708 },
+ { url = "https://files.pythonhosted.org/packages/61/fa/aae8e10512b83de633f2646506a6d835b151edf4b30d18d73afd01447253/protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a", size = 434508 },
+ { url = "https://files.pythonhosted.org/packages/dd/04/3eaedc2ba17a088961d0e3bd396eac764450f431621b58a04ce898acd126/protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e", size = 417825 },
+ { url = "https://files.pythonhosted.org/packages/4f/06/7c467744d23c3979ce250397e26d8ad8eeb2bea7b18ca12ad58313c1b8d5/protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84", size = 319573 },
+ { url = "https://files.pythonhosted.org/packages/a8/45/2ebbde52ad2be18d3675b6bee50e68cd73c9e0654de77d595540b5129df8/protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f", size = 319672 },
+ { url = "https://files.pythonhosted.org/packages/85/a6/bf65a38f8be5ab8c3b575822acfd338702fdf7ac9abd8c81630cc7c9f4bd/protobuf-5.29.3-cp39-cp39-win32.whl", hash = "sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7", size = 422676 },
+ { url = "https://files.pythonhosted.org/packages/ac/e2/48d46adc86369ff092eaece3e537f76b3baaab45ca3dde257838cde831d2/protobuf-5.29.3-cp39-cp39-win_amd64.whl", hash = "sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da", size = 434593 },
+ { url = "https://files.pythonhosted.org/packages/fd/b2/ab07b09e0f6d143dfb839693aa05765257bceaa13d03bf1a696b78323e7a/protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f", size = 172550 },
+]
+
+[[package]]
+name = "psycopg"
+version = "3.2.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { name = "tzdata", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e0/f2/954b1467b3e2ca5945b83b5e320268be1f4df486c3e8ffc90f4e4b707979/psycopg-3.2.4.tar.gz", hash = "sha256:f26f1346d6bf1ef5f5ef1714dd405c67fb365cfd1c6cea07de1792747b167b92", size = 156109 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/40/49/15114d5f7ee68983f4e1a24d47e75334568960352a07c6f0e796e912685d/psycopg-3.2.4-py3-none-any.whl", hash = "sha256:43665368ccd48180744cab26b74332f46b63b7e06e8ce0775547a3533883d381", size = 198716 },
+]
+
+[[package]]
+name = "pyasn1"
+version = "0.6.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 },
+]
+
+[[package]]
+name = "pyasn1-modules"
+version = "0.4.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyasn1" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1d/67/6afbf0d507f73c32d21084a79946bfcfca5fbc62a72057e9c23797a737c9/pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c", size = 310028 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/77/89/bc88a6711935ba795a679ea6ebee07e128050d6382eaa35a0a47c8032bdc/pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", size = 181537 },
+]
+
+[[package]]
+name = "pycparser"
+version = "2.22"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
+]
+
+[[package]]
+name = "pydata-sphinx-theme"
+version = "0.16.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "accessible-pygments" },
+ { name = "babel" },
+ { name = "beautifulsoup4" },
+ { name = "docutils" },
+ { name = "pygments" },
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e2/0d/8ba33fa83a7dcde13eb3c1c2a0c1cc29950a048bfed6d9b0d8b6bd710b4c/pydata_sphinx_theme-0.16.1-py3-none-any.whl", hash = "sha256:225331e8ac4b32682c18fcac5a57a6f717c4e632cea5dd0e247b55155faeccde", size = 6723264 },
+]
+
+[[package]]
+name = "pygments"
+version = "2.19.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
+]
+
+[[package]]
+name = "pylint"
+version = "3.3.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "astroid" },
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "dill" },
+ { name = "isort" },
+ { name = "mccabe" },
+ { name = "platformdirs" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "tomlkit" },
+ { name = "typing-extensions", marker = "python_full_version < '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/17/fd/e9a739afac274a39596bbe562e9d966db6f3917fdb2bd7322ffc56da0ba2/pylint-3.3.3.tar.gz", hash = "sha256:07c607523b17e6d16e2ae0d7ef59602e332caa762af64203c24b41c27139f36a", size = 1516550 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/91/e1/26d55acea92b1ea4d33672e48f09ceeb274e84d7d542a4fb9a32a556db46/pylint-3.3.3-py3-none-any.whl", hash = "sha256:26e271a2bc8bce0fc23833805a9076dd9b4d5194e2a02164942cb3cdc37b4183", size = 521918 },
+]
+
+[[package]]
+name = "pymssql"
+version = "2.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/30/66/f98a16e1db6592b9ab7fa85a3cb5542b4304178b6ad67928e69927590493/pymssql-2.3.1.tar.gz", hash = "sha256:ddee15c4c193e14c92fe2cd720ca9be1dba1e0f4178240380b8f5f6f00da04c6", size = 186468 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/80/57/2a21bffa24de74b33d3e8b3b53bc270dab9fd9010bdc94e2bf45d1001f43/pymssql-2.3.1-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:001b3321a5f620b80d1427933fcca11b05f29a808d7772a84d18d01e640ee60a", size = 2736605 },
+ { url = "https://files.pythonhosted.org/packages/ba/e3/33cedaf1f54f73bcc9f2b16b74ada5730a401df1580a0c73e657fa5ec414/pymssql-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15466dd41be5e32302f0c4791f612aadd608a0e6ec0b10d769e76cbb4c86aa97", size = 3896710 },
+ { url = "https://files.pythonhosted.org/packages/02/9e/232f4242b540f60ad7dda1f7598f0c509a8fbe1337887df2f41fc889188c/pymssql-2.3.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74349040d4ff6f05894aefb5109ecffcd416e1e366d9951085d3225a9d09c46b", size = 3903098 },
+ { url = "https://files.pythonhosted.org/packages/72/3e/829047b3e96d00b454992d7f4f233cb20d64ea972af725cfffca43b2d8bf/pymssql-2.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc79dbe5eca8825b73830c8bb147b6f588300dc7510393822682162dc4ff003f", size = 4249021 },
+ { url = "https://files.pythonhosted.org/packages/00/21/f7e36c686362d0a20b63169d00d2a8fc2c166242be4de79b988a1eeef6a1/pymssql-2.3.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0b93ebe2feb45e772ca708bc4cd70f3e4c72796ec1b157fd5d80cdc589c786aa", size = 4600946 },
+ { url = "https://files.pythonhosted.org/packages/ce/31/1d89c23a7f3efdf340cbc1588bfc9ba7cae103aeed60b961df2d3ffb56c0/pymssql-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:44b1c8752c0fc6750902c1c521f258bdf4271bfbf7b2a5fee469b6ad00631aab", size = 3985400 },
+ { url = "https://files.pythonhosted.org/packages/c2/a6/0d7c3bb53d8cb978300627b3c49f5990b3469c1c23c4ec12d1716501fcdb/pymssql-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fdfadb055a9ecad58356decfecc41626999ad7b548cc7ea898cf159e2217f7bb", size = 4003810 },
+ { url = "https://files.pythonhosted.org/packages/f1/b2/e772bf3a5cb242a94ae301b36e2903e4a03b4021590548002b582c3075bf/pymssql-2.3.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:46f1074c6763e9a899128f22a0f72e9fb0035535f48efabd6a294db1c149e6f1", size = 4256678 },
+ { url = "https://files.pythonhosted.org/packages/54/2e/2f463b97342ec57beb0e3d5a852cf48a6b44c32b8b2e9bb09b1e89c37f01/pymssql-2.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ebb11b61d99ec5bbe0b8c411ff748a90263cdaf474881de231da8184e721c42c", size = 4483516 },
+ { url = "https://files.pythonhosted.org/packages/d1/86/06df652cd0985ead33b7cf503f28d9f6539ff39eba0640abef652691fa44/pymssql-2.3.1-cp310-cp310-win32.whl", hash = "sha256:2ef07fdee3e9652d39b4c081c5c5e1a1031abd122b402ed66813bceb3874ccea", size = 1319901 },
+ { url = "https://files.pythonhosted.org/packages/2c/c4/0a7212d32b822603aed9fba03df58c3257258dc23a78a5035856fc6ac1e1/pymssql-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:791522339215cb7f88db54c831a2347e0c4d69dd3092a343eea5b9339adf4412", size = 2005259 },
+ { url = "https://files.pythonhosted.org/packages/cd/b5/c0eddea051884f315413e600fefe544061d2dd2f0a45c4d1a405d41eb696/pymssql-2.3.1-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:0433ffa1c86290a93e81176f377621cb70405be66ade8f3070d3f5ec9cfebdba", size = 3033322 },
+ { url = "https://files.pythonhosted.org/packages/2b/af/130e7012c6ab1a7f766dabfebaf34d3ac15c67a21e8f798915b926e14535/pymssql-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6182d82ebfbe46f0e7748d068c6a1c16c0f4fe1f34f1c390f63375cee79b44b0", size = 4045717 },
+ { url = "https://files.pythonhosted.org/packages/cf/d8/1f505bf7556a9db449cfe10a124accefda5682771f1ab7d152efbcdb9e22/pymssql-2.3.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfbe07dcf0aaee8ce630624669cb2fb77b76743d4dd925f99331422be8704de3", size = 4033763 },
+ { url = "https://files.pythonhosted.org/packages/2f/ba/23e0fee86294af9ce628ae9cad6e7f054c000381023a3a63fa72e7eb85e6/pymssql-2.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d999c8e5d5d48e9305c4132392825de402f13feea15694e4e7103029b6eae06", size = 4391889 },
+ { url = "https://files.pythonhosted.org/packages/6f/c2/c765cb00163c3e31093bf52f54dda26da756004f36ba1332585117a66f40/pymssql-2.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2dced0a76d8e99c283103a2e3c825ca22c67f1f8fc5cff657510f4d2ffb9d188", size = 4769376 },
+ { url = "https://files.pythonhosted.org/packages/25/17/57246ab45a8e374565e9aa0eee3fe1cf8b3393a32721a2dc64af9127f605/pymssql-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:880d3173025dea3babf5ab862875b3c76a5cf8df5b292418050c7793c651c0b2", size = 4124566 },
+ { url = "https://files.pythonhosted.org/packages/4b/52/66073fe963f096c05c774d4e4b422bafcfbd0e936240e4f9d3ba81056ea3/pymssql-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9f89c698e29ce5c576e4980ded89c00b45e482ec02759bfbfc1aa326648cf64a", size = 4158161 },
+ { url = "https://files.pythonhosted.org/packages/f9/f3/5c7834ed163358a675b3875db6d8dd93f5878c843d0ef76a19f789fb5a03/pymssql-2.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f4f2a38ce6e39ed2414c20ca16deaea4340868033a4bb23d5e4e30c72290caf", size = 4417236 },
+ { url = "https://files.pythonhosted.org/packages/05/c7/011bd07c0265b13c0bf3494c06766aa855096d611b273f69fb98b62af2bc/pymssql-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e34e8aa1d3da555dbf23141b02f401267c0be32104b4f030afd0bae62d26d735", size = 4647511 },
+ { url = "https://files.pythonhosted.org/packages/94/17/0035b8796474e964aafe4b7819b0c3864c6e25c32a162f7efc1c3526c290/pymssql-2.3.1-cp311-cp311-win32.whl", hash = "sha256:72e57e20802bf97399e050a0760a4541996fc27bc605a1a25e48ca6fe4913c48", size = 1318988 },
+ { url = "https://files.pythonhosted.org/packages/88/2a/515460530e9836f1ab3acf5be157b7d19a923a268a665f670f7ec57fb69a/pymssql-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b5d3604bca2fa8d5ba2eed1582a3c8a83970a8d2edabfcfd87c1edecb7617d16", size = 2006401 },
+ { url = "https://files.pythonhosted.org/packages/fd/cf/ac241b624b25e608f4f17f3f454cc34a8daea6fb1fe102572edd6b529d9d/pymssql-2.3.1-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:c28f1b9560b82fe1a1e51d8c56f6d36bca7c507a8cdf2caa2a0642503c220d5c", size = 3016607 },
+ { url = "https://files.pythonhosted.org/packages/b0/31/adf26807d4cd47d7b2f6af54df68ac9388626aa2bad7f3cec0152deb0659/pymssql-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3509b75747eb22ae89f3d47ae316a4b9eac7d952269e88b356ef117a1b8e3b8", size = 3988751 },
+ { url = "https://files.pythonhosted.org/packages/7a/23/05bc3b71f25be8b14c19bee0b1e449cf2b63e688a316a7ce67de916bb1ea/pymssql-2.3.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cca3bed27e1ab867e482fa8b529d408489ad57e8b60452f75ef288da90573db6", size = 3962788 },
+ { url = "https://files.pythonhosted.org/packages/12/92/cc04eefd9fd5bb765afa0227a5c77b2d6273de7a2aeeb2f1526579b532df/pymssql-2.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fe3276915e6040daec409203e3143aa2826984adb8d223c155dab91010110a4", size = 4351414 },
+ { url = "https://files.pythonhosted.org/packages/10/5a/29da9679faae85b41a0857299c9f84e362daf67e272068c07dc01ff993a9/pymssql-2.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d36d566d0d6997c95442c3d2902800e6b072ccc017c6284e5b1bd4e17dc8fada", size = 4713561 },
+ { url = "https://files.pythonhosted.org/packages/c1/56/bf26d808e514fdb49372906d29f5bb08f1ba8805d1c2955a60ef4aa25a3a/pymssql-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3564df40a678623a769acd9677dc68228b2694170132c6f296eb62bf766d31e4", size = 4046091 },
+ { url = "https://files.pythonhosted.org/packages/ea/b9/be068a30be5c92485c62c9f4cf0b1a12dba8e2283e0c5e9129e2c18b82c0/pymssql-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3dbd4106faabf97f028d0ac59b30d132cfb5e48cf5314b0476f293123dbf3422", size = 4108868 },
+ { url = "https://files.pythonhosted.org/packages/ff/0c/905141171152bc1294df59105cff9ab70e85bfa5a11e5a726fd9ca3e13d2/pymssql-2.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:acd1690d9b1b2ece9d0e1fd7d68571fc9fa56b6ba8697a3132446419ff7fb3f4", size = 4353834 },
+ { url = "https://files.pythonhosted.org/packages/f9/de/f386ddcea2d4d30e8ca5c2394d9fc3ca3b1431cf89175a9bc29c06b5987c/pymssql-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:126e0b78773975136e6385da7286c277e2e0320c1f4bee0e4dc61a5edcf98c41", size = 4591403 },
+ { url = "https://files.pythonhosted.org/packages/e1/f0/ef6a459cf32c71d9c6e34585a2d870a06373ed0b77487552be012cdd223e/pymssql-2.3.1-cp312-cp312-win32.whl", hash = "sha256:21803b731b8c8780fc974d9b4931fa8f1ca29c227502a4c317e12773c8bdef43", size = 1307087 },
+ { url = "https://files.pythonhosted.org/packages/e8/e2/2c3855864e78edc691fb2ed390aaedf6495dd4eb9238460d08c878c0aaac/pymssql-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:6b0224fc5ce4cf0703278859f145e3e921c04d9feb59739a104d3020bbf0c0c1", size = 1990797 },
+ { url = "https://files.pythonhosted.org/packages/49/9e/5342bdc6ad39506de7530329e976ec5631cb409fc7d64dc8a2613fb7df75/pymssql-2.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:709c1df3134e330ee9590437253be363b558154bde5bb54856fc5fe68a03c971", size = 3982419 },
+ { url = "https://files.pythonhosted.org/packages/f8/41/f98041f8683879d58d473344db64383e15955d5019a3bcd0b9df0dfcaeba/pymssql-2.3.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9381eafaf529815f2d61f22b99e0538e744b31234f17d4384f5b0496bd1fbed", size = 3965721 },
+ { url = "https://files.pythonhosted.org/packages/2a/09/7feb951f66ef0125936376c2a4cd8b7cbb855efe8d3f0c3c3c4fd2ce6d85/pymssql-2.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3bf78789014f202855f5d00de982bbcd95177fe8bcf920f0ce730b72456c173", size = 4347162 },
+ { url = "https://files.pythonhosted.org/packages/97/f1/5607b7f11545080a0d14716e494b08dbc3c7ff5b99abf870b3622b862858/pymssql-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4b44280eedd0a3f031e9464d4fc632a215fadcfb375bb479065b61a6337df402", size = 4709283 },
+ { url = "https://files.pythonhosted.org/packages/53/73/ce8f282ab4f3dbdd8f2780890e38e71e02023b279be818e15eb482cfd02c/pymssql-2.3.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:922f536b925880c260968c8f2130b1c9d6315b83f300f18365b5421933f034a2", size = 4048710 },
+ { url = "https://files.pythonhosted.org/packages/42/0f/48b80a7d1b5fca3dbac20b9c518338433287794afb0ba764edff24d0513b/pymssql-2.3.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f00f618d1c0f58617de548e5094f7d55ab6034b94068d7eebba60a034866b10b", size = 4110644 },
+ { url = "https://files.pythonhosted.org/packages/54/7c/094ad0d2a12817df9c5dc8eb2616b700f1e817102d90dc8d31078bb0e853/pymssql-2.3.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b363db86a1a3fe16df9b4253e17b02a268d0f2e2753679b8e85cee268e2fe8c4", size = 4353461 },
+ { url = "https://files.pythonhosted.org/packages/16/e3/e56978cdd5f4861f2d0d2e50b6b59d54778b98df59c079b6fe401f503eeb/pymssql-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:396a26cf576196cc4a3d77890b2b8eb62655ff02846288757dd8b587352cc4f5", size = 4590722 },
+ { url = "https://files.pythonhosted.org/packages/6b/8d/2ff7c92ea85b32fc69599a6450e08b6918c60dab45410bb2870ad42bb1d9/pymssql-2.3.1-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:750078568dafc1e0a24cf0f51eecfe548b13440976a2c8b19cc6e5d38e7b10bc", size = 2737074 },
+ { url = "https://files.pythonhosted.org/packages/a2/83/8b671b99e4786ab2fae19bbe01c5ec999109cf06e4155e44226065d92111/pymssql-2.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a651dd98f67eef98f429c949fb50ea0a92fcf8668834cc35909237c24c1b906", size = 3895460 },
+ { url = "https://files.pythonhosted.org/packages/9b/b6/d3b6bba0f8d7d75ec950c616d2e1b1afd7a4ca9e30d5228212b1b4d9495d/pymssql-2.3.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1ecedaeec8f4d8643d088b4985f0b742d9669bff701153a845b0d1900260b81", size = 3899926 },
+ { url = "https://files.pythonhosted.org/packages/65/c5/2020ae1afc0964592f6bc7e9c8eb8c3b6f27bd08e61134864f0f71ca0768/pymssql-2.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015f6ccd1bcb53f22a3226653d0d8155da40f4afbc1fd0cec25de5fe8decf126", size = 4246818 },
+ { url = "https://files.pythonhosted.org/packages/43/d2/b5947363e911f8a4cd0aa38afa65e1f2eef4f57a32a4225b92dc9150d6c8/pymssql-2.3.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:da44761ca2f996d88f90c0f972b583dfe9c389db84888bd8209cdb83508f7c7a", size = 4599719 },
+ { url = "https://files.pythonhosted.org/packages/22/f9/2c61700cbc4a73d444db84a3eab7c811a5e2021c777a525e217db2c7027a/pymssql-2.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9557b738475e06dfd53f97d8a2c2b259b9b9fd79bf1a4e084ae4e9f164be644d", size = 2771577 },
+ { url = "https://files.pythonhosted.org/packages/57/1f/f39b4524551e81bb173a37373463b0458688d270470d3d6a39173fdf0ab7/pymssql-2.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a1f3f2e2792364a50417f3c2dc0d8f125955c1b641f36eb313daf666045b9748", size = 2871961 },
+ { url = "https://files.pythonhosted.org/packages/f0/e4/ef743d02408e51731bc1c4dbe2593551228f57e4a971634673e9e3a53445/pymssql-2.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:be8af4dea025f171ffb1e5b17cb0c9cbc92b0e3c32d0517bc678fff6f660e5fb", size = 3983963 },
+ { url = "https://files.pythonhosted.org/packages/6e/5c/28d3460f5a603ed96a4e897fafe0f21291128c7b0a412f3fc2c5a66f7836/pymssql-2.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a87950fb1a2b1c4028064fac971f3e191adebb58657ca985330f70e02f95223e", size = 4005377 },
+ { url = "https://files.pythonhosted.org/packages/ca/6b/5d80ef0947002a6b1dca8e7170df1d1c8657f16dddd5b6950ebfe626c62c/pymssql-2.3.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9ea04bf8e13d567650631a944c88886c99a5622d9491e896a9b5a9ffbef2e352", size = 4270272 },
+ { url = "https://files.pythonhosted.org/packages/42/93/84f26b656c1cdd2fe7b0ebc6db2e6ee434ac1c36b771c4deef24b6e253f3/pymssql-2.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d93a82f8ad7d3606354b81bbbe7e7832f70fd6e9ccb2e04a2975117da5df973", size = 4477919 },
+ { url = "https://files.pythonhosted.org/packages/48/59/f06c7db1ecfa6d6161739bc6b25b79d7afde9914679ac47261d758eed402/pymssql-2.3.1-cp39-cp39-win32.whl", hash = "sha256:6a2657152d4007314b66f353a25fc2742155c2770083320b5255fc576103661e", size = 1319907 },
+ { url = "https://files.pythonhosted.org/packages/73/bd/772ffe32ef1c78da8b3a7d7b2bb6af5b185ca2f1f6062182ecf92c03ea8d/pymssql-2.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6c9ffb3ef110bf0fc2a41c845f231cf749162b1d71e02b0aceb6c0ebc603e2e9", size = 2005572 },
+]
+
+[[package]]
+name = "pytest"
+version = "8.3.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 },
+]
+
+[[package]]
+name = "pytest-cdist"
+version = "0.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6a/20/223a90f6b5ededee0804d691a4dac0c10f5191aadcc45c8eea64d451016c/pytest_cdist-0.3.0.tar.gz", hash = "sha256:5b00422039a3baa2bca49e3754d4d12c94e355cb92f8c50beb847faa4ce43631", size = 26632 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a6/84/c35c444f488b58de778a380f80deee836b71dd181730005edb15b33faa60/pytest_cdist-0.3.0-py3-none-any.whl", hash = "sha256:7a7c40702203f1b5c8de71cee58a44c088df3c81807a00aaefbe6550eba617e2", size = 7871 },
+]
+
+[[package]]
+name = "pytest-click"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ec/ec/bca3cd29ba2b025ae41666b851f6ff05fb77cb4c13719baaeda6a757772a/pytest_click-1.1.0.tar.gz", hash = "sha256:fdd9f6721f877dda021e7c5dc73e70aecd37e5ed23ec6820f8a7b3fd7b4f8d30", size = 5054 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/72/1a/eb53371999b94b3c995c00117f3a232dbf6f56c7152a52cf3e3777e7d49d/pytest_click-1.1.0-py3-none-any.whl", hash = "sha256:eade4742c2f02c345e78a32534a43e8db04acf98d415090539dacc880b7cd0e9", size = 4110 },
+]
+
+[[package]]
+name = "pytest-cov"
+version = "6.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "coverage", extra = ["toml"] },
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 },
+]
+
+[[package]]
+name = "pytest-databases"
+version = "0.10.0"
+source = { editable = "." }
+dependencies = [
+ { name = "docker" },
+ { name = "filelock" },
+ { name = "pytest" },
+]
+
+[package.optional-dependencies]
+azure-storage = [
+ { name = "azure-storage-blob" },
+]
+bigquery = [
+ { name = "google-cloud-bigquery" },
+]
+cockroachdb = [
+ { name = "psycopg" },
+]
+dragonfly = [
+ { name = "redis" },
+]
+elasticsearch7 = [
+ { name = "elasticsearch7" },
+]
+elasticsearch8 = [
+ { name = "elasticsearch8" },
+]
+keydb = [
+ { name = "redis" },
+]
+mariadb = [
+ { name = "mariadb" },
+]
+mssql = [
+ { name = "pymssql" },
+]
+mysql = [
+ { name = "mysql-connector-python" },
+]
+oracle = [
+ { name = "oracledb" },
+]
+postgres = [
+ { name = "psycopg" },
+]
+redis = [
+ { name = "redis" },
+]
+spanner = [
+ { name = "google-cloud-spanner" },
+]
+
+[package.dev-dependencies]
+dev = [
+ { name = "auto-pytabs", extra = ["sphinx"] },
+ { name = "coverage", extra = ["toml"] },
+ { name = "litestar-sphinx-theme" },
+ { name = "mypy" },
+ { name = "pre-commit" },
+ { name = "pylint" },
+ { name = "pytest" },
+ { name = "pytest-cdist" },
+ { name = "pytest-click" },
+ { name = "pytest-cov" },
+ { name = "pytest-databases", extra = ["azure-storage", "bigquery", "cockroachdb", "dragonfly", "elasticsearch7", "elasticsearch8", "keydb", "mariadb", "mssql", "mysql", "oracle", "postgres", "redis", "spanner"] },
+ { name = "pytest-mock" },
+ { name = "pytest-vcr" },
+ { name = "pytest-xdist" },
+ { name = "ruff" },
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "sphinx-autobuild" },
+ { name = "sphinx-click" },
+ { name = "sphinx-copybutton" },
+ { name = "sphinx-design" },
+ { name = "sphinx-toolbox" },
+ { name = "sphinxcontrib-mermaid" },
+ { name = "types-click" },
+ { name = "types-decorator" },
+ { name = "types-docutils" },
+ { name = "types-pymysql" },
+ { name = "types-pyyaml" },
+ { name = "types-redis" },
+ { name = "types-six" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "azure-storage-blob", marker = "extra == 'azure-storage'" },
+ { name = "docker" },
+ { name = "elasticsearch7", marker = "extra == 'elasticsearch7'" },
+ { name = "elasticsearch8", marker = "extra == 'elasticsearch8'" },
+ { name = "filelock" },
+ { name = "google-cloud-bigquery", marker = "extra == 'bigquery'" },
+ { name = "google-cloud-spanner", marker = "extra == 'spanner'" },
+ { name = "mariadb", marker = "extra == 'mariadb'" },
+ { name = "mysql-connector-python", marker = "extra == 'mysql'" },
+ { name = "oracledb", marker = "extra == 'oracle'" },
+ { name = "psycopg", marker = "extra == 'cockroachdb'" },
+ { name = "psycopg", marker = "extra == 'postgres'", specifier = ">=3" },
+ { name = "pymssql", marker = "extra == 'mssql'", specifier = "<=2.3.1" },
+ { name = "pytest" },
+ { name = "redis", marker = "extra == 'dragonfly'" },
+ { name = "redis", marker = "extra == 'keydb'" },
+ { name = "redis", marker = "extra == 'redis'" },
+]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "auto-pytabs", extras = ["sphinx"], specifier = ">=0.4.0" },
+ { name = "coverage", extras = ["toml"], specifier = ">=6.2" },
+ { name = "litestar-sphinx-theme", git = "https://github.com/litestar-org/litestar-sphinx-theme.git" },
+ { name = "mypy" },
+ { name = "pre-commit" },
+ { name = "pylint" },
+ { name = "pytest" },
+ { name = "pytest-cdist", specifier = ">=0.2" },
+ { name = "pytest-click" },
+ { name = "pytest-cov" },
+ { name = "pytest-databases", extras = ["azure-storage", "bigquery", "cockroachdb", "dragonfly", "elasticsearch7", "elasticsearch8", "keydb", "mssql", "mysql", "mariadb", "oracle", "postgres", "redis", "spanner"] },
+ { name = "pytest-mock" },
+ { name = "pytest-vcr" },
+ { name = "pytest-xdist" },
+ { name = "ruff" },
+ { name = "sphinx", specifier = ">=7.1.2" },
+ { name = "sphinx-autobuild", specifier = ">=2021.3.14" },
+ { name = "sphinx-click", specifier = ">=5.0.1" },
+ { name = "sphinx-copybutton", specifier = ">=0.5.2" },
+ { name = "sphinx-design", specifier = ">=0.5.0" },
+ { name = "sphinx-toolbox", specifier = ">=3.5.0" },
+ { name = "sphinxcontrib-mermaid", specifier = ">=0.9.2" },
+ { name = "types-click" },
+ { name = "types-decorator" },
+ { name = "types-docutils" },
+ { name = "types-pymysql" },
+ { name = "types-pyyaml" },
+ { name = "types-redis" },
+ { name = "types-six" },
+]
+
+[[package]]
+name = "pytest-mock"
+version = "3.14.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 },
+]
+
+[[package]]
+name = "pytest-vcr"
+version = "1.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pytest" },
+ { name = "vcrpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1a/60/104c619483c1a42775d3f8b27293f1ecfc0728014874d065e68cb9702d49/pytest-vcr-1.0.2.tar.gz", hash = "sha256:23ee51b75abbcc43d926272773aae4f39f93aceb75ed56852d0bf618f92e1896", size = 3810 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9d/d3/ff520d11e6ee400602711d1ece8168dcfc5b6d8146fb7db4244a6ad6a9c3/pytest_vcr-1.0.2-py2.py3-none-any.whl", hash = "sha256:2f316e0539399bea0296e8b8401145c62b6f85e9066af7e57b6151481b0d6d9c", size = 4137 },
+]
+
+[[package]]
+name = "pytest-xdist"
+version = "3.6.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "execnet" },
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/41/c4/3c310a19bc1f1e9ef50075582652673ef2bfc8cd62afef9585683821902f/pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d", size = 84060 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7", size = 46108 },
+]
+
+[[package]]
+name = "python-dateutil"
+version = "2.9.0.post0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
+]
+
+[[package]]
+name = "pywin32"
+version = "308"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/72/a6/3e9f2c474895c1bb61b11fa9640be00067b5c5b363c501ee9c3fa53aec01/pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e", size = 5927028 },
+ { url = "https://files.pythonhosted.org/packages/d9/b4/84e2463422f869b4b718f79eb7530a4c1693e96b8a4e5e968de38be4d2ba/pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e", size = 6558484 },
+ { url = "https://files.pythonhosted.org/packages/9f/8f/fb84ab789713f7c6feacaa08dad3ec8105b88ade8d1c4f0f0dfcaaa017d6/pywin32-308-cp310-cp310-win_arm64.whl", hash = "sha256:a5ab5381813b40f264fa3495b98af850098f814a25a63589a8e9eb12560f450c", size = 7971454 },
+ { url = "https://files.pythonhosted.org/packages/eb/e2/02652007469263fe1466e98439831d65d4ca80ea1a2df29abecedf7e47b7/pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a", size = 5928156 },
+ { url = "https://files.pythonhosted.org/packages/48/ef/f4fb45e2196bc7ffe09cad0542d9aff66b0e33f6c0954b43e49c33cad7bd/pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b", size = 6559559 },
+ { url = "https://files.pythonhosted.org/packages/79/ef/68bb6aa865c5c9b11a35771329e95917b5559845bd75b65549407f9fc6b4/pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6", size = 7972495 },
+ { url = "https://files.pythonhosted.org/packages/00/7c/d00d6bdd96de4344e06c4afbf218bc86b54436a94c01c71a8701f613aa56/pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", size = 5939729 },
+ { url = "https://files.pythonhosted.org/packages/21/27/0c8811fbc3ca188f93b5354e7c286eb91f80a53afa4e11007ef661afa746/pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", size = 6543015 },
+ { url = "https://files.pythonhosted.org/packages/9d/0f/d40f8373608caed2255781a3ad9a51d03a594a1248cd632d6a298daca693/pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", size = 7976033 },
+ { url = "https://files.pythonhosted.org/packages/a9/a4/aa562d8935e3df5e49c161b427a3a2efad2ed4e9cf81c3de636f1fdddfd0/pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed", size = 5938579 },
+ { url = "https://files.pythonhosted.org/packages/c7/50/b0efb8bb66210da67a53ab95fd7a98826a97ee21f1d22949863e6d588b22/pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4", size = 6542056 },
+ { url = "https://files.pythonhosted.org/packages/26/df/2b63e3e4f2df0224f8aaf6d131f54fe4e8c96400eb9df563e2aae2e1a1f9/pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd", size = 7974986 },
+ { url = "https://files.pythonhosted.org/packages/a8/41/ead05a7657ffdbb1edabb954ab80825c4f87a3de0285d59f8290457f9016/pywin32-308-cp39-cp39-win32.whl", hash = "sha256:7873ca4dc60ab3287919881a7d4f88baee4a6e639aa6962de25a98ba6b193341", size = 5991824 },
+ { url = "https://files.pythonhosted.org/packages/e4/cd/0838c9a6063bff2e9bac2388ae36524c26c50288b5d7b6aebb6cdf8d375d/pywin32-308-cp39-cp39-win_amd64.whl", hash = "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920", size = 6640327 },
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 },
+ { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 },
+ { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 },
+ { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 },
+ { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 },
+ { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 },
+ { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 },
+ { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 },
+ { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 },
+ { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 },
+ { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 },
+ { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 },
+ { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 },
+ { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 },
+ { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 },
+ { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 },
+ { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 },
+ { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 },
+ { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
+ { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
+ { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
+ { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
+ { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
+ { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
+ { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
+ { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
+ { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
+ { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
+ { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
+ { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
+ { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
+ { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
+ { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
+ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
+ { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
+ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
+ { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 },
+ { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 },
+ { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 },
+ { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 },
+ { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 },
+ { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 },
+ { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 },
+ { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 },
+ { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 },
+]
+
+[[package]]
+name = "redis"
+version = "5.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "async-timeout", marker = "python_full_version < '3.11.3'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/47/da/d283a37303a995cd36f8b92db85135153dc4f7a8e4441aa827721b442cfb/redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f", size = 4608355 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/5f/fa26b9b2672cbe30e07d9a5bdf39cf16e3b80b42916757c5f92bca88e4ba/redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4", size = 261502 },
+]
+
+[[package]]
+name = "requests"
+version = "2.32.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
+]
+
+[[package]]
+name = "rsa"
+version = "4.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyasn1" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21", size = 29711 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315 },
+]
+
+[[package]]
+name = "ruamel-yaml"
+version = "0.18.10"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "ruamel-yaml-clib", marker = "python_full_version < '3.13' and platform_python_implementation == 'CPython'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729 },
+]
+
+[[package]]
+name = "ruamel-yaml-clib"
+version = "0.2.12"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/57/40a958e863e299f0c74ef32a3bde9f2d1ea8d69669368c0c502a0997f57f/ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5", size = 131301 },
+ { url = "https://files.pythonhosted.org/packages/98/a8/29a3eb437b12b95f50a6bcc3d7d7214301c6c529d8fdc227247fa84162b5/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969", size = 633728 },
+ { url = "https://files.pythonhosted.org/packages/35/6d/ae05a87a3ad540259c3ad88d71275cbd1c0f2d30ae04c65dcbfb6dcd4b9f/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df", size = 722230 },
+ { url = "https://files.pythonhosted.org/packages/7f/b7/20c6f3c0b656fe609675d69bc135c03aac9e3865912444be6339207b6648/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76", size = 686712 },
+ { url = "https://files.pythonhosted.org/packages/cd/11/d12dbf683471f888d354dac59593873c2b45feb193c5e3e0f2ebf85e68b9/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6", size = 663936 },
+ { url = "https://files.pythonhosted.org/packages/72/14/4c268f5077db5c83f743ee1daeb236269fa8577133a5cfa49f8b382baf13/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd", size = 696580 },
+ { url = "https://files.pythonhosted.org/packages/30/fc/8cd12f189c6405a4c1cf37bd633aa740a9538c8e40497c231072d0fef5cf/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a", size = 663393 },
+ { url = "https://files.pythonhosted.org/packages/80/29/c0a017b704aaf3cbf704989785cd9c5d5b8ccec2dae6ac0c53833c84e677/ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da", size = 100326 },
+ { url = "https://files.pythonhosted.org/packages/3a/65/fa39d74db4e2d0cd252355732d966a460a41cd01c6353b820a0952432839/ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28", size = 118079 },
+ { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224 },
+ { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480 },
+ { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068 },
+ { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012 },
+ { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352 },
+ { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344 },
+ { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498 },
+ { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205 },
+ { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185 },
+ { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433 },
+ { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362 },
+ { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118 },
+ { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497 },
+ { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042 },
+ { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831 },
+ { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692 },
+ { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777 },
+ { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523 },
+ { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011 },
+ { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488 },
+ { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066 },
+ { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785 },
+ { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017 },
+ { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270 },
+ { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059 },
+ { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583 },
+ { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190 },
+ { url = "https://files.pythonhosted.org/packages/e5/46/ccdef7a84ad745c37cb3d9a81790f28fbc9adf9c237dba682017b123294e/ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987", size = 131834 },
+ { url = "https://files.pythonhosted.org/packages/29/09/932360f30ad1b7b79f08757e0a6fb8c5392a52cdcc182779158fe66d25ac/ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45", size = 636120 },
+ { url = "https://files.pythonhosted.org/packages/a2/2a/5b27602e7a4344c1334e26bf4739746206b7a60a8acdba33a61473468b73/ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519", size = 724914 },
+ { url = "https://files.pythonhosted.org/packages/da/1c/23497017c554fc06ff5701b29355522cff850f626337fff35d9ab352cb18/ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7", size = 689072 },
+ { url = "https://files.pythonhosted.org/packages/68/e6/f3d4ff3223f9ea49c3b7169ec0268e42bd49f87c70c0e3e853895e4a7ae2/ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285", size = 667091 },
+ { url = "https://files.pythonhosted.org/packages/84/62/ead07043527642491e5011b143f44b81ef80f1025a96069b7210e0f2f0f3/ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed", size = 699111 },
+ { url = "https://files.pythonhosted.org/packages/52/b3/fe4d84446f7e4887e3bea7ceff0a7df23790b5ed625f830e79ace88ebefb/ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7", size = 666365 },
+ { url = "https://files.pythonhosted.org/packages/6e/b3/7feb99a00bfaa5c6868617bb7651308afde85e5a0b23cd187fe5de65feeb/ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12", size = 100863 },
+ { url = "https://files.pythonhosted.org/packages/93/07/de635108684b7a5bb06e432b0930c5a04b6c59efe73bd966d8db3cc208f2/ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b", size = 118653 },
+]
+
+[[package]]
+name = "ruff"
+version = "0.9.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1e/7f/60fda2eec81f23f8aa7cbbfdf6ec2ca11eb11c273827933fb2541c2ce9d8/ruff-0.9.3.tar.gz", hash = "sha256:8293f89985a090ebc3ed1064df31f3b4b56320cdfcec8b60d3295bddb955c22a", size = 3586740 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/77/4fb790596d5d52c87fd55b7160c557c400e90f6116a56d82d76e95d9374a/ruff-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624", size = 11656815 },
+ { url = "https://files.pythonhosted.org/packages/a2/a8/3338ecb97573eafe74505f28431df3842c1933c5f8eae615427c1de32858/ruff-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c", size = 11594821 },
+ { url = "https://files.pythonhosted.org/packages/8e/89/320223c3421962762531a6b2dd58579b858ca9916fb2674874df5e97d628/ruff-0.9.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c59ab92f8e92d6725b7ded9d4a31be3ef42688a115c6d3da9457a5bda140e2b4", size = 11040475 },
+ { url = "https://files.pythonhosted.org/packages/b2/bd/1d775eac5e51409535804a3a888a9623e87a8f4b53e2491580858a083692/ruff-0.9.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc153c25e715be41bb228bc651c1e9b1a88d5c6e5ed0194fa0dfea02b026439", size = 11856207 },
+ { url = "https://files.pythonhosted.org/packages/7f/c6/3e14e09be29587393d188454064a4aa85174910d16644051a80444e4fd88/ruff-0.9.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:646909a1e25e0dc28fbc529eab8eb7bb583079628e8cbe738192853dbbe43af5", size = 11420460 },
+ { url = "https://files.pythonhosted.org/packages/ef/42/b7ca38ffd568ae9b128a2fa76353e9a9a3c80ef19746408d4ce99217ecc1/ruff-0.9.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a5a46e09355695fbdbb30ed9889d6cf1c61b77b700a9fafc21b41f097bfbba4", size = 12605472 },
+ { url = "https://files.pythonhosted.org/packages/a6/a1/3167023f23e3530fde899497ccfe239e4523854cb874458ac082992d206c/ruff-0.9.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c4bb09d2bbb394e3730d0918c00276e79b2de70ec2a5231cd4ebb51a57df9ba1", size = 13243123 },
+ { url = "https://files.pythonhosted.org/packages/d0/b4/3c600758e320f5bf7de16858502e849f4216cb0151f819fa0d1154874802/ruff-0.9.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96a87ec31dc1044d8c2da2ebbed1c456d9b561e7d087734336518181b26b3aa5", size = 12744650 },
+ { url = "https://files.pythonhosted.org/packages/be/38/266fbcbb3d0088862c9bafa8b1b99486691d2945a90b9a7316336a0d9a1b/ruff-0.9.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb7554aca6f842645022fe2d301c264e6925baa708b392867b7a62645304df4", size = 14458585 },
+ { url = "https://files.pythonhosted.org/packages/63/a6/47fd0e96990ee9b7a4abda62de26d291bd3f7647218d05b7d6d38af47c30/ruff-0.9.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabc332b7075a914ecea912cd1f3d4370489c8018f2c945a30bcc934e3bc06a6", size = 12419624 },
+ { url = "https://files.pythonhosted.org/packages/84/5d/de0b7652e09f7dda49e1a3825a164a65f4998175b6486603c7601279baad/ruff-0.9.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:33866c3cc2a575cbd546f2cd02bdd466fed65118e4365ee538a3deffd6fcb730", size = 11843238 },
+ { url = "https://files.pythonhosted.org/packages/9e/be/3f341ceb1c62b565ec1fb6fd2139cc40b60ae6eff4b6fb8f94b1bb37c7a9/ruff-0.9.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:006e5de2621304c8810bcd2ee101587712fa93b4f955ed0985907a36c427e0c2", size = 11484012 },
+ { url = "https://files.pythonhosted.org/packages/a3/c8/ff8acbd33addc7e797e702cf00bfde352ab469723720c5607b964491d5cf/ruff-0.9.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ba6eea4459dbd6b1be4e6bfc766079fb9b8dd2e5a35aff6baee4d9b1514ea519", size = 12038494 },
+ { url = "https://files.pythonhosted.org/packages/73/b1/8d9a2c0efbbabe848b55f877bc10c5001a37ab10aca13c711431673414e5/ruff-0.9.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90230a6b8055ad47d3325e9ee8f8a9ae7e273078a66401ac66df68943ced029b", size = 12473639 },
+ { url = "https://files.pythonhosted.org/packages/cb/44/a673647105b1ba6da9824a928634fe23186ab19f9d526d7bdf278cd27bc3/ruff-0.9.3-py3-none-win32.whl", hash = "sha256:eabe5eb2c19a42f4808c03b82bd313fc84d4e395133fb3fc1b1516170a31213c", size = 9834353 },
+ { url = "https://files.pythonhosted.org/packages/c3/01/65cadb59bf8d4fbe33d1a750103e6883d9ef302f60c28b73b773092fbde5/ruff-0.9.3-py3-none-win_amd64.whl", hash = "sha256:040ceb7f20791dfa0e78b4230ee9dce23da3b64dd5848e40e3bf3ab76468dcf4", size = 10821444 },
+ { url = "https://files.pythonhosted.org/packages/69/cb/b3fe58a136a27d981911cba2f18e4b29f15010623b79f0f2510fd0d31fd3/ruff-0.9.3-py3-none-win_arm64.whl", hash = "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b", size = 10038168 },
+]
+
+[[package]]
+name = "six"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
+]
+
+[[package]]
+name = "snowballstemmer"
+version = "2.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 },
+]
+
+[[package]]
+name = "soupsieve"
+version = "2.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 },
+]
+
+[[package]]
+name = "sphinx"
+version = "7.4.7"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.10' and platform_python_implementation == 'PyPy'",
+ "python_full_version < '3.10' and platform_python_implementation != 'PyPy'",
+]
+dependencies = [
+ { name = "alabaster", version = "0.7.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "babel", marker = "python_full_version < '3.10'" },
+ { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" },
+ { name = "docutils", marker = "python_full_version < '3.10'" },
+ { name = "imagesize", marker = "python_full_version < '3.10'" },
+ { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
+ { name = "jinja2", marker = "python_full_version < '3.10'" },
+ { name = "packaging", marker = "python_full_version < '3.10'" },
+ { name = "pygments", marker = "python_full_version < '3.10'" },
+ { name = "requests", marker = "python_full_version < '3.10'" },
+ { name = "snowballstemmer", marker = "python_full_version < '3.10'" },
+ { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.10'" },
+ { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.10'" },
+ { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.10'" },
+ { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.10'" },
+ { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.10'" },
+ { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.10'" },
+ { name = "tomli", marker = "python_full_version < '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624 },
+]
+
+[[package]]
+name = "sphinx"
+version = "8.1.3"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'",
+ "python_full_version >= '3.13' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.10.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.10.*' and platform_python_implementation != 'PyPy'",
+]
+dependencies = [
+ { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "babel", marker = "python_full_version >= '3.10'" },
+ { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" },
+ { name = "docutils", marker = "python_full_version >= '3.10'" },
+ { name = "imagesize", marker = "python_full_version >= '3.10'" },
+ { name = "jinja2", marker = "python_full_version >= '3.10'" },
+ { name = "packaging", marker = "python_full_version >= '3.10'" },
+ { name = "pygments", marker = "python_full_version >= '3.10'" },
+ { name = "requests", marker = "python_full_version >= '3.10'" },
+ { name = "snowballstemmer", marker = "python_full_version >= '3.10'" },
+ { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.10'" },
+ { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.10'" },
+ { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.10'" },
+ { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.10'" },
+ { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.10'" },
+ { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.10'" },
+ { name = "tomli", marker = "python_full_version == '3.10.*'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 },
+]
+
+[[package]]
+name = "sphinx-autobuild"
+version = "2024.10.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama" },
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "starlette" },
+ { name = "uvicorn" },
+ { name = "watchfiles" },
+ { name = "websockets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/c0/eba125db38c84d3c74717008fd3cb5000b68cd7e2cbafd1349c6a38c3d3b/sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa", size = 11908 },
+]
+
+[[package]]
+name = "sphinx-autodoc-typehints"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.10' and platform_python_implementation == 'PyPy'",
+ "python_full_version < '3.10' and platform_python_implementation != 'PyPy'",
+]
+dependencies = [
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/74/cd/03e7b917230dc057922130a79ba0240df1693bfd76727ea33fae84b39138/sphinx_autodoc_typehints-2.3.0.tar.gz", hash = "sha256:535c78ed2d6a1bad393ba9f3dfa2602cf424e2631ee207263e07874c38fde084", size = 40709 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/f3/e0a4ce49da4b6f4e4ce84b3c39a0677831884cb9d8a87ccbf1e9e56e53ac/sphinx_autodoc_typehints-2.3.0-py3-none-any.whl", hash = "sha256:3098e2c6d0ba99eacd013eb06861acc9b51c6e595be86ab05c08ee5506ac0c67", size = 19836 },
+]
+
+[[package]]
+name = "sphinx-autodoc-typehints"
+version = "3.0.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'",
+ "python_full_version >= '3.13' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.10.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.10.*' and platform_python_implementation != 'PyPy'",
+]
+dependencies = [
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/26/f0/43c6a5ff3e7b08a8c3b32f81b859f1b518ccc31e45f22e2b41ced38be7b9/sphinx_autodoc_typehints-3.0.1.tar.gz", hash = "sha256:b9b40dd15dee54f6f810c924f863f9cf1c54f9f3265c495140ea01be7f44fa55", size = 36282 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/dc/dc46c5c7c566b7ec5e8f860f9c89533bf03c0e6aadc96fb9b337867e4460/sphinx_autodoc_typehints-3.0.1-py3-none-any.whl", hash = "sha256:4b64b676a14b5b79cefb6628a6dc8070e320d4963e8ff640a2f3e9390ae9045a", size = 20245 },
+]
+
+[[package]]
+name = "sphinx-click"
+version = "6.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "docutils" },
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/db/0a/5b1e8d0579dbb4ca8114e456ca4a68020bfe8e15c7001f3856be4929ab83/sphinx_click-6.0.0.tar.gz", hash = "sha256:f5d664321dc0c6622ff019f1e1c84e58ce0cecfddeb510e004cf60c2a3ab465b", size = 29574 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/d7/8621c4726ad3f788a1db4c0c409044b16edc563f5c9542807b3724037555/sphinx_click-6.0.0-py3-none-any.whl", hash = "sha256:1e0a3c83bcb7c55497751b19d07ebe56b5d7b85eb76dd399cf9061b497adc317", size = 9922 },
+]
+
+[[package]]
+name = "sphinx-copybutton"
+version = "0.5.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343 },
+]
+
+[[package]]
+name = "sphinx-design"
+version = "0.6.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz", hash = "sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size = 2193689 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/43/65c0acbd8cc6f50195a3a1fc195c404988b15c67090e73c7a41a9f57d6bd/sphinx_design-0.6.1-py3-none-any.whl", hash = "sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c", size = 2215338 },
+]
+
+[[package]]
+name = "sphinx-jinja2-compat"
+version = "0.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jinja2" },
+ { name = "markupsafe" },
+ { name = "standard-imghdr", marker = "python_full_version >= '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/26/df/27282da6f8c549f765beca9de1a5fc56f9651ed87711a5cac1e914137753/sphinx_jinja2_compat-0.3.0.tar.gz", hash = "sha256:f3c1590b275f42e7a654e081db5e3e5fb97f515608422bde94015ddf795dfe7c", size = 4998 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6f/42/2fd09d672eaaa937d6893d8b747d07943f97a6e5e30653aee6ebd339b704/sphinx_jinja2_compat-0.3.0-py3-none-any.whl", hash = "sha256:b1e4006d8e1ea31013fa9946d1b075b0c8d2a42c6e3425e63542c1e9f8be9084", size = 7883 },
+]
+
+[[package]]
+name = "sphinx-prompt"
+version = "1.8.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.10' and platform_python_implementation == 'PyPy'",
+ "python_full_version < '3.10' and platform_python_implementation != 'PyPy'",
+]
+dependencies = [
+ { name = "docutils", marker = "python_full_version < '3.10'" },
+ { name = "pygments", marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e7/fb/7a07b8df1ca2418147a6b13e3f6b445071f2565198b45efa631d0d6ef0cd/sphinx_prompt-1.8.0.tar.gz", hash = "sha256:47482f86fcec29662fdfd23e7c04ef03582714195d01f5d565403320084372ed", size = 5121 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/39/49/f890a2668b7cbf375f5528b549c8d36dd2e801b0fbb7b2b5ef65663ecb6c/sphinx_prompt-1.8.0-py3-none-any.whl", hash = "sha256:369ecc633f0711886f9b3a078c83264245be1adf46abeeb9b88b5519e4b51007", size = 7298 },
+]
+
+[[package]]
+name = "sphinx-prompt"
+version = "1.9.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'",
+ "python_full_version >= '3.13' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.10.*' and platform_python_implementation == 'PyPy'",
+ "python_full_version == '3.10.*' and platform_python_implementation != 'PyPy'",
+]
+dependencies = [
+ { name = "certifi", marker = "python_full_version >= '3.10'" },
+ { name = "docutils", marker = "python_full_version >= '3.10'" },
+ { name = "idna", marker = "python_full_version >= '3.10'" },
+ { name = "pygments", marker = "python_full_version >= '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "urllib3", marker = "python_full_version >= '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/34/fe/ac4e24f35b5148b31ac717ae7dcc7a2f7ec56eb729e22c7252ed8ad2d9a5/sphinx_prompt-1.9.0.tar.gz", hash = "sha256:471b3c6d466dce780a9b167d9541865fd4e9a80ed46e31b06a52a0529ae995a1", size = 5340 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/98/e90ca466e0ede452d3e5a8d92b8fb68db6de269856e019ed9cab69440522/sphinx_prompt-1.9.0-py3-none-any.whl", hash = "sha256:fd731446c03f043d1ff6df9f22414495b23067c67011cc21658ea8d36b3575fc", size = 7311 },
+]
+
+[[package]]
+name = "sphinx-tabs"
+version = "3.4.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "docutils" },
+ { name = "pygments" },
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/27/32/ab475e252dc2b704e82a91141fa404cdd8901a5cf34958fd22afacebfccd/sphinx-tabs-3.4.5.tar.gz", hash = "sha256:ba9d0c1e3e37aaadd4b5678449eb08176770e0fc227e769b6ce747df3ceea531", size = 16070 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/20/9f/4ac7dbb9f23a2ff5a10903a4f9e9f43e0ff051f63a313e989c962526e305/sphinx_tabs-3.4.5-py3-none-any.whl", hash = "sha256:92cc9473e2ecf1828ca3f6617d0efc0aa8acb06b08c56ba29d1413f2f0f6cf09", size = 9904 },
+]
+
+[[package]]
+name = "sphinx-toolbox"
+version = "3.8.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "apeye" },
+ { name = "autodocsumm" },
+ { name = "beautifulsoup4" },
+ { name = "cachecontrol", extra = ["filecache"] },
+ { name = "dict2css" },
+ { name = "docutils" },
+ { name = "domdf-python-tools" },
+ { name = "filelock" },
+ { name = "html5lib" },
+ { name = "ruamel-yaml" },
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "sphinx-autodoc-typehints", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "sphinx-jinja2-compat" },
+ { name = "sphinx-prompt", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx-prompt", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "sphinx-tabs" },
+ { name = "tabulate" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/30/80/f837e85c8c216cdeef9b60393e4b00c9092a1e3d734106e0021abbf5930c/sphinx_toolbox-3.8.1.tar.gz", hash = "sha256:a4b39a6ea24fc8f10e24f052199bda17837a0bf4c54163a56f521552395f5e1a", size = 111977 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/d6/2a28ee4cbc158ae65afb2cfcb6895ef54d972ce1e167f8a63c135b14b080/sphinx_toolbox-3.8.1-py3-none-any.whl", hash = "sha256:53d8e77dd79e807d9ef18590c4b2960a5aa3c147415054b04c31a91afed8b88b", size = 194621 },
+]
+
+[[package]]
+name = "sphinxcontrib-applehelp"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 },
+]
+
+[[package]]
+name = "sphinxcontrib-devhelp"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 },
+]
+
+[[package]]
+name = "sphinxcontrib-htmlhelp"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 },
+]
+
+[[package]]
+name = "sphinxcontrib-jsmath"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 },
+]
+
+[[package]]
+name = "sphinxcontrib-mermaid"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyyaml" },
+ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/97/69/bf039237ad260073e8c02f820b3e00dc34f3a2de20aff7861e6b19d2f8c5/sphinxcontrib_mermaid-1.0.0.tar.gz", hash = "sha256:2e8ab67d3e1e2816663f9347d026a8dee4a858acdd4ad32dd1c808893db88146", size = 15153 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cd/c8/784b9ac6ea08aa594c1a4becbd0dbe77186785362e31fd633b8c6ae0197a/sphinxcontrib_mermaid-1.0.0-py3-none-any.whl", hash = "sha256:60b72710ea02087f212028feb09711225fbc2e343a10d34822fe787510e1caa3", size = 9597 },
+]
+
+[[package]]
+name = "sphinxcontrib-qthelp"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 },
+]
+
+[[package]]
+name = "sphinxcontrib-serializinghtml"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 },
+]
+
+[[package]]
+name = "sqlparse"
+version = "0.5.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415 },
+]
+
+[[package]]
+name = "standard-imghdr"
+version = "3.10.14"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/09/d2/2eb5521072c9598886035c65c023f39f7384bcb73eed70794f469e34efac/standard_imghdr-3.10.14.tar.gz", hash = "sha256:2598fe2e7c540dbda34b233295e10957ab8dc8ac6f3bd9eaa8d38be167232e52", size = 5474 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/d0/9852f70eb01f814843530c053542b72d30e9fbf74da7abb0107e71938389/standard_imghdr-3.10.14-py3-none-any.whl", hash = "sha256:cdf6883163349624dee9a81d2853a20260337c4cd41c04e99c082e01833a08e2", size = 5598 },
+]
+
+[[package]]
+name = "starlette"
+version = "0.45.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.10'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ff/fb/2984a686808b89a6781526129a4b51266f678b2d2b97ab2d325e56116df8/starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f", size = 2574076 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 },
+]
+
+[[package]]
+name = "tabulate"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 },
+]
+
+[[package]]
+name = "tomli"
+version = "2.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 },
+ { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 },
+ { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 },
+ { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 },
+ { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 },
+ { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 },
+ { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 },
+ { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 },
+ { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 },
+ { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 },
+ { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 },
+ { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 },
+ { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 },
+ { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 },
+ { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 },
+ { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 },
+ { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 },
+ { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 },
+ { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 },
+ { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 },
+ { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 },
+ { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 },
+ { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 },
+ { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 },
+ { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 },
+ { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 },
+ { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 },
+ { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 },
+ { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 },
+ { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 },
+ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 },
+]
+
+[[package]]
+name = "tomlkit"
+version = "0.13.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 },
+]
+
+[[package]]
+name = "types-cffi"
+version = "1.16.0.20241221"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "types-setuptools" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5e/00/ecd613293b6c41081b4e5c33bc42ba22a839c493bf8b6ee9480ce3b7a4e8/types_cffi-1.16.0.20241221.tar.gz", hash = "sha256:1c96649618f4b6145f58231acb976e0b448be6b847f7ab733dabe62dfbff6591", size = 15938 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/00/ec/ebf35741fe824e66a57e7f35199b51021bff3aa4b01a7a2720c7f7668ee8/types_cffi-1.16.0.20241221-py3-none-any.whl", hash = "sha256:e5b76b4211d7a9185f6ab8d06a106d56c7eb80af7cdb8bfcb4186ade10fb112f", size = 19309 },
+]
+
+[[package]]
+name = "types-click"
+version = "7.1.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/00/ff/0e6a56108d45c80c61cdd4743312d0304d8192482aea4cce96c554aaa90d/types-click-7.1.8.tar.gz", hash = "sha256:b6604968be6401dc516311ca50708a0a28baa7a0cb840efd7412f0dbbff4e092", size = 10015 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ee/ad/607454a5f991c5b3e14693a7113926758f889138371058a5f72f567fa131/types_click-7.1.8-py3-none-any.whl", hash = "sha256:8cb030a669e2e927461be9827375f83c16b8178c365852c060a34e24871e7e81", size = 12929 },
+]
+
+[[package]]
+name = "types-decorator"
+version = "5.1.8.20250121"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f4/e6/88de14bb1d1073495b9d9459f90fbb78fe93d89beefcf0af94b871993a56/types_decorator-5.1.8.20250121.tar.gz", hash = "sha256:1b89bb1c481a1d3399e28f1aa3459366b76dde951490992ae8475ba91287cd04", size = 8496 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/0e/59b9637fa66fbe419886b17d59b90e5e4256325c01f94f81dcc44fbeda53/types_decorator-5.1.8.20250121-py3-none-any.whl", hash = "sha256:6bfd5f4464f444a1ee0aea92705ed8466d74c0ddd7ade4bbd003c235db51d21a", size = 8078 },
+]
+
+[[package]]
+name = "types-docutils"
+version = "0.21.0.20241128"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/dd/df/64e7ab01a4fc5ce46895dc94e31cffc8b8087c8d91ee54c45ac2d8d82445/types_docutils-0.21.0.20241128.tar.gz", hash = "sha256:4dd059805b83ac6ec5a223699195c4e9eeb0446a4f7f2aeff1759a4a7cc17473", size = 26739 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/59/b6/10ba95739f2cbb9c5bd2f6568148d62b468afe01a94c633e8892a2936d8a/types_docutils-0.21.0.20241128-py3-none-any.whl", hash = "sha256:e0409204009639e9b0bf4521eeabe58b5e574ce9c0db08421c2ac26c32be0039", size = 34677 },
+]
+
+[[package]]
+name = "types-pymysql"
+version = "1.1.0.20241103"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b3/ac/5a23decbcf53893df11636b7d61cc000a97b0ed45e09cee94d6c75f159ec/types-PyMySQL-1.1.0.20241103.tar.gz", hash = "sha256:a7628542919a0ba87625fb79eefb2a2de45fb4ad32afe6e561e8f2f27fb58b8c", size = 14987 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3e/04/d02323dd4dfd6e0af4ecbb88a00215c37aa79894a2d158390700c84c8597/types_PyMySQL-1.1.0.20241103-py3-none-any.whl", hash = "sha256:1a32efd8a74b5bf74c4de92a86c1cc6edaf3802dcfd5546635ab501eb5e3c096", size = 15610 },
+]
+
+[[package]]
+name = "types-pyopenssl"
+version = "24.1.0.20240722"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cryptography" },
+ { name = "types-cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/93/29/47a346550fd2020dac9a7a6d033ea03fccb92fa47c726056618cc889745e/types-pyOpenSSL-24.1.0.20240722.tar.gz", hash = "sha256:47913b4678a01d879f503a12044468221ed8576263c1540dcb0484ca21b08c39", size = 8458 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/98/05/c868a850b6fbb79c26f5f299b768ee0adc1f9816d3461dcf4287916f655b/types_pyOpenSSL-24.1.0.20240722-py3-none-any.whl", hash = "sha256:6a7a5d2ec042537934cfb4c9d4deb0e16c4c6250b09358df1f083682fe6fda54", size = 7499 },
+]
+
+[[package]]
+name = "types-pyyaml"
+version = "6.0.12.20241230"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9a/f9/4d566925bcf9396136c0a2e5dc7e230ff08d86fa011a69888dd184469d80/types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c", size = 17078 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e8/c1/48474fbead512b70ccdb4f81ba5eb4a58f69d100ba19f17c92c0c4f50ae6/types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6", size = 20029 },
+]
+
+[[package]]
+name = "types-redis"
+version = "4.6.0.20241004"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cryptography" },
+ { name = "types-pyopenssl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3a/95/c054d3ac940e8bac4ca216470c80c26688a0e79e09f520a942bb27da3386/types-redis-4.6.0.20241004.tar.gz", hash = "sha256:5f17d2b3f9091ab75384153bfa276619ffa1cf6a38da60e10d5e6749cc5b902e", size = 49679 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/55/82/7d25dce10aad92d2226b269bce2f85cfd843b4477cd50245d7d40ecf8f89/types_redis-4.6.0.20241004-py3-none-any.whl", hash = "sha256:ef5da68cb827e5f606c8f9c0b49eeee4c2669d6d97122f301d3a55dc6a63f6ed", size = 58737 },
+]
+
+[[package]]
+name = "types-setuptools"
+version = "75.8.0.20250110"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f7/42/5713e90d4f9683f2301d900f33e4fc2405ad8ac224dda30f6cb7f4cd215b/types_setuptools-75.8.0.20250110.tar.gz", hash = "sha256:96f7ec8bbd6e0a54ea180d66ad68ad7a1d7954e7281a710ea2de75e355545271", size = 48185 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cf/a3/dbfd106751b11c728cec21cc62cbfe7ff7391b935c4b6e8f0bdc2e6fd541/types_setuptools-75.8.0.20250110-py3-none-any.whl", hash = "sha256:a9f12980bbf9bcdc23ecd80755789085bad6bfce4060c2275bc2b4ca9f2bc480", size = 71521 },
+]
+
+[[package]]
+name = "types-six"
+version = "1.17.0.20241205"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ca/01/1e1088033a79faa46d1b0761b5b00f2d50e5f89c567a140d55b79d1f2658/types_six-1.17.0.20241205.tar.gz", hash = "sha256:1f662347a8f3b2bf30517d629d82f591420df29811794b0bf3804e14d716f6e0", size = 15460 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/96/c6/67812fcc6c4d6cb469a7f38a293ad6d6effcfb4b599eb0f1ba287878ec0f/types_six-1.17.0.20241205-py3-none-any.whl", hash = "sha256:a4947c2bdcd9ab69d44466a533a15839ff48ddc27223615cb8145d73ab805bc2", size = 20068 },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.12.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
+]
+
+[[package]]
+name = "tzdata"
+version = "2025.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/0f/fa4723f22942480be4ca9527bbde8d43f6c3f2fe8412f00e7f5f6746bc8b/tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", size = 194950 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762 },
+]
+
+[[package]]
+name = "urllib3"
+version = "1.26.20"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e4/e8/6ff5e6bc22095cfc59b6ea711b687e2b7ed4bdb373f7eeec370a97d7392f/urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32", size = 307380 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/33/cf/8435d5a7159e2a9c83a95896ed596f68cf798005fe107cc655b5c5c14704/urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", size = 144225 },
+]
+
+[[package]]
+name = "uvicorn"
+version = "0.34.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "h11" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 },
+]
+
+[[package]]
+name = "vcrpy"
+version = "7.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyyaml" },
+ { name = "urllib3" },
+ { name = "wrapt" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/25/d3/856e06184d4572aada1dd559ddec3bedc46df1f2edc5ab2c91121a2cccdb/vcrpy-7.0.0.tar.gz", hash = "sha256:176391ad0425edde1680c5b20738ea3dc7fb942520a48d2993448050986b3a50", size = 85502 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/13/5d/1f15b252890c968d42b348d1e9b0aa12d5bf3e776704178ec37cceccdb63/vcrpy-7.0.0-py2.py3-none-any.whl", hash = "sha256:55791e26c18daa363435054d8b35bd41a4ac441b6676167635d1b37a71dbe124", size = 42321 },
+]
+
+[[package]]
+name = "virtualenv"
+version = "20.29.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "distlib" },
+ { name = "filelock" },
+ { name = "platformdirs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a7/ca/f23dcb02e161a9bba141b1c08aa50e8da6ea25e6d780528f1d385a3efe25/virtualenv-20.29.1.tar.gz", hash = "sha256:b8b8970138d32fb606192cb97f6cd4bb644fa486be9308fb9b63f81091b5dc35", size = 7658028 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/89/9b/599bcfc7064fbe5740919e78c5df18e5dceb0887e676256a1061bb5ae232/virtualenv-20.29.1-py3-none-any.whl", hash = "sha256:4e4cb403c0b0da39e13b46b1b2476e505cb0046b25f242bee80f62bf990b2779", size = 4282379 },
+]
+
+[[package]]
+name = "watchfiles"
+version = "1.0.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f5/26/c705fc77d0a9ecdb9b66f1e2976d95b81df3cae518967431e7dbf9b5e219/watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205", size = 94625 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/14/02/22fcaed0396730b0d362bc8d1ffb3be2658fd473eecbb2ba84243e157f11/watchfiles-1.0.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ba5bb3073d9db37c64520681dd2650f8bd40902d991e7b4cfaeece3e32561d08", size = 395212 },
+ { url = "https://files.pythonhosted.org/packages/e9/3d/ec5a2369a46edf3ebe092c39d9ae48e8cb6dacbde51c4b4f98936c524269/watchfiles-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f25d0ba0fe2b6d2c921cf587b2bf4c451860086534f40c384329fb96e2044d1", size = 384815 },
+ { url = "https://files.pythonhosted.org/packages/df/b4/898991cececbe171e67142c31905510203649569d9817848f47c4177ee42/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47eb32ef8c729dbc4f4273baece89398a4d4b5d21a1493efea77a17059f4df8a", size = 450680 },
+ { url = "https://files.pythonhosted.org/packages/58/f7/d4aa3000e812cfb5e5c2c6c0a3ec9d0a46a42489a8727edd160631c4e210/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:076f293100db3b0b634514aa0d294b941daa85fc777f9c698adb1009e5aca0b1", size = 455923 },
+ { url = "https://files.pythonhosted.org/packages/dd/95/7e2e4c6aba1b02fb5c76d2f6a450b85215921ec5f8f7ad5efd075369563f/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1eacd91daeb5158c598fe22d7ce66d60878b6294a86477a4715154990394c9b3", size = 482339 },
+ { url = "https://files.pythonhosted.org/packages/bb/67/4265b0fabcc2ef2c9e3e8802ba7908cf718a357ebfb49c72e53787156a48/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13c2ce7b72026cfbca120d652f02c7750f33b4c9395d79c9790b27f014c8a5a2", size = 519908 },
+ { url = "https://files.pythonhosted.org/packages/0d/96/b57802d5f8164bdf070befb4fd3dec4edba5a364ec0670965a97eb8098ce/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90192cdc15ab7254caa7765a98132a5a41471cf739513cc9bcf7d2ffcc0ec7b2", size = 501410 },
+ { url = "https://files.pythonhosted.org/packages/8b/18/6db0de4e8911ba14e31853201b40c0fa9fea5ecf3feb86b0ad58f006dfc3/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278aaa395f405972e9f523bd786ed59dfb61e4b827856be46a42130605fd0899", size = 452876 },
+ { url = "https://files.pythonhosted.org/packages/df/df/092a961815edf723a38ba2638c49491365943919c3526cc9cf82c42786a6/watchfiles-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a462490e75e466edbb9fc4cd679b62187153b3ba804868452ef0577ec958f5ff", size = 615353 },
+ { url = "https://files.pythonhosted.org/packages/f3/cf/b85fe645de4ff82f3f436c5e9032379fce37c303f6396a18f9726cc34519/watchfiles-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8d0d0630930f5cd5af929040e0778cf676a46775753e442a3f60511f2409f48f", size = 613187 },
+ { url = "https://files.pythonhosted.org/packages/f6/d4/a9fea27aef4dd69689bc3556718c1157a7accb72aa035ece87c1fa8483b5/watchfiles-1.0.4-cp310-cp310-win32.whl", hash = "sha256:cc27a65069bcabac4552f34fd2dce923ce3fcde0721a16e4fb1b466d63ec831f", size = 270799 },
+ { url = "https://files.pythonhosted.org/packages/df/02/dbe9d4439f15dd4ad0720b6e039bde9d66d1f830331f34c18eb70fa6608e/watchfiles-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:8b1f135238e75d075359cf506b27bf3f4ca12029c47d3e769d8593a2024ce161", size = 284145 },
+ { url = "https://files.pythonhosted.org/packages/0f/bb/8461adc4b1fed009546fb797fc0d5698dcfe5e289cb37e1b8f16a93cdc30/watchfiles-1.0.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2a9f93f8439639dc244c4d2902abe35b0279102bca7bbcf119af964f51d53c19", size = 394869 },
+ { url = "https://files.pythonhosted.org/packages/55/88/9ebf36b3547176d1709c320de78c1fa3263a46be31b5b1267571d9102686/watchfiles-1.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9eea33ad8c418847dd296e61eb683cae1c63329b6d854aefcd412e12d94ee235", size = 384905 },
+ { url = "https://files.pythonhosted.org/packages/03/8a/04335ce23ef78d8c69f0913e8b20cf7d9233e3986543aeef95ef2d6e43d2/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31f1a379c9dcbb3f09cf6be1b7e83b67c0e9faabed0471556d9438a4a4e14202", size = 449944 },
+ { url = "https://files.pythonhosted.org/packages/17/4e/c8d5dcd14fe637f4633616dabea8a4af0a10142dccf3b43e0f081ba81ab4/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab594e75644421ae0a2484554832ca5895f8cab5ab62de30a1a57db460ce06c6", size = 456020 },
+ { url = "https://files.pythonhosted.org/packages/5e/74/3e91e09e1861dd7fbb1190ce7bd786700dc0fbc2ccd33bb9fff5de039229/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc2eb5d14a8e0d5df7b36288979176fbb39672d45184fc4b1c004d7c3ce29317", size = 482983 },
+ { url = "https://files.pythonhosted.org/packages/a1/3d/e64de2d1ce4eb6a574fd78ce3a28c279da263be9ef3cfcab6f708df192f2/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f68d8e9d5a321163ddacebe97091000955a1b74cd43724e346056030b0bacee", size = 520320 },
+ { url = "https://files.pythonhosted.org/packages/2c/bd/52235f7063b57240c66a991696ed27e2a18bd6fcec8a1ea5a040b70d0611/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9ce064e81fe79faa925ff03b9f4c1a98b0bbb4a1b8c1b015afa93030cb21a49", size = 500988 },
+ { url = "https://files.pythonhosted.org/packages/3a/b0/ff04194141a5fe650c150400dd9e42667916bc0f52426e2e174d779b8a74/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b77d5622ac5cc91d21ae9c2b284b5d5c51085a0bdb7b518dba263d0af006132c", size = 452573 },
+ { url = "https://files.pythonhosted.org/packages/3d/9d/966164332c5a178444ae6d165082d4f351bd56afd9c3ec828eecbf190e6a/watchfiles-1.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1941b4e39de9b38b868a69b911df5e89dc43767feeda667b40ae032522b9b5f1", size = 615114 },
+ { url = "https://files.pythonhosted.org/packages/94/df/f569ae4c1877f96ad4086c153a8eee5a19a3b519487bf5c9454a3438c341/watchfiles-1.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f8c4998506241dedf59613082d1c18b836e26ef2a4caecad0ec41e2a15e4226", size = 613076 },
+ { url = "https://files.pythonhosted.org/packages/15/ae/8ce5f29e65d5fa5790e3c80c289819c55e12be2e1b9f5b6a0e55e169b97d/watchfiles-1.0.4-cp311-cp311-win32.whl", hash = "sha256:4ebbeca9360c830766b9f0df3640b791be569d988f4be6c06d6fae41f187f105", size = 271013 },
+ { url = "https://files.pythonhosted.org/packages/a4/c6/79dc4a7c598a978e5fafa135090aaf7bbb03b8dec7bada437dfbe578e7ed/watchfiles-1.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:05d341c71f3d7098920f8551d4df47f7b57ac5b8dad56558064c3431bdfc0b74", size = 284229 },
+ { url = "https://files.pythonhosted.org/packages/37/3d/928633723211753f3500bfb138434f080363b87a1b08ca188b1ce54d1e05/watchfiles-1.0.4-cp311-cp311-win_arm64.whl", hash = "sha256:32b026a6ab64245b584acf4931fe21842374da82372d5c039cba6bf99ef722f3", size = 276824 },
+ { url = "https://files.pythonhosted.org/packages/5b/1a/8f4d9a1461709756ace48c98f07772bc6d4519b1e48b5fa24a4061216256/watchfiles-1.0.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2", size = 391345 },
+ { url = "https://files.pythonhosted.org/packages/bc/d2/6750b7b3527b1cdaa33731438432e7238a6c6c40a9924049e4cebfa40805/watchfiles-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9", size = 381515 },
+ { url = "https://files.pythonhosted.org/packages/4e/17/80500e42363deef1e4b4818729ed939aaddc56f82f4e72b2508729dd3c6b/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712", size = 449767 },
+ { url = "https://files.pythonhosted.org/packages/10/37/1427fa4cfa09adbe04b1e97bced19a29a3462cc64c78630787b613a23f18/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12", size = 455677 },
+ { url = "https://files.pythonhosted.org/packages/c5/7a/39e9397f3a19cb549a7d380412fd9e507d4854eddc0700bfad10ef6d4dba/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844", size = 482219 },
+ { url = "https://files.pythonhosted.org/packages/45/2d/7113931a77e2ea4436cad0c1690c09a40a7f31d366f79c6f0a5bc7a4f6d5/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733", size = 518830 },
+ { url = "https://files.pythonhosted.org/packages/f9/1b/50733b1980fa81ef3c70388a546481ae5fa4c2080040100cd7bf3bf7b321/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af", size = 497997 },
+ { url = "https://files.pythonhosted.org/packages/2b/b4/9396cc61b948ef18943e7c85ecfa64cf940c88977d882da57147f62b34b1/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a", size = 452249 },
+ { url = "https://files.pythonhosted.org/packages/fb/69/0c65a5a29e057ad0dc691c2fa6c23b2983c7dabaa190ba553b29ac84c3cc/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff", size = 614412 },
+ { url = "https://files.pythonhosted.org/packages/7f/b9/319fcba6eba5fad34327d7ce16a6b163b39741016b1996f4a3c96b8dd0e1/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e", size = 611982 },
+ { url = "https://files.pythonhosted.org/packages/f1/47/143c92418e30cb9348a4387bfa149c8e0e404a7c5b0585d46d2f7031b4b9/watchfiles-1.0.4-cp312-cp312-win32.whl", hash = "sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94", size = 271822 },
+ { url = "https://files.pythonhosted.org/packages/ea/94/b0165481bff99a64b29e46e07ac2e0df9f7a957ef13bec4ceab8515f44e3/watchfiles-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c", size = 285441 },
+ { url = "https://files.pythonhosted.org/packages/11/de/09fe56317d582742d7ca8c2ca7b52a85927ebb50678d9b0fa8194658f536/watchfiles-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90", size = 277141 },
+ { url = "https://files.pythonhosted.org/packages/08/98/f03efabec64b5b1fa58c0daab25c68ef815b0f320e54adcacd0d6847c339/watchfiles-1.0.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9", size = 390954 },
+ { url = "https://files.pythonhosted.org/packages/16/09/4dd49ba0a32a45813debe5fb3897955541351ee8142f586303b271a02b40/watchfiles-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60", size = 381133 },
+ { url = "https://files.pythonhosted.org/packages/76/59/5aa6fc93553cd8d8ee75c6247763d77c02631aed21551a97d94998bf1dae/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407", size = 449516 },
+ { url = "https://files.pythonhosted.org/packages/4c/aa/df4b6fe14b6317290b91335b23c96b488d365d65549587434817e06895ea/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d", size = 454820 },
+ { url = "https://files.pythonhosted.org/packages/5e/71/185f8672f1094ce48af33252c73e39b48be93b761273872d9312087245f6/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d", size = 481550 },
+ { url = "https://files.pythonhosted.org/packages/85/d7/50ebba2c426ef1a5cb17f02158222911a2e005d401caf5d911bfca58f4c4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b", size = 518647 },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/4c009342e393c545d68987e8010b937f72f47937731225b2b29b7231428f/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590", size = 497547 },
+ { url = "https://files.pythonhosted.org/packages/0f/7c/1cf50b35412d5c72d63b2bf9a4fffee2e1549a245924960dd087eb6a6de4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902", size = 452179 },
+ { url = "https://files.pythonhosted.org/packages/d6/a9/3db1410e1c1413735a9a472380e4f431ad9a9e81711cda2aaf02b7f62693/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1", size = 614125 },
+ { url = "https://files.pythonhosted.org/packages/f2/e1/0025d365cf6248c4d1ee4c3d2e3d373bdd3f6aff78ba4298f97b4fad2740/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303", size = 611911 },
+ { url = "https://files.pythonhosted.org/packages/55/55/035838277d8c98fc8c917ac9beeb0cd6c59d675dc2421df5f9fcf44a0070/watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80", size = 271152 },
+ { url = "https://files.pythonhosted.org/packages/f0/e5/96b8e55271685ddbadc50ce8bc53aa2dff278fb7ac4c2e473df890def2dc/watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc", size = 285216 },
+ { url = "https://files.pythonhosted.org/packages/15/81/54484fc2fa715abe79694b975692af963f0878fb9d72b8251aa542bf3f10/watchfiles-1.0.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:d3452c1ec703aa1c61e15dfe9d482543e4145e7c45a6b8566978fbb044265a21", size = 394967 },
+ { url = "https://files.pythonhosted.org/packages/14/b3/557f0cd90add86586fe3deeebd11e8299db6bc3452b44a534f844c6ab831/watchfiles-1.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7b75fee5a16826cf5c46fe1c63116e4a156924d668c38b013e6276f2582230f0", size = 384707 },
+ { url = "https://files.pythonhosted.org/packages/03/a3/34638e1bffcb85a405e7b005e30bb211fd9be2ab2cb1847f2ceb81bef27b/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e997802d78cdb02623b5941830ab06f8860038faf344f0d288d325cc9c5d2ff", size = 450442 },
+ { url = "https://files.pythonhosted.org/packages/8f/9f/6a97460dd11a606003d634c7158d9fea8517e98daffc6f56d0f5fde2e86a/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0611d244ce94d83f5b9aff441ad196c6e21b55f77f3c47608dcf651efe54c4a", size = 455959 },
+ { url = "https://files.pythonhosted.org/packages/9d/bb/e0648c6364e4d37ec692bc3f0c77507d17d8bb8f75689148819142010bbf/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9745a4210b59e218ce64c91deb599ae8775c8a9da4e95fb2ee6fe745fc87d01a", size = 483187 },
+ { url = "https://files.pythonhosted.org/packages/dd/ad/d9290586a25288a81dfa8ad6329cf1de32aa1a9798ace45259eb95dcfb37/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4810ea2ae622add560f4aa50c92fef975e475f7ac4900ce5ff5547b2434642d8", size = 519733 },
+ { url = "https://files.pythonhosted.org/packages/4e/a9/150c1666825cc9637093f8cae7fc6f53b3296311ab8bd65f1389acb717cb/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:740d103cd01458f22462dedeb5a3382b7f2c57d07ff033fbc9465919e5e1d0f3", size = 502275 },
+ { url = "https://files.pythonhosted.org/packages/44/dc/5bfd21e20a330aca1706ac44713bc322838061938edf4b53130f97a7b211/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdbd912a61543a36aef85e34f212e5d2486e7c53ebfdb70d1e0b060cc50dd0bf", size = 452907 },
+ { url = "https://files.pythonhosted.org/packages/50/fe/8f4fc488f1699f564687b697456eb5c0cb8e2b0b8538150511c234c62094/watchfiles-1.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0bc80d91ddaf95f70258cf78c471246846c1986bcc5fd33ccc4a1a67fcb40f9a", size = 615927 },
+ { url = "https://files.pythonhosted.org/packages/ad/19/2e45f6f6eec89dd97a4d281635e3d73c17e5f692e7432063bdfdf9562c89/watchfiles-1.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab0311bb2ffcd9f74b6c9de2dda1612c13c84b996d032cd74799adb656af4e8b", size = 613435 },
+ { url = "https://files.pythonhosted.org/packages/91/17/dc5ac62ca377827c24321d68050efc2eaee2ebaf3f21d055bbce2206d309/watchfiles-1.0.4-cp39-cp39-win32.whl", hash = "sha256:02a526ee5b5a09e8168314c905fc545c9bc46509896ed282aeb5a8ba9bd6ca27", size = 270810 },
+ { url = "https://files.pythonhosted.org/packages/82/2b/dad851342492d538e7ffe72a8c756f747dd147988abb039ac9d6577d2235/watchfiles-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:a5ae5706058b27c74bac987d615105da17724172d5aaacc6c362a40599b6de43", size = 284866 },
+ { url = "https://files.pythonhosted.org/packages/6f/06/175d5ac6b838fb319008c0cd981d7bf289317c510154d411d3584ca2b67b/watchfiles-1.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdcc92daeae268de1acf5b7befcd6cfffd9a047098199056c72e4623f531de18", size = 396269 },
+ { url = "https://files.pythonhosted.org/packages/86/ee/5db93b0b57dc0587abdbac4149296ee73275f615d790a82cb5598af0557f/watchfiles-1.0.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8d3d9203705b5797f0af7e7e5baa17c8588030aaadb7f6a86107b7247303817", size = 386010 },
+ { url = "https://files.pythonhosted.org/packages/75/61/fe0dc5fedf152bfc085a53711f740701f6bdb8ab6b5c950402b681d4858b/watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdef5a1be32d0b07dcea3318a0be95d42c98ece24177820226b56276e06b63b0", size = 450913 },
+ { url = "https://files.pythonhosted.org/packages/9f/dd/3c7731af3baf1a9957afc643d176f94480921a690ec3237c9f9d11301c08/watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:342622287b5604ddf0ed2d085f3a589099c9ae8b7331df3ae9845571586c4f3d", size = 453474 },
+ { url = "https://files.pythonhosted.org/packages/6b/b4/c3998f54c91a35cee60ee6d3a855a069c5dff2bae6865147a46e9090dccd/watchfiles-1.0.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9fe37a2de80aa785d340f2980276b17ef697ab8db6019b07ee4fd28a8359d2f3", size = 395565 },
+ { url = "https://files.pythonhosted.org/packages/3f/05/ac1a4d235beb9ddfb8ac26ce93a00ba6bd1b1b43051ef12d7da957b4a9d1/watchfiles-1.0.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9d1ef56b56ed7e8f312c934436dea93bfa3e7368adfcf3df4c0da6d4de959a1e", size = 385406 },
+ { url = "https://files.pythonhosted.org/packages/4c/ea/36532e7d86525f4e52a10efed182abf33efb106a93d49f5fbc994b256bcd/watchfiles-1.0.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b42cac65beae3a362629950c444077d1b44f1790ea2772beaea95451c086bb", size = 450424 },
+ { url = "https://files.pythonhosted.org/packages/7a/e9/3cbcf4d70cd0b6d3f30631deae1bf37cc0be39887ca327a44462fe546bf5/watchfiles-1.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e0227b8ed9074c6172cf55d85b5670199c99ab11fd27d2c473aa30aec67ee42", size = 452488 },
+]
+
+[[package]]
+name = "webencodings"
+version = "0.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 },
+]
+
+[[package]]
+name = "websockets"
+version = "14.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/54/8359678c726243d19fae38ca14a334e740782336c9f19700858c4eb64a1e/websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5", size = 164394 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/28/fa/76607eb7dcec27b2d18d63f60a32e60e2b8629780f343bb83a4dbb9f4350/websockets-14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e8179f95323b9ab1c11723e5d91a89403903f7b001828161b480a7810b334885", size = 163089 },
+ { url = "https://files.pythonhosted.org/packages/9e/00/ad2246b5030575b79e7af0721810fdaecaf94c4b2625842ef7a756fa06dd/websockets-14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d8c3e2cdb38f31d8bd7d9d28908005f6fa9def3324edb9bf336d7e4266fd397", size = 160741 },
+ { url = "https://files.pythonhosted.org/packages/72/f7/60f10924d333a28a1ff3fcdec85acf226281331bdabe9ad74947e1b7fc0a/websockets-14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:714a9b682deb4339d39ffa674f7b674230227d981a37d5d174a4a83e3978a610", size = 160996 },
+ { url = "https://files.pythonhosted.org/packages/63/7c/c655789cf78648c01ac6ecbe2d6c18f91b75bdc263ffee4d08ce628d12f0/websockets-14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2e53c72052f2596fb792a7acd9704cbc549bf70fcde8a99e899311455974ca3", size = 169974 },
+ { url = "https://files.pythonhosted.org/packages/fb/5b/013ed8b4611857ac92ac631079c08d9715b388bd1d88ec62e245f87a39df/websockets-14.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3fbd68850c837e57373d95c8fe352203a512b6e49eaae4c2f4088ef8cf21980", size = 168985 },
+ { url = "https://files.pythonhosted.org/packages/cd/33/aa3e32fd0df213a5a442310754fe3f89dd87a0b8e5b4e11e0991dd3bcc50/websockets-14.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b27ece32f63150c268593d5fdb82819584831a83a3f5809b7521df0685cd5d8", size = 169297 },
+ { url = "https://files.pythonhosted.org/packages/93/17/dae0174883d6399f57853ac44abf5f228eaba86d98d160f390ffabc19b6e/websockets-14.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4daa0faea5424d8713142b33825fff03c736f781690d90652d2c8b053345b0e7", size = 169677 },
+ { url = "https://files.pythonhosted.org/packages/42/e2/0375af7ac00169b98647c804651c515054b34977b6c1354f1458e4116c1e/websockets-14.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bc63cee8596a6ec84d9753fd0fcfa0452ee12f317afe4beae6b157f0070c6c7f", size = 169089 },
+ { url = "https://files.pythonhosted.org/packages/73/8d/80f71d2a351a44b602859af65261d3dde3a0ce4e76cf9383738a949e0cc3/websockets-14.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a570862c325af2111343cc9b0257b7119b904823c675b22d4ac547163088d0d", size = 169026 },
+ { url = "https://files.pythonhosted.org/packages/48/97/173b1fa6052223e52bb4054a141433ad74931d94c575e04b654200b98ca4/websockets-14.2-cp310-cp310-win32.whl", hash = "sha256:75862126b3d2d505e895893e3deac0a9339ce750bd27b4ba515f008b5acf832d", size = 163967 },
+ { url = "https://files.pythonhosted.org/packages/c0/5b/2fcf60f38252a4562b28b66077e0d2b48f91fef645d5f78874cd1dec807b/websockets-14.2-cp310-cp310-win_amd64.whl", hash = "sha256:cc45afb9c9b2dc0852d5c8b5321759cf825f82a31bfaf506b65bf4668c96f8b2", size = 164413 },
+ { url = "https://files.pythonhosted.org/packages/15/b6/504695fb9a33df0ca56d157f5985660b5fc5b4bf8c78f121578d2d653392/websockets-14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3bdc8c692c866ce5fefcaf07d2b55c91d6922ac397e031ef9b774e5b9ea42166", size = 163088 },
+ { url = "https://files.pythonhosted.org/packages/81/26/ebfb8f6abe963c795122439c6433c4ae1e061aaedfc7eff32d09394afbae/websockets-14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c93215fac5dadc63e51bcc6dceca72e72267c11def401d6668622b47675b097f", size = 160745 },
+ { url = "https://files.pythonhosted.org/packages/a1/c6/1435ad6f6dcbff80bb95e8986704c3174da8866ddb751184046f5c139ef6/websockets-14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c9b6535c0e2cf8a6bf938064fb754aaceb1e6a4a51a80d884cd5db569886910", size = 160995 },
+ { url = "https://files.pythonhosted.org/packages/96/63/900c27cfe8be1a1f2433fc77cd46771cf26ba57e6bdc7cf9e63644a61863/websockets-14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a52a6d7cf6938e04e9dceb949d35fbdf58ac14deea26e685ab6368e73744e4c", size = 170543 },
+ { url = "https://files.pythonhosted.org/packages/00/8b/bec2bdba92af0762d42d4410593c1d7d28e9bfd952c97a3729df603dc6ea/websockets-14.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f05702e93203a6ff5226e21d9b40c037761b2cfb637187c9802c10f58e40473", size = 169546 },
+ { url = "https://files.pythonhosted.org/packages/6b/a9/37531cb5b994f12a57dec3da2200ef7aadffef82d888a4c29a0d781568e4/websockets-14.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22441c81a6748a53bfcb98951d58d1af0661ab47a536af08920d129b4d1c3473", size = 169911 },
+ { url = "https://files.pythonhosted.org/packages/60/d5/a6eadba2ed9f7e65d677fec539ab14a9b83de2b484ab5fe15d3d6d208c28/websockets-14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd9b868d78b194790e6236d9cbc46d68aba4b75b22497eb4ab64fa640c3af56", size = 170183 },
+ { url = "https://files.pythonhosted.org/packages/76/57/a338ccb00d1df881c1d1ee1f2a20c9c1b5b29b51e9e0191ee515d254fea6/websockets-14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a5a20d5843886d34ff8c57424cc65a1deda4375729cbca4cb6b3353f3ce4142", size = 169623 },
+ { url = "https://files.pythonhosted.org/packages/64/22/e5f7c33db0cb2c1d03b79fd60d189a1da044e2661f5fd01d629451e1db89/websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d", size = 169583 },
+ { url = "https://files.pythonhosted.org/packages/aa/2e/2b4662237060063a22e5fc40d46300a07142afe30302b634b4eebd717c07/websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a", size = 163969 },
+ { url = "https://files.pythonhosted.org/packages/94/a5/0cda64e1851e73fc1ecdae6f42487babb06e55cb2f0dc8904b81d8ef6857/websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b", size = 164408 },
+ { url = "https://files.pythonhosted.org/packages/c1/81/04f7a397653dc8bec94ddc071f34833e8b99b13ef1a3804c149d59f92c18/websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c", size = 163096 },
+ { url = "https://files.pythonhosted.org/packages/ec/c5/de30e88557e4d70988ed4d2eabd73fd3e1e52456b9f3a4e9564d86353b6d/websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967", size = 160758 },
+ { url = "https://files.pythonhosted.org/packages/e5/8c/d130d668781f2c77d106c007b6c6c1d9db68239107c41ba109f09e6c218a/websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990", size = 160995 },
+ { url = "https://files.pythonhosted.org/packages/a6/bc/f6678a0ff17246df4f06765e22fc9d98d1b11a258cc50c5968b33d6742a1/websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda", size = 170815 },
+ { url = "https://files.pythonhosted.org/packages/d8/b2/8070cb970c2e4122a6ef38bc5b203415fd46460e025652e1ee3f2f43a9a3/websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95", size = 169759 },
+ { url = "https://files.pythonhosted.org/packages/81/da/72f7caabd94652e6eb7e92ed2d3da818626e70b4f2b15a854ef60bf501ec/websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3", size = 170178 },
+ { url = "https://files.pythonhosted.org/packages/31/e0/812725b6deca8afd3a08a2e81b3c4c120c17f68c9b84522a520b816cda58/websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9", size = 170453 },
+ { url = "https://files.pythonhosted.org/packages/66/d3/8275dbc231e5ba9bb0c4f93144394b4194402a7a0c8ffaca5307a58ab5e3/websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267", size = 169830 },
+ { url = "https://files.pythonhosted.org/packages/a3/ae/e7d1a56755ae15ad5a94e80dd490ad09e345365199600b2629b18ee37bc7/websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe", size = 169824 },
+ { url = "https://files.pythonhosted.org/packages/b6/32/88ccdd63cb261e77b882e706108d072e4f1c839ed723bf91a3e1f216bf60/websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205", size = 163981 },
+ { url = "https://files.pythonhosted.org/packages/b3/7d/32cdb77990b3bdc34a306e0a0f73a1275221e9a66d869f6ff833c95b56ef/websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce", size = 164421 },
+ { url = "https://files.pythonhosted.org/packages/82/94/4f9b55099a4603ac53c2912e1f043d6c49d23e94dd82a9ce1eb554a90215/websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e", size = 163102 },
+ { url = "https://files.pythonhosted.org/packages/8e/b7/7484905215627909d9a79ae07070057afe477433fdacb59bf608ce86365a/websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad", size = 160766 },
+ { url = "https://files.pythonhosted.org/packages/a3/a4/edb62efc84adb61883c7d2c6ad65181cb087c64252138e12d655989eec05/websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03", size = 160998 },
+ { url = "https://files.pythonhosted.org/packages/f5/79/036d320dc894b96af14eac2529967a6fc8b74f03b83c487e7a0e9043d842/websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f", size = 170780 },
+ { url = "https://files.pythonhosted.org/packages/63/75/5737d21ee4dd7e4b9d487ee044af24a935e36a9ff1e1419d684feedcba71/websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5", size = 169717 },
+ { url = "https://files.pythonhosted.org/packages/2c/3c/bf9b2c396ed86a0b4a92ff4cdaee09753d3ee389be738e92b9bbd0330b64/websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a", size = 170155 },
+ { url = "https://files.pythonhosted.org/packages/75/2d/83a5aca7247a655b1da5eb0ee73413abd5c3a57fc8b92915805e6033359d/websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20", size = 170495 },
+ { url = "https://files.pythonhosted.org/packages/79/dd/699238a92761e2f943885e091486378813ac8f43e3c84990bc394c2be93e/websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2", size = 169880 },
+ { url = "https://files.pythonhosted.org/packages/c8/c9/67a8f08923cf55ce61aadda72089e3ed4353a95a3a4bc8bf42082810e580/websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307", size = 169856 },
+ { url = "https://files.pythonhosted.org/packages/17/b1/1ffdb2680c64e9c3921d99db460546194c40d4acbef999a18c37aa4d58a3/websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc", size = 163974 },
+ { url = "https://files.pythonhosted.org/packages/14/13/8b7fc4cb551b9cfd9890f0fd66e53c18a06240319915533b033a56a3d520/websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f", size = 164420 },
+ { url = "https://files.pythonhosted.org/packages/6f/eb/367e0ed7b8a960b4fc12c7c6bf3ebddf06875037de641637994849560d47/websockets-14.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7cd5706caec1686c5d233bc76243ff64b1c0dc445339bd538f30547e787c11fe", size = 163087 },
+ { url = "https://files.pythonhosted.org/packages/96/f7/1f18d028ec4a2c14598dfec6a73381a915c27464b693873198c1de872095/websockets-14.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec607328ce95a2f12b595f7ae4c5d71bf502212bddcea528290b35c286932b12", size = 160740 },
+ { url = "https://files.pythonhosted.org/packages/5c/db/b4b353fb9c3f0eaa8138ea4c76e6fa555b6d2821ed2d51d0ac3c320bc57e/websockets-14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da85651270c6bfb630136423037dd4975199e5d4114cae6d3066641adcc9d1c7", size = 160992 },
+ { url = "https://files.pythonhosted.org/packages/b9/b1/9149e420c61f375e432654d5c1545e563b90ac1f829ee1a8d1dccaf0869d/websockets-14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ecadc7ce90accf39903815697917643f5b7cfb73c96702318a096c00aa71f5", size = 169757 },
+ { url = "https://files.pythonhosted.org/packages/2b/33/0bb58204191e113212360f1392b6b1e9f85f62c7ca5b3b15f52f2f835516/websockets-14.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1979bee04af6a78608024bad6dfcc0cc930ce819f9e10342a29a05b5320355d0", size = 168762 },
+ { url = "https://files.pythonhosted.org/packages/be/3d/c3c192f16210d7b7535fbf4ee9a299612f4dccff665587617b13fa0a6aa3/websockets-14.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dddacad58e2614a24938a50b85969d56f88e620e3f897b7d80ac0d8a5800258", size = 169060 },
+ { url = "https://files.pythonhosted.org/packages/a6/73/75efa8d9e4b1b257818a7b7a0b9ac84a07c91120b52148941370ef2c8f16/websockets-14.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:89a71173caaf75fa71a09a5f614f450ba3ec84ad9fca47cb2422a860676716f0", size = 169457 },
+ { url = "https://files.pythonhosted.org/packages/a4/11/300cf36cfd6990ffb218394862f0513be8c21917c9ff5e362f94599caedd/websockets-14.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6af6a4b26eea4fc06c6818a6b962a952441e0e39548b44773502761ded8cc1d4", size = 168860 },
+ { url = "https://files.pythonhosted.org/packages/c0/3d/5fd82500714ab7c09f003bde671dad1a3a131ac77b6b11ada72e466de4f6/websockets-14.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:80c8efa38957f20bba0117b48737993643204645e9ec45512579132508477cfc", size = 168825 },
+ { url = "https://files.pythonhosted.org/packages/88/16/715580eb6caaacc232f303e9619103a42dcd354b0854baa5ed26aacaf828/websockets-14.2-cp39-cp39-win32.whl", hash = "sha256:2e20c5f517e2163d76e2729104abc42639c41cf91f7b1839295be43302713661", size = 163960 },
+ { url = "https://files.pythonhosted.org/packages/63/a7/a1035cb198eaa12eaa9621aaaa3ec021b0e3bac96e1df9ceb6bfe5e53e5f/websockets-14.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4c8cef610e8d7c70dea92e62b6814a8cd24fbd01d7103cc89308d2bfe1659ef", size = 164424 },
+ { url = "https://files.pythonhosted.org/packages/10/3d/91d3d2bb1325cd83e8e2c02d0262c7d4426dc8fa0831ef1aa4d6bf2041af/websockets-14.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d7d9cafbccba46e768be8a8ad4635fa3eae1ffac4c6e7cb4eb276ba41297ed29", size = 160773 },
+ { url = "https://files.pythonhosted.org/packages/33/7c/cdedadfef7381939577858b1b5718a4ab073adbb584e429dd9d9dc9bfe16/websockets-14.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c76193c1c044bd1e9b3316dcc34b174bbf9664598791e6fb606d8d29000e070c", size = 161007 },
+ { url = "https://files.pythonhosted.org/packages/ca/35/7a20a3c450b27c04e50fbbfc3dfb161ed8e827b2a26ae31c4b59b018b8c6/websockets-14.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd475a974d5352390baf865309fe37dec6831aafc3014ffac1eea99e84e83fc2", size = 162264 },
+ { url = "https://files.pythonhosted.org/packages/e8/9c/e3f9600564b0c813f2448375cf28b47dc42c514344faed3a05d71fb527f9/websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6c0097a41968b2e2b54ed3424739aab0b762ca92af2379f152c1aef0187e1c", size = 161873 },
+ { url = "https://files.pythonhosted.org/packages/3f/37/260f189b16b2b8290d6ae80c9f96d8b34692cf1bb3475df54c38d3deb57d/websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7ff794c8b36bc402f2e07c0b2ceb4a2424147ed4785ff03e2a7af03711d60a", size = 161818 },
+ { url = "https://files.pythonhosted.org/packages/ff/1e/e47dedac8bf7140e59aa6a679e850c4df9610ae844d71b6015263ddea37b/websockets-14.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dec254fcabc7bd488dab64846f588fc5b6fe0d78f641180030f8ea27b76d72c3", size = 164465 },
+ { url = "https://files.pythonhosted.org/packages/f7/c0/8e9325c4987dcf66d4a0d63ec380d4aefe8dcc1e521af71ad17adf2c1ae2/websockets-14.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bbe03eb853e17fd5b15448328b4ec7fb2407d45fb0245036d06a3af251f8e48f", size = 160773 },
+ { url = "https://files.pythonhosted.org/packages/5a/6e/c9a7f2edd4afddc4f8cccfc4e12468b7f6ec40f28d1b1e966a8d0298b875/websockets-14.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3c4aa3428b904d5404a0ed85f3644d37e2cb25996b7f096d77caeb0e96a3b42", size = 161006 },
+ { url = "https://files.pythonhosted.org/packages/f3/10/b90ece894828c954e674a81cb0db250e6c324c54db30a8b19e96431f928f/websockets-14.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:577a4cebf1ceaf0b65ffc42c54856214165fb8ceeba3935852fc33f6b0c55e7f", size = 162260 },
+ { url = "https://files.pythonhosted.org/packages/52/93/1147b6b5464a5fb6e8987da3ec7991dcc44f9090f67d9c841d7382fed429/websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad1c1d02357b7665e700eca43a31d52814ad9ad9b89b58118bdabc365454b574", size = 161868 },
+ { url = "https://files.pythonhosted.org/packages/32/ab/f7d80b4049bff0aa617507330db3a27389d0e70df54e29f7a3d76bbd2086/websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f390024a47d904613577df83ba700bd189eedc09c57af0a904e5c39624621270", size = 161813 },
+ { url = "https://files.pythonhosted.org/packages/cd/cc/adc9fb85f031b8df8e9f3d96cc004df25d2643e503953af5223c5b6825b7/websockets-14.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c1426c021c38cf92b453cdf371228d3430acd775edee6bac5a4d577efc72365", size = 164457 },
+ { url = "https://files.pythonhosted.org/packages/7b/c8/d529f8a32ce40d98309f4470780631e971a5a842b60aec864833b3615786/websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b", size = 157416 },
+]
+
+[[package]]
+name = "wrapt"
+version = "1.17.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", size = 53307 },
+ { url = "https://files.pythonhosted.org/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", size = 38486 },
+ { url = "https://files.pythonhosted.org/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", size = 38777 },
+ { url = "https://files.pythonhosted.org/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", size = 83314 },
+ { url = "https://files.pythonhosted.org/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", size = 74947 },
+ { url = "https://files.pythonhosted.org/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", size = 82778 },
+ { url = "https://files.pythonhosted.org/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", size = 81716 },
+ { url = "https://files.pythonhosted.org/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", size = 74548 },
+ { url = "https://files.pythonhosted.org/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", size = 81334 },
+ { url = "https://files.pythonhosted.org/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", size = 36427 },
+ { url = "https://files.pythonhosted.org/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", size = 38774 },
+ { url = "https://files.pythonhosted.org/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308 },
+ { url = "https://files.pythonhosted.org/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488 },
+ { url = "https://files.pythonhosted.org/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776 },
+ { url = "https://files.pythonhosted.org/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776 },
+ { url = "https://files.pythonhosted.org/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420 },
+ { url = "https://files.pythonhosted.org/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199 },
+ { url = "https://files.pythonhosted.org/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307 },
+ { url = "https://files.pythonhosted.org/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025 },
+ { url = "https://files.pythonhosted.org/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879 },
+ { url = "https://files.pythonhosted.org/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419 },
+ { url = "https://files.pythonhosted.org/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773 },
+ { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 },
+ { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 },
+ { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 },
+ { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 },
+ { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 },
+ { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 },
+ { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 },
+ { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 },
+ { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 },
+ { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 },
+ { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 },
+ { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 },
+ { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 },
+ { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 },
+ { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 },
+ { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 },
+ { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 },
+ { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 },
+ { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 },
+ { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 },
+ { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 },
+ { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 },
+ { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 },
+ { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 },
+ { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 },
+ { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 },
+ { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 },
+ { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 },
+ { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 },
+ { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 },
+ { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 },
+ { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 },
+ { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 },
+ { url = "https://files.pythonhosted.org/packages/8a/f4/6ed2b8f6f1c832933283974839b88ec7c983fd12905e01e97889dadf7559/wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a", size = 53308 },
+ { url = "https://files.pythonhosted.org/packages/a2/a9/712a53f8f4f4545768ac532619f6e56d5d0364a87b2212531685e89aeef8/wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061", size = 38489 },
+ { url = "https://files.pythonhosted.org/packages/fa/9b/e172c8f28a489a2888df18f953e2f6cb8d33b1a2e78c9dfc52d8bf6a5ead/wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82", size = 38776 },
+ { url = "https://files.pythonhosted.org/packages/cf/cb/7a07b51762dcd59bdbe07aa97f87b3169766cadf240f48d1cbe70a1be9db/wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9", size = 83050 },
+ { url = "https://files.pythonhosted.org/packages/a5/51/a42757dd41032afd6d8037617aa3bc6803ba971850733b24dfb7d5c627c4/wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f", size = 74718 },
+ { url = "https://files.pythonhosted.org/packages/bf/bb/d552bfe47db02fcfc950fc563073a33500f8108efa5f7b41db2f83a59028/wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b", size = 82590 },
+ { url = "https://files.pythonhosted.org/packages/77/99/77b06b3c3c410dbae411105bf22496facf03a5496bfaca8fbcf9da381889/wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f", size = 81462 },
+ { url = "https://files.pythonhosted.org/packages/2d/21/cf0bd85ae66f92600829ea1de8e1da778e5e9f6e574ccbe74b66db0d95db/wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8", size = 74309 },
+ { url = "https://files.pythonhosted.org/packages/6d/16/112d25e9092398a0dd6fec50ab7ac1b775a0c19b428f049785096067ada9/wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9", size = 81081 },
+ { url = "https://files.pythonhosted.org/packages/2b/49/364a615a0cc0872685646c495c7172e4fc7bf1959e3b12a1807a03014e05/wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb", size = 36423 },
+ { url = "https://files.pythonhosted.org/packages/00/ad/5d2c1b34ba3202cd833d9221833e74d6500ce66730974993a8dc9a94fb8c/wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb", size = 38772 },
+ { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 },
+]
+
+[[package]]
+name = "yarl"
+version = "1.18.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "multidict" },
+ { name = "propcache" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/4b94a8e6d2b51b599516a5cb88e5bc99b4d8d4583e468057eaa29d5f0918/yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", size = 181062 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/98/e005bc608765a8a5569f58e650961314873c8469c333616eb40bff19ae97/yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34", size = 141458 },
+ { url = "https://files.pythonhosted.org/packages/df/5d/f8106b263b8ae8a866b46d9be869ac01f9b3fb7f2325f3ecb3df8003f796/yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7", size = 94365 },
+ { url = "https://files.pythonhosted.org/packages/56/3e/d8637ddb9ba69bf851f765a3ee288676f7cf64fb3be13760c18cbc9d10bd/yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed", size = 92181 },
+ { url = "https://files.pythonhosted.org/packages/76/f9/d616a5c2daae281171de10fba41e1c0e2d8207166fc3547252f7d469b4e1/yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde", size = 315349 },
+ { url = "https://files.pythonhosted.org/packages/bb/b4/3ea5e7b6f08f698b3769a06054783e434f6d59857181b5c4e145de83f59b/yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b", size = 330494 },
+ { url = "https://files.pythonhosted.org/packages/55/f1/e0fc810554877b1b67420568afff51b967baed5b53bcc983ab164eebf9c9/yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5", size = 326927 },
+ { url = "https://files.pythonhosted.org/packages/a9/42/b1753949b327b36f210899f2dd0a0947c0c74e42a32de3f8eb5c7d93edca/yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc", size = 319703 },
+ { url = "https://files.pythonhosted.org/packages/f0/6d/e87c62dc9635daefb064b56f5c97df55a2e9cc947a2b3afd4fd2f3b841c7/yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd", size = 310246 },
+ { url = "https://files.pythonhosted.org/packages/e3/ef/e2e8d1785cdcbd986f7622d7f0098205f3644546da7919c24b95790ec65a/yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990", size = 319730 },
+ { url = "https://files.pythonhosted.org/packages/fc/15/8723e22345bc160dfde68c4b3ae8b236e868f9963c74015f1bc8a614101c/yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db", size = 321681 },
+ { url = "https://files.pythonhosted.org/packages/86/09/bf764e974f1516efa0ae2801494a5951e959f1610dd41edbfc07e5e0f978/yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62", size = 324812 },
+ { url = "https://files.pythonhosted.org/packages/f6/4c/20a0187e3b903c97d857cf0272d687c1b08b03438968ae8ffc50fe78b0d6/yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760", size = 337011 },
+ { url = "https://files.pythonhosted.org/packages/c9/71/6244599a6e1cc4c9f73254a627234e0dad3883ece40cc33dce6265977461/yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b", size = 338132 },
+ { url = "https://files.pythonhosted.org/packages/af/f5/e0c3efaf74566c4b4a41cb76d27097df424052a064216beccae8d303c90f/yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690", size = 331849 },
+ { url = "https://files.pythonhosted.org/packages/8a/b8/3d16209c2014c2f98a8f658850a57b716efb97930aebf1ca0d9325933731/yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6", size = 84309 },
+ { url = "https://files.pythonhosted.org/packages/fd/b7/2e9a5b18eb0fe24c3a0e8bae994e812ed9852ab4fd067c0107fadde0d5f0/yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8", size = 90484 },
+ { url = "https://files.pythonhosted.org/packages/40/93/282b5f4898d8e8efaf0790ba6d10e2245d2c9f30e199d1a85cae9356098c/yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069", size = 141555 },
+ { url = "https://files.pythonhosted.org/packages/6d/9c/0a49af78df099c283ca3444560f10718fadb8a18dc8b3edf8c7bd9fd7d89/yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193", size = 94351 },
+ { url = "https://files.pythonhosted.org/packages/5a/a1/205ab51e148fdcedad189ca8dd587794c6f119882437d04c33c01a75dece/yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889", size = 92286 },
+ { url = "https://files.pythonhosted.org/packages/ed/fe/88b690b30f3f59275fb674f5f93ddd4a3ae796c2b62e5bb9ece8a4914b83/yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8", size = 340649 },
+ { url = "https://files.pythonhosted.org/packages/07/eb/3b65499b568e01f36e847cebdc8d7ccb51fff716dbda1ae83c3cbb8ca1c9/yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca", size = 356623 },
+ { url = "https://files.pythonhosted.org/packages/33/46/f559dc184280b745fc76ec6b1954de2c55595f0ec0a7614238b9ebf69618/yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8", size = 354007 },
+ { url = "https://files.pythonhosted.org/packages/af/ba/1865d85212351ad160f19fb99808acf23aab9a0f8ff31c8c9f1b4d671fc9/yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae", size = 344145 },
+ { url = "https://files.pythonhosted.org/packages/94/cb/5c3e975d77755d7b3d5193e92056b19d83752ea2da7ab394e22260a7b824/yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3", size = 336133 },
+ { url = "https://files.pythonhosted.org/packages/19/89/b77d3fd249ab52a5c40859815765d35c91425b6bb82e7427ab2f78f5ff55/yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb", size = 347967 },
+ { url = "https://files.pythonhosted.org/packages/35/bd/f6b7630ba2cc06c319c3235634c582a6ab014d52311e7d7c22f9518189b5/yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e", size = 346397 },
+ { url = "https://files.pythonhosted.org/packages/18/1a/0b4e367d5a72d1f095318344848e93ea70da728118221f84f1bf6c1e39e7/yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59", size = 350206 },
+ { url = "https://files.pythonhosted.org/packages/b5/cf/320fff4367341fb77809a2d8d7fe75b5d323a8e1b35710aafe41fdbf327b/yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d", size = 362089 },
+ { url = "https://files.pythonhosted.org/packages/57/cf/aadba261d8b920253204085268bad5e8cdd86b50162fcb1b10c10834885a/yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e", size = 366267 },
+ { url = "https://files.pythonhosted.org/packages/54/58/fb4cadd81acdee6dafe14abeb258f876e4dd410518099ae9a35c88d8097c/yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a", size = 359141 },
+ { url = "https://files.pythonhosted.org/packages/9a/7a/4c571597589da4cd5c14ed2a0b17ac56ec9ee7ee615013f74653169e702d/yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1", size = 84402 },
+ { url = "https://files.pythonhosted.org/packages/ae/7b/8600250b3d89b625f1121d897062f629883c2f45339623b69b1747ec65fa/yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5", size = 91030 },
+ { url = "https://files.pythonhosted.org/packages/33/85/bd2e2729752ff4c77338e0102914897512e92496375e079ce0150a6dc306/yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50", size = 142644 },
+ { url = "https://files.pythonhosted.org/packages/ff/74/1178322cc0f10288d7eefa6e4a85d8d2e28187ccab13d5b844e8b5d7c88d/yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576", size = 94962 },
+ { url = "https://files.pythonhosted.org/packages/be/75/79c6acc0261e2c2ae8a1c41cf12265e91628c8c58ae91f5ff59e29c0787f/yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640", size = 92795 },
+ { url = "https://files.pythonhosted.org/packages/6b/32/927b2d67a412c31199e83fefdce6e645247b4fb164aa1ecb35a0f9eb2058/yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2", size = 332368 },
+ { url = "https://files.pythonhosted.org/packages/19/e5/859fca07169d6eceeaa4fde1997c91d8abde4e9a7c018e371640c2da2b71/yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75", size = 342314 },
+ { url = "https://files.pythonhosted.org/packages/08/75/76b63ccd91c9e03ab213ef27ae6add2e3400e77e5cdddf8ed2dbc36e3f21/yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512", size = 341987 },
+ { url = "https://files.pythonhosted.org/packages/1a/e1/a097d5755d3ea8479a42856f51d97eeff7a3a7160593332d98f2709b3580/yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba", size = 336914 },
+ { url = "https://files.pythonhosted.org/packages/0b/42/e1b4d0e396b7987feceebe565286c27bc085bf07d61a59508cdaf2d45e63/yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb", size = 325765 },
+ { url = "https://files.pythonhosted.org/packages/7e/18/03a5834ccc9177f97ca1bbb245b93c13e58e8225276f01eedc4cc98ab820/yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272", size = 344444 },
+ { url = "https://files.pythonhosted.org/packages/c8/03/a713633bdde0640b0472aa197b5b86e90fbc4c5bc05b727b714cd8a40e6d/yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6", size = 340760 },
+ { url = "https://files.pythonhosted.org/packages/eb/99/f6567e3f3bbad8fd101886ea0276c68ecb86a2b58be0f64077396cd4b95e/yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e", size = 346484 },
+ { url = "https://files.pythonhosted.org/packages/8e/a9/84717c896b2fc6cb15bd4eecd64e34a2f0a9fd6669e69170c73a8b46795a/yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb", size = 359864 },
+ { url = "https://files.pythonhosted.org/packages/1e/2e/d0f5f1bef7ee93ed17e739ec8dbcb47794af891f7d165fa6014517b48169/yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393", size = 364537 },
+ { url = "https://files.pythonhosted.org/packages/97/8a/568d07c5d4964da5b02621a517532adb8ec5ba181ad1687191fffeda0ab6/yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285", size = 357861 },
+ { url = "https://files.pythonhosted.org/packages/7d/e3/924c3f64b6b3077889df9a1ece1ed8947e7b61b0a933f2ec93041990a677/yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2", size = 84097 },
+ { url = "https://files.pythonhosted.org/packages/34/45/0e055320daaabfc169b21ff6174567b2c910c45617b0d79c68d7ab349b02/yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477", size = 90399 },
+ { url = "https://files.pythonhosted.org/packages/30/c7/c790513d5328a8390be8f47be5d52e141f78b66c6c48f48d241ca6bd5265/yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb", size = 140789 },
+ { url = "https://files.pythonhosted.org/packages/30/aa/a2f84e93554a578463e2edaaf2300faa61c8701f0898725842c704ba5444/yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa", size = 94144 },
+ { url = "https://files.pythonhosted.org/packages/c6/fc/d68d8f83714b221a85ce7866832cba36d7c04a68fa6a960b908c2c84f325/yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782", size = 91974 },
+ { url = "https://files.pythonhosted.org/packages/56/4e/d2563d8323a7e9a414b5b25341b3942af5902a2263d36d20fb17c40411e2/yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0", size = 333587 },
+ { url = "https://files.pythonhosted.org/packages/25/c9/cfec0bc0cac8d054be223e9f2c7909d3e8442a856af9dbce7e3442a8ec8d/yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482", size = 344386 },
+ { url = "https://files.pythonhosted.org/packages/ab/5d/4c532190113b25f1364d25f4c319322e86232d69175b91f27e3ebc2caf9a/yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186", size = 345421 },
+ { url = "https://files.pythonhosted.org/packages/23/d1/6cdd1632da013aa6ba18cee4d750d953104a5e7aac44e249d9410a972bf5/yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58", size = 339384 },
+ { url = "https://files.pythonhosted.org/packages/9a/c4/6b3c39bec352e441bd30f432cda6ba51681ab19bb8abe023f0d19777aad1/yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53", size = 326689 },
+ { url = "https://files.pythonhosted.org/packages/23/30/07fb088f2eefdc0aa4fc1af4e3ca4eb1a3aadd1ce7d866d74c0f124e6a85/yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2", size = 345453 },
+ { url = "https://files.pythonhosted.org/packages/63/09/d54befb48f9cd8eec43797f624ec37783a0266855f4930a91e3d5c7717f8/yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8", size = 341872 },
+ { url = "https://files.pythonhosted.org/packages/91/26/fd0ef9bf29dd906a84b59f0cd1281e65b0c3e08c6aa94b57f7d11f593518/yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1", size = 347497 },
+ { url = "https://files.pythonhosted.org/packages/d9/b5/14ac7a256d0511b2ac168d50d4b7d744aea1c1aa20c79f620d1059aab8b2/yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a", size = 359981 },
+ { url = "https://files.pythonhosted.org/packages/ca/b3/d493221ad5cbd18bc07e642894030437e405e1413c4236dd5db6e46bcec9/yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10", size = 366229 },
+ { url = "https://files.pythonhosted.org/packages/04/56/6a3e2a5d9152c56c346df9b8fb8edd2c8888b1e03f96324d457e5cf06d34/yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8", size = 360383 },
+ { url = "https://files.pythonhosted.org/packages/fd/b7/4b3c7c7913a278d445cc6284e59b2e62fa25e72758f888b7a7a39eb8423f/yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d", size = 310152 },
+ { url = "https://files.pythonhosted.org/packages/f5/d5/688db678e987c3e0fb17867970700b92603cadf36c56e5fb08f23e822a0c/yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c", size = 315723 },
+ { url = "https://files.pythonhosted.org/packages/6a/3b/fec4b08f5e88f68e56ee698a59284a73704df2e0e0b5bdf6536c86e76c76/yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04", size = 142780 },
+ { url = "https://files.pythonhosted.org/packages/ed/85/796b0d6a22d536ec8e14bdbb86519250bad980cec450b6e299b1c2a9079e/yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719", size = 94981 },
+ { url = "https://files.pythonhosted.org/packages/ee/0e/a830fd2238f7a29050f6dd0de748b3d6f33a7dbb67dbbc081a970b2bbbeb/yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e", size = 92789 },
+ { url = "https://files.pythonhosted.org/packages/0f/4f/438c9fd668954779e48f08c0688ee25e0673380a21bb1e8ccc56de5b55d7/yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee", size = 317327 },
+ { url = "https://files.pythonhosted.org/packages/bd/79/a78066f06179b4ed4581186c136c12fcfb928c475cbeb23743e71a991935/yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789", size = 336999 },
+ { url = "https://files.pythonhosted.org/packages/55/02/527963cf65f34a06aed1e766ff9a3b3e7d0eaa1c90736b2948a62e528e1d/yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8", size = 331693 },
+ { url = "https://files.pythonhosted.org/packages/a2/2a/167447ae39252ba624b98b8c13c0ba35994d40d9110e8a724c83dbbb5822/yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c", size = 321473 },
+ { url = "https://files.pythonhosted.org/packages/55/03/07955fabb20082373be311c91fd78abe458bc7ff9069d34385e8bddad20e/yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5", size = 313571 },
+ { url = "https://files.pythonhosted.org/packages/95/e2/67c8d3ec58a8cd8ddb1d63bd06eb7e7b91c9f148707a3eeb5a7ed87df0ef/yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1", size = 325004 },
+ { url = "https://files.pythonhosted.org/packages/06/43/51ceb3e427368fe6ccd9eccd162be227fd082523e02bad1fd3063daf68da/yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24", size = 322677 },
+ { url = "https://files.pythonhosted.org/packages/e4/0e/7ef286bfb23267739a703f7b967a858e2128c10bea898de8fa027e962521/yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318", size = 332806 },
+ { url = "https://files.pythonhosted.org/packages/c8/94/2d1f060f4bfa47c8bd0bcb652bfe71fba881564bcac06ebb6d8ced9ac3bc/yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985", size = 339919 },
+ { url = "https://files.pythonhosted.org/packages/8e/8d/73b5f9a6ab69acddf1ca1d5e7bc92f50b69124512e6c26b36844531d7f23/yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910", size = 340960 },
+ { url = "https://files.pythonhosted.org/packages/41/13/ce6bc32be4476b60f4f8694831f49590884b2c975afcffc8d533bf2be7ec/yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1", size = 336592 },
+ { url = "https://files.pythonhosted.org/packages/81/d5/6e0460292d6299ac3919945f912b16b104f4e81ab20bf53e0872a1296daf/yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5", size = 84833 },
+ { url = "https://files.pythonhosted.org/packages/b2/fc/a8aef69156ad5508165d8ae956736d55c3a68890610834bd985540966008/yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9", size = 90968 },
+ { url = "https://files.pythonhosted.org/packages/f5/4b/a06e0ec3d155924f77835ed2d167ebd3b211a7b0853da1cf8d8414d784ef/yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", size = 45109 },
+]
+
+[[package]]
+name = "zipp"
+version = "3.21.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 },
+]