From 1dbc7d8bf382c07f2de155e0e5554e9997a84732 Mon Sep 17 00:00:00 2001 From: RomualdLemesle Date: Fri, 16 Jan 2026 21:43:59 +0100 Subject: [PATCH 1/8] [teams] feat(injector): add Microsoft Teams injector --- .../injector_common/data_helpers.py | 25 + teams/.dockerignore | 3 + teams/.gitignore | 2 + teams/Dockerfile | 37 + teams/README.md | 211 +++ teams/assets/body_message.png | Bin 0 -> 8910 bytes teams/assets/workflow.png | Bin 0 -> 13359 bytes teams/docker-compose.yml | 13 + teams/poetry.lock | 1286 +++++++++++++++++ teams/pyproject.toml | 38 + teams/teams/__init__.py | 0 teams/teams/client/__init__.py | 0 teams/teams/client/teams_client.py | 66 + teams/teams/config.yml.sample | 9 + teams/teams/contracts_teams.py | 54 + teams/teams/helpers/__init__.py | 0 teams/teams/helpers/teams_helper.py | 11 + teams/teams/img/icon-teams.png | Bin 0 -> 56603 bytes teams/teams/openaev_teams.py | 97 ++ 19 files changed, 1852 insertions(+) create mode 100644 injector_common/injector_common/data_helpers.py create mode 100644 teams/.dockerignore create mode 100644 teams/.gitignore create mode 100644 teams/Dockerfile create mode 100644 teams/README.md create mode 100644 teams/assets/body_message.png create mode 100644 teams/assets/workflow.png create mode 100644 teams/docker-compose.yml create mode 100644 teams/poetry.lock create mode 100644 teams/pyproject.toml create mode 100644 teams/teams/__init__.py create mode 100644 teams/teams/client/__init__.py create mode 100644 teams/teams/client/teams_client.py create mode 100644 teams/teams/config.yml.sample create mode 100644 teams/teams/contracts_teams.py create mode 100644 teams/teams/helpers/__init__.py create mode 100644 teams/teams/helpers/teams_helper.py create mode 100644 teams/teams/img/icon-teams.png create mode 100644 teams/teams/openaev_teams.py diff --git a/injector_common/injector_common/data_helpers.py b/injector_common/injector_common/data_helpers.py new file mode 100644 index 00000000..b78a815f --- /dev/null +++ b/injector_common/injector_common/data_helpers.py @@ -0,0 +1,25 @@ +from typing import Dict + + +class DataHelpers: + + @staticmethod + def get_injector_contract_id(data: Dict) -> str: + try: + return data["injection"]["inject_injector_contract"]["injector_contract_id"] + except KeyError as e: + raise ValueError("Invalid data: missing injector contract id") from e + + @staticmethod + def get_content(data: Dict) -> Dict: + try: + return data["injection"]["inject_content"] + except KeyError as e: + raise ValueError("Invalid data: missing inject content") from e + + @staticmethod + def get_inject_id(data: Dict) -> str: + try: + return data["injection"]["inject_id"] + except KeyError as e: + raise ValueError("Invalid data: missing inject id") from e diff --git a/teams/.dockerignore b/teams/.dockerignore new file mode 100644 index 00000000..8f7bc48f --- /dev/null +++ b/teams/.dockerignore @@ -0,0 +1,3 @@ +config.yml +src/__pycache__ +__pycache__ diff --git a/teams/.gitignore b/teams/.gitignore new file mode 100644 index 00000000..b93baaf2 --- /dev/null +++ b/teams/.gitignore @@ -0,0 +1,2 @@ +config.yml +__pycache__ \ No newline at end of file diff --git a/teams/Dockerfile b/teams/Dockerfile new file mode 100644 index 00000000..98584294 --- /dev/null +++ b/teams/Dockerfile @@ -0,0 +1,37 @@ +FROM python:3.13-alpine AS builder + +RUN apk update && apk upgrade + +WORKDIR /opt/injector_common +COPY --from=injector_common ./ ./ + +# poetry version available on Ubuntu 24.04 +RUN pip3 install poetry==2.1.3 + +ARG installdir=/opt/injector +ADD . ${installdir} +WORKDIR ${installdir} +RUN poetry build + +FROM python:3.13-alpine AS runner + +WORKDIR /opt/injector_common +COPY --from=injector_common ./ ./ + +ARG installdir=/opt/injector +WORKDIR ${installdir} +COPY --from=builder ${installdir} ${installdir} +RUN pip3 install --no-cache-dir "$(ls dist/*.whl)[prod]" + +# Declare the build argument +ARG PYOAEV_GIT_BRANCH_OVERRIDE + +RUN if [[ ${PYOAEV_GIT_BRANCH_OVERRIDE} ]] ; then \ + echo "Forcing specific version of client-python" && \ + apk add --no-cache git && \ + pip install pip3-autoremove && \ + pip-autoremove pyoaev -y && \ + pip install git+https://github.com/OpenAEV-Platform/client-python@${PYOAEV_GIT_BRANCH_OVERRIDE} ; \ + fi + +CMD ["python3", "-m", "teams.openaev_teams"] diff --git a/teams/README.md b/teams/README.md new file mode 100644 index 00000000..7dfa4dd3 --- /dev/null +++ b/teams/README.md @@ -0,0 +1,211 @@ +# OpenAEV Microsoft Teams Injector + +The **OpenAEV Teams Injector** allows OpenAEV to send notifications to Microsoft Teams channels through HTTP-based +integrations (typically via Power Automate). + +## Table of Contents + +* [OpenAEV Teams Injector](#openaev-microsoft-teams-injector) + + * [Prerequisites](#prerequisites) + * [Configuration](#configuration) + + * [OpenAEV Environment Variables](#openaev-environment-variables) + * [Injector Environment Variables](#injector-environment-variables) + * [Deployment](#deployment) + + * [Docker Deployment](#docker-deployment) + * [Manual Deployment](#manual-deployment) + * [Development](#development) + * [Behavior](#behavior) + + * [Power Automate Workflow Requirements](#power-automate-workflow-requirements) + * [Use Cases](#use-cases) + +## Prerequisites + +This injector communicates with the OpenAEV platform through **RabbitMQ**, using the configuration provided by OpenAEV. + +To function properly, the injector **must be able to reach the RabbitMQ service** (hostname and port) defined in the +OpenAEV configuration. + +## Configuration + +Configuration values can be provided either: + +* via `docker-compose.yml` (Docker deployment), or +* via `config.yml` (manual deployment). + +### OpenAEV Environment Variables + +The following parameters are required to connect the injector to the OpenAEV platform: + +| Parameter | `config.yml` | Docker Variable | Mandatory | Description | +| ------------- | ------------ | --------------- | --------- | --------------------------------------------------- | +| OpenAEV URL | `url` | `OPENAEV_URL` | Yes | Base URL of the OpenAEV platform. | +| OpenAEV Token | `token` | `OPENAEV_TOKEN` | Yes | Admin API token configured in the OpenAEV platform. | + +### Injector Environment Variables + +The following parameters control the injector runtime behavior: + +| Parameter | `config.yml` | Docker Variable | Default | Mandatory | Description | +| ------------- | ------------ | -------------------- | ------- | --------- | ------------------------------------------------------- | +| Injector ID | `id` | `INJECTOR_ID` | — | Yes | Unique `UUIDv4` identifying this injector instance. | +| Injector Name | `name` | `INJECTOR_NAME` | — | Yes | Human-readable name of the injector. | +| Log Level | `log_level` | `INJECTOR_LOG_LEVEL` | `info` | Yes | Logging verbosity: `debug`, `info`, `warn`, or `error`. | + +## Deployment + +### Docker Deployment + +Build the Docker image using the provided `Dockerfile`: + +```shell +docker build --build-context injector_common=../injector_common . -t openaev/injector-teams:latest +``` + +Then configure the environment variables in `docker-compose.yml` and start the injector: + +```shell +docker compose up -d +``` + +### Manual Deployment + +1. Create a `config.yml` file based on `config.yml.sample` +2. Adjust the configuration values to match your environment + +#### Requirements + +* Python package manager **Poetry** (version 2.1 or later) + 👉 [https://python-poetry.org/](https://python-poetry.org/) + +#### Installation + +**Production environment** + +```shell +poetry install --extras prod +``` + +**Development environment** + +For development, you should also clone the `pyoaev` repository following the instructions provided in the OpenAEV +documentation. + +```shell +poetry install --extras dev +``` + +## Development + +This project follows strict code formatting rules to ensure consistency and readability across the OpenAEV ecosystem. + +Before submitting any **Pull Request**, contributors **must** format the codebase using **isort** and **black**. + +### Code Formatting + +The following tools are required (already included in the development dependencies): + +* **isort** – import sorting +* **black** – code formatter + +Run them from the project root: + +```shell +poetry run isort --profile black . +poetry run black . +``` + +Both commands must complete **without errors or changes** before opening a PR. + +> ⚠️ Pull Requests that do not respect formatting rules may be rejected or require additional review cycles. + +#### Run the Injector + +```shell +poetry run python -m teams.openaev_teams +``` + +## Behavior + +This injector introduces **Microsoft Teams-specific inject contracts** in OpenAEV. + +Each inject results in: + +* an HTTP `POST` request, +* a JSON payload, +* targeting a **Power Automate HTTP trigger**, which then posts a message into a Microsoft Teams channel. + +The injector reports execution status and metadata back to OpenAEV after completion. + +## Power Automate Workflow Requirements + +⚠️ **This injector only works with a specific type of Power Automate workflow.** + +The injector **does not send messages directly to Microsoft Teams**. +It relies on **Power Automate** to expose an HTTP endpoint and forward messages to Teams. + +### Supported Workflow Type + +Only **Cloud flows** using the following trigger are supported: + +> **When an HTTP request is received** + +This trigger: + +* Exposes an HTTP `POST` endpoint +* Accepts a JSON payload sent by the injector +* Allows posting messages to Teams using built-in actions + +Other workflow types (scheduled flows, Teams-triggered flows, etc.) **are not compatible**. + +### Minimal Workflow Structure + +The Power Automate flow must follow this structure: + +1. **Trigger** + + * *When an HTTP request is received* + +2. **Optional** + + * *Initialize variable* (used to store or manipulate the request body) + +3. **Action** + + * *Post a message in a chat or channel* + * Posted as **Flow bot** + * Targeting a **Team** and **Channel** + * Message content mapped from the HTTP payload + +### Expected Payload + +The injector sends a JSON payload containing at least: + +* `title` – Message title (e.g. alert name, exercise start) +* `message` – Main message content + +These fields must be explicitly mapped in the **Post a message** action. + +### Important Notes + +* The HTTP endpoint URL generated by Power Automate **must be reachable** by the injector +* Authentication relies solely on the webhook URL +* Message formatting (emojis, layout, icons) is handled **inside Power Automate** +* The flow must be **saved and published** after any change + +## Use Cases + +### Supported Power Automate Workflow Example + +Work with this kind of Power Automate workflow: + +![Power Automate Workflow](./assets/workflow.png) + +### Message Body Mapping Example + +Example of message body configuration in the **Post a message in a chat or channel** step: + +![Body message](./assets/body_message.png) \ No newline at end of file diff --git a/teams/assets/body_message.png b/teams/assets/body_message.png new file mode 100644 index 0000000000000000000000000000000000000000..c867d867b73da85a12c31bdc086d9387b762c4e2 GIT binary patch literal 8910 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

dwB-`iRA+Q>m@Io%Dom%l7 zPW|2`xgN#|BqRBAaB8`hG`fYy-a#v(NLaN`M~D@k^#QR;zf9?E+= z(u&R7rwdJe7(H5ho>yvwo0>-e>H0(|Zj(ssO66Oi-?&w<(l<&vIonYs9IlT;mV0M= zNooc#2yuAd`m!0xUHEExyo8Z5ZAj8H{!@&-N^5$9U2=Nu6KVEDoC`$KWU`Dk z+{sBrmF73hqEt&u#rY)3L|+JExJd&06Jsh=MMR+QulcT3 zgUQ;`_g1^;hwI(_h6>ZK!0F?9g*GLioUO=E)}+n#YC=ymC*!yLK%7)8M>rC+DR}1d zXK%01QhU_ocdA6z+LXy;SERgg>$6)|2V2;gfrH0cBhcH}K=ar%&i+i7PZxsJchUbi ztriTY?}mH4ciQP% zz2%;cr<$f?bTq*SJhTILil1^R$#UC>W-nxTC;>Arm;wd7J_zt3alLQ~OcQ)~d)BY2 zO{BbOo4nq83a-^JT5N-Y691kQMuF8NC=K`lDf_(L7Bsva)(fM^+V@>G`D=lrTdk+{ zI53(!Gvvb8e%-pQ|8`sIa+WAT&YsPZos1bzI1dQ(Z*y~_0UP}_=byAl|6%X+p9~~w z&^DP}UQ^4<9G~i3M7Q5khfZ;7J3=u4&_r*mi?w9*3mUgFfnEF5UA|DO;(}T)&v70Z zHA^a+bRNZ)IssV(C>}Q{=YGXaXp@(QB(0KaYNWE;G&CxvLtoa+ZDGV7%UC2oQLFo^ zm5DhE{&i!^!N5%k_X=1v-8&0#B4DpfWuub9;o+nHib0dlVh%V6aXp?d@So{Z0EKRY zjpDTpAa?wo{K)V(*X-<__GHZ_xGs@B_~4pJMxPGVyXe;D#EA8pW8(!?j}<>-^WrvY z%+P|Tf^SnmQg`*nl(*BT#}V>C#VvY??CT}EWBp8H%E&0(+Tui6Rgnoq{*t0vkw;>c zmVaApGK4%iMp!6H&qd1k+DCV#)!t6Ct%0As=9nl^mCValo19yRTzm6<%cI3Xr1W|} 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 O&txPOB`U;?1OF3%Z%%{& literal 0 HcmV?d00001 diff --git a/teams/docker-compose.yml b/teams/docker-compose.yml new file mode 100644 index 00000000..3f9dca0c --- /dev/null +++ b/teams/docker-compose.yml @@ -0,0 +1,13 @@ +services: + injector-teams: + image: openaev/injector-teams:2.0.11 + environment: + - OPENAEV_URL=http://localhost + - OPENAEV_TOKEN=ChangeMe + - INJECTOR_ID=ChangeMe + - INJECTOR_NAME=Teams + - INJECTOR_LOG_LEVEL=error + restart: always + depends_on: + openaev: + condition: service_healthy diff --git a/teams/poetry.lock b/teams/poetry.lock new file mode 100644 index 00000000..3673264e --- /dev/null +++ b/teams/poetry.lock @@ -0,0 +1,1286 @@ +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "black" +version = "25.12.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "black-25.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f85ba1ad15d446756b4ab5f3044731bf68b777f8f9ac9cdabd2425b97cd9c4e8"}, + {file = "black-25.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:546eecfe9a3a6b46f9d69d8a642585a6eaf348bcbbc4d87a19635570e02d9f4a"}, + {file = "black-25.12.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17dcc893da8d73d8f74a596f64b7c98ef5239c2cd2b053c0f25912c4494bf9ea"}, + {file = "black-25.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:09524b0e6af8ba7a3ffabdfc7a9922fb9adef60fed008c7cd2fc01f3048e6e6f"}, + {file = "black-25.12.0-cp310-cp310-win_arm64.whl", hash = "sha256:b162653ed89eb942758efeb29d5e333ca5bb90e5130216f8369857db5955a7da"}, + {file = "black-25.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0cfa263e85caea2cff57d8f917f9f51adae8e20b610e2b23de35b5b11ce691a"}, + {file = "black-25.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a2f578ae20c19c50a382286ba78bfbeafdf788579b053d8e4980afb079ab9be"}, + {file = "black-25.12.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e1b65634b0e471d07ff86ec338819e2ef860689859ef4501ab7ac290431f9b"}, + {file = "black-25.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a3fa71e3b8dd9f7c6ac4d818345237dfb4175ed3bf37cd5a581dbc4c034f1ec5"}, + {file = "black-25.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:51e267458f7e650afed8445dc7edb3187143003d52a1b710c7321aef22aa9655"}, + {file = "black-25.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31f96b7c98c1ddaeb07dc0f56c652e25bdedaac76d5b68a059d998b57c55594a"}, + {file = "black-25.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05dd459a19e218078a1f98178c13f861fe6a9a5f88fc969ca4d9b49eb1809783"}, + {file = "black-25.12.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1f68c5eff61f226934be6b5b80296cf6939e5d2f0c2f7d543ea08b204bfaf59"}, + {file = "black-25.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:274f940c147ddab4442d316b27f9e332ca586d39c85ecf59ebdea82cc9ee8892"}, + {file = "black-25.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:169506ba91ef21e2e0591563deda7f00030cb466e747c4b09cb0a9dae5db2f43"}, + {file = "black-25.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a05ddeb656534c3e27a05a29196c962877c83fa5503db89e68857d1161ad08a5"}, + {file = "black-25.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ec77439ef3e34896995503865a85732c94396edcc739f302c5673a2315e1e7f"}, + {file = "black-25.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e509c858adf63aa61d908061b52e580c40eae0dfa72415fa47ac01b12e29baf"}, + {file = "black-25.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:252678f07f5bac4ff0d0e9b261fbb029fa530cfa206d0a636a34ab445ef8ca9d"}, + {file = "black-25.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc5b1c09fe3c931ddd20ee548511c64ebf964ada7e6f0763d443947fd1c603ce"}, + {file = "black-25.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0a0953b134f9335c2434864a643c842c44fba562155c738a2a37a4d61f00cad5"}, + {file = "black-25.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2355bbb6c3b76062870942d8cc450d4f8ac71f9c93c40122762c8784df49543f"}, + {file = "black-25.12.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9678bd991cc793e81d19aeeae57966ee02909877cb65838ccffef24c3ebac08f"}, + {file = "black-25.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:97596189949a8aad13ad12fcbb4ae89330039b96ad6742e6f6b45e75ad5cfd83"}, + {file = "black-25.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:778285d9ea197f34704e3791ea9404cd6d07595745907dd2ce3da7a13627b29b"}, + {file = "black-25.12.0-py3-none-any.whl", hash = "sha256:48ceb36c16dbc84062740049eef990bb2ce07598272e673c17d1a7720c71c828"}, + {file = "black-25.12.0.tar.gz", hash = "sha256:8d3dd9cea14bff7ddc0eb243c811cdb1a011ebb4800a5f0335a01a68654796a7"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +pytokens = ">=0.3.0" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cachetools" +version = "5.5.2" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, + {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, +] + +[[package]] +name = "certifi" +version = "2025.10.5" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, + {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, + {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, + {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, +] + +[[package]] +name = "click" +version = "8.3.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, + {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "platform_system == \"Windows\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "dataclasses-json" +version = "0.6.7" +description = "Easily serialize dataclasses to and from JSON." +optional = false +python-versions = "<4.0,>=3.7" +groups = ["main"] +files = [ + {file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"}, + {file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"}, +] + +[package.dependencies] +marshmallow = ">=3.18.0,<4.0.0" +typing-inspect = ">=0.4.0,<1" + +[[package]] +name = "datefinder" +version = "0.7.3" +description = "Extract datetime objects from strings" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "datefinder-0.7.3-py2.py3-none-any.whl", hash = "sha256:c012e8cf60f8e80ee2df203f69338211ab2a68ec6e8a83c5c1e1f424d5743a9c"}, +] + +[package.dependencies] +python-dateutil = ">=2.4.2" +pytz = "*" +regex = ">=2017.02.08" + +[package.extras] +dev = ["mock", "pylint (==2.1.1)", "pytest (>=2.8.5)", "pytz (>=2015.7)"] +test = ["mock", "pytest (>=2.8.5)", "pytz (>=2015.7)"] + +[[package]] +name = "idna" +version = "3.11" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, + {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "injector-common" +version = "1.0.0" +description = "" +optional = false +python-versions = "*" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +pyoaev = "2.0.11" + +[package.extras] +dev = ["black (>=25.11.0,<25.12.0)", "wheel (>=0.45.1,<0.46.0)"] + +[package.source] +type = "directory" +url = "../injector_common" + +[[package]] +name = "isort" +version = "7.0.0" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.10.0" +groups = ["dev"] +files = [ + {file = "isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1"}, + {file = "isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187"}, +] + +[package.extras] +colors = ["colorama"] +plugins = ["setuptools"] + +[[package]] +name = "marshmallow" +version = "3.26.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"}, + {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<5.0)", "tox"] +docs = ["autodocsumm (==0.2.14)", "furo (==2024.8.6)", "sphinx (==8.1.3)", "sphinx-copybutton (==0.5.2)", "sphinx-issues (==5.0.0)", "sphinxext-opengraph (==0.9.1)"] +tests = ["pytest", "simplejson"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "opentelemetry-api" +version = "1.35.0" +description = "OpenTelemetry Python API" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_api-1.35.0-py3-none-any.whl", hash = "sha256:c4ea7e258a244858daf18474625e9cc0149b8ee354f37843415771a40c25ee06"}, + {file = "opentelemetry_api-1.35.0.tar.gz", hash = "sha256:a111b959bcfa5b4d7dffc2fbd6a241aa72dd78dd8e79b5b1662bda896c5d2ffe"}, +] + +[package.dependencies] +importlib-metadata = ">=6.0,<8.8.0" +typing-extensions = ">=4.5.0" + +[[package]] +name = "opentelemetry-sdk" +version = "1.35.0" +description = "OpenTelemetry Python SDK" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_sdk-1.35.0-py3-none-any.whl", hash = "sha256:223d9e5f5678518f4842311bb73966e0b6db5d1e0b74e35074c052cd2487f800"}, + {file = "opentelemetry_sdk-1.35.0.tar.gz", hash = "sha256:2a400b415ab68aaa6f04e8a6a9f6552908fb3090ae2ff78d6ae0c597ac581954"}, +] + +[package.dependencies] +opentelemetry-api = "1.35.0" +opentelemetry-semantic-conventions = "0.56b0" +typing-extensions = ">=4.5.0" + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.56b0" +description = "OpenTelemetry Semantic Conventions" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_semantic_conventions-0.56b0-py3-none-any.whl", hash = "sha256:df44492868fd6b482511cc43a942e7194be64e94945f572db24df2e279a001a2"}, + {file = "opentelemetry_semantic_conventions-0.56b0.tar.gz", hash = "sha256:c114c2eacc8ff6d3908cb328c811eaf64e6d68623840be9224dc829c4fd6c2ea"}, +] + +[package.dependencies] +opentelemetry-api = "1.35.0" +typing-extensions = ">=4.5.0" + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pathspec" +version = "1.0.3" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c"}, + {file = "pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] +tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] + +[[package]] +name = "pika" +version = "1.3.2" +description = "Pika Python AMQP Client Library" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "pika-1.3.2-py3-none-any.whl", hash = "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f"}, + {file = "pika-1.3.2.tar.gz", hash = "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f"}, +] + +[package.extras] +gevent = ["gevent"] +tornado = ["tornado"] +twisted = ["twisted"] + +[[package]] +name = "platformdirs" +version = "4.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, + {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, +] + +[package.extras] +docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"] +type = ["mypy (>=1.18.2)"] + +[[package]] +name = "prometheus-client" +version = "0.22.1" +description = "Python client for the Prometheus monitoring system." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "prometheus_client-0.22.1-py3-none-any.whl", hash = "sha256:cca895342e308174341b2cbf99a56bef291fbc0ef7b9e5412a0f26d653ba7094"}, + {file = "prometheus_client-0.22.1.tar.gz", hash = "sha256:190f1331e783cf21eb60bca559354e0a4d4378facecf78f5428c39b675d20d28"}, +] + +[package.extras] +twisted = ["twisted"] + +[[package]] +name = "pydantic" +version = "2.11.10" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic-2.11.10-py3-none-any.whl", hash = "sha256:802a655709d49bd004c31e865ef37da30b540786a46bfce02333e0e24b5fe29a"}, + {file = "pydantic-2.11.10.tar.gz", hash = "sha256:dc280f0982fbda6c38fada4e476dc0a4f3aeaf9c6ad4c28df68a666ec3c61423"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.33.2" +typing-extensions = ">=4.12.2" +typing-inspection = ">=0.4.0" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, + {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pyoaev" +version = "2.0.11" +description = "Python API client for OpenAEV." +optional = false +python-versions = ">=3.11" +groups = ["main"] +files = [ + {file = "pyoaev-2.0.11-py3-none-any.whl", hash = "sha256:d6559f82b9929bc12f7810726df5229fc4be96e1c5c7fe2aa0c9f2c6d78f65d3"}, + {file = "pyoaev-2.0.11.tar.gz", hash = "sha256:60617e1d2b722fbc4bb168bc421acd1906734c0ba682d51aae78e2ff2b73ff49"}, +] + +[package.dependencies] +cachetools = ">=5.5.0,<5.6.0" +dataclasses-json = ">=0.6.4,<0.7.0" +datefinder = ">=0.7.3,<0.8" +opentelemetry-api = ">=1.35.0,<1.36.0" +opentelemetry-sdk = ">=1.35.0,<1.36.0" +pika = ">=1.3.0,<1.4.0" +prometheus-client = ">=0.22.1,<0.23.0" +pydantic = ">=2.11.3,<2.12.0" +python_json_logger = ">=3.3.0,<3.4.0" +python-magic = {version = ">=0.4.27,<0.5", markers = "sys_platform == \"linux\" or sys_platform == \"darwin\""} +python-magic-bin = {version = ">=0.4.14,<0.5", markers = "sys_platform == \"win32\""} +PyYAML = ">=6.0,<6.1" +requests = ">=2.32.3,<2.33.0" +requests-toolbelt = ">=1.0.0,<1.1.0" +setuptools = ">=80.9.0,<80.10.0" +thefuzz = ">=0.22,<0.23" + +[package.extras] +dev = ["black (>=25.1.0,<25.2.0)", "build (>=1.2.1,<1.3.0)", "isort (>=6.0.0,<6.1.0)", "pre-commit (>=4.2.0,<4.3.0)", "types-python-dateutil (>=2.9.0,<2.10.0)", "types-pytz (>=2025.2.0.20250326,<2025.3.0.0)", "wheel (>=0.45.1,<0.46.0)"] +doc = ["autoapi (>=2.0.1,<2.1.0)", "sphinx-autodoc-typehints (>=3.2.0,<3.3.0)", "sphinx-rtd-theme (>=3.0.2,<3.1.0)"] + +[[package]] +name = "pyoaev" +version = "2.0.11" +description = "Python API client for OpenAEV." +optional = false +python-versions = ">=3.11" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +cachetools = ">=5.5.0,<5.6.0" +dataclasses-json = ">=0.6.4,<0.7.0" +datefinder = ">=0.7.3,<0.8" +opentelemetry-api = ">=1.35.0,<1.36.0" +opentelemetry-sdk = ">=1.35.0,<1.36.0" +pika = ">=1.3.0,<1.4.0" +prometheus-client = ">=0.22.1,<0.23.0" +pydantic = ">=2.11.3,<2.12.0" +python_json_logger = ">=3.3.0,<3.4.0" +python-magic = {version = ">=0.4.27,<0.5", markers = "sys_platform == \"linux\" or sys_platform == \"darwin\""} +python-magic-bin = {version = ">=0.4.14,<0.5", markers = "sys_platform == \"win32\""} +PyYAML = ">=6.0,<6.1" +requests = ">=2.32.3,<2.33.0" +requests-toolbelt = ">=1.0.0,<1.1.0" +setuptools = ">=80.9.0,<80.10.0" +thefuzz = ">=0.22,<0.23" + +[package.extras] +dev = ["black (>=25.11.0,<25.12.0)", "build (>=1.3.0,<1.4.0)", "isort (>=6.1.0,<6.2.0)", "pre-commit (>=4.2.0,<4.3.0)", "types-python-dateutil (>=2.9.0,<2.10.0)", "types-pytz (>=2025.2.0.20250326,<2025.3.0.0)", "wheel (>=0.45.1,<0.46.0)"] +doc = ["autoapi (>=2.0.1,<2.1.0)", "sphinx-autodoc-typehints (>=3.2.0,<3.3.0)", "sphinx-rtd-theme (>=3.0.2,<3.1.0)"] + +[package.source] +type = "directory" +url = "../../client-python" + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-json-logger" +version = "3.3.0" +description = "JSON Log Formatter for the Python Logging Package" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "python_json_logger-3.3.0-py3-none-any.whl", hash = "sha256:dd980fae8cffb24c13caf6e158d3d61c0d6d22342f932cb6e9deedab3d35eec7"}, + {file = "python_json_logger-3.3.0.tar.gz", hash = "sha256:12b7e74b17775e7d565129296105bbe3910842d9d0eb083fc83a6a617aa8df84"}, +] + +[package.extras] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "black", "build", "freezegun", "mdx_truly_sane_lists", "mike", "mkdocs", "mkdocs-awesome-pages-plugin", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material (>=8.5)", "mkdocstrings[python]", "msgspec ; implementation_name != \"pypy\"", "mypy", "orjson ; implementation_name != \"pypy\"", "pylint", "pytest", "tzdata", "validate-pyproject[all]"] + +[[package]] +name = "python-magic" +version = "0.4.27" +description = "File type identification using libmagic" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] +markers = "sys_platform == \"linux\" or sys_platform == \"darwin\"" +files = [ + {file = "python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b"}, + {file = "python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3"}, +] + +[[package]] +name = "python-magic-bin" +version = "0.4.14" +description = "File type identification using libmagic binary package" +optional = false +python-versions = "*" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "python_magic_bin-0.4.14-py2.py3-none-macosx_10_6_intel.whl", hash = "sha256:7b1743b3dbf16601d6eedf4e7c2c9a637901b0faaf24ad4df4d4527e7d8f66a4"}, + {file = "python_magic_bin-0.4.14-py2.py3-none-win32.whl", hash = "sha256:34a788c03adde7608028203e2dbb208f1f62225ad91518787ae26d603ae68892"}, + {file = "python_magic_bin-0.4.14-py2.py3-none-win_amd64.whl", hash = "sha256:90be6206ad31071a36065a2fc169c5afb5e0355cbe6030e87641c6c62edc2b69"}, +] + +[[package]] +name = "pytokens" +version = "0.3.0" +description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3"}, + {file = "pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a"}, +] + +[package.extras] +dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"] + +[[package]] +name = "pytz" +version = "2025.2" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"}, + {file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + +[[package]] +name = "rapidfuzz" +version = "3.14.1" +description = "rapid fuzzy string matching" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "rapidfuzz-3.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:489440e4b5eea0d150a31076eb183bed0ec84f934df206c72ae4fc3424501758"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eff22cc938c3f74d194df03790a6c3325d213b28cf65cdefd6fdeae759b745d5"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0307f018b16feaa36074bcec2496f6f120af151a098910296e72e233232a62f"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bc133652da143aca1ab72de235446432888b2b7f44ee332d006f8207967ecb8a"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e9e71b3fe7e4a1590843389a90fe2a8684649fc74b9b7446e17ee504ddddb7de"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c51519eb2f20b52eba6fc7d857ae94acc6c2a1f5d0f2d794b9d4977cdc29dd7"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:fe87d94602624f8f25fff9a0a7b47f33756c4d9fc32b6d3308bb142aa483b8a4"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2d665380503a575dda52eb712ea521f789e8f8fd629c7a8e6c0f8ff480febc78"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0f0dd022b8a7cbf3c891f6de96a80ab6a426f1069a085327816cea749e096c2"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf1ba22d36858b265c95cd774ba7fe8991e80a99cd86fe4f388605b01aee81a3"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ca1c1494ac9f9386d37f0e50cbaf4d07d184903aed7691549df1b37e9616edc9"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9e4b12e921b0fa90d7c2248742a536f21eae5562174090b83edd0b4ab8b557d7"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-win32.whl", hash = "sha256:5e1c1f2292baa4049535b07e9e81feb29e3650d2ba35ee491e64aca7ae4cb15e"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:59a8694beb9a13c4090ab3d1712cabbd896c6949706d1364e2a2e1713c413760"}, + {file = "rapidfuzz-3.14.1-cp310-cp310-win_arm64.whl", hash = "sha256:e94cee93faa792572c574a615abe12912124b4ffcf55876b72312914ab663345"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d976701060886a791c8a9260b1d4139d14c1f1e9a6ab6116b45a1acf3baff67"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e6ba7e6eb2ab03870dcab441d707513db0b4264c12fba7b703e90e8b4296df2"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e532bf46de5fd3a1efde73a16a4d231d011bce401c72abe3c6ecf9de681003f"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f9b6a6fb8ed9b951e5f3b82c1ce6b1665308ec1a0da87f799b16e24fc59e4662"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b6ac3f9810949caef0e63380b11a3c32a92f26bacb9ced5e32c33560fcdf8d1"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e52e4c34fd567f77513e886b66029c1ae02f094380d10eba18ba1c68a46d8b90"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:2ef72e41b1a110149f25b14637f1cedea6df192462120bea3433980fe9d8ac05"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fb654a35b373d712a6b0aa2a496b2b5cdd9d32410cfbaecc402d7424a90ba72a"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2b2c12e5b9eb8fe9a51b92fe69e9ca362c0970e960268188a6d295e1dec91e6d"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4f069dec5c450bd987481e752f0a9979e8fdf8e21e5307f5058f5c4bb162fa56"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4d0d9163725b7ad37a8c46988cae9ebab255984db95ad01bf1987ceb9e3058dd"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db656884b20b213d846f6bc990c053d1f4a60e6d4357f7211775b02092784ca1"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-win32.whl", hash = "sha256:4b42f7b9c58cbcfbfaddc5a6278b4ca3b6cd8983e7fd6af70ca791dff7105fb9"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:e5847f30d7d4edefe0cb37294d956d3495dd127c1c56e9128af3c2258a520bb4"}, + {file = "rapidfuzz-3.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:5087d8ad453092d80c042a08919b1cb20c8ad6047d772dc9312acd834da00f75"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:809515194f628004aac1b1b280c3734c5ea0ccbd45938c9c9656a23ae8b8f553"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0afcf2d6cb633d0d4260d8df6a40de2d9c93e9546e2c6b317ab03f89aa120ad7"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c1c3d07d53dcafee10599da8988d2b1f39df236aee501ecbd617bd883454fcd"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6e9ee3e1eb0a027717ee72fe34dc9ac5b3e58119f1bd8dd15bc19ed54ae3e62b"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:70c845b64a033a20c44ed26bc890eeb851215148cc3e696499f5f65529afb6cb"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26db0e815213d04234298dea0d884d92b9cb8d4ba954cab7cf67a35853128a33"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:6ad3395a416f8b126ff11c788531f157c7debeb626f9d897c153ff8980da10fb"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:61c5b9ab6f730e6478aa2def566223712d121c6f69a94c7cc002044799442afd"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:13e0ea3d0c533969158727d1bb7a08c2cc9a816ab83f8f0dcfde7e38938ce3e6"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6325ca435b99f4001aac919ab8922ac464999b100173317defb83eae34e82139"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:07a9fad3247e68798424bdc116c1094e88ecfabc17b29edf42a777520347648e"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8ff5dbe78db0a10c1f916368e21d328935896240f71f721e073cf6c4c8cdedd"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-win32.whl", hash = "sha256:9c83270e44a6ae7a39fc1d7e72a27486bccc1fa5f34e01572b1b90b019e6b566"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:e06664c7fdb51c708e082df08a6888fce4c5c416d7e3cc2fa66dd80eb76a149d"}, + {file = "rapidfuzz-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:6c7c26025f7934a169a23dafea6807cfc3fb556f1dd49229faf2171e5d8101cc"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8d69f470d63ee824132ecd80b1974e1d15dd9df5193916901d7860cef081a260"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6f571d20152fc4833b7b5e781b36d5e4f31f3b5a596a3d53cf66a1bd4436b4f4"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61d77e09b2b6bc38228f53b9ea7972a00722a14a6048be9a3672fb5cb08bad3a"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b41d95ef86a6295d353dc3bb6c80550665ba2c3bef3a9feab46074d12a9af8f"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0591df2e856ad583644b40a2b99fb522f93543c65e64b771241dda6d1cfdc96b"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f277801f55b2f3923ef2de51ab94689a0671a4524bf7b611de979f308a54cd6f"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:893fdfd4f66ebb67f33da89eb1bd1674b7b30442fdee84db87f6cb9074bf0ce9"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fe2651258c1f1afa9b66f44bf82f639d5f83034f9804877a1bbbae2120539ad1"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ace21f7a78519d8e889b1240489cd021c5355c496cb151b479b741a4c27f0a25"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cb5acf24590bc5e57027283b015950d713f9e4d155fda5cfa71adef3b3a84502"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:67ea46fa8cc78174bad09d66b9a4b98d3068e85de677e3c71ed931a1de28171f"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:44e741d785de57d1a7bae03599c1cbc7335d0b060a35e60c44c382566e22782e"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-win32.whl", hash = "sha256:b1fe6001baa9fa36bcb565e24e88830718f6c90896b91ceffcb48881e3adddbc"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:83b8cc6336709fa5db0579189bfd125df280a554af544b2dc1c7da9cdad7e44d"}, + {file = "rapidfuzz-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:cf75769662eadf5f9bd24e865c19e5ca7718e879273dce4e7b3b5824c4da0eb4"}, + {file = "rapidfuzz-3.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d937dbeda71c921ef6537c6d41a84f1b8112f107589c9977059de57a1d726dd6"}, + {file = "rapidfuzz-3.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a2d80cc1a4fcc7e259ed4f505e70b36433a63fa251f1bb69ff279fe376c5efd"}, + {file = "rapidfuzz-3.14.1-cp313-cp313t-win32.whl", hash = "sha256:40875e0c06f1a388f1cab3885744f847b557e0b1642dfc31ff02039f9f0823ef"}, + {file = "rapidfuzz-3.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:876dc0c15552f3d704d7fb8d61bdffc872ff63bedf683568d6faad32e51bbce8"}, + {file = "rapidfuzz-3.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:61458e83b0b3e2abc3391d0953c47d6325e506ba44d6a25c869c4401b3bc222c"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e84d9a844dc2e4d5c4cabd14c096374ead006583304333c14a6fbde51f612a44"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:40301b93b99350edcd02dbb22e37ca5f2a75d0db822e9b3c522da451a93d6f27"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fedd5097a44808dddf341466866e5c57a18a19a336565b4ff50aa8f09eb528f6"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e3e61c9e80d8c26709d8aa5c51fdd25139c81a4ab463895f8a567f8347b0548"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da011a373722fac6e64687297a1d17dc8461b82cb12c437845d5a5b161bc24b9"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5967d571243cfb9ad3710e6e628ab68c421a237b76e24a67ac22ee0ff12784d6"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:474f416cbb9099676de54aa41944c154ba8d25033ee460f87bb23e54af6d01c9"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ae2d57464b59297f727c4e201ea99ec7b13935f1f056c753e8103da3f2fc2404"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:57047493a1f62f11354c7143c380b02f1b355c52733e6b03adb1cb0fe8fb8816"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4acc20776f225ee37d69517a237c090b9fa7e0836a0b8bc58868e9168ba6ef6f"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4373f914ff524ee0146919dea96a40a8200ab157e5a15e777a74a769f73d8a4a"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:37017b84953927807847016620d61251fe236bd4bcb25e27b6133d955bb9cafb"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-win32.whl", hash = "sha256:c8d1dd1146539e093b84d0805e8951475644af794ace81d957ca612e3eb31598"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:f51c7571295ea97387bac4f048d73cecce51222be78ed808263b45c79c40a440"}, + {file = "rapidfuzz-3.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:01eab10ec90912d7d28b3f08f6c91adbaf93458a53f849ff70776ecd70dd7a7a"}, + {file = "rapidfuzz-3.14.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:60879fcae2f7618403c4c746a9a3eec89327d73148fb6e89a933b78442ff0669"}, + {file = "rapidfuzz-3.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f94d61e44db3fc95a74006a394257af90fa6e826c900a501d749979ff495d702"}, + {file = "rapidfuzz-3.14.1-cp314-cp314t-win32.whl", hash = "sha256:93b6294a3ffab32a9b5f9b5ca048fa0474998e7e8bb0f2d2b5e819c64cb71ec7"}, + {file = "rapidfuzz-3.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6cb56b695421538fdbe2c0c85888b991d833b8637d2f2b41faa79cea7234c000"}, + {file = "rapidfuzz-3.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7cd312c380d3ce9d35c3ec9726b75eee9da50e8a38e89e229a03db2262d3d96b"}, + {file = "rapidfuzz-3.14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:673ce55a9be5b772dade911909e42382c0828b8a50ed7f9168763fa6b9f7054d"}, + {file = "rapidfuzz-3.14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:45c62ada1980ebf4c64c4253993cc8daa018c63163f91db63bb3af69cb74c2e3"}, + {file = "rapidfuzz-3.14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4d51efb29c0df0d4f7f64f672a7624c2146527f0745e3572098d753676538800"}, + {file = "rapidfuzz-3.14.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4a21ccdf1bd7d57a1009030527ba8fae1c74bf832d0a08f6b67de8f5c506c96f"}, + {file = "rapidfuzz-3.14.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:589fb0af91d3aff318750539c832ea1100dbac2c842fde24e42261df443845f6"}, + {file = "rapidfuzz-3.14.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a4f18092db4825f2517d135445015b40033ed809a41754918a03ef062abe88a0"}, + {file = "rapidfuzz-3.14.1.tar.gz", hash = "sha256:b02850e7f7152bd1edff27e9d584505b84968cacedee7a734ec4050c655a803c"}, +] + +[package.extras] +all = ["numpy"] + +[[package]] +name = "regex" +version = "2025.9.18" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "regex-2025.9.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:12296202480c201c98a84aecc4d210592b2f55e200a1d193235c4db92b9f6788"}, + {file = "regex-2025.9.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:220381f1464a581f2ea988f2220cf2a67927adcef107d47d6897ba5a2f6d51a4"}, + {file = "regex-2025.9.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87f681bfca84ebd265278b5daa1dcb57f4db315da3b5d044add7c30c10442e61"}, + {file = "regex-2025.9.18-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34d674cbba70c9398074c8a1fcc1a79739d65d1105de2a3c695e2b05ea728251"}, + {file = "regex-2025.9.18-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:385c9b769655cb65ea40b6eea6ff763cbb6d69b3ffef0b0db8208e1833d4e746"}, + {file = "regex-2025.9.18-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8900b3208e022570ae34328712bef6696de0804c122933414014bae791437ab2"}, + {file = "regex-2025.9.18-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c204e93bf32cd7a77151d44b05eb36f469d0898e3fba141c026a26b79d9914a0"}, + {file = "regex-2025.9.18-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3acc471d1dd7e5ff82e6cacb3b286750decd949ecd4ae258696d04f019817ef8"}, + {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6479d5555122433728760e5f29edb4c2b79655a8deb681a141beb5c8a025baea"}, + {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:431bd2a8726b000eb6f12429c9b438a24062a535d06783a93d2bcbad3698f8a8"}, + {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0cc3521060162d02bd36927e20690129200e5ac9d2c6d32b70368870b122db25"}, + {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a021217b01be2d51632ce056d7a837d3fa37c543ede36e39d14063176a26ae29"}, + {file = "regex-2025.9.18-cp310-cp310-win32.whl", hash = "sha256:4a12a06c268a629cb67cc1d009b7bb0be43e289d00d5111f86a2efd3b1949444"}, + {file = "regex-2025.9.18-cp310-cp310-win_amd64.whl", hash = "sha256:47acd811589301298c49db2c56bde4f9308d6396da92daf99cba781fa74aa450"}, + {file = "regex-2025.9.18-cp310-cp310-win_arm64.whl", hash = "sha256:16bd2944e77522275e5ee36f867e19995bcaa533dcb516753a26726ac7285442"}, + {file = "regex-2025.9.18-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:51076980cd08cd13c88eb7365427ae27f0d94e7cebe9ceb2bb9ffdae8fc4d82a"}, + {file = "regex-2025.9.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:828446870bd7dee4e0cbeed767f07961aa07f0ea3129f38b3ccecebc9742e0b8"}, + {file = "regex-2025.9.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c28821d5637866479ec4cc23b8c990f5bc6dd24e5e4384ba4a11d38a526e1414"}, + {file = "regex-2025.9.18-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:726177ade8e481db669e76bf99de0b278783be8acd11cef71165327abd1f170a"}, + {file = "regex-2025.9.18-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5cca697da89b9f8ea44115ce3130f6c54c22f541943ac8e9900461edc2b8bd4"}, + {file = "regex-2025.9.18-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dfbde38f38004703c35666a1e1c088b778e35d55348da2b7b278914491698d6a"}, + {file = "regex-2025.9.18-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f2f422214a03fab16bfa495cfec72bee4aaa5731843b771860a471282f1bf74f"}, + {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a295916890f4df0902e4286bc7223ee7f9e925daa6dcdec4192364255b70561a"}, + {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5db95ff632dbabc8c38c4e82bf545ab78d902e81160e6e455598014f0abe66b9"}, + {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb967eb441b0f15ae610b7069bdb760b929f267efbf522e814bbbfffdf125ce2"}, + {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f04d2f20da4053d96c08f7fde6e1419b7ec9dbcee89c96e3d731fca77f411b95"}, + {file = "regex-2025.9.18-cp311-cp311-win32.whl", hash = "sha256:895197241fccf18c0cea7550c80e75f185b8bd55b6924fcae269a1a92c614a07"}, + {file = "regex-2025.9.18-cp311-cp311-win_amd64.whl", hash = "sha256:7e2b414deae99166e22c005e154a5513ac31493db178d8aec92b3269c9cce8c9"}, + {file = "regex-2025.9.18-cp311-cp311-win_arm64.whl", hash = "sha256:fb137ec7c5c54f34a25ff9b31f6b7b0c2757be80176435bf367111e3f71d72df"}, + {file = "regex-2025.9.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:436e1b31d7efd4dcd52091d076482031c611dde58bf9c46ca6d0a26e33053a7e"}, + {file = "regex-2025.9.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c190af81e5576b9c5fdc708f781a52ff20f8b96386c6e2e0557a78402b029f4a"}, + {file = "regex-2025.9.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e4121f1ce2b2b5eec4b397cc1b277686e577e658d8f5870b7eb2d726bd2300ab"}, + {file = "regex-2025.9.18-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:300e25dbbf8299d87205e821a201057f2ef9aa3deb29caa01cd2cac669e508d5"}, + {file = "regex-2025.9.18-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7b47fcf9f5316c0bdaf449e879407e1b9937a23c3b369135ca94ebc8d74b1742"}, + {file = "regex-2025.9.18-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:57a161bd3acaa4b513220b49949b07e252165e6b6dc910ee7617a37ff4f5b425"}, + {file = "regex-2025.9.18-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f130c3a7845ba42de42f380fff3c8aebe89a810747d91bcf56d40a069f15352"}, + {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f96fa342b6f54dcba928dd452e8d8cb9f0d63e711d1721cd765bb9f73bb048d"}, + {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f0d676522d68c207828dcd01fb6f214f63f238c283d9f01d85fc664c7c85b56"}, + {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:40532bff8a1a0621e7903ae57fce88feb2e8a9a9116d341701302c9302aef06e"}, + {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:039f11b618ce8d71a1c364fdee37da1012f5a3e79b1b2819a9f389cd82fd6282"}, + {file = "regex-2025.9.18-cp312-cp312-win32.whl", hash = "sha256:e1dd06f981eb226edf87c55d523131ade7285137fbde837c34dc9d1bf309f459"}, + {file = "regex-2025.9.18-cp312-cp312-win_amd64.whl", hash = "sha256:3d86b5247bf25fa3715e385aa9ff272c307e0636ce0c9595f64568b41f0a9c77"}, + {file = "regex-2025.9.18-cp312-cp312-win_arm64.whl", hash = "sha256:032720248cbeeae6444c269b78cb15664458b7bb9ed02401d3da59fe4d68c3a5"}, + {file = "regex-2025.9.18-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2a40f929cd907c7e8ac7566ac76225a77701a6221bca937bdb70d56cb61f57b2"}, + {file = "regex-2025.9.18-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c90471671c2cdf914e58b6af62420ea9ecd06d1554d7474d50133ff26ae88feb"}, + {file = "regex-2025.9.18-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a351aff9e07a2dabb5022ead6380cff17a4f10e4feb15f9100ee56c4d6d06af"}, + {file = "regex-2025.9.18-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc4b8e9d16e20ddfe16430c23468a8707ccad3365b06d4536142e71823f3ca29"}, + {file = "regex-2025.9.18-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b8cdbddf2db1c5e80338ba2daa3cfa3dec73a46fff2a7dda087c8efbf12d62f"}, + {file = "regex-2025.9.18-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a276937d9d75085b2c91fb48244349c6954f05ee97bba0963ce24a9d915b8b68"}, + {file = "regex-2025.9.18-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92a8e375ccdc1256401c90e9dc02b8642894443d549ff5e25e36d7cf8a80c783"}, + {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0dc6893b1f502d73037cf807a321cdc9be29ef3d6219f7970f842475873712ac"}, + {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a61e85bfc63d232ac14b015af1261f826260c8deb19401c0597dbb87a864361e"}, + {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1ef86a9ebc53f379d921fb9a7e42b92059ad3ee800fcd9e0fe6181090e9f6c23"}, + {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d3bc882119764ba3a119fbf2bd4f1b47bc56c1da5d42df4ed54ae1e8e66fdf8f"}, + {file = "regex-2025.9.18-cp313-cp313-win32.whl", hash = "sha256:3810a65675845c3bdfa58c3c7d88624356dd6ee2fc186628295e0969005f928d"}, + {file = "regex-2025.9.18-cp313-cp313-win_amd64.whl", hash = "sha256:16eaf74b3c4180ede88f620f299e474913ab6924d5c4b89b3833bc2345d83b3d"}, + {file = "regex-2025.9.18-cp313-cp313-win_arm64.whl", hash = "sha256:4dc98ba7dd66bd1261927a9f49bd5ee2bcb3660f7962f1ec02617280fc00f5eb"}, + {file = "regex-2025.9.18-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:fe5d50572bc885a0a799410a717c42b1a6b50e2f45872e2b40f4f288f9bce8a2"}, + {file = "regex-2025.9.18-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b9d9a2d6cda6621551ca8cf7a06f103adf72831153f3c0d982386110870c4d3"}, + {file = "regex-2025.9.18-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:13202e4c4ac0ef9a317fff817674b293c8f7e8c68d3190377d8d8b749f566e12"}, + {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:874ff523b0fecffb090f80ae53dc93538f8db954c8bb5505f05b7787ab3402a0"}, + {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d13ab0490128f2bb45d596f754148cd750411afc97e813e4b3a61cf278a23bb6"}, + {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:05440bc172bc4b4b37fb9667e796597419404dbba62e171e1f826d7d2a9ebcef"}, + {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5514b8e4031fdfaa3d27e92c75719cbe7f379e28cacd939807289bce76d0e35a"}, + {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:65d3c38c39efce73e0d9dc019697b39903ba25b1ad45ebbd730d2cf32741f40d"}, + {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ae77e447ebc144d5a26d50055c6ddba1d6ad4a865a560ec7200b8b06bc529368"}, + {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e3ef8cf53dc8df49d7e28a356cf824e3623764e9833348b655cfed4524ab8a90"}, + {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9feb29817df349c976da9a0debf775c5c33fc1c8ad7b9f025825da99374770b7"}, + {file = "regex-2025.9.18-cp313-cp313t-win32.whl", hash = "sha256:168be0d2f9b9d13076940b1ed774f98595b4e3c7fc54584bba81b3cc4181742e"}, + {file = "regex-2025.9.18-cp313-cp313t-win_amd64.whl", hash = "sha256:d59ecf3bb549e491c8104fea7313f3563c7b048e01287db0a90485734a70a730"}, + {file = "regex-2025.9.18-cp313-cp313t-win_arm64.whl", hash = "sha256:dbef80defe9fb21310948a2595420b36c6d641d9bea4c991175829b2cc4bc06a"}, + {file = "regex-2025.9.18-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c6db75b51acf277997f3adcd0ad89045d856190d13359f15ab5dda21581d9129"}, + {file = "regex-2025.9.18-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8f9698b6f6895d6db810e0bda5364f9ceb9e5b11328700a90cae573574f61eea"}, + {file = "regex-2025.9.18-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29cd86aa7cb13a37d0f0d7c21d8d949fe402ffa0ea697e635afedd97ab4b69f1"}, + {file = "regex-2025.9.18-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c9f285a071ee55cd9583ba24dde006e53e17780bb309baa8e4289cd472bcc47"}, + {file = "regex-2025.9.18-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5adf266f730431e3be9021d3e5b8d5ee65e563fec2883ea8093944d21863b379"}, + {file = "regex-2025.9.18-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1137cabc0f38807de79e28d3f6e3e3f2cc8cfb26bead754d02e6d1de5f679203"}, + {file = "regex-2025.9.18-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cc9e5525cada99699ca9223cce2d52e88c52a3d2a0e842bd53de5497c604164"}, + {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bbb9246568f72dce29bcd433517c2be22c7791784b223a810225af3b50d1aafb"}, + {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6a52219a93dd3d92c675383efff6ae18c982e2d7651c792b1e6d121055808743"}, + {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:ae9b3840c5bd456780e3ddf2f737ab55a79b790f6409182012718a35c6d43282"}, + {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d488c236ac497c46a5ac2005a952c1a0e22a07be9f10c3e735bc7d1209a34773"}, + {file = "regex-2025.9.18-cp314-cp314-win32.whl", hash = "sha256:0c3506682ea19beefe627a38872d8da65cc01ffa25ed3f2e422dffa1474f0788"}, + {file = "regex-2025.9.18-cp314-cp314-win_amd64.whl", hash = "sha256:57929d0f92bebb2d1a83af372cd0ffba2263f13f376e19b1e4fa32aec4efddc3"}, + {file = "regex-2025.9.18-cp314-cp314-win_arm64.whl", hash = "sha256:6a4b44df31d34fa51aa5c995d3aa3c999cec4d69b9bd414a8be51984d859f06d"}, + {file = "regex-2025.9.18-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b176326bcd544b5e9b17d6943f807697c0cb7351f6cfb45bf5637c95ff7e6306"}, + {file = "regex-2025.9.18-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0ffd9e230b826b15b369391bec167baed57c7ce39efc35835448618860995946"}, + {file = "regex-2025.9.18-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec46332c41add73f2b57e2f5b642f991f6b15e50e9f86285e08ffe3a512ac39f"}, + {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b80fa342ed1ea095168a3f116637bd1030d39c9ff38dc04e54ef7c521e01fc95"}, + {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4d97071c0ba40f0cf2a93ed76e660654c399a0a04ab7d85472239460f3da84b"}, + {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0ac936537ad87cef9e0e66c5144484206c1354224ee811ab1519a32373e411f3"}, + {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dec57f96d4def58c422d212d414efe28218d58537b5445cf0c33afb1b4768571"}, + {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48317233294648bf7cd068857f248e3a57222259a5304d32c7552e2284a1b2ad"}, + {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:274687e62ea3cf54846a9b25fc48a04459de50af30a7bd0b61a9e38015983494"}, + {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a78722c86a3e7e6aadf9579e3b0ad78d955f2d1f1a8ca4f67d7ca258e8719d4b"}, + {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:06104cd203cdef3ade989a1c45b6215bf42f8b9dd705ecc220c173233f7cba41"}, + {file = "regex-2025.9.18-cp314-cp314t-win32.whl", hash = "sha256:2e1eddc06eeaffd249c0adb6fafc19e2118e6308c60df9db27919e96b5656096"}, + {file = "regex-2025.9.18-cp314-cp314t-win_amd64.whl", hash = "sha256:8620d247fb8c0683ade51217b459cb4a1081c0405a3072235ba43a40d355c09a"}, + {file = "regex-2025.9.18-cp314-cp314t-win_arm64.whl", hash = "sha256:b7531a8ef61de2c647cdf68b3229b071e46ec326b3138b2180acb4275f470b01"}, + {file = "regex-2025.9.18-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3dbcfcaa18e9480669030d07371713c10b4f1a41f791ffa5cb1a99f24e777f40"}, + {file = "regex-2025.9.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1e85f73ef7095f0380208269055ae20524bfde3f27c5384126ddccf20382a638"}, + {file = "regex-2025.9.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9098e29b3ea4ffffeade423f6779665e2a4f8db64e699c0ed737ef0db6ba7b12"}, + {file = "regex-2025.9.18-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90b6b7a2d0f45b7ecaaee1aec6b362184d6596ba2092dd583ffba1b78dd0231c"}, + {file = "regex-2025.9.18-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c81b892af4a38286101502eae7aec69f7cd749a893d9987a92776954f3943408"}, + {file = "regex-2025.9.18-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3b524d010973f2e1929aeb635418d468d869a5f77b52084d9f74c272189c251d"}, + {file = "regex-2025.9.18-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b498437c026a3d5d0be0020023ff76d70ae4d77118e92f6f26c9d0423452446"}, + {file = "regex-2025.9.18-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0716e4d6e58853d83f6563f3cf25c281ff46cf7107e5f11879e32cb0b59797d9"}, + {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:065b6956749379d41db2625f880b637d4acc14c0a4de0d25d609a62850e96d36"}, + {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d4a691494439287c08ddb9b5793da605ee80299dd31e95fa3f323fac3c33d9d4"}, + {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ef8d10cc0989565bcbe45fb4439f044594d5c2b8919d3d229ea2c4238f1d55b0"}, + {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4baeb1b16735ac969a7eeecc216f1f8b7caf60431f38a2671ae601f716a32d25"}, + {file = "regex-2025.9.18-cp39-cp39-win32.whl", hash = "sha256:8e5f41ad24a1e0b5dfcf4c4e5d9f5bd54c895feb5708dd0c1d0d35693b24d478"}, + {file = "regex-2025.9.18-cp39-cp39-win_amd64.whl", hash = "sha256:50e8290707f2fb8e314ab3831e594da71e062f1d623b05266f8cfe4db4949afd"}, + {file = "regex-2025.9.18-cp39-cp39-win_arm64.whl", hash = "sha256:039a9d7195fd88c943d7c777d4941e8ef736731947becce773c31a1009cb3c35"}, + {file = "regex-2025.9.18.tar.gz", hash = "sha256:c5ba23274c61c6fef447ba6a39333297d0c247f53059dba0bca415cac511edc4"}, +] + +[[package]] +name = "requests" +version = "2.32.5" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "setuptools" +version = "80.9.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, + {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "thefuzz" +version = "0.22.1" +description = "Fuzzy string matching in python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "thefuzz-0.22.1-py3-none-any.whl", hash = "sha256:59729b33556850b90e1093c4cf9e618af6f2e4c985df193fdf3c5b5cf02ca481"}, + {file = "thefuzz-0.22.1.tar.gz", hash = "sha256:7138039a7ecf540da323792d8592ef9902b1d79eb78c147d4f20664de79f3680"}, +] + +[package.dependencies] +rapidfuzz = ">=3.0.0,<4.0.0" + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[[package]] +name = "typing-inspect" +version = "0.9.0" +description = "Runtime inspection utilities for typing module." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, + {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, +] + +[package.dependencies] +mypy-extensions = ">=0.3.0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "urllib3" +version = "2.5.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "zipp" +version = "3.23.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + +[extras] +dev = ["pyoaev", "pyoaev"] +prod = ["pyoaev", "pyoaev"] + +[metadata] +lock-version = "2.1" +python-versions = "^3.11" +content-hash = "4fa3500408b7e9a6b92dc14e917a80646cc95aae8067b92b59cf4795809107a4" diff --git a/teams/pyproject.toml b/teams/pyproject.toml new file mode 100644 index 00000000..7c6eea01 --- /dev/null +++ b/teams/pyproject.toml @@ -0,0 +1,38 @@ +[tool.poetry] +name = "openaev-teams-injector" +version = "2.0.11" +description = "An injector for Microsoft Teams" +authors = ["Filigran "] +license = "Apache-2.0" +readme = "README.md" +packages = [ + { include = "teams" } +] + + +[tool.poetry.dependencies] +python = "^3.11" +requests = "2.32.3" +pyoaev = [ + { markers = "extra == 'prod' and extra != 'dev'", version = "2.0.11", source = "pypi" }, + { markers = "extra == 'dev' and extra != 'prod'", path = "../../client-python", develop = true }, +] +injector_common = { path = "../injector_common", develop = true } + +[tool.poetry.extras] +prod = ["pyoaev"] +dev = ["pyoaev"] + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.isort] +profile = "black" +known_local_folder = ["teams"] + +[dependency-groups] +dev = [ + "black (>=25.12.0,<26.0.0)", + "isort (>=7.0.0,<8.0.0)" +] diff --git a/teams/teams/__init__.py b/teams/teams/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/teams/teams/client/__init__.py b/teams/teams/client/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/teams/teams/client/teams_client.py b/teams/teams/client/teams_client.py new file mode 100644 index 00000000..bc7df309 --- /dev/null +++ b/teams/teams/client/teams_client.py @@ -0,0 +1,66 @@ +from dataclasses import dataclass +from typing import Dict + +import requests +from requests.exceptions import RequestException, Timeout + +DEFAULT_TIMEOUT = 5 # seconds + + +@dataclass +class ExecutionResult: + url: str + status_code: int + success: bool + message: str + + @staticmethod + def from_http_response(response): + success = 200 <= response.status_code < 300 + + message = ( + "Teams notification sent successfully" + if success + else f"Teams notification failed (HTTP {response.status_code})" + ) + + return ExecutionResult( + url=response.url, + status_code=response.status_code, + success=success, + message=message, + ) + + @staticmethod + def failure(url: str, message: str) -> "ExecutionResult": + return ExecutionResult( + url=url, + status_code=0, + success=False, + message=message, + ) + + +class TeamsClient: + + @staticmethod + def post_message(url: str, payload: Dict) -> ExecutionResult: + try: + response = requests.post( + url=url, + json=payload, + timeout=DEFAULT_TIMEOUT, + ) + return ExecutionResult.from_http_response(response) + + except Timeout: + return ExecutionResult.failure( + url=url, + message="Teams notification timed out", + ) + + except RequestException as e: + return ExecutionResult.failure( + url=url, + message=f"Teams notification failed: {e}", + ) diff --git a/teams/teams/config.yml.sample b/teams/teams/config.yml.sample new file mode 100644 index 00000000..71c6cdc1 --- /dev/null +++ b/teams/teams/config.yml.sample @@ -0,0 +1,9 @@ +openaev: + url: 'http://localhost:3001' + token: 'ChangeMe' + +injector: + id: 'changeme' + name: 'Teams' + log_level: 'info' + diff --git a/teams/teams/contracts_teams.py b/teams/teams/contracts_teams.py new file mode 100644 index 00000000..79c09076 --- /dev/null +++ b/teams/teams/contracts_teams.py @@ -0,0 +1,54 @@ +from typing import List + +from pyoaev.contracts import ContractBuilder +from pyoaev.contracts.contract_config import ( + Contract, + ContractConfig, + ContractElement, + ContractText, + ContractTextArea, + SupportedLanguage, + prepare_contracts, +) +from pyoaev.security_domain.types import SecurityDomains + +CONTRACT_TYPE = "openaev_teams" +CONTRACT_ID = "c98e373e-3265-4aee-a2bb-36f7cb81e93e" + + +class TeamsContracts: + + @staticmethod + def build() -> List[Contract]: + # Config + contract_config = ContractConfig( + type=CONTRACT_TYPE, + label={ + SupportedLanguage.en: "Teams", + SupportedLanguage.fr: "Teams", + }, + color_dark="#00bcd4", + color_light="#00bcd4", + expose=True, + ) + # Fields + teams_fields: List[ContractElement] = ( + ContractBuilder() + .mandatory(ContractText(key="uri", label="Power Automate URL")) + .mandatory(ContractText(key="title", label="Title")) + .mandatory(ContractTextArea(key="message", label="Message")) + .build_fields() + ) + teams_contract = Contract( + contract_id=CONTRACT_ID, + config=contract_config, + label={ + SupportedLanguage.en: "Teams - Channel message", + SupportedLanguage.fr: "Teams - Message dans un canal", + }, + fields=teams_fields, + outputs=[], + manual=False, + domains=[SecurityDomains.TABLE_TOP.value], + ) + return prepare_contracts([teams_contract]) diff --git a/teams/teams/helpers/__init__.py b/teams/teams/helpers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/teams/teams/helpers/teams_helper.py b/teams/teams/helpers/teams_helper.py new file mode 100644 index 00000000..06525301 --- /dev/null +++ b/teams/teams/helpers/teams_helper.py @@ -0,0 +1,11 @@ +from typing import Dict + + +class TeamsPayloadBuilder: + + @staticmethod + def build(content: Dict) -> Dict: + return { + "title": content["title"], + "message": content["message"], + } diff --git a/teams/teams/img/icon-teams.png b/teams/teams/img/icon-teams.png new file mode 100644 index 0000000000000000000000000000000000000000..c089332af76d5813d3defd8f317d6943c07273da GIT binary patch literal 56603 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 SOy9r+NM1%oy5g~E=>GtiTeR!| literal 0 HcmV?d00001 diff --git a/teams/teams/openaev_teams.py b/teams/teams/openaev_teams.py new file mode 100644 index 00000000..ea1e1ef6 --- /dev/null +++ b/teams/teams/openaev_teams.py @@ -0,0 +1,97 @@ +import time +from typing import Dict + +from pyoaev.helpers import OpenAEVConfigHelper, OpenAEVInjectorHelper + +from injector_common.data_helpers import DataHelpers +from teams.client.teams_client import ExecutionResult, TeamsClient +from teams.contracts_teams import ( + CONTRACT_ID, + TeamsContracts, +) +from teams.helpers.teams_helper import TeamsPayloadBuilder + + +class OpenAEVTeamsInjector: + + def __init__(self): + self.config = OpenAEVConfigHelper( + __file__, + { + # API information + "openaev_url": {"env": "OPENAEV_URL", "file_path": ["openaev", "url"]}, + "openaev_token": { + "env": "OPENAEV_TOKEN", + "file_path": ["openaev", "token"], + }, + # Config information + "injector_id": {"env": "INJECTOR_ID", "file_path": ["injector", "id"]}, + "injector_name": { + "env": "INJECTOR_NAME", + "file_path": ["injector", "name"], + }, + "injector_type": { + "env": "INJECTOR_TYPE", + "file_path": ["injector", "type"], + "default": "openaev_teams", + }, + "injector_contracts": {"data": TeamsContracts.build()}, + }, + ) + with open("teams/img/icon-teams.png", "rb") as icon_file: + icon_bytes = icon_file.read() + self.helper = OpenAEVInjectorHelper(self.config, icon_bytes) + + def execute(self, data: Dict) -> ExecutionResult: + # Contract execution + inject_contract = DataHelpers.get_injector_contract_id(data) + if inject_contract != CONTRACT_ID: + raise ValueError("Unsupported contract for Teams injector") + + content = DataHelpers.get_content(data) + payload = TeamsPayloadBuilder.build(content) + + return TeamsClient.post_message(content["uri"], payload) + + def process_message(self, data: Dict) -> None: + start = time.time() + inject_id = DataHelpers.get_inject_id(data) + + # Notify API of reception and expected number of operations + self.helper.api.inject.execution_reception( + inject_id=inject_id, data={"tracking_total_count": 1} + ) + + # Execute inject + try: + result = self.execute(data) + + callback_data = { + "execution_message": result.message, + "execution_status": "SUCCESS" if result.success else "ERROR", + "execution_duration": int(time.time() - start), + "execution_action": "complete", + } + + self.helper.api.inject.execution_callback( + inject_id=inject_id, data=callback_data + ) + + except Exception as e: + callback_data = { + "execution_message": str(e), + "execution_status": "ERROR", + "execution_duration": int(time.time() - start), + "execution_action": "complete", + } + self.helper.api.inject.execution_callback( + inject_id=inject_id, data=callback_data + ) + + def start(self): + self.helper.listen(message_callback=self.process_message) + + +if __name__ == "__main__": + injector = OpenAEVTeamsInjector() + injector.start() From ef8c6aac0b8ed23349a22fd8bc9e4db657a2f418 Mon Sep 17 00:00:00 2001 From: RomualdLemesle Date: Tue, 27 Jan 2026 17:09:37 +0100 Subject: [PATCH 2/8] [teams] feat(injector): improv code --- teams/README.md | 27 +++++++- teams/docker-compose.yml | 6 +- teams/poetry.lock | 66 +++++++++++++++---- teams/pyproject.toml | 4 +- teams/teams/configuration/__init__.py | 0 teams/teams/configuration/config_loader.py | 27 ++++++++ .../configuration/injector_config_override.py | 25 +++++++ teams/teams/openaev_teams.py | 27 ++------ 8 files changed, 139 insertions(+), 43 deletions(-) create mode 100644 teams/teams/configuration/__init__.py create mode 100644 teams/teams/configuration/config_loader.py create mode 100644 teams/teams/configuration/injector_config_override.py diff --git a/teams/README.md b/teams/README.md index 7dfa4dd3..4d40e1e9 100644 --- a/teams/README.md +++ b/teams/README.md @@ -41,7 +41,7 @@ Configuration values can be provided either: The following parameters are required to connect the injector to the OpenAEV platform: | Parameter | `config.yml` | Docker Variable | Mandatory | Description | -| ------------- | ------------ | --------------- | --------- | --------------------------------------------------- | +|---------------|--------------|-----------------|-----------|-----------------------------------------------------| | OpenAEV URL | `url` | `OPENAEV_URL` | Yes | Base URL of the OpenAEV platform. | | OpenAEV Token | `token` | `OPENAEV_TOKEN` | Yes | Admin API token configured in the OpenAEV platform. | @@ -50,7 +50,7 @@ The following parameters are required to connect the injector to the OpenAEV pla The following parameters control the injector runtime behavior: | Parameter | `config.yml` | Docker Variable | Default | Mandatory | Description | -| ------------- | ------------ | -------------------- | ------- | --------- | ------------------------------------------------------- | +|---------------|--------------|----------------------|---------|-----------|---------------------------------------------------------| | Injector ID | `id` | `INJECTOR_ID` | — | Yes | Unique `UUIDv4` identifying this injector instance. | | Injector Name | `name` | `INJECTOR_NAME` | — | Yes | Human-readable name of the injector. | | Log Level | `log_level` | `INJECTOR_LOG_LEVEL` | `info` | Yes | Logging verbosity: `debug`, `info`, `warn`, or `error`. | @@ -64,6 +64,9 @@ Build the Docker image using the provided `Dockerfile`: ```shell docker build --build-context injector_common=../injector_common . -t openaev/injector-teams:latest ``` +```shell +podman build --build-context injector_common=../injector_common . -t openaev/injector-teams:latest +``` Then configure the environment variables in `docker-compose.yml` and start the injector: @@ -71,6 +74,10 @@ Then configure the environment variables in `docker-compose.yml` and start the i docker compose up -d ``` +```shell +podman compose up -d +``` + ### Manual Deployment 1. Create a `config.yml` file based on `config.yml.sample` @@ -91,9 +98,23 @@ poetry install --extras prod **Development environment** -For development, you should also clone the `pyoaev` repository following the instructions provided in the OpenAEV +For development, you should first clone the `pyoaev` repository following the instructions provided in the OpenAEV documentation. +First, create a dedicated virtual environment to keep things isolated: + +```shell +py -3.12 -m venv venv +``` + +Activate it depending on your OS: + +```shell +.\venv\Scripts\activate +``` + +Then install development dependencies + ```shell poetry install --extras dev ``` diff --git a/teams/docker-compose.yml b/teams/docker-compose.yml index 3f9dca0c..8b560f71 100644 --- a/teams/docker-compose.yml +++ b/teams/docker-compose.yml @@ -2,9 +2,9 @@ services: injector-teams: image: openaev/injector-teams:2.0.11 environment: - - OPENAEV_URL=http://localhost - - OPENAEV_TOKEN=ChangeMe - - INJECTOR_ID=ChangeMe + - OPENAEV_URL=${OPENAEV_URL} + - OPENAEV_TOKEN=${OPENAEV_TOKEN} + - INJECTOR_ID=${INJECTOR_ID} - INJECTOR_NAME=Teams - INJECTOR_LOG_LEVEL=error restart: always diff --git a/teams/poetry.lock b/teams/poetry.lock index 3673264e..966060c3 100644 --- a/teams/poetry.lock +++ b/teams/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -268,7 +268,7 @@ files = [ [package.dependencies] python-dateutil = ">=2.4.2" pytz = "*" -regex = ">=2017.02.08" +regex = ">=2017.2.8" [package.extras] dev = ["mock", "pylint (==2.1.1)", "pytest (>=2.8.5)", "pytz (>=2015.7)"] @@ -324,7 +324,7 @@ files = [] develop = true [package.dependencies] -pyoaev = "2.0.11" +pyoaev = "2.0.14" [package.extras] dev = ["black (>=25.11.0,<25.12.0)", "wheel (>=0.45.1,<0.46.0)"] @@ -643,16 +643,40 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pydantic-settings" +version = "2.11.0" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c"}, + {file = "pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180"}, +] + +[package.dependencies] +pydantic = ">=2.7.0" +python-dotenv = ">=0.21.0" +typing-inspection = ">=0.4.0" + +[package.extras] +aws-secrets-manager = ["boto3 (>=1.35.0)", "boto3-stubs[secretsmanager]"] +azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] +gcp-secret-manager = ["google-cloud-secret-manager (>=2.23.1)"] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + [[package]] name = "pyoaev" -version = "2.0.11" +version = "2.0.14" description = "Python API client for OpenAEV." optional = false python-versions = ">=3.11" groups = ["main"] files = [ - {file = "pyoaev-2.0.11-py3-none-any.whl", hash = "sha256:d6559f82b9929bc12f7810726df5229fc4be96e1c5c7fe2aa0c9f2c6d78f65d3"}, - {file = "pyoaev-2.0.11.tar.gz", hash = "sha256:60617e1d2b722fbc4bb168bc421acd1906734c0ba682d51aae78e2ff2b73ff49"}, + {file = "pyoaev-2.0.14-py3-none-any.whl", hash = "sha256:8a03dd1b2ce1d30c0d71249a8974ee27b61ebd72613b0a94f4b81a9d170009b0"}, + {file = "pyoaev-2.0.14.tar.gz", hash = "sha256:a93b75db0248f824d625b06fdaa1d66898902076e9b69a84abce5b933a8dcdbd"}, ] [package.dependencies] @@ -679,7 +703,7 @@ doc = ["autoapi (>=2.0.1,<2.1.0)", "sphinx-autodoc-typehints (>=3.2.0,<3.3.0)", [[package]] name = "pyoaev" -version = "2.0.11" +version = "2.0.14" description = "Python API client for OpenAEV." optional = false python-versions = ">=3.11" @@ -696,6 +720,7 @@ opentelemetry-sdk = ">=1.35.0,<1.36.0" pika = ">=1.3.0,<1.4.0" prometheus-client = ">=0.22.1,<0.23.0" pydantic = ">=2.11.3,<2.12.0" +pydantic-settings = ">=2.11.0,<2.12.0" python_json_logger = ">=3.3.0,<3.4.0" python-magic = {version = ">=0.4.27,<0.5", markers = "sys_platform == \"linux\" or sys_platform == \"darwin\""} python-magic-bin = {version = ">=0.4.14,<0.5", markers = "sys_platform == \"win32\""} @@ -728,6 +753,21 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-dotenv" +version = "1.2.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61"}, + {file = "python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "python-json-logger" version = "3.3.0" @@ -1112,19 +1152,19 @@ files = [ [[package]] name = "requests" -version = "2.32.5" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, - {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset_normalizer = ">=2,<4" +charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" @@ -1283,4 +1323,4 @@ prod = ["pyoaev", "pyoaev"] [metadata] lock-version = "2.1" python-versions = "^3.11" -content-hash = "4fa3500408b7e9a6b92dc14e917a80646cc95aae8067b92b59cf4795809107a4" +content-hash = "a0bca52807663dab44d842f246d624429ed03ceb12aa9d2c0e6bb1e8b0bd3fb8" diff --git a/teams/pyproject.toml b/teams/pyproject.toml index 7c6eea01..30fce9ab 100644 --- a/teams/pyproject.toml +++ b/teams/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "openaev-teams-injector" -version = "2.0.11" +version = "2.0.14" description = "An injector for Microsoft Teams" authors = ["Filigran "] license = "Apache-2.0" @@ -14,7 +14,7 @@ packages = [ python = "^3.11" requests = "2.32.3" pyoaev = [ - { markers = "extra == 'prod' and extra != 'dev'", version = "2.0.11", source = "pypi" }, + { markers = "extra == 'prod' and extra != 'dev'", version = "2.0.14", source = "pypi" }, { markers = "extra == 'dev' and extra != 'prod'", path = "../../client-python", develop = true }, ] injector_common = { path = "../injector_common", develop = true } diff --git a/teams/teams/configuration/__init__.py b/teams/teams/configuration/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/teams/teams/configuration/config_loader.py b/teams/teams/configuration/config_loader.py new file mode 100644 index 00000000..38a93449 --- /dev/null +++ b/teams/teams/configuration/config_loader.py @@ -0,0 +1,27 @@ +from pydantic import Field +from pyoaev.configuration import ConfigLoaderOAEV, Configuration, SettingsLoader + +from teams.configuration.injector_config_override import InjectorConfigOverride +from teams.contracts_teams import TeamsContracts + + +class ConfigLoader(SettingsLoader): + openaev: ConfigLoaderOAEV = Field(default_factory=ConfigLoaderOAEV) + injector: InjectorConfigOverride = Field(default_factory=InjectorConfigOverride) + + def to_daemon_config(self) -> Configuration: + return Configuration( + config_hints={ + # OpenAEV configuration (flattened) + "openaev_url": {"data": str(self.openaev.url)}, + "openaev_token": {"data": self.openaev.token}, + # Injector configuration (flattened) + "injector_id": {"data": self.injector.id}, + "injector_name": {"data": self.injector.name}, + "injector_type": {"data": self.injector.type}, + "injector_contracts": {"data": TeamsContracts.build()}, + "injector_log_level": {"data": self.injector.log_level}, + "injector_icon_filepath": {"data": self.injector.icon_filepath}, + }, + config_base_model=self, + ) diff --git a/teams/teams/configuration/injector_config_override.py b/teams/teams/configuration/injector_config_override.py new file mode 100644 index 00000000..e5df9e4e --- /dev/null +++ b/teams/teams/configuration/injector_config_override.py @@ -0,0 +1,25 @@ +from pydantic import Field +from pyoaev.configuration import ConfigLoaderCollector + + +# To be change ConfigLoaderCollector +class InjectorConfigOverride(ConfigLoaderCollector): + id: str = Field( + description="A unique UUIDv4 identifier for this injector instance.", + ) + name: str = Field( + description="Name of the injector.", + default="Teams", + ) + icon_filepath: str | None = Field( + description="Path to the icon file", + default="teams/img/icon-teams.png", + ) + type: str = Field( + description="Type of the injector.", + default="openaev_teams", + ) + log_level: str = Field( + description="Determines the verbosity of the logs. Options: debug, info, warn, or error.", + default="info", + ) diff --git a/teams/teams/openaev_teams.py b/teams/teams/openaev_teams.py index ea1e1ef6..aedb8a35 100644 --- a/teams/teams/openaev_teams.py +++ b/teams/teams/openaev_teams.py @@ -4,7 +4,9 @@ from pyoaev.helpers import OpenAEVConfigHelper, OpenAEVInjectorHelper from injector_common.data_helpers import DataHelpers +from injector_common.dump_config import intercept_dump_argument from teams.client.teams_client import ExecutionResult, TeamsClient +from teams.configuration.config_loader import ConfigLoader from teams.contracts_teams import ( CONTRACT_ID, TeamsContracts, @@ -15,29 +17,10 @@ class OpenAEVTeamsInjector: def __init__(self): - self.config = OpenAEVConfigHelper( - __file__, - { - # API information - "openaev_url": {"env": "OPENAEV_URL", "file_path": ["openaev", "url"]}, - "openaev_token": { - "env": "OPENAEV_TOKEN", - "file_path": ["openaev", "token"], - }, - # Config information - "injector_id": {"env": "INJECTOR_ID", "file_path": ["injector", "id"]}, - "injector_name": { - "env": "INJECTOR_NAME", - "file_path": ["injector", "name"], - }, - "injector_type": { - "env": "INJECTOR_TYPE", - "file_path": ["injector", "type"], - "default": "openaev_teams", - }, - "injector_contracts": {"data": TeamsContracts.build()}, - }, + self.config = OpenAEVConfigHelper.from_configuration_object( + ConfigLoader().to_daemon_config() ) + intercept_dump_argument(self.config.get_config_obj()) with open("teams/img/icon-teams.png", "rb") as icon_file: icon_bytes = icon_file.read() self.helper = OpenAEVInjectorHelper(self.config, icon_bytes) From fc9cf7a8071f370fafe59ae7039730456d2bff2c Mon Sep 17 00:00:00 2001 From: RomualdLemesle Date: Tue, 27 Jan 2026 17:11:51 +0100 Subject: [PATCH 3/8] [teams] feat(injector): improv code --- teams/teams/openaev_teams.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/teams/teams/openaev_teams.py b/teams/teams/openaev_teams.py index aedb8a35..59f2090c 100644 --- a/teams/teams/openaev_teams.py +++ b/teams/teams/openaev_teams.py @@ -1,10 +1,10 @@ import time from typing import Dict -from pyoaev.helpers import OpenAEVConfigHelper, OpenAEVInjectorHelper - from injector_common.data_helpers import DataHelpers from injector_common.dump_config import intercept_dump_argument +from pyoaev.helpers import OpenAEVConfigHelper, OpenAEVInjectorHelper + from teams.client.teams_client import ExecutionResult, TeamsClient from teams.configuration.config_loader import ConfigLoader from teams.contracts_teams import ( From a3d1d55190173417c2cacbec57e8c924a1df8c39 Mon Sep 17 00:00:00 2001 From: RomualdLemesle Date: Tue, 27 Jan 2026 17:14:20 +0100 Subject: [PATCH 4/8] [teams] feat(injector): improv code --- teams/teams/openaev_teams.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/teams/teams/openaev_teams.py b/teams/teams/openaev_teams.py index 59f2090c..f76bd2cd 100644 --- a/teams/teams/openaev_teams.py +++ b/teams/teams/openaev_teams.py @@ -1,15 +1,14 @@ import time from typing import Dict -from injector_common.data_helpers import DataHelpers -from injector_common.dump_config import intercept_dump_argument from pyoaev.helpers import OpenAEVConfigHelper, OpenAEVInjectorHelper +from injector_common.data_helpers import DataHelpers +from injector_common.dump_config import intercept_dump_argument from teams.client.teams_client import ExecutionResult, TeamsClient from teams.configuration.config_loader import ConfigLoader from teams.contracts_teams import ( CONTRACT_ID, - TeamsContracts, ) from teams.helpers.teams_helper import TeamsPayloadBuilder From 5fc9caa1557252725e8935a9138ac78b784d61d9 Mon Sep 17 00:00:00 2001 From: RomualdLemesle Date: Tue, 27 Jan 2026 17:17:15 +0100 Subject: [PATCH 5/8] [ci] chore(circleci): improv script --- .circleci/config.yml | 156 +++++++++++++++---------------------------- 1 file changed, 52 insertions(+), 104 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1a08d146..463609ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,9 @@ version: 2.1 + orbs: slack: circleci/slack@5.2.3 kubernetes: circleci/kubernetes@1.3.1 + jobs: ensure_formatting: docker: @@ -59,39 +61,26 @@ jobs: name: Install poetry command: pip install poetry==2.1.3 && poetry config installer.re-resolve false - run: - working_directory: ~/openaev/nuclei - name: Run tests for Nuclei injector - command: | - poetry install --extras prod - if [ "${CIRCLE_BRANCH}" = "main" ]; then - poetry run pip install --force-reinstall git+https://github.com/OpenAEV-Platform/client-python.git@main - else - poetry run pip install --force-reinstall git+https://github.com/OpenAEV-Platform/client-python.git@release/current - fi - poetry run python -m unittest - - run: - working_directory: ~/openaev/nmap - name: Run tests for nmap injector - command: | - poetry install --extras prod - if [ "${CIRCLE_BRANCH}" = "main" ]; then - poetry run pip install --force-reinstall git+https://github.com/OpenAEV-Platform/client-python.git@main - else - poetry run pip install --force-reinstall git+https://github.com/OpenAEV-Platform/client-python.git@release/current - fi - poetry run python -m unittest - - - run: - working_directory: ~/openaev/http-query - name: Run tests for HTTP Query injector + name: Run injector tests + working_directory: ~/openaev command: | - poetry install --extras prod + INJECTORS="nuclei nmap http-query" + if [ "${CIRCLE_BRANCH}" = "main" ]; then - poetry run pip install --force-reinstall git+https://github.com/OpenAEV-Platform/client-python.git@main + PYOAEV_REF="main" else - poetry run pip install --force-reinstall git+https://github.com/OpenAEV-Platform/client-python.git@release/current + PYOAEV_REF="release/current" fi - poetry run python -m unittest + + for injector in $INJECTORS; do + echo "Running tests for injector: $injector" + + cd ~/openaev/$injector + + poetry install --extras prod + poetry run pip install --force-reinstall git+https://github.com/OpenAEV-Platform/client-python.git@${PYOAEV_REF} + poetry run python -m unittest + done build_docker_images: working_directory: ~/openaev docker: @@ -121,45 +110,22 @@ jobs: find . -name pyproject.toml | xargs -I ___ sed "s|branch = 'release/current'|branch = '${CIRCLE_BRANCH}'|" -i ___; fi; - run: - working_directory: ~/openaev/aws - name: Build Docker image openaev/injector-aws - command: | - if [ "${CIRCLE_BRANCH}" = "release/current" ]; then - docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-aws:${CIRCLE_SHA1} --build-arg PYOAEV_GIT_BRANCH_OVERRIDE="${CIRCLE_BRANCH}" . - else - docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-aws:${CIRCLE_SHA1} . - fi - docker save -o ~/openaev/images/injector-aws openaev/injector-aws:${CIRCLE_SHA1} - - run: - working_directory: ~/openaev/http-query - name: Build Docker image openaev/injector-http-query - command: | - if [ "${CIRCLE_BRANCH}" = "release/current" ]; then - docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-http-query:${CIRCLE_SHA1} --build-arg PYOAEV_GIT_BRANCH_OVERRIDE="${CIRCLE_BRANCH}" . - else - docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-http-query:${CIRCLE_SHA1} . - fi - docker save -o ~/openaev/images/injector-http-query openaev/injector-http-query:${CIRCLE_SHA1} - - run: - working_directory: ~/openaev/nmap - name: Build Docker image openaev/injector-nmap - command: | - if [ "${CIRCLE_BRANCH}" = "release/current" ]; then - docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-nmap:${CIRCLE_SHA1} --build-arg PYOAEV_GIT_BRANCH_OVERRIDE="${CIRCLE_BRANCH}" . - else - docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-nmap:${CIRCLE_SHA1} . - fi - docker save -o ~/openaev/images/injector-nmap openaev/injector-nmap:${CIRCLE_SHA1} - - run: - working_directory: ~/openaev/nuclei - name: Build Docker image openaev/injector-nuclei + name: Build injector Docker images + working_directory: ~/openaev command: | - if [ "${CIRCLE_BRANCH}" = "release/current" ]; then - docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-nuclei:${CIRCLE_SHA1} --build-arg PYOAEV_GIT_BRANCH_OVERRIDE="${CIRCLE_BRANCH}" . - else - docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-nuclei:${CIRCLE_SHA1} . - fi - docker save -o ~/openaev/images/injector-nuclei openaev/injector-nuclei:${CIRCLE_SHA1} + INJECTORS="aws http-query nmap nuclei teams" + + for injector in $INJECTORS; do + echo "Building injector: $injector" + + if [ "${CIRCLE_BRANCH}" = "release/current" ]; then + docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-$injector:${CIRCLE_SHA1} --build-arg PYOAEV_GIT_BRANCH_OVERRIDE="${CIRCLE_BRANCH}" ./$injector + else + docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-$injector:${CIRCLE_SHA1} ./$injector + fi + + docker save -o ~/openaev/images/injector-$injector openaev/injector-$injector:${CIRCLE_SHA1} + done - persist_to_workspace: root: ~/openaev paths: @@ -167,6 +133,7 @@ jobs: - slack/notify: event: fail template: basic_fail_1 + publish_images: working_directory: ~/openaev docker: @@ -201,53 +168,34 @@ jobs: exit 1 fi echo "Image tag: ${IMAGETAG}" + + INJECTORS="aws http-query nmap nuclei teams" - docker image load < injector-aws - docker tag openaev/injector-aws:${CIRCLE_SHA1} openaev/injector-aws:${IMAGETAG} - docker tag openaev/injector-aws:${CIRCLE_SHA1} openbas/injector-aws:${IMAGETAG} - docker image load < injector-http-query - docker tag openaev/injector-http-query:${CIRCLE_SHA1} openaev/injector-http-query:${IMAGETAG} - docker tag openaev/injector-http-query:${CIRCLE_SHA1} openbas/injector-http-query:${IMAGETAG} - docker image load < injector-nmap - docker tag openaev/injector-nmap:${CIRCLE_SHA1} openaev/injector-nmap:${IMAGETAG} - docker tag openaev/injector-nmap:${CIRCLE_SHA1} openbas/injector-nmap:${IMAGETAG} - docker image load < injector-nuclei - docker tag openaev/injector-nuclei:${CIRCLE_SHA1} openaev/injector-nuclei:${IMAGETAG} - docker tag openaev/injector-nuclei:${CIRCLE_SHA1} openbas/injector-nuclei:${IMAGETAG} + for injector in $INJECTORS; do + docker image load < injector-$injector + docker tag openaev/injector-$injector:${CIRCLE_SHA1} openaev/injector-$injector:${IMAGETAG} + docker tag openaev/injector-$injector:${CIRCLE_SHA1} openbas/injector-$injector:${IMAGETAG} + done echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin - docker push openaev/injector-aws:${IMAGETAG} - docker push openbas/injector-aws:${IMAGETAG} - docker push openaev/injector-http-query:${IMAGETAG} - docker push openbas/injector-http-query:${IMAGETAG} - docker push openaev/injector-nmap:${IMAGETAG} - docker push openbas/injector-nmap:${IMAGETAG} - docker push openaev/injector-nuclei:${IMAGETAG} - docker push openbas/injector-nuclei:${IMAGETAG} + for injector in $INJECTORS; do + docker push openaev/injector-$injector:${IMAGETAG} + docker push openbas/injector-$injector:${IMAGETAG} + done if [ "${IS_LATEST}" == "true" ] then - docker tag openaev/injector-aws:${IMAGETAG} openaev/injector-aws:latest - docker tag openaev/injector-aws:${IMAGETAG} openbas/injector-aws:latest - docker tag openaev/injector-http-query:${IMAGETAG} openaev/injector-http-query:latest - docker tag openaev/injector-http-query:${IMAGETAG} openbas/injector-http-query:latest - docker tag openaev/injector-nmap:${IMAGETAG} openaev/injector-nmap:latest - docker tag openaev/injector-nmap:${IMAGETAG} openbas/injector-nmap:latest - docker tag openaev/injector-nuclei:${IMAGETAG} openaev/injector-nuclei:latest - docker tag openaev/injector-nuclei:${IMAGETAG} openbas/injector-nuclei:latest - - docker push openaev/injector-aws:latest - docker push openbas/injector-aws:latest - docker push openaev/injector-http-query:latest - docker push openbas/injector-http-query:latest - docker push openaev/injector-nmap:latest - docker push openbas/injector-nmap:latest - docker push openaev/injector-nuclei:latest - docker push openbas/injector-nuclei:latest + for injector in $INJECTORS; do + docker tag openaev/injector-$injector:${IMAGETAG} openaev/injector-$injector:latest + docker tag openaev/injector-$injector:${IMAGETAG} openbas/injector-$injector:latest + docker push openaev/injector-$injector:latest + docker push openbas/injector-$injector:latest + done fi - slack/notify: event: fail template: basic_fail_1 + deploy_testing: docker: - image: cimg/base:current-24.04 From 62b92d9b786ce323e8e46597694a19f286cadd4a Mon Sep 17 00:00:00 2001 From: RomualdLemesle Date: Wed, 28 Jan 2026 12:10:51 +0100 Subject: [PATCH 6/8] [ci] chore(circleci): improv script --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 463609ea..cffa024f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,9 +119,9 @@ jobs: echo "Building injector: $injector" if [ "${CIRCLE_BRANCH}" = "release/current" ]; then - docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-$injector:${CIRCLE_SHA1} --build-arg PYOAEV_GIT_BRANCH_OVERRIDE="${CIRCLE_BRANCH}" ./$injector + docker build --progress=plain --build-context injector_common=injector_common -t openaev/injector-$injector:${CIRCLE_SHA1} --build-arg PYOAEV_GIT_BRANCH_OVERRIDE="${CIRCLE_BRANCH}" ./$injector else - docker build --progress=plain --build-context injector_common=../injector_common -t openaev/injector-$injector:${CIRCLE_SHA1} ./$injector + docker build --progress=plain --build-context injector_common=injector_common -t openaev/injector-$injector:${CIRCLE_SHA1} ./$injector fi docker save -o ~/openaev/images/injector-$injector openaev/injector-$injector:${CIRCLE_SHA1} From 3d99e2b1022cdf59b1523a0eaf07fadf8fb75ef2 Mon Sep 17 00:00:00 2001 From: RomualdLemesle Date: Wed, 11 Feb 2026 07:04:16 +0100 Subject: [PATCH 7/8] [teams] feat(injector): add Microsoft Teams injector --- teams/README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/teams/README.md b/teams/README.md index 4d40e1e9..d040192f 100644 --- a/teams/README.md +++ b/teams/README.md @@ -101,19 +101,7 @@ poetry install --extras prod For development, you should first clone the `pyoaev` repository following the instructions provided in the OpenAEV documentation. -First, create a dedicated virtual environment to keep things isolated: - -```shell -py -3.12 -m venv venv -``` - -Activate it depending on your OS: - -```shell -.\venv\Scripts\activate -``` - -Then install development dependencies +Install development dependencies ```shell poetry install --extras dev From 1aa4e1f6bb7efc03cfefebe2a7da991622b3f652 Mon Sep 17 00:00:00 2001 From: RomualdLemesle Date: Wed, 11 Feb 2026 07:10:43 +0100 Subject: [PATCH 8/8] [teams] feat(injector): add Microsoft Teams injector --- teams/poetry.lock | 10 +++++----- teams/pyproject.toml | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/teams/poetry.lock b/teams/poetry.lock index 966060c3..0cdb41b8 100644 --- a/teams/poetry.lock +++ b/teams/poetry.lock @@ -324,7 +324,7 @@ files = [] develop = true [package.dependencies] -pyoaev = "2.0.14" +pyoaev = "2.1.6" [package.extras] dev = ["black (>=25.11.0,<25.12.0)", "wheel (>=0.45.1,<0.46.0)"] @@ -669,14 +669,14 @@ yaml = ["pyyaml (>=6.0.1)"] [[package]] name = "pyoaev" -version = "2.0.14" +version = "2.1.6" description = "Python API client for OpenAEV." optional = false python-versions = ">=3.11" groups = ["main"] files = [ - {file = "pyoaev-2.0.14-py3-none-any.whl", hash = "sha256:8a03dd1b2ce1d30c0d71249a8974ee27b61ebd72613b0a94f4b81a9d170009b0"}, - {file = "pyoaev-2.0.14.tar.gz", hash = "sha256:a93b75db0248f824d625b06fdaa1d66898902076e9b69a84abce5b933a8dcdbd"}, + {file = "pyoaev-2.1.6-py3-none-any.whl", hash = "sha256:8a03dd1b2ce1d30c0d71249a8974ee27b61ebd72613b0a94f4b81a9d170009b0"}, + {file = "pyoaev-2.1.6.tar.gz", hash = "sha256:a93b75db0248f824d625b06fdaa1d66898902076e9b69a84abce5b933a8dcdbd"}, ] [package.dependencies] @@ -703,7 +703,7 @@ doc = ["autoapi (>=2.0.1,<2.1.0)", "sphinx-autodoc-typehints (>=3.2.0,<3.3.0)", [[package]] name = "pyoaev" -version = "2.0.14" +version = "2.1.6" description = "Python API client for OpenAEV." optional = false python-versions = ">=3.11" diff --git a/teams/pyproject.toml b/teams/pyproject.toml index 30fce9ab..a8f4e177 100644 --- a/teams/pyproject.toml +++ b/teams/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "openaev-teams-injector" -version = "2.0.14" +version = "2.1.6" description = "An injector for Microsoft Teams" authors = ["Filigran "] license = "Apache-2.0" @@ -14,7 +14,7 @@ packages = [ python = "^3.11" requests = "2.32.3" pyoaev = [ - { markers = "extra == 'prod' and extra != 'dev'", version = "2.0.14", source = "pypi" }, + { markers = "extra == 'prod' and extra != 'dev'", version = "2.1.6", source = "pypi" }, { markers = "extra == 'dev' and extra != 'prod'", path = "../../client-python", develop = true }, ] injector_common = { path = "../injector_common", develop = true }