You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2026/04/08 16:26:00 UTC
(airflow) branch main updated: (squashed) adds Vespa provider (#63988)
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new bc26d6b135d (squashed) adds Vespa provider (#63988)
bc26d6b135d is described below
commit bc26d6b135d9a2b6b60e6b3208328adab88b03c4
Author: Radu Gheorghe <ra...@vespa.ai>
AuthorDate: Wed Apr 8 19:25:45 2026 +0300
(squashed) adds Vespa provider (#63988)
---
.github/CODEOWNERS | 1 +
.github/ISSUE_TEMPLATE/1-airflow_bug_report.yml | 1 +
.github/boring-cyborg.yml | 3 +
airflow-core/docs/extra-packages-ref.rst | 2 +
dev/breeze/doc/images/output_build-docs.svg | 2 +-
dev/breeze/doc/images/output_build-docs.txt | 2 +-
dev/breeze/doc/images/output_pr_auto-triage.svg | 12 +-
dev/breeze/doc/images/output_pr_auto-triage.txt | 2 +-
...tput_release-management_add-back-references.svg | 2 +-
...tput_release-management_add-back-references.txt | 2 +-
...management_generate-issue-content-providers.svg | 2 +-
...management_generate-issue-content-providers.txt | 2 +-
...ease-management_generate-providers-metadata.svg | 2 +-
...ease-management_generate-providers-metadata.txt | 2 +-
...e-management_prepare-provider-distributions.svg | 2 +-
...e-management_prepare-provider-distributions.txt | 2 +-
...e-management_prepare-provider-documentation.svg | 2 +-
...e-management_prepare-provider-documentation.txt | 2 +-
.../output_release-management_publish-docs.svg | 2 +-
.../output_release-management_publish-docs.txt | 2 +-
...output_sbom_generate-providers-requirements.svg | 2 +-
...output_sbom_generate-providers-requirements.txt | 2 +-
.../images/output_workflow-run_publish-docs.svg | 2 +-
.../images/output_workflow-run_publish-docs.txt | 2 +-
docs/spelling_wordlist.txt | 3 +
providers/sftp/docs/index.rst | 2 +-
providers/sftp/pyproject.toml | 2 +-
providers/ssh/docs/index.rst | 2 +-
providers/ssh/pyproject.toml | 2 +-
providers/vespa/.gitignore | 1 +
providers/vespa/LICENSE | 201 ++++++
providers/vespa/NOTICE | 5 +
providers/vespa/README.rst | 81 +++
providers/vespa/docs/changelog.rst | 31 +
providers/vespa/docs/commits.rst | 34 +
providers/vespa/docs/conf.py | 27 +
providers/vespa/docs/connections.rst | 71 ++
providers/{ssh => vespa}/docs/index.rst | 42 +-
.../docs/installing-providers-from-sources.rst | 18 +
providers/vespa/docs/operators/vespa.rst | 46 ++
providers/vespa/docs/security.rst | 18 +
providers/vespa/provider.yaml | 156 +++++
providers/{ssh => vespa}/pyproject.toml | 21 +-
providers/vespa/src/airflow/__init__.py | 17 +
providers/vespa/src/airflow/providers/__init__.py | 17 +
.../vespa/src/airflow/providers/vespa/__init__.py | 39 ++
.../airflow/providers/vespa/get_provider_info.py | 110 ++++
.../src/airflow/providers/vespa/hooks/__init__.py | 16 +
.../src/airflow/providers/vespa/hooks/vespa.py | 356 ++++++++++
.../airflow/providers/vespa/operators/__init__.py | 16 +
.../providers/vespa/operators/vespa_ingest.py | 101 +++
.../airflow/providers/vespa/triggers/__init__.py | 16 +
.../providers/vespa/triggers/vespa_feed_trigger.py | 104 +++
providers/vespa/tests/conftest.py | 19 +
providers/vespa/tests/system/__init__.py | 17 +
providers/vespa/tests/system/vespa/__init__.py | 16 +
.../vespa/tests/system/vespa/example_dag_vespa.py | 112 ++++
providers/vespa/tests/unit/__init__.py | 17 +
providers/vespa/tests/unit/vespa/__init__.py | 16 +
providers/vespa/tests/unit/vespa/hooks/__init__.py | 16 +
.../vespa/tests/unit/vespa/hooks/test_vespa.py | 727 +++++++++++++++++++++
.../vespa/tests/unit/vespa/operators/__init__.py | 16 +
.../unit/vespa/operators/test_vespa_ingest.py | 243 +++++++
.../vespa/tests/unit/vespa/triggers/__init__.py | 16 +
.../unit/vespa/triggers/test_vespa_feed_trigger.py | 243 +++++++
pyproject.toml | 10 +
scripts/ci/docker-compose/remove-sources.yml | 1 +
scripts/ci/docker-compose/tests-sources.yml | 1 +
uv.lock | 536 +++++++++------
69 files changed, 3346 insertions(+), 272 deletions(-)
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index e23ac33fa87..2da9dbc9c27 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -105,6 +105,7 @@ airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/ @Lee-W @jason810496 @guan
/providers/openlineage/ @mobuchowski
/providers/smtp/ @hussein-awala
/providers/snowflake/ @potiuk
+/providers/vespa/ @potiuk # + @radu-gheorghe @thomasht86
# Generated metadata
diff --git a/.github/ISSUE_TEMPLATE/1-airflow_bug_report.yml b/.github/ISSUE_TEMPLATE/1-airflow_bug_report.yml
index 61fb8b44151..33254b162eb 100644
--- a/.github/ISSUE_TEMPLATE/1-airflow_bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/1-airflow_bug_report.yml
@@ -206,6 +206,7 @@ body:
- teradata
- trino
- vertica
+ - vespa
- weaviate
- yandex
- ydb
diff --git a/.github/boring-cyborg.yml b/.github/boring-cyborg.yml
index 1df8a75b82b..97bb6f0cc9c 100644
--- a/.github/boring-cyborg.yml
+++ b/.github/boring-cyborg.yml
@@ -303,6 +303,9 @@ labelPRBasedOnFilePath:
provider:vertica:
- providers/vertica/**
+ provider:vespa:
+ - providers/vespa/**
+
provider:weaviate:
- providers/weaviate/**
diff --git a/airflow-core/docs/extra-packages-ref.rst b/airflow-core/docs/extra-packages-ref.rst
index a3acd03df1e..19875b959c5 100644
--- a/airflow-core/docs/extra-packages-ref.rst
+++ b/airflow-core/docs/extra-packages-ref.rst
@@ -299,6 +299,8 @@ These are extras that add dependencies needed for integration with external serv
+---------------------+-----------------------------------------------------+-----------------------------------------------------+
| vertica | ``pip install 'apache-airflow[vertica]'`` | Vertica hook support as an Airflow backend |
+---------------------+-----------------------------------------------------+-----------------------------------------------------+
+| vespa | ``pip install 'apache-airflow[vespa]'`` | Vespa hooks and operators |
++---------------------+-----------------------------------------------------+-----------------------------------------------------+
| weaviate | ``pip install 'apache-airflow[weaviate]'`` | Weaviate hook and operators |
+---------------------+-----------------------------------------------------+-----------------------------------------------------+
| yandex | ``pip install 'apache-airflow[yandex]'`` | Yandex.cloud hooks and operators |
diff --git a/dev/breeze/doc/images/output_build-docs.svg b/dev/breeze/doc/images/output_build-docs.svg
index ebdd2875195..4dae99c17ce 100644
--- a/dev/breeze/doc/images/output_build-docs.svg
+++ b/dev/breeze/doc/images/output_build-docs.svg
@@ -241,7 +241,7 @@
</text><text class="breeze-build-docs-r4" x="12.2" y="264" textLength="109.8" clip-path="url(#breeze-build-docs-line-10)">microsoft</text><text class="breeze-build-docs-r1" x="122" y="264" textLength="12.2" clip-path="url(#breeze-build-docs-line-10)">.</text><text class="breeze-build-docs-r4" x="134.2" y="264" textLength="61" clip-path="url(#breeze-build-docs-line-10)">mssql</text><text class="breeze-build-docs-r1" x="195.2" y="264" textLength="36.6" clip-path="url(#breeze-build-docs-lin [...]
</text><text class="breeze-build-docs-r4" x="12.2" y="288.4" textLength="122" clip-path="url(#breeze-build-docs-line-11)">opensearch</text><text class="breeze-build-docs-r1" x="134.2" y="288.4" textLength="36.6" clip-path="url(#breeze-build-docs-line-11)"> | </text><text class="breeze-build-docs-r4" x="170.8" y="288.4" textLength="97.6" clip-path="url(#breeze-build-docs-line-11)">opsgenie</text><text class="breeze-build-docs-r1" x="268.4" y="288.4" textLength="36.6" clip-path=" [...]
</text><text class="breeze-build-docs-r4" x="12.2" y="312.8" textLength="122" clip-path="url(#breeze-build-docs-line-12)">salesforce</text><text class="breeze-build-docs-r1" x="134.2" y="312.8" textLength="36.6" clip-path="url(#breeze-build-docs-line-12)"> | </text><text class="breeze-build-docs-r4" x="170.8" y="312.8" textLength="61" clip-path="url(#breeze-build-docs-line-12)">samba</text><text class="breeze-build-docs-r1" x="231.8" y="312.8" textLength="36.6" clip-path="url(# [...]
-</text><text class="breeze-build-docs-r4" x="12.2" y="337.2" textLength="85.4" clip-path="url(#breeze-build-docs-line-13)">tableau</text><text class="breeze-build-docs-r1" x="97.6" y="337.2" textLength="36.6" clip-path="url(#breeze-build-docs-line-13)"> | </text><text class="breeze-build-docs-r4" x="134.2" y="337.2" textLength="48.8" clip-path="url(#breeze-build-docs-line-13)">task</text><text class="breeze-build-docs-r1" x="183" y="337.2" textLength="12.2" clip-path="url(#bree [...]
+</text><text class="breeze-build-docs-r4" x="12.2" y="337.2" textLength="85.4" clip-path="url(#breeze-build-docs-line-13)">tableau</text><text class="breeze-build-docs-r1" x="97.6" y="337.2" textLength="36.6" clip-path="url(#breeze-build-docs-line-13)"> | </text><text class="breeze-build-docs-r4" x="134.2" y="337.2" textLength="48.8" clip-path="url(#breeze-build-docs-line-13)">task</text><text class="breeze-build-docs-r1" x="183" y="337.2" textLength="12.2" clip-path="url(#bree [...]
</text><text class="breeze-build-docs-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#breeze-build-docs-line-14)">
</text><text class="breeze-build-docs-r1" x="12.2" y="386" textLength="195.2" clip-path="url(#breeze-build-docs-line-15)">Build documents.</text><text class="breeze-build-docs-r1" x="1464" y="386" textLength="12.2" clip-path="url(#breeze-build-docs-line-15)">
</text><text class="breeze-build-docs-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#breeze-build-docs-line-16)">
diff --git a/dev/breeze/doc/images/output_build-docs.txt b/dev/breeze/doc/images/output_build-docs.txt
index ea24111dadf..31a7c75e0ac 100644
--- a/dev/breeze/doc/images/output_build-docs.txt
+++ b/dev/breeze/doc/images/output_build-docs.txt
@@ -1 +1 @@
-5023b820002e3f33104ae46d617645c4
+5a672545cd009db3d79db5c526c3bbd6
diff --git a/dev/breeze/doc/images/output_pr_auto-triage.svg b/dev/breeze/doc/images/output_pr_auto-triage.svg
index 23937ddcfc8..acb22d594e0 100644
--- a/dev/breeze/doc/images/output_pr_auto-triage.svg
+++ b/dev/breeze/doc/images/output_pr_auto-triage.svg
@@ -400,12 +400,12 @@
</text><text class="breeze-pr-auto-triage-r5" x="0" y="1288.8" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-52)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1288.8" textLength="1110.2" clip-path="url(#breeze-pr-auto-triage-line-52)">| provider:sendgrid | provider:sftp | provider:singularity | provider:slack | provider:smtp</text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1288.8" textLength="12.2" clip- [...]
</text><text class="breeze-pr-auto-triage-r5" x="0" y="1313.2" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-53)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1313.2" textLength="927.2" clip-path="url(#breeze-pr-auto-triage-line-53)">| provider:snowflake | provider:sqlite | provider:ssh | provider:standard | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1313.2" textLength="12.2" clip-path="url(#breez [...]
</text><text class="breeze-pr-auto-triage-r5" x="0" y="1337.6" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-54)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1337.6" textLength="927.2" clip-path="url(#breeze-pr-auto-triage-line-54)">provider:tableau | provider:telegram | provider:teradata | provider:trino | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1337.6" textLength="12.2" clip-path="url(#breeze-pr- [...]
-</text><text class="breeze-pr-auto-triage-r5" x="0" y="1362" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-55)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1362" textLength="1110.2" clip-path="url(#breeze-pr-auto-triage-line-55)">provider:vertica | provider:weaviate | provider:yandex | provider:ydb | provider:zendesk | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1362" textLength="12.2" clip-p [...]
-</text><text class="breeze-pr-auto-triage-r5" x="0" y="1386.4" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-56)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1386.4" textLength="1098" clip-path="url(#breeze-pr-auto-triage-line-56)">translation:ar | translation:ca | translation:de | translation:default | translation:el | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1386.4" textLength="12.2" cli [...]
-</text><text class="breeze-pr-auto-triage-r5" x="0" y="1410.8" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-57)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1410.8" textLength="1037" clip-path="url(#breeze-pr-auto-triage-line-57)">translation:es | translation:fr | translation:he | translation:hi | translation:hu | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1410.8" textLength="12.2" clip-pat [...]
-</text><text class="breeze-pr-auto-triage-r5" x="0" y="1435.2" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-58)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1435.2" textLength="1037" clip-path="url(#breeze-pr-auto-triage-line-58)">translation:it | translation:ja | translation:ko | translation:nl | translation:pl | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1435.2" textLength="12.2" clip-pat [...]
-</text><text class="breeze-pr-auto-triage-r5" x="0" y="1459.6" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-59)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1459.6" textLength="1073.6" clip-path="url(#breeze-pr-auto-triage-line-59)">translation:pt | translation:ru | translation:th | translation:tr | translation:zh-CN | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1459.6" textLength="12.2" cli [...]
-</text><text class="breeze-pr-auto-triage-r5" x="0" y="1484" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-60)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1484" textLength="219.6" clip-path="url(#breeze-pr-auto-triage-line-60)">translation:zh-TW)</text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1484" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-60)">│</text><text class="breeze-pr-auto-triage-r1" x="1464" y="1484" textLength="12.2" [...]
+</text><text class="breeze-pr-auto-triage-r5" x="0" y="1362" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-55)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1362" textLength="1085.8" clip-path="url(#breeze-pr-auto-triage-line-55)">provider:vertica | provider:vespa | provider:weaviate | provider:yandex | provider:ydb | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1362" textLength="12.2" clip-pat [...]
+</text><text class="breeze-pr-auto-triage-r5" x="0" y="1386.4" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-56)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1386.4" textLength="1110.2" clip-path="url(#breeze-pr-auto-triage-line-56)">provider:zendesk | translation:ar | translation:ca | translation:de | translation:default |</text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1386.4" textLength="12.2" clip- [...]
+</text><text class="breeze-pr-auto-triage-r5" x="0" y="1410.8" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-57)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1410.8" textLength="1037" clip-path="url(#breeze-pr-auto-triage-line-57)">translation:el | translation:es | translation:fr | translation:he | translation:hi | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1410.8" textLength="12.2" clip-pat [...]
+</text><text class="breeze-pr-auto-triage-r5" x="0" y="1435.2" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-58)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1435.2" textLength="1037" clip-path="url(#breeze-pr-auto-triage-line-58)">translation:hu | translation:it | translation:ja | translation:ko | translation:nl | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1435.2" textLength="12.2" clip-pat [...]
+</text><text class="breeze-pr-auto-triage-r5" x="0" y="1459.6" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-59)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1459.6" textLength="1037" clip-path="url(#breeze-pr-auto-triage-line-59)">translation:pl | translation:pt | translation:ru | translation:th | translation:tr | </text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1459.6" textLength="12.2" clip-pat [...]
+</text><text class="breeze-pr-auto-triage-r5" x="0" y="1484" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-60)">│</text><text class="breeze-pr-auto-triage-r6" x="329.4" y="1484" textLength="463.6" clip-path="url(#breeze-pr-auto-triage-line-60)">translation:zh-CN | translation:zh-TW)</text><text class="breeze-pr-auto-triage-r5" x="1451.8" y="1484" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-60)">│</text><text class="breeze-pr-auto-triage-r1" x="14 [...]
</text><text class="breeze-pr-auto-triage-r5" x="0" y="1508.4" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-61)">│</text><text class="breeze-pr-auto-triage-r4" x="24.4" y="1508.4" textLength="280.6" clip-path="url(#breeze-pr-auto-triage-line-61)">--exclude-label        </text><text class="breeze-pr-auto-triage-r1" x="329.4" y="1508.4" textLength="805.2" clip-path="url(#breeze-pr-auto-triage-line-61)">Exclude PRs with  [...]
</text><text class="breeze-pr-auto-triage-r5" x="0" y="1532.8" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-62)">│</text><text class="breeze-pr-auto-triage-r4" x="24.4" y="1532.8" textLength="280.6" clip-path="url(#breeze-pr-auto-triage-line-62)">--created-after        </text><text class="breeze-pr-auto-triage-r1" x="329.4" y="1532.8" textLength="646.6" clip-path="url(#breeze-pr-auto-triage-line-62)">Only PRs created  [...]
</text><text class="breeze-pr-auto-triage-r5" x="0" y="1557.2" textLength="12.2" clip-path="url(#breeze-pr-auto-triage-line-63)">│</text><text class="breeze-pr-auto-triage-r4" x="24.4" y="1557.2" textLength="280.6" clip-path="url(#breeze-pr-auto-triage-line-63)">--created-before       </text><text class="breeze-pr-auto-triage-r1" x="329.4" y="1557.2" textLength="658.8" clip-path="url(#breeze-pr-auto-triage-line-63)">Only PRs created on [...]
diff --git a/dev/breeze/doc/images/output_pr_auto-triage.txt b/dev/breeze/doc/images/output_pr_auto-triage.txt
index 2fb0e6f6277..d9b1de5b3a0 100644
--- a/dev/breeze/doc/images/output_pr_auto-triage.txt
+++ b/dev/breeze/doc/images/output_pr_auto-triage.txt
@@ -1 +1 @@
-15636cdbbdf6fde36f2ae6e5c43da51e
+aea67302554bf746d0490edd816b5568
diff --git a/dev/breeze/doc/images/output_release-management_add-back-references.svg b/dev/breeze/doc/images/output_release-management_add-back-references.svg
index 297996f63b0..89fe5aebeb4 100644
--- a/dev/breeze/doc/images/output_release-management_add-back-references.svg
+++ b/dev/breeze/doc/images/output_release-management_add-back-references.svg
@@ -156,7 +156,7 @@
</text><text class="breeze-release-management-add-back-references-r4" x="12.2" y="264" textLength="109.8" clip-path="url(#breeze-release-management-add-back-references-line-10)">microsoft</text><text class="breeze-release-management-add-back-references-r1" x="122" y="264" textLength="12.2" clip-path="url(#breeze-release-management-add-back-references-line-10)">.</text><text class="breeze-release-management-add-back-references-r4" x="134.2" y="264" textLength="61" clip-path="url(#breeze-r [...]
</text><text class="breeze-release-management-add-back-references-r4" x="12.2" y="288.4" textLength="122" clip-path="url(#breeze-release-management-add-back-references-line-11)">opensearch</text><text class="breeze-release-management-add-back-references-r1" x="134.2" y="288.4" textLength="36.6" clip-path="url(#breeze-release-management-add-back-references-line-11)"> | </text><text class="breeze-release-management-add-back-references-r4" x="170.8" y="288.4" textLength="97.6" cli [...]
</text><text class="breeze-release-management-add-back-references-r4" x="12.2" y="312.8" textLength="122" clip-path="url(#breeze-release-management-add-back-references-line-12)">salesforce</text><text class="breeze-release-management-add-back-references-r1" x="134.2" y="312.8" textLength="36.6" clip-path="url(#breeze-release-management-add-back-references-line-12)"> | </text><text class="breeze-release-management-add-back-references-r4" x="170.8" y="312.8" textLength="61" clip- [...]
-</text><text class="breeze-release-management-add-back-references-r4" x="12.2" y="337.2" textLength="85.4" clip-path="url(#breeze-release-management-add-back-references-line-13)">tableau</text><text class="breeze-release-management-add-back-references-r1" x="97.6" y="337.2" textLength="36.6" clip-path="url(#breeze-release-management-add-back-references-line-13)"> | </text><text class="breeze-release-management-add-back-references-r4" x="134.2" y="337.2" textLength="48.8" clip-p [...]
+</text><text class="breeze-release-management-add-back-references-r4" x="12.2" y="337.2" textLength="85.4" clip-path="url(#breeze-release-management-add-back-references-line-13)">tableau</text><text class="breeze-release-management-add-back-references-r1" x="97.6" y="337.2" textLength="36.6" clip-path="url(#breeze-release-management-add-back-references-line-13)"> | </text><text class="breeze-release-management-add-back-references-r4" x="134.2" y="337.2" textLength="48.8" clip-p [...]
</text><text class="breeze-release-management-add-back-references-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#breeze-release-management-add-back-references-line-14)">
</text><text class="breeze-release-management-add-back-references-r1" x="12.2" y="386" textLength="976" clip-path="url(#breeze-release-management-add-back-references-line-15)">Command to add back references for documentation to make it backward compatible.</text><text class="breeze-release-management-add-back-references-r1" x="1464" y="386" textLength="12.2" clip-path="url(#breeze-release-management-add-back-references-line-15)">
</text><text class="breeze-release-management-add-back-references-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#breeze-release-management-add-back-references-line-16)">
diff --git a/dev/breeze/doc/images/output_release-management_add-back-references.txt b/dev/breeze/doc/images/output_release-management_add-back-references.txt
index d8b21fbf0ba..c42c85324f0 100644
--- a/dev/breeze/doc/images/output_release-management_add-back-references.txt
+++ b/dev/breeze/doc/images/output_release-management_add-back-references.txt
@@ -1 +1 @@
-c4f3137fd042c7fe7f6cf21479523c5d
+4212e26e30b312eabb2bec58b55e9019
diff --git a/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.svg b/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.svg
index 66c37e87ed9..377de72e454 100644
--- a/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.svg
+++ b/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.svg
@@ -147,7 +147,7 @@
</text><text class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" y="239.6" textLength="109.8" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-9)">microsoft</text><text class="breeze-release-management-generate-issue-content-providers-r1" x="122" y="239.6" textLength="12.2" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-9)">.</text><text class="breeze-release-management-generate-issue-content-provi [...]
</text><text class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" y="264" textLength="97.6" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-10)">opsgenie</text><text class="breeze-release-management-generate-issue-content-providers-r1" x="109.8" y="264" textLength="36.6" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-10)"> | </text><text class="breeze-release-management-generate-issue-con [...]
</text><text class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" y="288.4" textLength="61" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-11)">samba</text><text class="breeze-release-management-generate-issue-content-providers-r1" x="73.2" y="288.4" textLength="36.6" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-11)"> | </text><text class="breeze-release-management-generate-issue-conte [...]
-</text><text class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" y="312.8" textLength="97.6" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-12)">telegram</text><text class="breeze-release-management-generate-issue-content-providers-r1" x="109.8" y="312.8" textLength="36.6" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-12)"> | </text><text class="breeze-release-management-generate-issue [...]
+</text><text class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" y="312.8" textLength="97.6" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-12)">telegram</text><text class="breeze-release-management-generate-issue-content-providers-r1" x="109.8" y="312.8" textLength="36.6" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-12)"> | </text><text class="breeze-release-management-generate-issue [...]
</text><text class="breeze-release-management-generate-issue-content-providers-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-13)">
</text><text class="breeze-release-management-generate-issue-content-providers-r1" x="12.2" y="361.6" textLength="585.6" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-14)">Generates content for issue to test the release.</text><text class="breeze-release-management-generate-issue-content-providers-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-14)">
</text><text class="breeze-release-management-generate-issue-content-providers-r1" x="1464" y="386" textLength="12.2" clip-path="url(#breeze-release-management-generate-issue-content-providers-line-15)">
diff --git a/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.txt b/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.txt
index bb8e773660e..3d046af2db4 100644
--- a/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.txt
+++ b/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.txt
@@ -1 +1 @@
-c8b768e8bdd550b61a4ed50f08a5166d
+8842218524208746ad1cffa81b926604
diff --git a/dev/breeze/doc/images/output_release-management_generate-providers-metadata.svg b/dev/breeze/doc/images/output_release-management_generate-providers-metadata.svg
index f3a910fd302..3700192f515 100644
--- a/dev/breeze/doc/images/output_release-management_generate-providers-metadata.svg
+++ b/dev/breeze/doc/images/output_release-management_generate-providers-metadata.svg
@@ -177,7 +177,7 @@
</text><text class="breeze-release-management-generate-providers-metadata-r5" x="0" y="532.4" textLength="12.2" clip-path="url(#breeze-release-management-generate-providers-metadata-line-21)">│</text><text class="breeze-release-management-generate-providers-metadata-r6" x="268.4" y="532.4" textLength="1171.2" clip-path="url(#breeze-release-management-generate-providers-metadata-line-21)">| neo4j | odbc | openai | openfaas | openlineage |& [...]
</text><text class="breeze-release-management-generate-providers-metadata-r5" x="0" y="556.8" textLength="12.2" clip-path="url(#breeze-release-management-generate-providers-metadata-line-22)">│</text><text class="breeze-release-management-generate-providers-metadata-r6" x="268.4" y="556.8" textLength="1122.4" clip-path="url(#breeze-release-management-generate-providers-metadata-line-22)">papermill | pgvector | pinecone | postgres | presto | [...]
</text><text class="breeze-release-management-generate-providers-metadata-r5" x="0" y="581.2" textLength="12.2" clip-path="url(#breeze-release-management-generate-providers-metadata-line-23)">│</text><text class="breeze-release-management-generate-providers-metadata-r6" x="268.4" y="581.2" textLength="1159" clip-path="url(#breeze-release-management-generate-providers-metadata-line-23)">segment | sendgrid | sftp | singularity | slack | smt [...]
-</text><text class="breeze-release-management-generate-providers-metadata-r5" x="0" y="605.6" textLength="12.2" clip-path="url(#breeze-release-management-generate-providers-metadata-line-24)">│</text><text class="breeze-release-management-generate-providers-metadata-r6" x="268.4" y="605.6" textLength="1024.8" clip-path="url(#breeze-release-management-generate-providers-metadata-line-24)">tableau | telegram | teradata | trino | vertica | w [...]
+</text><text class="breeze-release-management-generate-providers-metadata-r5" x="0" y="605.6" textLength="12.2" clip-path="url(#breeze-release-management-generate-providers-metadata-line-24)">│</text><text class="breeze-release-management-generate-providers-metadata-r6" x="268.4" y="605.6" textLength="1122.4" clip-path="url(#breeze-release-management-generate-providers-metadata-line-24)">tableau | telegram | teradata | trino | vertica | v [...]
</text><text class="breeze-release-management-generate-providers-metadata-r5" x="0" y="630" textLength="12.2" clip-path="url(#breeze-release-management-generate-providers-metadata-line-25)">│</text><text class="breeze-release-management-generate-providers-metadata-r4" x="24.4" y="630" textLength="219.6" clip-path="url(#breeze-release-management-generate-providers-metadata-line-25)">--provider-version</text><text class="breeze-release-management-generate-providers-metadata-r1" x="268.4" y [...]
</text><text class="breeze-release-management-generate-providers-metadata-r5" x="0" y="654.4" textLength="12.2" clip-path="url(#breeze-release-management-generate-providers-metadata-line-26)">│</text><text class="breeze-release-management-generate-providers-metadata-r1" x="268.4" y="654.4" textLength="805.2" clip-path="url(#breeze-release-management-generate-providers-metadata-line-26)">running metadata generation to only this version of the p [...]
</text><text class="breeze-release-management-generate-providers-metadata-r5" x="0" y="678.8" textLength="1464" clip-path="url(#breeze-release-management-generate-providers-metadata-line-27)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-release-management-generate-providers-metadata-r1" x="1464" y="678.8" textLength="12.2" clip-path="url(#breeze-release-management-generate-providers-meta [...]
diff --git a/dev/breeze/doc/images/output_release-management_generate-providers-metadata.txt b/dev/breeze/doc/images/output_release-management_generate-providers-metadata.txt
index 111ec617ead..e1d6f576303 100644
--- a/dev/breeze/doc/images/output_release-management_generate-providers-metadata.txt
+++ b/dev/breeze/doc/images/output_release-management_generate-providers-metadata.txt
@@ -1 +1 @@
-86362717620d15b504b32ed3a904881e
+5e7b2d2037b6a8588b7c0b0bf0a70b3a
diff --git a/dev/breeze/doc/images/output_release-management_prepare-provider-distributions.svg b/dev/breeze/doc/images/output_release-management_prepare-provider-distributions.svg
index f4ce6bc52a0..09753afc56a 100644
--- a/dev/breeze/doc/images/output_release-management_prepare-provider-distributions.svg
+++ b/dev/breeze/doc/images/output_release-management_prepare-provider-distributions.svg
@@ -189,7 +189,7 @@
</text><text class="breeze-release-management-prepare-provider-distributions-r4" x="12.2" y="239.6" textLength="109.8" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-9)">microsoft</text><text class="breeze-release-management-prepare-provider-distributions-r1" x="122" y="239.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-9)">.</text><text class="breeze-release-management-prepare-provider-distributions-r4" x [...]
</text><text class="breeze-release-management-prepare-provider-distributions-r4" x="12.2" y="264" textLength="97.6" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-10)">opsgenie</text><text class="breeze-release-management-prepare-provider-distributions-r1" x="109.8" y="264" textLength="36.6" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-10)"> | </text><text class="breeze-release-management-prepare-provider-distribut [...]
</text><text class="breeze-release-management-prepare-provider-distributions-r4" x="12.2" y="288.4" textLength="61" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-11)">samba</text><text class="breeze-release-management-prepare-provider-distributions-r1" x="73.2" y="288.4" textLength="36.6" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-11)"> | </text><text class="breeze-release-management-prepare-provider-distributio [...]
-</text><text class="breeze-release-management-prepare-provider-distributions-r4" x="12.2" y="312.8" textLength="97.6" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-12)">telegram</text><text class="breeze-release-management-prepare-provider-distributions-r1" x="109.8" y="312.8" textLength="36.6" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-12)"> | </text><text class="breeze-release-management-prepare-provider-distr [...]
+</text><text class="breeze-release-management-prepare-provider-distributions-r4" x="12.2" y="312.8" textLength="97.6" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-12)">telegram</text><text class="breeze-release-management-prepare-provider-distributions-r1" x="109.8" y="312.8" textLength="36.6" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-12)"> | </text><text class="breeze-release-management-prepare-provider-distr [...]
</text><text class="breeze-release-management-prepare-provider-distributions-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-13)">
</text><text class="breeze-release-management-prepare-provider-distributions-r1" x="12.2" y="361.6" textLength="646.6" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-14)">Prepare sdist/whl distributions of Airflow Providers.</text><text class="breeze-release-management-prepare-provider-distributions-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-14)">
</text><text class="breeze-release-management-prepare-provider-distributions-r1" x="1464" y="386" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-distributions-line-15)">
diff --git a/dev/breeze/doc/images/output_release-management_prepare-provider-distributions.txt b/dev/breeze/doc/images/output_release-management_prepare-provider-distributions.txt
index 72ce17860c8..32193df2e79 100644
--- a/dev/breeze/doc/images/output_release-management_prepare-provider-distributions.txt
+++ b/dev/breeze/doc/images/output_release-management_prepare-provider-distributions.txt
@@ -1 +1 @@
-9d80a1e75041f4ea3a67cb3e05c11720
+e521f889a0553cda7737d292aceae195
diff --git a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg
index 4c669df9e6d..848ffe1194e 100644
--- a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg
+++ b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg
@@ -216,7 +216,7 @@
</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" y="239.6" textLength="109.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-9)">microsoft</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="122" y="239.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-9)">.</text><text class="breeze-release-management-prepare-provider-documentation-r4" x [...]
</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" y="264" textLength="97.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-10)">opsgenie</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="109.8" y="264" textLength="36.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-10)"> | </text><text class="breeze-release-management-prepare-provider-documenta [...]
</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" y="288.4" textLength="61" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-11)">samba</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="73.2" y="288.4" textLength="36.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-11)"> | </text><text class="breeze-release-management-prepare-provider-documentati [...]
-</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" y="312.8" textLength="97.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-12)">telegram</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="109.8" y="312.8" textLength="36.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-12)"> | </text><text class="breeze-release-management-prepare-provider-docum [...]
+</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" y="312.8" textLength="97.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-12)">telegram</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="109.8" y="312.8" textLength="36.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-12)"> | </text><text class="breeze-release-management-prepare-provider-docum [...]
</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-13)">
</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="12.2" y="361.6" textLength="780.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-14)">Prepare CHANGELOG, README and COMMITS information for providers.</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation- [...]
</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="386" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-15)">
diff --git a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt
index eb24b754104..ecfbecc0b92 100644
--- a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt
+++ b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt
@@ -1 +1 @@
-2f1aac6a4d4f8825e7b93d4e3add2af2
+2e38d3826e90845ac9776d70023cc638
diff --git a/dev/breeze/doc/images/output_release-management_publish-docs.svg b/dev/breeze/doc/images/output_release-management_publish-docs.svg
index f3f41e88906..7bb8567554d 100644
--- a/dev/breeze/doc/images/output_release-management_publish-docs.svg
+++ b/dev/breeze/doc/images/output_release-management_publish-docs.svg
@@ -195,7 +195,7 @@
</text><text class="breeze-release-management-publish-docs-r4" x="12.2" y="264" textLength="109.8" clip-path="url(#breeze-release-management-publish-docs-line-10)">microsoft</text><text class="breeze-release-management-publish-docs-r1" x="122" y="264" textLength="12.2" clip-path="url(#breeze-release-management-publish-docs-line-10)">.</text><text class="breeze-release-management-publish-docs-r4" x="134.2" y="264" textLength="61" clip-path="url(#breeze-release-management-publish-docs-line [...]
</text><text class="breeze-release-management-publish-docs-r4" x="12.2" y="288.4" textLength="122" clip-path="url(#breeze-release-management-publish-docs-line-11)">opensearch</text><text class="breeze-release-management-publish-docs-r1" x="134.2" y="288.4" textLength="36.6" clip-path="url(#breeze-release-management-publish-docs-line-11)"> | </text><text class="breeze-release-management-publish-docs-r4" x="170.8" y="288.4" textLength="97.6" clip-path="url(#breeze-release-managem [...]
</text><text class="breeze-release-management-publish-docs-r4" x="12.2" y="312.8" textLength="122" clip-path="url(#breeze-release-management-publish-docs-line-12)">salesforce</text><text class="breeze-release-management-publish-docs-r1" x="134.2" y="312.8" textLength="36.6" clip-path="url(#breeze-release-management-publish-docs-line-12)"> | </text><text class="breeze-release-management-publish-docs-r4" x="170.8" y="312.8" textLength="61" clip-path="url(#breeze-release-managemen [...]
-</text><text class="breeze-release-management-publish-docs-r4" x="12.2" y="337.2" textLength="85.4" clip-path="url(#breeze-release-management-publish-docs-line-13)">tableau</text><text class="breeze-release-management-publish-docs-r1" x="97.6" y="337.2" textLength="36.6" clip-path="url(#breeze-release-management-publish-docs-line-13)"> | </text><text class="breeze-release-management-publish-docs-r4" x="134.2" y="337.2" textLength="48.8" clip-path="url(#breeze-release-management [...]
+</text><text class="breeze-release-management-publish-docs-r4" x="12.2" y="337.2" textLength="85.4" clip-path="url(#breeze-release-management-publish-docs-line-13)">tableau</text><text class="breeze-release-management-publish-docs-r1" x="97.6" y="337.2" textLength="36.6" clip-path="url(#breeze-release-management-publish-docs-line-13)"> | </text><text class="breeze-release-management-publish-docs-r4" x="134.2" y="337.2" textLength="48.8" clip-path="url(#breeze-release-management [...]
</text><text class="breeze-release-management-publish-docs-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#breeze-release-management-publish-docs-line-14)">
</text><text class="breeze-release-management-publish-docs-r1" x="12.2" y="386" textLength="707.6" clip-path="url(#breeze-release-management-publish-docs-line-15)">Command to publish generated documentation to airflow-site</text><text class="breeze-release-management-publish-docs-r1" x="1464" y="386" textLength="12.2" clip-path="url(#breeze-release-management-publish-docs-line-15)">
</text><text class="breeze-release-management-publish-docs-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#breeze-release-management-publish-docs-line-16)">
diff --git a/dev/breeze/doc/images/output_release-management_publish-docs.txt b/dev/breeze/doc/images/output_release-management_publish-docs.txt
index 12e06cfa472..c5e04a1b1ec 100644
--- a/dev/breeze/doc/images/output_release-management_publish-docs.txt
+++ b/dev/breeze/doc/images/output_release-management_publish-docs.txt
@@ -1 +1 @@
-7967a912265c05eb878af2ba3ddf2057
+2a2b639f232bc385cade07f37da8645b
diff --git a/dev/breeze/doc/images/output_sbom_generate-providers-requirements.svg b/dev/breeze/doc/images/output_sbom_generate-providers-requirements.svg
index aef687a4db0..dd0ea273eca 100644
--- a/dev/breeze/doc/images/output_sbom_generate-providers-requirements.svg
+++ b/dev/breeze/doc/images/output_sbom_generate-providers-requirements.svg
@@ -191,7 +191,7 @@
</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" y="434.8" textLength="12.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-17)">│</text><text class="breeze-sbom-generate-providers-requirements-r6" x="268.4" y="434.8" textLength="1134.6" clip-path="url(#breeze-sbom-generate-providers-requirements-line-17)">openfaas | openlineage | opensearch | opsgenie | oracle | pagerduty | papermill&# [...]
</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" y="459.2" textLength="12.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-18)">│</text><text class="breeze-sbom-generate-providers-requirements-r6" x="268.4" y="459.2" textLength="1171.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-18)">pinecone | postgres | presto | qdrant | redis | salesforce | samba | se [...]
</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" y="483.6" textLength="12.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-19)">│</text><text class="breeze-sbom-generate-providers-requirements-r6" x="268.4" y="483.6" textLength="1171.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-19)">singularity | slack | smtp | snowflake | sqlite | ssh | standard | tabl [...]
-</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" y="508" textLength="12.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-20)">│</text><text class="breeze-sbom-generate-providers-requirements-r6" x="268.4" y="508" textLength="658.8" clip-path="url(#breeze-sbom-generate-providers-requirements-line-20)">| trino | vertica | weaviate | yandex | ydb | zendesk)</text><text class="breeze-sbom-gener [...]
+</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" y="508" textLength="12.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-20)">│</text><text class="breeze-sbom-generate-providers-requirements-r6" x="268.4" y="508" textLength="756.4" clip-path="url(#breeze-sbom-generate-providers-requirements-line-20)">| trino | vertica | vespa | weaviate | yandex | ydb | zendesk)</text><text class= [...]
</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" y="532.4" textLength="12.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-21)">│</text><text class="breeze-sbom-generate-providers-requirements-r4" x="24.4" y="532.4" textLength="219.6" clip-path="url(#breeze-sbom-generate-providers-requirements-line-21)">--provider-version</text><text class="breeze-sbom-generate-providers-requirements-r1" x="268.4" y="532.4" textLength="1171.2" clip-path="url(#b [...]
</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" y="556.8" textLength="12.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-22)">│</text><text class="breeze-sbom-generate-providers-requirements-r1" x="268.4" y="556.8" textLength="744.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-22)">value to account for the most recent version of the provider </text><text class="breeze [...]
</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" y="581.2" textLength="12.2" clip-path="url(#breeze-sbom-generate-providers-requirements-line-23)">│</text><text class="breeze-sbom-generate-providers-requirements-r4" x="24.4" y="581.2" textLength="219.6" clip-path="url(#breeze-sbom-generate-providers-requirements-line-23)">--force           </text><text class="breeze-sbom-generate-providers-requirements-r1" x= [...]
diff --git a/dev/breeze/doc/images/output_sbom_generate-providers-requirements.txt b/dev/breeze/doc/images/output_sbom_generate-providers-requirements.txt
index 0c39f82ba37..158f228b662 100644
--- a/dev/breeze/doc/images/output_sbom_generate-providers-requirements.txt
+++ b/dev/breeze/doc/images/output_sbom_generate-providers-requirements.txt
@@ -1 +1 @@
-cb4611abc6764a8d7c1aacad63da03e3
+c5e62520c600c1704df855523f6b02ad
diff --git a/dev/breeze/doc/images/output_workflow-run_publish-docs.svg b/dev/breeze/doc/images/output_workflow-run_publish-docs.svg
index d57016971c4..c3ca46d7e26 100644
--- a/dev/breeze/doc/images/output_workflow-run_publish-docs.svg
+++ b/dev/breeze/doc/images/output_workflow-run_publish-docs.svg
@@ -201,7 +201,7 @@
</text><text class="breeze-workflow-run-publish-docs-r4" x="12.2" y="264" textLength="109.8" clip-path="url(#breeze-workflow-run-publish-docs-line-10)">microsoft</text><text class="breeze-workflow-run-publish-docs-r1" x="122" y="264" textLength="12.2" clip-path="url(#breeze-workflow-run-publish-docs-line-10)">.</text><text class="breeze-workflow-run-publish-docs-r4" x="134.2" y="264" textLength="61" clip-path="url(#breeze-workflow-run-publish-docs-line-10)">mssql</text><text class="breez [...]
</text><text class="breeze-workflow-run-publish-docs-r4" x="12.2" y="288.4" textLength="122" clip-path="url(#breeze-workflow-run-publish-docs-line-11)">opensearch</text><text class="breeze-workflow-run-publish-docs-r1" x="134.2" y="288.4" textLength="36.6" clip-path="url(#breeze-workflow-run-publish-docs-line-11)"> | </text><text class="breeze-workflow-run-publish-docs-r4" x="170.8" y="288.4" textLength="97.6" clip-path="url(#breeze-workflow-run-publish-docs-line-11)">opsgenie< [...]
</text><text class="breeze-workflow-run-publish-docs-r4" x="12.2" y="312.8" textLength="122" clip-path="url(#breeze-workflow-run-publish-docs-line-12)">salesforce</text><text class="breeze-workflow-run-publish-docs-r1" x="134.2" y="312.8" textLength="36.6" clip-path="url(#breeze-workflow-run-publish-docs-line-12)"> | </text><text class="breeze-workflow-run-publish-docs-r4" x="170.8" y="312.8" textLength="61" clip-path="url(#breeze-workflow-run-publish-docs-line-12)">samba</text [...]
-</text><text class="breeze-workflow-run-publish-docs-r4" x="12.2" y="337.2" textLength="85.4" clip-path="url(#breeze-workflow-run-publish-docs-line-13)">tableau</text><text class="breeze-workflow-run-publish-docs-r1" x="97.6" y="337.2" textLength="36.6" clip-path="url(#breeze-workflow-run-publish-docs-line-13)"> | </text><text class="breeze-workflow-run-publish-docs-r4" x="134.2" y="337.2" textLength="48.8" clip-path="url(#breeze-workflow-run-publish-docs-line-13)">task</text>< [...]
+</text><text class="breeze-workflow-run-publish-docs-r4" x="12.2" y="337.2" textLength="85.4" clip-path="url(#breeze-workflow-run-publish-docs-line-13)">tableau</text><text class="breeze-workflow-run-publish-docs-r1" x="97.6" y="337.2" textLength="36.6" clip-path="url(#breeze-workflow-run-publish-docs-line-13)"> | </text><text class="breeze-workflow-run-publish-docs-r4" x="134.2" y="337.2" textLength="48.8" clip-path="url(#breeze-workflow-run-publish-docs-line-13)">task</text>< [...]
</text><text class="breeze-workflow-run-publish-docs-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#breeze-workflow-run-publish-docs-line-14)">
</text><text class="breeze-workflow-run-publish-docs-r1" x="12.2" y="386" textLength="427" clip-path="url(#breeze-workflow-run-publish-docs-line-15)">Trigger publish docs to S3 workflow</text><text class="breeze-workflow-run-publish-docs-r1" x="1464" y="386" textLength="12.2" clip-path="url(#breeze-workflow-run-publish-docs-line-15)">
</text><text class="breeze-workflow-run-publish-docs-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#breeze-workflow-run-publish-docs-line-16)">
diff --git a/dev/breeze/doc/images/output_workflow-run_publish-docs.txt b/dev/breeze/doc/images/output_workflow-run_publish-docs.txt
index 3a57f0bb6d6..f032e9220ce 100644
--- a/dev/breeze/doc/images/output_workflow-run_publish-docs.txt
+++ b/dev/breeze/doc/images/output_workflow-run_publish-docs.txt
@@ -1 +1 @@
-7915334135094723635d68ce396033bf
+58e5b5dd710b9517c1309e7b4a24a9dc
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index d038d92cac3..c8981a00bd8 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -1285,6 +1285,7 @@ pytest
pythonic
PythonOperator
pythonpath
+pyvespa
pywinrm
Qdrant
qdrant
@@ -1782,6 +1783,8 @@ venv
venvs
Vertica
vertica
+Vespa
+vespa
videointelligence
views
virtualenv
diff --git a/providers/sftp/docs/index.rst b/providers/sftp/docs/index.rst
index 52468e032b9..4bc4c0694ef 100644
--- a/providers/sftp/docs/index.rst
+++ b/providers/sftp/docs/index.rst
@@ -103,7 +103,7 @@ PIP package Version required
``apache-airflow`` ``>=2.11.0``
``apache-airflow-providers-ssh`` ``>=4.0.0``
``apache-airflow-providers-common-compat`` ``>=1.12.0``
-``paramiko`` ``>=3.4.0,<4.0.0``
+``paramiko`` ``>=3.5.1,<4.0.0``
``asyncssh`` ``>=2.12.0; python_version < "3.14"``
``asyncssh`` ``>=2.22.0; python_version >= "3.14"``
========================================== ======================================
diff --git a/providers/sftp/pyproject.toml b/providers/sftp/pyproject.toml
index 17d5715cf1f..f4f5dfe3d78 100644
--- a/providers/sftp/pyproject.toml
+++ b/providers/sftp/pyproject.toml
@@ -63,7 +63,7 @@ dependencies = [
"apache-airflow-providers-ssh>=4.0.0",
"apache-airflow-providers-common-compat>=1.12.0",
# TODO: Bump to >= 4.0.0 once https://github.com/apache/airflow/issues/54079 is handled
- "paramiko>=3.4.0,<4.0.0",
+ "paramiko>=3.5.1,<4.0.0",
"asyncssh>=2.12.0; python_version < '3.14'",
"asyncssh>=2.22.0; python_version >= '3.14'",
]
diff --git a/providers/ssh/docs/index.rst b/providers/ssh/docs/index.rst
index ab1fa122a55..c01cf72a0a1 100644
--- a/providers/ssh/docs/index.rst
+++ b/providers/ssh/docs/index.rst
@@ -95,7 +95,7 @@ PIP package Version required
``apache-airflow`` ``>=2.11.0``
``apache-airflow-providers-common-compat`` ``>=1.12.0``
``asyncssh`` ``>=2.12.0``
-``paramiko`` ``>=3.4.0,<4.0.0``
+``paramiko`` ``>=3.5.1,<4.0.0``
========================================== ==================
Cross provider package dependencies
diff --git a/providers/ssh/pyproject.toml b/providers/ssh/pyproject.toml
index ab8a24631b4..af54bd30c03 100644
--- a/providers/ssh/pyproject.toml
+++ b/providers/ssh/pyproject.toml
@@ -63,7 +63,7 @@ dependencies = [
"apache-airflow-providers-common-compat>=1.12.0",
"asyncssh>=2.12.0",
# TODO: Bump to >= 4.0.0 once https://github.com/apache/airflow/issues/54079 is handled
- "paramiko>=3.4.0,<4.0.0",
+ "paramiko>=3.5.1,<4.0.0",
]
diff --git a/providers/vespa/.gitignore b/providers/vespa/.gitignore
new file mode 100644
index 00000000000..bff2d762960
--- /dev/null
+++ b/providers/vespa/.gitignore
@@ -0,0 +1 @@
+*.iml
diff --git a/providers/vespa/LICENSE b/providers/vespa/LICENSE
new file mode 100644
index 00000000000..11069edd790
--- /dev/null
+++ b/providers/vespa/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/providers/vespa/NOTICE b/providers/vespa/NOTICE
new file mode 100644
index 00000000000..a51bd9390d0
--- /dev/null
+++ b/providers/vespa/NOTICE
@@ -0,0 +1,5 @@
+Apache Airflow
+Copyright 2016-2026 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/providers/vespa/README.rst b/providers/vespa/README.rst
new file mode 100644
index 00000000000..a6d57d7e6d4
--- /dev/null
+++ b/providers/vespa/README.rst
@@ -0,0 +1,81 @@
+
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+.. http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE OVERWRITTEN!
+
+.. IF YOU WANT TO MODIFY TEMPLATE FOR THIS FILE, YOU SHOULD MODIFY THE TEMPLATE
+ ``PROVIDER_README_TEMPLATE.rst.jinja2`` IN the ``dev/breeze/src/airflow_breeze/templates`` DIRECTORY
+
+Package ``apache-airflow-providers-vespa``
+
+Release: ``0.1.0``
+
+
+`Vespa <https://vespa.ai/>`__
+
+
+Provider package
+----------------
+
+This is a provider package for ``vespa`` provider. All classes for this provider package
+are in ``airflow.providers.vespa`` python package.
+
+You can find package information and changelog for the provider
+in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-vespa/0.1.0/>`_.
+
+Installation
+------------
+
+You can install this package on top of an existing Airflow installation (see ``Requirements`` below
+for the minimum Airflow version supported) via
+``pip install apache-airflow-providers-vespa``
+
+The package supports the following python versions: 3.10,3.11,3.12,3.13
+
+Requirements
+------------
+
+========================================== ==================
+PIP package Version required
+========================================== ==================
+``apache-airflow`` ``>=2.11.0``
+``apache-airflow-providers-common-compat`` ``>=1.12.0``
+``pyvespa`` ``>=1.1.2``
+========================================== ==================
+
+Cross provider package dependencies
+-----------------------------------
+
+Those are dependencies that might be needed in order to use all the features of the package.
+You need to install the specified providers in order to use them.
+
+You can install such cross-provider dependencies when installing from PyPI. For example:
+
+.. code-block:: bash
+
+ pip install apache-airflow-providers-vespa[common.compat]
+
+
+================================================================================================================== =================
+Dependent package Extra
+================================================================================================================== =================
+`apache-airflow-providers-common-compat <https://airflow.apache.org/docs/apache-airflow-providers-common-compat>`_ ``common.compat``
+================================================================================================================== =================
+
+The changelog for the provider package can be found in the
+`changelog <https://airflow.apache.org/docs/apache-airflow-providers-vespa/0.1.0/changelog.html>`_.
diff --git a/providers/vespa/docs/changelog.rst b/providers/vespa/docs/changelog.rst
new file mode 100644
index 00000000000..b274915cadb
--- /dev/null
+++ b/providers/vespa/docs/changelog.rst
@@ -0,0 +1,31 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. NOTE TO CONTRIBUTORS:
+ Please, only add notes to the Changelog just below the "Changelog" header when there
+ are some breaking changes and you want to add an explanation to the users on how they are supposed
+ to deal with them. The changelog is updated and maintained semi-automatically by release manager.
+
+``apache-airflow-providers-vespa``
+
+Changelog
+---------
+
+0.1.0
+.....
+
+Initial version of the provider.
diff --git a/providers/vespa/docs/commits.rst b/providers/vespa/docs/commits.rst
new file mode 100644
index 00000000000..7b51b8a92db
--- /dev/null
+++ b/providers/vespa/docs/commits.rst
@@ -0,0 +1,34 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+ .. NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE OVERWRITTEN!
+
+ .. IF YOU WANT TO MODIFY THIS FILE, YOU SHOULD MODIFY THE TEMPLATE
+ `PROVIDER_COMMITS_TEMPLATE.rst.jinja2` IN the `dev/breeze/src/airflow_breeze/templates` DIRECTORY
+
+ .. THE REMAINDER OF THE FILE IS AUTOMATICALLY GENERATED. IT WILL BE OVERWRITTEN!
+
+Package apache-airflow-providers-vespa
+------------------------------------------------------
+
+`Vespa <https://vespa.ai/>`__
+
+
+This is detailed commit list of changes for versions provider package: ``vespa``.
+For high-level changelog, see :doc:`package information including changelog <index>`.
+
+.. airflow-providers-commits::
diff --git a/providers/vespa/docs/conf.py b/providers/vespa/docs/conf.py
new file mode 100644
index 00000000000..7b165943707
--- /dev/null
+++ b/providers/vespa/docs/conf.py
@@ -0,0 +1,27 @@
+# Disable Flake8 because of all the sphinx imports
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Configuration of Providers docs building."""
+
+from __future__ import annotations
+
+import os
+
+os.environ["AIRFLOW_PACKAGE_NAME"] = "apache-airflow-providers-vespa"
+
+from docs.provider_conf import * # noqa: F403
diff --git a/providers/vespa/docs/connections.rst b/providers/vespa/docs/connections.rst
new file mode 100644
index 00000000000..b6bf2214ce7
--- /dev/null
+++ b/providers/vespa/docs/connections.rst
@@ -0,0 +1,71 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. _howto/connection:vespa:
+
+Vespa Connection
+================
+
+The `Vespa <https://vespa.ai/>`__ connection type enables access to Vespa clusters.
+
+Default Connection IDs
+----------------------
+
+The Vespa hook uses the ``vespa_default`` connection ID by default.
+
+Configuring the Connection
+--------------------------
+
+Endpoint
+ Hostname or full URL of the Vespa endpoint. If a scheme is already included, it takes precedence over the
+ ``protocol`` extra field.
+
+Document Type
+ Set in the standard Airflow ``schema`` field. This becomes the Vespa document type used by the hook and operator.
+
+ .. note::
+
+ The example DAG assumes the Vespa document type has a ``body`` field because the
+ verification task queries on it.
+
+Port (optional)
+ Optional port appended to the endpoint when it is not already present in the host value.
+
+Namespace (optional)
+ Vespa document namespace. Defaults to ``default``.
+
+Protocol (optional)
+ Protocol used when the host does not already include one. Supported values are ``http`` and ``https``.
+ Defaults to ``http``.
+
+Vespa Cloud Secret Token (optional)
+ Token-based authentication for Vespa Cloud.
+
+Client Certificate Path (optional)
+ File path to the client certificate used for mTLS authentication.
+
+Client Key Path (optional)
+ File path to the client key used for mTLS authentication.
+
+Max Feed Queue Size (optional)
+ Passed through to ``feed_async_iterable`` to control the Vespa feed queue size.
+
+Max Feed Workers (optional)
+ Passed through to ``feed_async_iterable`` to control the number of feed workers.
+
+Max Feed Connections (optional)
+ Passed through to ``feed_async_iterable`` to control the maximum number of concurrent feed connections.
diff --git a/providers/ssh/docs/index.rst b/providers/vespa/docs/index.rst
similarity index 70%
copy from providers/ssh/docs/index.rst
copy to providers/vespa/docs/index.rst
index ab1fa122a55..9f1e1b23be2 100644
--- a/providers/ssh/docs/index.rst
+++ b/providers/vespa/docs/index.rst
@@ -9,15 +9,15 @@
.. http://www.apache.org/licenses/LICENSE-2.0
- .. Unless required by applicable law or agreed to in writing,
+.. Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-``apache-airflow-providers-ssh``
-================================
+``apache-airflow-providers-vespa``
+==================================
.. toctree::
@@ -34,23 +34,24 @@
:maxdepth: 1
:caption: Guides
- Connection types <connections/ssh>
- Operators <operators/ssh_remote_job>
+ Connection types <connections>
+ Operators <operators/vespa>
.. toctree::
:hidden:
:maxdepth: 1
- :caption: References
+ :caption: Resources
- Python API <_api/airflow/providers/ssh/index>
+ Python API <_api/airflow/providers/vespa/index>
+ PyPI Repository <https://pypi.org/project/apache-airflow-providers-vespa/>
+ Installing from sources <installing-providers-from-sources>
.. toctree::
:hidden:
:maxdepth: 1
- :caption: Resources
+ :caption: System tests
- PyPI Repository <https://pypi.org/project/apache-airflow-providers-ssh/>
- Installing from sources <installing-providers-from-sources>
+ System Tests <_api/tests/system/vespa/index>
.. THE REMAINDER OF THE FILE IS AUTOMATICALLY GENERATED. IT WILL BE OVERWRITTEN AT RELEASE TIME!
@@ -63,25 +64,25 @@
Detailed list of commits <commits>
-apache-airflow-providers-ssh package
+apache-airflow-providers-vespa package
------------------------------------------------------
-`Secure Shell (SSH) <https://tools.ietf.org/html/rfc4251>`__
+`Vespa <https://vespa.ai/>`__
-Release: 5.0.0
+Release: 0.1.0
Provider package
----------------
-This package is for the ``ssh`` provider.
-All classes for this package are included in the ``airflow.providers.ssh`` python package.
+This package is for the ``vespa`` provider.
+All classes for this package are included in the ``airflow.providers.vespa`` python package.
Installation
------------
You can install this package on top of an existing Airflow installation via
-``pip install apache-airflow-providers-ssh``.
+``pip install apache-airflow-providers-vespa``.
For the minimum Airflow version supported, see ``Requirements`` below.
Requirements
@@ -94,8 +95,7 @@ PIP package Version required
========================================== ==================
``apache-airflow`` ``>=2.11.0``
``apache-airflow-providers-common-compat`` ``>=1.12.0``
-``asyncssh`` ``>=2.12.0``
-``paramiko`` ``>=3.4.0,<4.0.0``
+``pyvespa`` ``>=1.1.2``
========================================== ==================
Cross provider package dependencies
@@ -108,7 +108,7 @@ You can install such cross-provider dependencies when installing from PyPI. For
.. code-block:: bash
- pip install apache-airflow-providers-ssh[common.compat]
+ pip install apache-airflow-providers-vespa[common.compat]
================================================================================================================== =================
@@ -123,5 +123,5 @@ Downloading official packages
You can download officially released packages and verify their checksums and signatures from the
`Official Apache Download site <https://downloads.apache.org/airflow/providers/>`_
-* `The apache-airflow-providers-ssh 5.0.0 sdist package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_ssh-5.0.0.tar.gz>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_ssh-5.0.0.tar.gz.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_ssh-5.0.0.tar.gz.sha512>`__)
-* `The apache-airflow-providers-ssh 5.0.0 wheel package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_ssh-5.0.0-py3-none-any.whl>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_ssh-5.0.0-py3-none-any.whl.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_ssh-5.0.0-py3-none-any.whl.sha512>`__)
+* `The apache-airflow-providers-vespa 0.1.0 sdist package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_vespa-0.1.0.tar.gz>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_vespa-0.1.0.tar.gz.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_vespa-0.1.0.tar.gz.sha512>`__)
+* `The apache-airflow-providers-vespa 0.1.0 wheel package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_vespa-0.1.0-py3-none-any.whl>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_vespa-0.1.0-py3-none-any.whl.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_vespa-0.1.0-py3-none-any.whl.sha512>`__)
diff --git a/providers/vespa/docs/installing-providers-from-sources.rst b/providers/vespa/docs/installing-providers-from-sources.rst
new file mode 100644
index 00000000000..a72b45ffaa6
--- /dev/null
+++ b/providers/vespa/docs/installing-providers-from-sources.rst
@@ -0,0 +1,18 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. include:: /../../../devel-common/src/sphinx_exts/includes/installing-providers-from-sources.rst
diff --git a/providers/vespa/docs/operators/vespa.rst b/providers/vespa/docs/operators/vespa.rst
new file mode 100644
index 00000000000..519ff7e4097
--- /dev/null
+++ b/providers/vespa/docs/operators/vespa.rst
@@ -0,0 +1,46 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. _howto/operator:VespaIngestOperator:
+
+VespaIngestOperator
+===================
+
+Use the :class:`~airflow.providers.vespa.operators.vespa_ingest.VespaIngestOperator` to
+ingest, update, or delete documents in a Vespa instance.
+
+
+Using the Operator
+^^^^^^^^^^^^^^^^^^
+
+The ``VespaIngestOperator`` accepts a list of documents through the ``docs`` argument and defers the
+feed operation to a trigger. Use ``vespa_conn_id`` to specify the Airflow connection to use when
+resolving the target Vespa instance.
+
+.. note::
+
+ Because the operator always defers execution, a running
+ `triggerer <https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/security/workload.html>`__
+ component is required.
+
+An example using the operator in this way:
+
+.. exampleinclude:: /../../vespa/tests/system/vespa/example_dag_vespa.py
+ :language: python
+ :dedent: 4
+ :start-after: [START howto_operator_vespa_ingest]
+ :end-before: [END howto_operator_vespa_ingest]
diff --git a/providers/vespa/docs/security.rst b/providers/vespa/docs/security.rst
new file mode 100644
index 00000000000..15a0ebbb2d0
--- /dev/null
+++ b/providers/vespa/docs/security.rst
@@ -0,0 +1,18 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. include:: /../../../devel-common/src/sphinx_exts/includes/security.rst
diff --git a/providers/vespa/provider.yaml b/providers/vespa/provider.yaml
new file mode 100644
index 00000000000..4303f4d3019
--- /dev/null
+++ b/providers/vespa/provider.yaml
@@ -0,0 +1,156 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+---
+package-name: apache-airflow-providers-vespa
+
+name: Vespa
+
+description: |
+ `Vespa <https://vespa.ai/>`__
+
+state: ready
+lifecycle: incubation
+source-date-epoch: 1773842590
+
+# Note that those versions are maintained by release manager - do not update them manually
+# with the exception of case where other provider in sources has >= new provider version.
+# In such case adding >= NEW_VERSION and bumping to NEW_VERSION in a provider have
+# to be done in the same PR
+versions:
+ - 0.1.0
+
+integrations:
+ - integration-name: Vespa
+ external-doc-url: https://docs.vespa.ai
+ how-to-guide:
+ - /docs/apache-airflow-providers-vespa/operators/vespa.rst
+ tags: [software]
+
+hooks:
+ - integration-name: Vespa
+ python-modules:
+ - airflow.providers.vespa.hooks.vespa
+
+operators:
+ - integration-name: Vespa
+ python-modules:
+ - airflow.providers.vespa.operators.vespa_ingest
+
+triggers:
+ - integration-name: Vespa
+ python-modules:
+ - airflow.providers.vespa.triggers.vespa_feed_trigger
+
+connection-types:
+ - hook-class-name: airflow.providers.vespa.hooks.vespa.VespaHook
+ connection-type: vespa
+ conn-fields:
+ namespace:
+ label: Namespace
+ schema:
+ type:
+ - string
+ - 'null'
+ default: default
+ description: >
+ Vespa document namespace. Groups documents within a document type for
+ access control and distribution. Defaults to "default".
+ protocol:
+ label: Protocol
+ schema:
+ type:
+ - string
+ - 'null'
+ default: http
+ description: >
+ Protocol used to connect to the Vespa endpoint, either "http" or "https".
+ Ignored when the Endpoint field already includes a scheme (e.g. "https://…").
+ Defaults to "http".
+ vespa_cloud_secret_token:
+ label: Vespa Cloud Secret Token
+ schema:
+ type:
+ - string
+ - 'null'
+ format: password
+ description: >
+ Authentication token for Vespa Cloud. Used as an alternative to mTLS
+ certificate authentication. Leave blank when using client certificates.
+ client_cert_path:
+ label: Client Certificate Path
+ schema:
+ type:
+ - string
+ - 'null'
+ description: >
+ Filesystem path to a PEM-encoded client certificate for mTLS
+ authentication. Both this and Client Key Path must be set to enable
+ mTLS; otherwise the hook falls back to token authentication.
+ client_key_path:
+ label: Client Key Path
+ schema:
+ type:
+ - string
+ - 'null'
+ description: >
+ Filesystem path to the PEM-encoded private key that corresponds to the
+ client certificate. Required together with Client Certificate Path for
+ mTLS authentication.
+ max_queue_size:
+ label: Max Feed Queue Size
+ schema:
+ type:
+ - integer
+ - 'null'
+ description: >
+ Maximum number of documents to buffer in the pyvespa async feed queue
+ before blocking. Increase for higher throughput at the cost of memory.
+ Uses the pyvespa default when unset.
+ max_workers:
+ label: Max Feed Workers
+ schema:
+ type:
+ - integer
+ - 'null'
+ description: >
+ Maximum number of concurrent worker threads used by the pyvespa async
+ feed client. Uses the pyvespa default when unset.
+ max_connections:
+ label: Max Feed Connections
+ schema:
+ type:
+ - integer
+ - 'null'
+ description: >
+ Maximum number of HTTP connections the pyvespa feed client keeps open
+ to the Vespa cluster. Uses the pyvespa default when unset.
+ ui-field-behaviour:
+ hidden-fields:
+ - login
+ - password
+ - extra
+ relabeling:
+ host: Endpoint
+ schema: Document Type
+ placeholders:
+ host: vespa-endpoint.example.com
+ schema: my_document_type
+ protocol: https
+ namespace: my_namespace
+ client_cert_path: /path/to/client.pem
+ client_key_path: /path/to/client.key
diff --git a/providers/ssh/pyproject.toml b/providers/vespa/pyproject.toml
similarity index 88%
copy from providers/ssh/pyproject.toml
copy to providers/vespa/pyproject.toml
index ab8a24631b4..067a65d97f6 100644
--- a/providers/ssh/pyproject.toml
+++ b/providers/vespa/pyproject.toml
@@ -24,9 +24,9 @@ requires = ["flit_core==3.12.0"]
build-backend = "flit_core.buildapi"
[project]
-name = "apache-airflow-providers-ssh"
-version = "5.0.0"
-description = "Provider package apache-airflow-providers-ssh for Apache Airflow"
+name = "apache-airflow-providers-vespa"
+version = "0.1.0"
+description = "Provider package apache-airflow-providers-vespa for Apache Airflow"
readme = "README.rst"
license = "Apache-2.0"
license-files = ['LICENSE', 'NOTICE']
@@ -36,7 +36,7 @@ authors = [
maintainers = [
{name="Apache Software Foundation", email="dev@airflow.apache.org"},
]
-keywords = [ "airflow-provider", "ssh", "airflow", "integration" ]
+keywords = [ "airflow-provider", "vespa", "airflow", "integration" ]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
@@ -61,10 +61,7 @@ requires-python = ">=3.10"
dependencies = [
"apache-airflow>=2.11.0",
"apache-airflow-providers-common-compat>=1.12.0",
- "asyncssh>=2.12.0",
- # TODO: Bump to >= 4.0.0 once https://github.com/apache/airflow/issues/54079 is handled
- "paramiko>=3.4.0,<4.0.0",
-
+ "pyvespa>=1.1.2",
]
[dependency-groups]
@@ -102,8 +99,8 @@ apache-airflow-providers-common-sql = {workspace = true}
apache-airflow-providers-standard = {workspace = true}
[project.urls]
-"Documentation" = "https://airflow.apache.org/docs/apache-airflow-providers-ssh/5.0.0"
-"Changelog" = "https://airflow.apache.org/docs/apache-airflow-providers-ssh/5.0.0/changelog.html"
+"Documentation" = "https://airflow.apache.org/docs/apache-airflow-providers-vespa/0.1.0"
+"Changelog" = "https://airflow.apache.org/docs/apache-airflow-providers-vespa/0.1.0/changelog.html"
"Bug Tracker" = "https://github.com/apache/airflow/issues"
"Source Code" = "https://github.com/apache/airflow"
"Slack Chat" = "https://s.apache.org/airflow-slack"
@@ -111,7 +108,7 @@ apache-airflow-providers-standard = {workspace = true}
"YouTube" = "https://www.youtube.com/channel/UCSXwxpWZQ7XZ1WL3wqevChA/"
[project.entry-points."apache_airflow_provider"]
-provider_info = "airflow.providers.ssh.get_provider_info:get_provider_info"
+provider_info = "airflow.providers.vespa.get_provider_info:get_provider_info"
[tool.flit.module]
-name = "airflow.providers.ssh"
+name = "airflow.providers.vespa"
diff --git a/providers/vespa/src/airflow/__init__.py b/providers/vespa/src/airflow/__init__.py
new file mode 100644
index 00000000000..5966d6b1d52
--- /dev/null
+++ b/providers/vespa/src/airflow/__init__.py
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+__path__ = __import__("pkgutil").extend_path(__path__, __name__)
diff --git a/providers/vespa/src/airflow/providers/__init__.py b/providers/vespa/src/airflow/providers/__init__.py
new file mode 100644
index 00000000000..5966d6b1d52
--- /dev/null
+++ b/providers/vespa/src/airflow/providers/__init__.py
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+__path__ = __import__("pkgutil").extend_path(__path__, __name__)
diff --git a/providers/vespa/src/airflow/providers/vespa/__init__.py b/providers/vespa/src/airflow/providers/vespa/__init__.py
new file mode 100644
index 00000000000..2f6b8fd4e35
--- /dev/null
+++ b/providers/vespa/src/airflow/providers/vespa/__init__.py
@@ -0,0 +1,39 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE
+# OVERWRITTEN WHEN PREPARING DOCUMENTATION FOR THE PACKAGES.
+#
+# IF YOU WANT TO MODIFY THIS FILE, YOU SHOULD MODIFY THE TEMPLATE
+# `PROVIDER__INIT__PY_TEMPLATE.py.jinja2` IN the `dev/breeze/src/airflow_breeze/templates` DIRECTORY
+#
+from __future__ import annotations
+
+import packaging.version
+
+from airflow import __version__ as airflow_version
+
+__all__ = ["__version__"]
+
+__version__ = "0.1.0"
+
+if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
+ "2.11.0"
+):
+ raise RuntimeError(
+ f"The package `apache-airflow-providers-vespa:{__version__}` needs Apache Airflow 2.11.0+"
+ )
diff --git a/providers/vespa/src/airflow/providers/vespa/get_provider_info.py b/providers/vespa/src/airflow/providers/vespa/get_provider_info.py
new file mode 100644
index 00000000000..dff77334e94
--- /dev/null
+++ b/providers/vespa/src/airflow/providers/vespa/get_provider_info.py
@@ -0,0 +1,110 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE OVERWRITTEN!
+#
+# IF YOU WANT TO MODIFY THIS FILE, YOU SHOULD MODIFY THE TEMPLATE
+# `get_provider_info_TEMPLATE.py.jinja2` IN the `dev/breeze/src/airflow_breeze/templates` DIRECTORY
+
+
+def get_provider_info():
+ return {
+ "package-name": "apache-airflow-providers-vespa",
+ "name": "Vespa",
+ "description": "`Vespa <https://vespa.ai/>`__\n",
+ "integrations": [
+ {
+ "integration-name": "Vespa",
+ "external-doc-url": "https://docs.vespa.ai",
+ "how-to-guide": ["/docs/apache-airflow-providers-vespa/operators/vespa.rst"],
+ "tags": ["software"],
+ }
+ ],
+ "hooks": [{"integration-name": "Vespa", "python-modules": ["airflow.providers.vespa.hooks.vespa"]}],
+ "operators": [
+ {
+ "integration-name": "Vespa",
+ "python-modules": ["airflow.providers.vespa.operators.vespa_ingest"],
+ }
+ ],
+ "triggers": [
+ {
+ "integration-name": "Vespa",
+ "python-modules": ["airflow.providers.vespa.triggers.vespa_feed_trigger"],
+ }
+ ],
+ "connection-types": [
+ {
+ "hook-class-name": "airflow.providers.vespa.hooks.vespa.VespaHook",
+ "connection-type": "vespa",
+ "conn-fields": {
+ "namespace": {
+ "label": "Namespace",
+ "schema": {"type": ["string", "null"], "default": "default"},
+ "description": 'Vespa document namespace. Groups documents within a document type for access control and distribution. Defaults to "default".\n',
+ },
+ "protocol": {
+ "label": "Protocol",
+ "schema": {"type": ["string", "null"], "default": "http"},
+ "description": 'Protocol used to connect to the Vespa endpoint, either "http" or "https". Ignored when the Endpoint field already includes a scheme (e.g. "https://…"). Defaults to "http".\n',
+ },
+ "vespa_cloud_secret_token": {
+ "label": "Vespa Cloud Secret Token",
+ "schema": {"type": ["string", "null"], "format": "password"},
+ "description": "Authentication token for Vespa Cloud. Used as an alternative to mTLS certificate authentication. Leave blank when using client certificates.\n",
+ },
+ "client_cert_path": {
+ "label": "Client Certificate Path",
+ "schema": {"type": ["string", "null"]},
+ "description": "Filesystem path to a PEM-encoded client certificate for mTLS authentication. Both this and Client Key Path must be set to enable mTLS; otherwise the hook falls back to token authentication.\n",
+ },
+ "client_key_path": {
+ "label": "Client Key Path",
+ "schema": {"type": ["string", "null"]},
+ "description": "Filesystem path to the PEM-encoded private key that corresponds to the client certificate. Required together with Client Certificate Path for mTLS authentication.\n",
+ },
+ "max_queue_size": {
+ "label": "Max Feed Queue Size",
+ "schema": {"type": ["integer", "null"]},
+ "description": "Maximum number of documents to buffer in the pyvespa async feed queue before blocking. Increase for higher throughput at the cost of memory. Uses the pyvespa default when unset.\n",
+ },
+ "max_workers": {
+ "label": "Max Feed Workers",
+ "schema": {"type": ["integer", "null"]},
+ "description": "Maximum number of concurrent worker threads used by the pyvespa async feed client. Uses the pyvespa default when unset.\n",
+ },
+ "max_connections": {
+ "label": "Max Feed Connections",
+ "schema": {"type": ["integer", "null"]},
+ "description": "Maximum number of HTTP connections the pyvespa feed client keeps open to the Vespa cluster. Uses the pyvespa default when unset.\n",
+ },
+ },
+ "ui-field-behaviour": {
+ "hidden-fields": ["login", "password", "extra"],
+ "relabeling": {"host": "Endpoint", "schema": "Document Type"},
+ "placeholders": {
+ "host": "vespa-endpoint.example.com",
+ "schema": "my_document_type",
+ "protocol": "https",
+ "namespace": "my_namespace",
+ "client_cert_path": "/path/to/client.pem",
+ "client_key_path": "/path/to/client.key",
+ },
+ },
+ }
+ ],
+ }
diff --git a/providers/vespa/src/airflow/providers/vespa/hooks/__init__.py b/providers/vespa/src/airflow/providers/vespa/hooks/__init__.py
new file mode 100644
index 00000000000..13a83393a91
--- /dev/null
+++ b/providers/vespa/src/airflow/providers/vespa/hooks/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/providers/vespa/src/airflow/providers/vespa/hooks/vespa.py b/providers/vespa/src/airflow/providers/vespa/hooks/vespa.py
new file mode 100644
index 00000000000..07d890ffaf5
--- /dev/null
+++ b/providers/vespa/src/airflow/providers/vespa/hooks/vespa.py
@@ -0,0 +1,356 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import uuid
+from collections.abc import Callable, Iterable
+from queue import Empty, Queue
+from typing import TYPE_CHECKING, Any
+
+from vespa.application import Vespa
+
+from airflow.providers.common.compat.sdk import BaseHook
+
+if TYPE_CHECKING:
+ from vespa.io import VespaResponse
+
+ from airflow.providers.common.compat.sdk import Connection
+
+VALID_OPERATION_TYPES = frozenset({"feed", "update", "delete"})
+
+
+class VespaHook(BaseHook):
+ """
+ Hook for interacting with a Vespa cluster via pyvespa.
+
+ Supports document feed, update, and delete operations through
+ ``feed_async_iterable``. Uses a custom ``vespa`` connection type.
+
+ Connection extras (bare keys canonical, ``extra__vespa__`` prefix
+ accepted for backward compatibility):
+
+ - ``namespace`` -- Vespa namespace (default: ``"default"``)
+ - ``max_queue_size`` / ``max_workers`` / ``max_connections`` -- feed tuning
+ - ``vespa_cloud_secret_token`` -- token auth
+ - ``client_cert_path`` / ``client_key_path`` -- mTLS auth
+ - ``protocol`` -- ``http`` or ``https`` (default: ``http``)
+ """
+
+ conn_name_attr = "conn_id"
+ default_conn_name = "vespa_default"
+ conn_type = "vespa"
+ hook_name = "Vespa"
+
+ @staticmethod
+ def _get_field(extra: dict[str, Any], field: str) -> Any:
+ """Return an extra-field value, preferring bare key over legacy prefix."""
+ if field in extra:
+ return extra[field]
+ return extra.get(f"extra__vespa__{field}")
+
+ @staticmethod
+ def _get_int_field(extra: dict[str, Any], field: str) -> int | None:
+ """Return an extra field cast to ``int``, or *None* if absent or empty."""
+ val = VespaHook._get_field(extra, field)
+ if val is None or val == "":
+ return None
+ try:
+ return int(val)
+ except (TypeError, ValueError):
+ raise ValueError(f"Connection extra '{field}' must be an integer, got {val!r}")
+
+ def __init__(
+ self,
+ conn_id: str = default_conn_name,
+ *,
+ namespace: str | None = None,
+ schema: str | None = None,
+ ) -> None:
+ """
+ Create a VespaHook.
+
+ Parameters ``namespace`` and ``schema`` override values stored in the
+ Airflow ``Connection``. This is convenient when you want to share the
+ same endpoint across multiple DAGs but write to different schemas or
+ namespaces without creating separate connections.
+ """
+ super().__init__()
+ self.conn_id = conn_id
+ conn: Connection = self.get_connection(conn_id)
+ self.conn = conn
+ extra = self.conn.extra_dejson or {}
+ host = self.conn.host
+
+ if not host:
+ raise ValueError("Vespa connection requires a host")
+
+ resolved_schema = schema or self.conn.schema or self._get_field(extra, "schema")
+ resolved_namespace = namespace or self._get_field(extra, "namespace") or "default"
+
+ self._configure_from_connection(
+ host=host,
+ port=self.conn.port,
+ namespace=resolved_namespace,
+ schema=resolved_schema,
+ extra=extra,
+ )
+
+ @classmethod
+ def from_resolved_connection(
+ cls,
+ *,
+ host: str,
+ port: int | None = None,
+ schema: str | None = None,
+ namespace: str | None = None,
+ extra: dict[str, Any],
+ ) -> VespaHook:
+ """
+ Instantiate without querying Airflow's metadata database.
+
+ Intended for trigger processes where synchronous DB access is
+ prohibited. The caller must supply the already-resolved connection
+ parameters.
+ """
+ self = cls.__new__(cls)
+ BaseHook.__init__(self)
+
+ resolved_schema = schema or cls._get_field(extra, "schema")
+ resolved_namespace = namespace or cls._get_field(extra, "namespace") or "default"
+
+ self._configure_from_connection(
+ host=host,
+ port=port,
+ namespace=resolved_namespace,
+ schema=resolved_schema,
+ extra=extra,
+ )
+ return self
+
+ # On Airflow 3 the YAML metadata in provider.yaml takes precedence and these
+ # methods are skipped. They are kept for Airflow 2.11 backward compatibility.
+
+ @classmethod
+ def get_connection_form_widgets(cls) -> dict[str, Any]:
+ """Return connection widgets to add to connection form."""
+ try:
+ from flask_appbuilder.fieldwidgets import BS3PasswordFieldWidget
+ from wtforms import PasswordField, StringField
+ except ImportError:
+ return {}
+
+ return {
+ "namespace": StringField("Namespace"),
+ "protocol": StringField("Protocol"),
+ "max_queue_size": StringField("Max feed queue size"),
+ "max_workers": StringField("Max feed workers"),
+ "max_connections": StringField("Max feed connections"),
+ "vespa_cloud_secret_token": PasswordField(
+ "Vespa Cloud secret token", widget=BS3PasswordFieldWidget()
+ ),
+ "client_cert_path": StringField("Client certificate file path"),
+ "client_key_path": StringField("Client key file path"),
+ }
+
+ @classmethod
+ def get_ui_field_behaviour(cls) -> dict[str, Any]:
+ """Return custom field behaviour."""
+ return {
+ "hidden_fields": ["login", "password", "extra"],
+ "relabeling": {
+ "host": "Endpoint",
+ "schema": "Document Type",
+ },
+ "placeholders": {
+ "host": "vespa-endpoint.example.com",
+ "schema": "my_document_type",
+ "protocol": "https",
+ "namespace": "my_namespace",
+ "client_cert_path": "/path/to/client.pem",
+ "client_key_path": "/path/to/client.key",
+ },
+ }
+
+ def _configure_from_connection(
+ self,
+ *,
+ host: str,
+ port: int | None = None,
+ namespace: str,
+ schema: str | None,
+ extra: dict[str, Any],
+ ) -> None:
+ """Populate instance attributes shared by both constructors."""
+ cert_file = self._get_field(extra, "client_cert_path")
+ key_file = self._get_field(extra, "client_key_path")
+ token = self._get_field(extra, "vespa_cloud_secret_token")
+
+ # mTLS requires both cert and key; if either is missing, discard both
+ # and fall through to token-based auth.
+ if not cert_file or not key_file:
+ self.log.info("No client certificate or key found, trying token authentication")
+ cert_file = None
+ key_file = None
+
+ if not token:
+ self.log.info("No token found either. Not authenticating.")
+ else:
+ self.log.info("Token authentication available")
+ else:
+ self.log.info("Using mTLS authentication with certificate files")
+
+ url = host.rstrip("/")
+ protocol = self._get_field(extra, "protocol") or "http"
+
+ if host.startswith("http://") or host.startswith("https://"):
+ url = host
+ else:
+ url = f"{protocol}://{host}"
+
+ if port and not url.endswith(f":{port}"):
+ url = f"{url}:{port}"
+
+ self.log.info("Connecting to Vespa at %s", url)
+
+ self.vespa_app = Vespa(
+ url=url,
+ vespa_cloud_secret_token=token,
+ cert=cert_file,
+ key=key_file,
+ )
+
+ self.namespace = namespace
+ self.schema = schema
+ self.max_queue_size = self._get_int_field(extra, "max_queue_size")
+ self.max_workers = self._get_int_field(extra, "max_workers")
+ self.max_connections = self._get_int_field(extra, "max_connections")
+ # pyvespa fires per-document callbacks from worker threads; a Queue
+ # collects errors safely so feed_iterable() can aggregate them.
+ self.feed_errors_queue: Queue = Queue()
+
+ def _normalise(
+ self,
+ bodies: Iterable[dict[str, Any]],
+ *,
+ operation_type: str = "feed",
+ ) -> list[dict[str, Any]]:
+ """
+ Convert documents to Vespa ``{id, fields}`` format.
+
+ Accepts both the Vespa-native ``{"id": ..., "fields": {...}}`` form
+ and a flat convenience form ``{"id": ..., "key": "val", ...}`` where
+ all non-``id`` keys become fields. Missing ``id`` on feed operations
+ is auto-generated; on update/delete it is an error.
+ """
+ norm: list[dict[str, Any]] = []
+ for i, body in enumerate(bodies):
+ if "fields" in body:
+ if operation_type in ("update", "delete") and "id" not in body:
+ raise ValueError(
+ f"Document at index {i} is missing required 'id' for {operation_type} operation"
+ )
+ norm.append(body)
+ continue
+
+ doc = dict(body)
+ doc_id = doc.pop("id", None)
+
+ if doc_id is None and operation_type in ("update", "delete"):
+ raise ValueError(
+ f"Document at index {i} is missing required 'id' for {operation_type} operation"
+ )
+ if doc_id is None:
+ doc_id = str(uuid.uuid4())
+
+ norm.append({"id": doc_id, "fields": doc})
+ return norm
+
+ def default_callback(self, response: VespaResponse, doc_id: str) -> None:
+ """Collect non-success responses from Vespa feed calls."""
+ if not response.is_successful():
+ try:
+ reason = response.get_json()
+ except Exception as err:
+ reason = {
+ "json_parse_error": str(err),
+ "status_code": response.status_code,
+ "response_type": type(response).__name__,
+ }
+
+ self.feed_errors_queue.put({"id": doc_id, "status": response.status_code, "reason": reason})
+
+ def feed_iterable(
+ self,
+ bodies: Iterable[dict[str, Any]],
+ callback: Callable[..., Any] | None = None,
+ operation_type: str = "feed",
+ **kwargs: Any,
+ ) -> dict[str, Any]:
+ """Feed, update, or delete documents via pyvespa."""
+ if operation_type not in VALID_OPERATION_TYPES:
+ raise ValueError(
+ f"Invalid operation_type {operation_type!r}. Must be one of {sorted(VALID_OPERATION_TYPES)}"
+ )
+
+ if callback is None:
+ callback = self.default_callback
+
+ docs = self._normalise(bodies, operation_type=operation_type)
+
+ feed_kwargs: dict[str, Any] = {
+ "iter": docs,
+ "operation_type": operation_type,
+ "schema": self.schema,
+ "namespace": self.namespace,
+ "callback": callback,
+ }
+
+ for key in ("max_queue_size", "max_workers", "max_connections"):
+ val = getattr(self, key, None)
+ if val is not None:
+ feed_kwargs[key] = val
+
+ feed_kwargs.update(kwargs)
+
+ self.feed_errors_queue = Queue()
+
+ self.log.info("Starting %s operation for %s documents", operation_type, len(docs))
+ self.vespa_app.feed_async_iterable(**feed_kwargs)
+
+ feed_errors: list[dict[str, Any]] = []
+ while not self.feed_errors_queue.empty():
+ try:
+ feed_errors.append(self.feed_errors_queue.get_nowait())
+ except Empty:
+ break
+
+ return {
+ "sent": len(docs),
+ "errors": len(feed_errors),
+ "error_details": feed_errors,
+ }
+
+ def test_connection(self) -> tuple[bool, str]:
+ """Test connectivity to the Vespa endpoint using pyvespa's own session."""
+ try:
+ resp = self.vespa_app.get_application_status()
+ if resp is not None and resp.status_code == 200:
+ return True, "Connection successful"
+ status = getattr(resp, "status_code", "unknown") if resp else "no response"
+ return False, f"Vespa returned HTTP {status}"
+ except Exception as err:
+ return False, str(err)
diff --git a/providers/vespa/src/airflow/providers/vespa/operators/__init__.py b/providers/vespa/src/airflow/providers/vespa/operators/__init__.py
new file mode 100644
index 00000000000..13a83393a91
--- /dev/null
+++ b/providers/vespa/src/airflow/providers/vespa/operators/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/providers/vespa/src/airflow/providers/vespa/operators/vespa_ingest.py b/providers/vespa/src/airflow/providers/vespa/operators/vespa_ingest.py
new file mode 100644
index 00000000000..0b6f2ccd544
--- /dev/null
+++ b/providers/vespa/src/airflow/providers/vespa/operators/vespa_ingest.py
@@ -0,0 +1,101 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import json
+from collections.abc import Iterable, Sequence
+from typing import TYPE_CHECKING, Any
+
+from airflow.providers.common.compat.sdk import AirflowException, BaseHook, BaseOperator, TaskDeferred
+from airflow.providers.vespa.hooks.vespa import VALID_OPERATION_TYPES, VespaHook
+from airflow.providers.vespa.triggers.vespa_feed_trigger import VespaFeedTrigger
+
+if TYPE_CHECKING:
+ from airflow.providers.common.compat.sdk import Context
+
+
+class VespaIngestOperator(BaseOperator):
+ """Defer a Vespa feed, update, or delete operation to a trigger."""
+
+ template_fields: Sequence[str] = ("docs", "vespa_conn_id")
+
+ def __init__(
+ self,
+ *,
+ docs: Iterable[dict[str, Any]],
+ vespa_conn_id: str = "vespa_default",
+ operation_type: str = "feed",
+ feed_kwargs: dict[str, Any] | None = None,
+ **kwargs: Any,
+ ) -> None:
+ super().__init__(**kwargs)
+ self.docs = docs
+ self.vespa_conn_id = vespa_conn_id
+ if operation_type not in VALID_OPERATION_TYPES:
+ raise ValueError(
+ f"Invalid operation_type {operation_type!r}. Must be one of {sorted(VALID_OPERATION_TYPES)}"
+ )
+ self.operation_type = operation_type
+ self.feed_kwargs = feed_kwargs or {}
+
+ def execute(self, context: Context) -> None:
+ """Resolve the Vespa connection in the worker and defer ingestion to a trigger."""
+ conn = BaseHook.get_connection(self.vespa_conn_id)
+ extra = conn.extra_dejson or {}
+
+ self.docs = list(self.docs) if not isinstance(self.docs, list) else self.docs
+
+ for i, doc in enumerate(self.docs):
+ if not isinstance(doc, dict):
+ raise TypeError(f"docs[{i}] must be a dict, got {type(doc).__name__}")
+
+ # feed_kwargs are serialized into the TriggerEvent; catch bad values early.
+ if self.feed_kwargs:
+ try:
+ json.dumps(self.feed_kwargs)
+ except (TypeError, ValueError) as err:
+ raise ValueError(
+ f"feed_kwargs must be JSON-serializable for trigger deferral: {err}"
+ ) from err
+
+ # Resolve the connection here (worker process) because trigger processes
+ # run inside the triggerer and cannot access the Airflow metadata DB.
+ conn_info = {
+ "host": conn.host,
+ "port": conn.port,
+ "schema": conn.schema,
+ "namespace": VespaHook._get_field(extra, "namespace") or "default",
+ "extra": extra,
+ }
+
+ raise TaskDeferred(
+ trigger=VespaFeedTrigger(
+ docs=self.docs,
+ conn_info=conn_info,
+ operation_type=self.operation_type,
+ feed_kwargs=self.feed_kwargs,
+ ),
+ method_name="execute_complete",
+ )
+
+ def execute_complete(self, context: Context, event: dict[str, Any]) -> dict[str, int]:
+ """Return a compact success payload or raise on trigger failure."""
+ if not event["success"]:
+ raise AirflowException(
+ f"{len(event['errors'])} document(s) failed; Error details: {event['errors']}"
+ )
+ return {"ingested": event.get("sent", 0)}
diff --git a/providers/vespa/src/airflow/providers/vespa/triggers/__init__.py b/providers/vespa/src/airflow/providers/vespa/triggers/__init__.py
new file mode 100644
index 00000000000..13a83393a91
--- /dev/null
+++ b/providers/vespa/src/airflow/providers/vespa/triggers/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/providers/vespa/src/airflow/providers/vespa/triggers/vespa_feed_trigger.py b/providers/vespa/src/airflow/providers/vespa/triggers/vespa_feed_trigger.py
new file mode 100644
index 00000000000..572cb23f71d
--- /dev/null
+++ b/providers/vespa/src/airflow/providers/vespa/triggers/vespa_feed_trigger.py
@@ -0,0 +1,104 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import asyncio
+from collections.abc import AsyncIterator
+from typing import Any
+
+from airflow.triggers.base import BaseTrigger, TriggerEvent
+
+
+class VespaFeedTrigger(BaseTrigger):
+ """Trigger that runs a Vespa feed, update, or delete operation."""
+
+ def __init__(
+ self,
+ docs: list[dict[str, Any]],
+ conn_info: dict[str, Any],
+ operation_type: str = "feed",
+ feed_kwargs: dict[str, Any] | None = None,
+ ) -> None:
+ super().__init__()
+ self.docs = list(docs)
+ self.conn_info = conn_info
+ self.operation_type = operation_type
+ self.feed_kwargs = feed_kwargs or {}
+
+ def serialize(self) -> tuple[str, dict[str, Any]]:
+ """Serialize VespaFeedTrigger arguments and class path."""
+ return (
+ "airflow.providers.vespa.triggers.vespa_feed_trigger.VespaFeedTrigger",
+ {
+ "docs": self.docs,
+ "conn_info": self.conn_info,
+ "operation_type": self.operation_type,
+ "feed_kwargs": self.feed_kwargs,
+ },
+ )
+
+ async def run(self) -> AsyncIterator[TriggerEvent]:
+ """Create the hook and execute the feed operation in the default executor."""
+ try:
+ # Lazy import: avoids pulling in pyvespa (and its transitive deps)
+ # at triggerer parse time.
+ from airflow.providers.vespa.hooks.vespa import VespaHook
+
+ hook = VespaHook.from_resolved_connection(
+ host=self.conn_info["host"],
+ port=self.conn_info.get("port"),
+ schema=self.conn_info.get("schema"),
+ namespace=self.conn_info.get("namespace"),
+ extra=self.conn_info.get("extra", {}),
+ )
+
+ # pyvespa's feed_async_iterable is synchronous/blocking, so we
+ # offload it to a thread to keep the triggerer event loop responsive.
+ loop = asyncio.get_running_loop()
+
+ def _feed() -> dict[str, Any]:
+ return hook.feed_iterable(
+ self.docs,
+ operation_type=self.operation_type,
+ **self.feed_kwargs,
+ )
+
+ summary = await loop.run_in_executor(None, _feed)
+
+ if summary["errors"]:
+ self.log.error(
+ "Vespa feed operation failed: %d error(s). Details: %s",
+ summary["errors"],
+ summary["error_details"],
+ )
+ yield TriggerEvent(
+ {
+ "success": False,
+ "sent": summary["sent"],
+ "errors": summary["error_details"],
+ }
+ )
+ else:
+ self.log.info(
+ "Vespa feed operation completed successfully for %d document(s)",
+ len(self.docs),
+ )
+ yield TriggerEvent({"success": True, "sent": summary["sent"]})
+ except Exception as err:
+ error_msg = f"Trigger failed: {type(err).__name__}: {err}"
+ self.log.exception(error_msg)
+ yield TriggerEvent({"success": False, "errors": [{"error": error_msg}]})
diff --git a/providers/vespa/tests/conftest.py b/providers/vespa/tests/conftest.py
new file mode 100644
index 00000000000..f56ccce0a3f
--- /dev/null
+++ b/providers/vespa/tests/conftest.py
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+pytest_plugins = "tests_common.pytest_plugin"
diff --git a/providers/vespa/tests/system/__init__.py b/providers/vespa/tests/system/__init__.py
new file mode 100644
index 00000000000..5966d6b1d52
--- /dev/null
+++ b/providers/vespa/tests/system/__init__.py
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+__path__ = __import__("pkgutil").extend_path(__path__, __name__)
diff --git a/providers/vespa/tests/system/vespa/__init__.py b/providers/vespa/tests/system/vespa/__init__.py
new file mode 100644
index 00000000000..13a83393a91
--- /dev/null
+++ b/providers/vespa/tests/system/vespa/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/providers/vespa/tests/system/vespa/example_dag_vespa.py b/providers/vespa/tests/system/vespa/example_dag_vespa.py
new file mode 100644
index 00000000000..a558b985038
--- /dev/null
+++ b/providers/vespa/tests/system/vespa/example_dag_vespa.py
@@ -0,0 +1,112 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from datetime import datetime
+
+from airflow.providers.common.compat.sdk import dag, task
+from airflow.providers.vespa.operators.vespa_ingest import VespaIngestOperator
+
+
+@dag(
+ dag_id="example_vespa_ingest",
+ start_date=datetime(2025, 7, 30),
+ schedule="@once",
+ catchup=False,
+ tags=["example", "vespa"],
+)
+def vespa_dynamic():
+ vespa_conn_id = "vespa_connection"
+
+ @task
+ def build_batches():
+ return [
+ [
+ {"id": "doc1", "fields": {"body": "first document"}},
+ {"id": "doc2", "fields": {"body": "second document"}},
+ ],
+ [
+ {"id": "doc3", "fields": {"body": "third document"}},
+ {"id": "doc4", "fields": {"body": "fourth document"}},
+ ],
+ ]
+
+ batches = build_batches()
+
+ # [START howto_operator_vespa_ingest]
+ send_batches = VespaIngestOperator.partial(vespa_conn_id=vespa_conn_id, task_id="send_batch").expand(
+ docs=batches
+ )
+
+ update_doc3 = VespaIngestOperator(
+ vespa_conn_id=vespa_conn_id,
+ task_id="update_doc3",
+ docs=[{"id": "doc3", "fields": {"body": "third document - UPDATED"}}],
+ operation_type="update",
+ )
+
+ delete_doc4 = VespaIngestOperator(
+ vespa_conn_id=vespa_conn_id,
+ task_id="delete_doc4",
+ docs=[{"id": "doc4"}],
+ operation_type="delete",
+ )
+ # [END howto_operator_vespa_ingest]
+
+ @task(task_id="verify_docs_with_hook")
+ def verify_docs_with_hook():
+ """Use the VespaHook to validate the example documents."""
+ from airflow.providers.vespa.hooks.vespa import VespaHook
+
+ hook = VespaHook(conn_id=vespa_conn_id)
+
+ def run_query(yql: str, params: dict | None = None):
+ params = params or {}
+ res = hook.vespa_app.query(yql=yql, **params)
+ hits = res.hits if hasattr(res, "hits") else []
+ return {
+ "count": len(hits),
+ "first": hits[0].get("fields") if hits else None,
+ }
+
+ q1 = run_query('select * from sources * where body contains "first document"')
+ q2 = run_query('select * from sources * where body contains "second document"')
+ q3_updated = run_query('select * from sources * where body contains "UPDATED"')
+ q4_deleted = run_query('select * from sources * where body contains "fourth document"')
+
+ assert q1["count"] >= 1, "doc1 not found"
+ assert q2["count"] >= 1, "doc2 not found"
+ assert q3_updated["count"] >= 1, "updated doc3 not found"
+ assert q4_deleted["count"] == 0, "doc4 appears to still be present"
+
+ return {
+ "doc1_found": q1["count"],
+ "doc2_found": q2["count"],
+ "doc3_updated_found": q3_updated["count"],
+ "doc4_found": q4_deleted["count"],
+ }
+
+ verify_docs_task = verify_docs_with_hook()
+ send_batches >> [update_doc3, delete_doc4] >> verify_docs_task
+
+
+example_dag = vespa_dynamic()
+
+
+from tests_common.test_utils.system_tests import get_test_run # noqa: E402
+
+test_run = get_test_run(example_dag)
diff --git a/providers/vespa/tests/unit/__init__.py b/providers/vespa/tests/unit/__init__.py
new file mode 100644
index 00000000000..5966d6b1d52
--- /dev/null
+++ b/providers/vespa/tests/unit/__init__.py
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+__path__ = __import__("pkgutil").extend_path(__path__, __name__)
diff --git a/providers/vespa/tests/unit/vespa/__init__.py b/providers/vespa/tests/unit/vespa/__init__.py
new file mode 100644
index 00000000000..13a83393a91
--- /dev/null
+++ b/providers/vespa/tests/unit/vespa/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/providers/vespa/tests/unit/vespa/hooks/__init__.py b/providers/vespa/tests/unit/vespa/hooks/__init__.py
new file mode 100644
index 00000000000..13a83393a91
--- /dev/null
+++ b/providers/vespa/tests/unit/vespa/hooks/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/providers/vespa/tests/unit/vespa/hooks/test_vespa.py b/providers/vespa/tests/unit/vespa/hooks/test_vespa.py
new file mode 100644
index 00000000000..6dde1a7f821
--- /dev/null
+++ b/providers/vespa/tests/unit/vespa/hooks/test_vespa.py
@@ -0,0 +1,727 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import json
+import uuid
+from unittest.mock import Mock, patch
+
+import pytest
+
+from airflow.providers.common.compat.sdk import Connection
+from airflow.providers.vespa.hooks.vespa import VespaHook
+
+
+def _make_connection(
+ *,
+ host: str,
+ schema: str | None,
+ port: int | None = None,
+ extra: dict | None = None,
+) -> Connection:
+ return Connection(
+ conn_id="vespa_test",
+ conn_type="vespa",
+ host=host,
+ schema=schema,
+ port=port,
+ extra=json.dumps(extra or {}),
+ )
+
+
+class TestGetField:
+ """Tests for the _get_field and _get_int_field helpers."""
+
+ def test_bare_key_preferred(self):
+ extra = {"namespace": "bare", "extra__vespa__namespace": "prefixed"}
+ assert VespaHook._get_field(extra, "namespace") == "bare"
+
+ def test_prefixed_fallback(self):
+ extra = {"extra__vespa__namespace": "prefixed"}
+ assert VespaHook._get_field(extra, "namespace") == "prefixed"
+
+ def test_missing_returns_none(self):
+ assert VespaHook._get_field({}, "namespace") is None
+
+ def test_int_field_from_string(self):
+ assert VespaHook._get_int_field({"max_queue_size": "500"}, "max_queue_size") == 500
+
+ def test_int_field_from_int(self):
+ assert VespaHook._get_int_field({"max_queue_size": 500}, "max_queue_size") == 500
+
+ def test_int_field_empty_string(self):
+ assert VespaHook._get_int_field({"max_queue_size": ""}, "max_queue_size") is None
+
+ def test_int_field_missing(self):
+ assert VespaHook._get_int_field({}, "max_queue_size") is None
+
+ def test_int_field_invalid_raises(self):
+ with pytest.raises(ValueError, match="must be an integer"):
+ VespaHook._get_int_field({"max_queue_size": "abc"}, "max_queue_size")
+
+
+class TestVespaHook:
+ @patch("airflow.providers.vespa.hooks.vespa.BaseHook.get_connection")
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_init_default(self, mock_vespa, mock_get_connection):
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.example.com:8080",
+ schema="test_schema",
+ extra={"namespace": "default"},
+ )
+
+ hook = VespaHook()
+
+ assert hook.namespace == "default"
+ assert hook.schema == "test_schema"
+
+ @patch("airflow.providers.vespa.hooks.vespa.BaseHook.get_connection")
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_init_with_params(self, mock_vespa, mock_get_connection):
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.example.com:8080",
+ schema="conn_schema",
+ extra={"namespace": "conn_ns"},
+ )
+
+ hook = VespaHook(conn_id="custom_conn", namespace="test_ns", schema="test_schema")
+
+ assert hook.namespace == "test_ns"
+ assert hook.schema == "test_schema"
+
+ @patch("airflow.providers.vespa.hooks.vespa.BaseHook.get_connection")
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_init_schema_resolution_chain(self, mock_vespa, mock_get_connection):
+ """Schema resolves: explicit arg > conn.schema > extra['schema']."""
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.example.com:8080",
+ schema="",
+ extra={"schema": "from_extra"},
+ )
+
+ hook = VespaHook()
+ assert hook.schema == "from_extra"
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_from_resolved_connection(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="document",
+ namespace="prod",
+ extra={},
+ )
+
+ assert hook.namespace == "prod"
+ assert hook.schema == "document"
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_from_resolved_connection_schema_fallback_to_extra(self, mock_vespa):
+ """When schema arg is empty, falls back to extra['schema']."""
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="",
+ extra={"schema": "doc_from_extra"},
+ )
+ assert hook.schema == "doc_from_extra"
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_normalise_already_vespa_format(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ docs = [
+ {"id": "doc1", "fields": {"title": "Test 1"}},
+ {"id": "doc2", "fields": {"title": "Test 2", "content": "Content"}},
+ ]
+ result = hook._normalise(docs)
+
+ assert len(result) == 2
+ assert result[0] == {"id": "doc1", "fields": {"title": "Test 1"}}
+ assert result[1] == {"id": "doc2", "fields": {"title": "Test 2", "content": "Content"}}
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_normalise_raw_fields(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ docs = [
+ {"id": "doc1", "title": "Test 1"},
+ {"id": "doc2", "title": "Test 2", "content": "Content"},
+ ]
+ result = hook._normalise(docs)
+
+ assert len(result) == 2
+ assert result[0] == {"id": "doc1", "fields": {"title": "Test 1"}}
+ assert result[1] == {"id": "doc2", "fields": {"title": "Test 2", "content": "Content"}}
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_normalise_does_not_mutate_input(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ original = {"id": "doc1", "title": "Test 1"}
+ docs = [original]
+ hook._normalise(docs)
+
+ assert "id" in original
+ assert original == {"id": "doc1", "title": "Test 1"}
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_normalise_generate_missing_ids_for_feed(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ docs = [{"title": "Test 1"}, {"title": "Test 2"}]
+ result = hook._normalise(docs, operation_type="feed")
+
+ assert len(result) == 2
+ assert result[0]["fields"] == {"title": "Test 1"}
+ assert result[1]["fields"] == {"title": "Test 2"}
+ uuid.UUID(result[0]["id"])
+ uuid.UUID(result[1]["id"])
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_normalise_update_requires_id(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ with pytest.raises(ValueError, match="missing required 'id'.*update"):
+ hook._normalise([{"title": "no id"}], operation_type="update")
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_normalise_delete_requires_id(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ with pytest.raises(ValueError, match="missing required 'id'.*delete"):
+ hook._normalise([{"fields": {}}], operation_type="delete")
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_normalise_update_with_vespa_format_requires_id(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ with pytest.raises(ValueError, match="missing required 'id'.*update"):
+ hook._normalise([{"fields": {"title": {"assign": "New"}}}], operation_type="update")
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_default_callback_successful_response(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ mock_response = Mock(spec=["is_successful", "status_code", "get_json"])
+ mock_response.is_successful.return_value = True
+
+ hook.default_callback(mock_response, "doc1")
+ assert hook.feed_errors_queue.empty()
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_default_callback_failed_response_with_json(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ mock_response = Mock(spec=["is_successful", "status_code", "get_json"])
+ mock_response.is_successful.return_value = False
+ mock_response.status_code = 400
+ mock_response.get_json.return_value = {"error": "Invalid document format"}
+
+ hook.default_callback(mock_response, "doc1")
+
+ assert not hook.feed_errors_queue.empty()
+ error = hook.feed_errors_queue.get()
+ assert error["id"] == "doc1"
+ assert error["status"] == 400
+ assert error["reason"] == {"error": "Invalid document format"}
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_default_callback_failed_response_json_parse_error(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ mock_response = Mock(spec=["is_successful", "status_code", "get_json"])
+ mock_response.is_successful.return_value = False
+ mock_response.status_code = 500
+ mock_response.get_json.side_effect = ValueError("Invalid JSON")
+
+ hook.default_callback(mock_response, "doc2")
+
+ error = hook.feed_errors_queue.get()
+ assert error["id"] == "doc2"
+ assert error["status"] == 500
+ assert "json_parse_error" in error["reason"]
+ assert "Invalid JSON" in error["reason"]["json_parse_error"]
+ assert error["reason"]["status_code"] == 500
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_feed_iterable_success_default_operation(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+ mock_app = Mock(spec=["feed_async_iterable"])
+ hook.vespa_app = mock_app
+
+ docs = [
+ {"id": "doc1", "title": "Test 1"},
+ {"id": "doc2", "title": "Test 2"},
+ ]
+ result = hook.feed_iterable(docs)
+
+ mock_app.feed_async_iterable.assert_called_once()
+ call_kwargs = mock_app.feed_async_iterable.call_args[1]
+
+ assert call_kwargs["operation_type"] == "feed"
+ assert call_kwargs["schema"] == "doc"
+ assert call_kwargs["namespace"] == "test"
+ assert call_kwargs["callback"] == hook.default_callback
+ assert len(call_kwargs["iter"]) == 2
+ assert call_kwargs["iter"][0] == {"id": "doc1", "fields": {"title": "Test 1"}}
+
+ assert result["sent"] == 2
+ assert result["errors"] == 0
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_feed_iterable_invalid_operation_type(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ with pytest.raises(ValueError, match="Invalid operation_type"):
+ hook.feed_iterable([{"id": "1"}], operation_type="upsert")
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_feed_iterable_custom_operation_type(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+ mock_app = Mock(spec=["feed_async_iterable"])
+ hook.vespa_app = mock_app
+
+ docs = [{"id": "doc1", "fields": {"title": {"assign": "Updated Title"}}}]
+ hook.feed_iterable(docs, operation_type="update")
+
+ call_kwargs = mock_app.feed_async_iterable.call_args[1]
+ assert call_kwargs["operation_type"] == "update"
+ assert call_kwargs["iter"][0] == {"id": "doc1", "fields": {"title": {"assign": "Updated Title"}}}
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_feed_iterable_forwards_kwargs(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+ mock_app = Mock(spec=["feed_async_iterable"])
+ hook.vespa_app = mock_app
+
+ docs = [{"id": "doc1", "fields": {"title": {"assign": "New"}}}]
+ hook.feed_iterable(docs, operation_type="update", auto_assign=False, create=True)
+
+ call_kwargs = mock_app.feed_async_iterable.call_args[1]
+ assert call_kwargs["auto_assign"] is False
+ assert call_kwargs["create"] is True
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_feed_iterable_custom_callback(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+ mock_app = Mock(spec=["feed_async_iterable"])
+ hook.vespa_app = mock_app
+
+ def custom_callback(*args, **kwargs):
+ return None
+
+ docs = [{"id": "doc1", "title": "Test"}]
+ hook.feed_iterable(docs, callback=custom_callback)
+
+ call_kwargs = mock_app.feed_async_iterable.call_args[1]
+ assert call_kwargs["callback"] == custom_callback
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_feed_iterable_with_optional_params(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={
+ "max_queue_size": 1000,
+ "max_workers": 8,
+ "max_connections": 20,
+ },
+ )
+ mock_app = Mock(spec=["feed_async_iterable"])
+ hook.vespa_app = mock_app
+
+ docs = [{"id": "doc1", "title": "Test"}]
+ hook.feed_iterable(docs)
+
+ call_kwargs = mock_app.feed_async_iterable.call_args[1]
+ assert call_kwargs["max_queue_size"] == 1000
+ assert call_kwargs["max_workers"] == 8
+ assert call_kwargs["max_connections"] == 20
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_feed_iterable_with_errors(self, mock_vespa):
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+ mock_app = Mock(spec=["feed_async_iterable"])
+ hook.vespa_app = mock_app
+
+ def mock_feed_async(**kwargs):
+ hook.feed_errors_queue.put({"id": "doc1", "status": 400, "reason": {"error": "Bad request"}})
+ hook.feed_errors_queue.put({"id": "doc3", "status": 500, "reason": {"error": "Server error"}})
+
+ mock_app.feed_async_iterable.side_effect = mock_feed_async
+
+ docs = [
+ {"id": "doc1", "title": "Bad doc"},
+ {"id": "doc2", "title": "Good doc"},
+ {"id": "doc3", "title": "Another bad doc"},
+ ]
+ result = hook.feed_iterable(docs)
+
+ assert result["sent"] == 3
+ assert result["errors"] == 2
+ assert result["error_details"][0]["id"] == "doc1"
+ assert result["error_details"][1]["id"] == "doc3"
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_authentication_mtls_with_certificates(self, mock_vespa_class):
+ VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={
+ "client_cert_path": "/path/to/client.pem",
+ "client_key_path": "/path/to/client.key",
+ "vespa_cloud_secret_token": "unused_token",
+ },
+ )
+
+ call_kwargs = mock_vespa_class.call_args[1]
+ assert call_kwargs["cert"] == "/path/to/client.pem"
+ assert call_kwargs["key"] == "/path/to/client.key"
+ assert call_kwargs["vespa_cloud_secret_token"] == "unused_token"
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_authentication_mtls_with_prefixed_keys(self, mock_vespa_class):
+ """Backward compatibility: prefixed keys still work."""
+ VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={
+ "extra__vespa__client_cert_path": "/path/to/client.pem",
+ "extra__vespa__client_key_path": "/path/to/client.key",
+ },
+ )
+
+ call_kwargs = mock_vespa_class.call_args[1]
+ assert call_kwargs["cert"] == "/path/to/client.pem"
+ assert call_kwargs["key"] == "/path/to/client.key"
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_authentication_token_fallback(self, mock_vespa_class):
+ VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={"vespa_cloud_secret_token": "secret_token_123"},
+ )
+
+ call_kwargs = mock_vespa_class.call_args[1]
+ assert call_kwargs["vespa_cloud_secret_token"] == "secret_token_123"
+ assert call_kwargs["cert"] is None
+ assert call_kwargs["key"] is None
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_authentication_partial_certificates_fallback_to_token(self, mock_vespa_class):
+ VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={
+ "client_cert_path": "/path/to/client.pem",
+ "vespa_cloud_secret_token": "fallback_token",
+ },
+ )
+
+ call_kwargs = mock_vespa_class.call_args[1]
+ assert call_kwargs["cert"] is None
+ assert call_kwargs["key"] is None
+ assert call_kwargs["vespa_cloud_secret_token"] == "fallback_token"
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_authentication_no_credentials(self, mock_vespa_class):
+ VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ call_kwargs = mock_vespa_class.call_args[1]
+ assert call_kwargs["cert"] is None
+ assert call_kwargs["key"] is None
+ assert call_kwargs["vespa_cloud_secret_token"] is None
+
+
+class TestVespaHookTestConnection:
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_test_connection_success(self, mock_vespa_class):
+ mock_vespa_instance = mock_vespa_class.return_value
+ mock_resp = Mock(spec=["status_code"], status_code=200)
+ mock_vespa_instance.get_application_status.return_value = mock_resp
+
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ ok, msg = hook.test_connection()
+
+ assert ok is True
+ assert msg == "Connection successful"
+ mock_vespa_instance.get_application_status.assert_called_once()
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_test_connection_failure(self, mock_vespa_class):
+ mock_vespa_instance = mock_vespa_class.return_value
+ mock_resp = Mock(spec=["status_code"], status_code=503)
+ mock_vespa_instance.get_application_status.return_value = mock_resp
+
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ ok, msg = hook.test_connection()
+
+ assert ok is False
+ assert "503" in msg
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_test_connection_none_response(self, mock_vespa_class):
+ mock_vespa_instance = mock_vespa_class.return_value
+ mock_vespa_instance.get_application_status.return_value = None
+
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ ok, msg = hook.test_connection()
+
+ assert ok is False
+ assert "no response" in msg
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_test_connection_exception(self, mock_vespa_class):
+ mock_vespa_instance = mock_vespa_class.return_value
+ mock_vespa_instance.get_application_status.side_effect = Exception("Connection refused")
+
+ hook = VespaHook.from_resolved_connection(
+ host="https://vespa.example.com:8080",
+ schema="doc",
+ namespace="test",
+ extra={},
+ )
+
+ ok, msg = hook.test_connection()
+
+ assert ok is False
+ assert "Connection refused" in msg
+
+
+class TestVespaHookConfiguration:
+ """Tests for Vespa client construction through both __init__ and from_resolved_connection."""
+
+ @patch("airflow.providers.vespa.hooks.vespa.BaseHook.get_connection")
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_conn_id_stored_on_instance(self, mock_vespa, mock_get_connection):
+ """Regression: conn_name_attr='conn_id' requires self.conn_id to be set."""
+ mock_get_connection.return_value = _make_connection(host="https://vespa.example.com", schema="doc")
+
+ hook = VespaHook(conn_id="custom_conn")
+
+ assert hook.conn_id == "custom_conn"
+
+ @patch("airflow.providers.vespa.hooks.vespa.BaseHook.get_connection")
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_init_bare_host_gets_protocol_prepended(self, mock_vespa, mock_get_connection):
+ mock_get_connection.return_value = _make_connection(
+ host="vespa.example.com",
+ schema="doc",
+ extra={"protocol": "https"},
+ )
+
+ VespaHook(conn_id="test")
+
+ assert mock_vespa.call_args[1]["url"] == "https://vespa.example.com"
+
+ @patch("airflow.providers.vespa.hooks.vespa.BaseHook.get_connection")
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_init_host_with_scheme_ignores_protocol(self, mock_vespa, mock_get_connection):
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.example.com",
+ schema="doc",
+ extra={"protocol": "http"},
+ )
+
+ VespaHook(conn_id="test")
+
+ assert mock_vespa.call_args[1]["url"] == "https://vespa.example.com"
+
+ @patch("airflow.providers.vespa.hooks.vespa.BaseHook.get_connection")
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_init_port_appended_when_missing(self, mock_vespa, mock_get_connection):
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.example.com",
+ schema="doc",
+ port=4080,
+ )
+
+ VespaHook(conn_id="test")
+
+ assert mock_vespa.call_args[1]["url"] == "https://vespa.example.com:4080"
+
+ @patch("airflow.providers.vespa.hooks.vespa.BaseHook.get_connection")
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_init_port_not_duplicated(self, mock_vespa, mock_get_connection):
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.example.com:4080",
+ schema="doc",
+ port=4080,
+ )
+
+ VespaHook(conn_id="test")
+
+ assert mock_vespa.call_args[1]["url"] == "https://vespa.example.com:4080"
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_from_resolved_token_passed_through(self, mock_vespa):
+ VespaHook.from_resolved_connection(
+ host="https://vespa.example.com",
+ schema="doc",
+ extra={"vespa_cloud_secret_token": "tok_123"},
+ )
+
+ call_kwargs = mock_vespa.call_args[1]
+ assert call_kwargs["vespa_cloud_secret_token"] == "tok_123"
+ assert call_kwargs["cert"] is None
+ assert call_kwargs["key"] is None
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_from_resolved_cert_and_key_passed_through(self, mock_vespa):
+ VespaHook.from_resolved_connection(
+ host="https://vespa.example.com",
+ schema="doc",
+ extra={
+ "client_cert_path": "/certs/client.pem",
+ "client_key_path": "/certs/client.key",
+ },
+ )
+
+ call_kwargs = mock_vespa.call_args[1]
+ assert call_kwargs["cert"] == "/certs/client.pem"
+ assert call_kwargs["key"] == "/certs/client.key"
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_from_resolved_partial_cert_falls_back_to_no_cert(self, mock_vespa):
+ VespaHook.from_resolved_connection(
+ host="https://vespa.example.com",
+ schema="doc",
+ extra={"client_cert_path": "/certs/client.pem"},
+ )
+
+ call_kwargs = mock_vespa.call_args[1]
+ assert call_kwargs["cert"] is None
+ assert call_kwargs["key"] is None
+
+ @patch("airflow.providers.vespa.hooks.vespa.Vespa")
+ def test_from_resolved_port_appended(self, mock_vespa):
+ VespaHook.from_resolved_connection(
+ host="https://vespa.example.com",
+ port=19071,
+ schema="doc",
+ extra={},
+ )
+
+ assert mock_vespa.call_args[1]["url"] == "https://vespa.example.com:19071"
diff --git a/providers/vespa/tests/unit/vespa/operators/__init__.py b/providers/vespa/tests/unit/vespa/operators/__init__.py
new file mode 100644
index 00000000000..13a83393a91
--- /dev/null
+++ b/providers/vespa/tests/unit/vespa/operators/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/providers/vespa/tests/unit/vespa/operators/test_vespa_ingest.py b/providers/vespa/tests/unit/vespa/operators/test_vespa_ingest.py
new file mode 100644
index 00000000000..7cf526a9afe
--- /dev/null
+++ b/providers/vespa/tests/unit/vespa/operators/test_vespa_ingest.py
@@ -0,0 +1,243 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import json
+from unittest.mock import Mock, patch
+
+import pytest
+
+from airflow.providers.common.compat.sdk import AirflowException, Connection, TaskDeferred
+from airflow.providers.vespa.operators.vespa_ingest import VespaIngestOperator
+
+
+def _make_context() -> dict[str, Mock]:
+ return {"task_instance": Mock(spec=["task_id"])}
+
+
+def _make_connection(
+ *,
+ host: str,
+ schema: str | None,
+ port: int | None = None,
+ extra: dict | None = None,
+) -> Connection:
+ return Connection(
+ conn_id="vespa_test",
+ conn_type="vespa",
+ host=host,
+ schema=schema,
+ port=port,
+ extra=json.dumps(extra or {}),
+ )
+
+
+class TestVespaIngestOperator:
+ def test_init(self):
+ docs = [{"id": "1", "title": "Test"}]
+ op = VespaIngestOperator(
+ task_id="test_ingest",
+ docs=docs,
+ vespa_conn_id="test_conn",
+ operation_type="feed",
+ )
+
+ assert op.docs == docs
+ assert op.vespa_conn_id == "test_conn"
+ assert op.operation_type == "feed"
+ assert op.feed_kwargs == {}
+
+ def test_init_with_feed_kwargs(self):
+ op = VespaIngestOperator(
+ task_id="test_ingest",
+ docs=[{"id": "1", "fields": {"x": {"assign": 1}}}],
+ operation_type="update",
+ feed_kwargs={"auto_assign": False, "create": True},
+ )
+
+ assert op.feed_kwargs == {"auto_assign": False, "create": True}
+
+ def test_init_invalid_operation_type(self):
+ with pytest.raises(ValueError, match="Invalid operation_type"):
+ VespaIngestOperator(task_id="test", docs=[{"id": "1"}], operation_type="upsert")
+
+ @pytest.mark.parametrize(
+ ("extra", "expected_namespace"),
+ [
+ pytest.param({"namespace": "test"}, "test", id="bare-namespace"),
+ pytest.param({"extra__vespa__namespace": "legacy-test"}, "legacy-test", id="legacy-namespace"),
+ pytest.param({}, "default", id="default-namespace"),
+ ],
+ )
+ def test_execute_defers_to_trigger(self, extra, expected_namespace):
+ with patch(
+ "airflow.providers.vespa.operators.vespa_ingest.BaseHook.get_connection"
+ ) as mock_get_connection:
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.test",
+ port=19071,
+ schema="doc",
+ extra=extra,
+ )
+
+ docs = [{"id": "1", "content": "test"}]
+ op = VespaIngestOperator(task_id="test_task", docs=docs, vespa_conn_id="test_conn")
+
+ with pytest.raises(TaskDeferred) as exc_info:
+ op.execute(_make_context())
+
+ trigger = exc_info.value.trigger
+ assert trigger.docs == docs
+ assert trigger.conn_info == {
+ "host": "https://vespa.test",
+ "port": 19071,
+ "schema": "doc",
+ "namespace": expected_namespace,
+ "extra": extra,
+ }
+ assert trigger.operation_type == "feed"
+ assert trigger.feed_kwargs == {}
+ assert exc_info.value.method_name == "execute_complete"
+
+ @patch("airflow.providers.vespa.operators.vespa_ingest.BaseHook.get_connection")
+ def test_execute_forwards_feed_kwargs(self, mock_get_connection):
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.test:8080",
+ schema="doc",
+ extra={"namespace": "test"},
+ )
+
+ op = VespaIngestOperator(
+ task_id="test_task",
+ docs=[{"id": "1", "fields": {"x": 1}}],
+ vespa_conn_id="test_conn",
+ operation_type="update",
+ feed_kwargs={"auto_assign": False},
+ )
+
+ with pytest.raises(TaskDeferred) as exc_info:
+ op.execute(_make_context())
+
+ assert exc_info.value.trigger.feed_kwargs == {"auto_assign": False}
+
+ @patch("airflow.providers.vespa.operators.vespa_ingest.BaseHook.get_connection")
+ def test_execute_materializes_iterator(self, mock_get_connection):
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.test:8080",
+ schema="doc",
+ extra={"namespace": "test"},
+ )
+
+ docs_iter = iter([{"id": "1"}, {"id": "2"}])
+ op = VespaIngestOperator(task_id="test_task", docs=docs_iter, vespa_conn_id="test_conn")
+
+ with pytest.raises(TaskDeferred) as exc_info:
+ op.execute(_make_context())
+
+ assert exc_info.value.trigger.docs == [{"id": "1"}, {"id": "2"}]
+
+ def test_execute_complete_success(self):
+ docs = [{"id": "1"}, {"id": "2"}]
+ op = VespaIngestOperator(task_id="test_ingest", docs=docs, vespa_conn_id="test_conn")
+
+ result = op.execute_complete({}, {"success": True, "sent": 2})
+ assert result == {"ingested": 2}
+
+ def test_execute_complete_failure_with_errors(self):
+ docs = [{"id": "1"}, {"id": "2"}, {"id": "3"}]
+ op = VespaIngestOperator(task_id="test_ingest", docs=docs, vespa_conn_id="test_conn")
+
+ failure_event = {
+ "success": False,
+ "sent": 3,
+ "errors": [
+ {"id": "2", "status": 400, "reason": {"error": "Invalid format"}},
+ {"id": "3", "status": 500, "reason": {"error": "Server error"}},
+ ],
+ }
+
+ with pytest.raises(AirflowException) as exc_info:
+ op.execute_complete({}, failure_event)
+
+ msg = str(exc_info.value)
+ assert "2 document(s) failed" in msg
+
+ def test_execute_complete_failure_with_exception_error(self):
+ op = VespaIngestOperator(task_id="test_ingest", docs=[{"id": "1"}], vespa_conn_id="test_conn")
+
+ failure_event = {
+ "success": False,
+ "errors": [{"error": "Trigger failed: ConnectionError: Unable to connect"}],
+ }
+
+ with pytest.raises(AirflowException) as exc_info:
+ op.execute_complete({}, failure_event)
+
+ assert "ConnectionError" in str(exc_info.value)
+
+ @patch("airflow.providers.vespa.operators.vespa_ingest.BaseHook.get_connection")
+ def test_execute_materializes_to_self_docs(self, mock_get_connection):
+ """After execute(), self.docs should be a list so execute_complete can use len()."""
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.test:8080",
+ schema="doc",
+ extra={"namespace": "test"},
+ )
+
+ docs_iter = iter([{"id": "1"}, {"id": "2"}])
+ op = VespaIngestOperator(task_id="test_task", docs=docs_iter, vespa_conn_id="test_conn")
+
+ with pytest.raises(TaskDeferred):
+ op.execute(_make_context())
+
+ assert isinstance(op.docs, list)
+ assert len(op.docs) == 2
+
+ @patch("airflow.providers.vespa.operators.vespa_ingest.BaseHook.get_connection")
+ def test_execute_rejects_non_dict_docs(self, mock_get_connection):
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.test:8080",
+ schema="doc",
+ extra={"namespace": "test"},
+ )
+
+ op = VespaIngestOperator(
+ task_id="test_task",
+ docs=[{"id": "1"}, "not-a-dict"],
+ vespa_conn_id="test_conn",
+ )
+
+ with pytest.raises(TypeError, match="docs\\[1\\] must be a dict"):
+ op.execute(_make_context())
+
+ @patch("airflow.providers.vespa.operators.vespa_ingest.BaseHook.get_connection")
+ def test_execute_rejects_non_serializable_feed_kwargs(self, mock_get_connection):
+ mock_get_connection.return_value = _make_connection(
+ host="https://vespa.test:8080",
+ schema="doc",
+ extra={"namespace": "test"},
+ )
+
+ op = VespaIngestOperator(
+ task_id="test_task",
+ docs=[{"id": "1", "fields": {"x": 1}}],
+ vespa_conn_id="test_conn",
+ feed_kwargs={"callback": lambda response: response},
+ )
+
+ with pytest.raises(ValueError, match="feed_kwargs must be JSON-serializable"):
+ op.execute(_make_context())
diff --git a/providers/vespa/tests/unit/vespa/triggers/__init__.py b/providers/vespa/tests/unit/vespa/triggers/__init__.py
new file mode 100644
index 00000000000..13a83393a91
--- /dev/null
+++ b/providers/vespa/tests/unit/vespa/triggers/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/providers/vespa/tests/unit/vespa/triggers/test_vespa_feed_trigger.py b/providers/vespa/tests/unit/vespa/triggers/test_vespa_feed_trigger.py
new file mode 100644
index 00000000000..5700b4ea824
--- /dev/null
+++ b/providers/vespa/tests/unit/vespa/triggers/test_vespa_feed_trigger.py
@@ -0,0 +1,243 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from unittest.mock import Mock, patch
+
+import pytest
+
+from airflow.providers.vespa.triggers.vespa_feed_trigger import VespaFeedTrigger
+
+
+class TestVespaFeedTrigger:
+ def test_init(self):
+ docs = [{"id": "1", "title": "Test"}]
+ conn_info = {"host": "https://vespa.test:8080", "namespace": "test", "schema": "doc"}
+
+ trigger = VespaFeedTrigger(docs=docs, conn_info=conn_info, operation_type="feed")
+
+ assert trigger.docs == docs
+ assert trigger.conn_info == conn_info
+ assert trigger.operation_type == "feed"
+ assert trigger.feed_kwargs == {}
+
+ def test_init_with_feed_kwargs(self):
+ trigger = VespaFeedTrigger(
+ docs=[{"id": "1"}],
+ conn_info={"host": "h"},
+ operation_type="update",
+ feed_kwargs={"auto_assign": False, "create": True},
+ )
+
+ assert trigger.feed_kwargs == {"auto_assign": False, "create": True}
+
+ def test_serialize(self):
+ docs = [{"id": "1"}]
+ conn_info = {"host": "test"}
+ kw = {"auto_assign": False}
+
+ trigger = VespaFeedTrigger(docs=docs, conn_info=conn_info, feed_kwargs=kw)
+ class_path, data = trigger.serialize()
+
+ assert class_path == "airflow.providers.vespa.triggers.vespa_feed_trigger.VespaFeedTrigger"
+ assert data["docs"] == docs
+ assert data["conn_info"] == conn_info
+ assert data["operation_type"] == "feed"
+ assert data["feed_kwargs"] == kw
+
+ @pytest.mark.asyncio
+ async def test_run_success(self):
+ docs = [{"id": "1", "content": "test"}]
+ conn_info = {
+ "host": "vespa.test",
+ "port": 19071,
+ "namespace": "test",
+ "schema": "doc",
+ "extra": {"protocol": "https", "max_workers": 4},
+ }
+ trigger = VespaFeedTrigger(docs=docs, conn_info=conn_info)
+
+ with patch("airflow.providers.vespa.hooks.vespa.VespaHook") as mock_hook_class:
+ mock_hook = Mock(spec=["feed_iterable"])
+ mock_hook_class.from_resolved_connection.return_value = mock_hook
+
+ def feed_iterable(*args, **kwargs):
+ mock_hook_class.from_resolved_connection.assert_called_once_with(
+ host="vespa.test",
+ port=19071,
+ schema="doc",
+ namespace="test",
+ extra={"protocol": "https", "max_workers": 4},
+ )
+ return {"sent": 1, "errors": 0, "error_details": []}
+
+ mock_hook.feed_iterable.side_effect = feed_iterable
+
+ events = []
+ async for event in trigger.run():
+ events.append(event)
+
+ mock_hook.feed_iterable.assert_called_once_with(docs, operation_type="feed")
+ assert len(events) == 1
+ assert events[0].payload["success"] is True
+ assert events[0].payload["sent"] == 1
+
+ @pytest.mark.asyncio
+ async def test_run_forwards_feed_kwargs(self):
+ docs = [{"id": "1", "fields": {"x": {"assign": 1}}}]
+ conn_info = {
+ "host": "https://vespa.test:8080",
+ "namespace": "test",
+ "schema": "doc",
+ "extra": {},
+ }
+ trigger = VespaFeedTrigger(
+ docs=docs,
+ conn_info=conn_info,
+ operation_type="update",
+ feed_kwargs={"auto_assign": False, "create": True},
+ )
+
+ with patch("airflow.providers.vespa.hooks.vespa.VespaHook") as mock_hook_class:
+ mock_hook = Mock(spec=["feed_iterable"])
+ mock_hook.feed_iterable.return_value = {"sent": 1, "errors": 0, "error_details": []}
+ mock_hook_class.from_resolved_connection.return_value = mock_hook
+
+ events = []
+ async for event in trigger.run():
+ events.append(event)
+
+ mock_hook.feed_iterable.assert_called_once_with(
+ docs, operation_type="update", auto_assign=False, create=True
+ )
+
+ @pytest.mark.asyncio
+ async def test_run_hook_creation_failure(self):
+ trigger = VespaFeedTrigger(
+ docs=[{"id": "1"}],
+ conn_info={"host": "invalid", "namespace": "test", "schema": "doc", "extra": {}},
+ )
+
+ with patch("airflow.providers.vespa.hooks.vespa.VespaHook") as mock_hook_class:
+ mock_hook_class.from_resolved_connection.side_effect = ValueError("Invalid host URL")
+
+ events = []
+ async for event in trigger.run():
+ events.append(event)
+
+ assert len(events) == 1
+ assert events[0].payload["success"] is False
+ assert "ValueError: Invalid host URL" in events[0].payload["errors"][0]["error"]
+
+ @pytest.mark.asyncio
+ async def test_run_feed_operation_failure(self):
+ trigger = VespaFeedTrigger(
+ docs=[{"id": "1"}],
+ conn_info={"host": "https://vespa.test:8080", "namespace": "test", "schema": "doc", "extra": {}},
+ )
+
+ with patch("airflow.providers.vespa.hooks.vespa.VespaHook") as mock_hook_class:
+ mock_hook = Mock(spec=["feed_iterable"])
+ mock_hook.feed_iterable.side_effect = ConnectionError("Unable to connect")
+ mock_hook_class.from_resolved_connection.return_value = mock_hook
+
+ events = []
+ async for event in trigger.run():
+ events.append(event)
+
+ assert events[0].payload["success"] is False
+ assert "ConnectionError" in events[0].payload["errors"][0]["error"]
+
+ @pytest.mark.asyncio
+ async def test_run_with_feed_errors(self):
+ docs = [{"id": "1"}, {"id": "2"}, {"id": "3"}]
+ conn_info = {"host": "https://vespa.test:8080", "namespace": "test", "schema": "doc", "extra": {}}
+ trigger = VespaFeedTrigger(docs=docs, conn_info=conn_info)
+
+ with patch("airflow.providers.vespa.hooks.vespa.VespaHook") as mock_hook_class:
+ mock_hook = Mock(spec=["feed_iterable"])
+ mock_hook.feed_iterable.return_value = {
+ "sent": 3,
+ "errors": 2,
+ "error_details": [
+ {"id": "2", "status": 400, "reason": {"error": "Invalid"}},
+ {"id": "3", "status": 500, "reason": {"error": "Server error"}},
+ ],
+ }
+ mock_hook_class.from_resolved_connection.return_value = mock_hook
+
+ events = []
+ async for event in trigger.run():
+ events.append(event)
+
+ assert events[0].payload["success"] is False
+ assert events[0].payload["sent"] == 3
+ assert len(events[0].payload["errors"]) == 2
+
+ @pytest.mark.asyncio
+ async def test_run_update_operation(self):
+ docs = [{"id": "1", "fields": {"title": {"assign": "Updated"}}}]
+ conn_info = {"host": "https://vespa.test:8080", "namespace": "test", "schema": "doc", "extra": {}}
+ trigger = VespaFeedTrigger(docs=docs, conn_info=conn_info, operation_type="update")
+
+ with patch("airflow.providers.vespa.hooks.vespa.VespaHook") as mock_hook_class:
+ mock_hook = Mock(spec=["feed_iterable"])
+ mock_hook.feed_iterable.return_value = {"sent": 1, "errors": 0, "error_details": []}
+ mock_hook_class.from_resolved_connection.return_value = mock_hook
+
+ events = []
+ async for event in trigger.run():
+ events.append(event)
+
+ mock_hook.feed_iterable.assert_called_once_with(docs, operation_type="update")
+ assert events[0].payload["success"] is True
+
+ @pytest.mark.asyncio
+ async def test_run_delete_operation(self):
+ docs = [{"id": "1"}, {"id": "2"}]
+ conn_info = {"host": "https://vespa.test:8080", "namespace": "test", "schema": "doc", "extra": {}}
+ trigger = VespaFeedTrigger(docs=docs, conn_info=conn_info, operation_type="delete")
+
+ with patch("airflow.providers.vespa.hooks.vespa.VespaHook") as mock_hook_class:
+ mock_hook = Mock(spec=["feed_iterable"])
+ mock_hook.feed_iterable.return_value = {"sent": 2, "errors": 0, "error_details": []}
+ mock_hook_class.from_resolved_connection.return_value = mock_hook
+
+ events = []
+ async for event in trigger.run():
+ events.append(event)
+
+ mock_hook.feed_iterable.assert_called_once_with(docs, operation_type="delete")
+ assert events[0].payload["success"] is True
+
+ @pytest.mark.asyncio
+ async def test_run_default_feed_operation(self):
+ docs = [{"id": "1", "content": "test"}]
+ conn_info = {"host": "https://vespa.test:8080", "namespace": "test", "schema": "doc", "extra": {}}
+ trigger = VespaFeedTrigger(docs=docs, conn_info=conn_info)
+
+ with patch("airflow.providers.vespa.hooks.vespa.VespaHook") as mock_hook_class:
+ mock_hook = Mock(spec=["feed_iterable"])
+ mock_hook.feed_iterable.return_value = {"sent": 1, "errors": 0, "error_details": []}
+ mock_hook_class.from_resolved_connection.return_value = mock_hook
+
+ events = []
+ async for event in trigger.run():
+ events.append(event)
+
+ mock_hook.feed_iterable.assert_called_once_with(docs, operation_type="feed")
+ assert events[0].payload["success"] is True
diff --git a/pyproject.toml b/pyproject.toml
index 5e9600bbbfe..3bebe69019c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -380,6 +380,9 @@ apache-airflow = "airflow.__main__:main"
"vertica" = [
"apache-airflow-providers-vertica>=3.9.1"
]
+"vespa" = [
+ "apache-airflow-providers-vespa>=0.1.0" # Set from local provider pyproject.toml
+]
"weaviate" = [
"apache-airflow-providers-weaviate>=3.0.0"
]
@@ -489,6 +492,7 @@ apache-airflow = "airflow.__main__:main"
"apache-airflow-providers-teradata>=2.6.1",
"apache-airflow-providers-trino>=5.8.1",
"apache-airflow-providers-vertica>=3.9.1",
+ "apache-airflow-providers-vespa>=0.1.0", # Set from local provider pyproject.toml
"apache-airflow-providers-weaviate>=3.0.0",
"apache-airflow-providers-yandex>=4.0.0",
"apache-airflow-providers-ydb>=1.4.0",
@@ -1238,6 +1242,8 @@ mypy_path = [
"$MYPY_CONFIG_FILE_DIR/providers/trino/tests",
"$MYPY_CONFIG_FILE_DIR/providers/vertica/src",
"$MYPY_CONFIG_FILE_DIR/providers/vertica/tests",
+ "$MYPY_CONFIG_FILE_DIR/providers/vespa/src",
+ "$MYPY_CONFIG_FILE_DIR/providers/vespa/tests",
"$MYPY_CONFIG_FILE_DIR/providers/weaviate/src",
"$MYPY_CONFIG_FILE_DIR/providers/weaviate/tests",
"$MYPY_CONFIG_FILE_DIR/providers/yandex/src",
@@ -1456,6 +1462,7 @@ apache-airflow-providers-telegram = false
apache-airflow-providers-teradata = false
apache-airflow-providers-trino = false
apache-airflow-providers-vertica = false
+apache-airflow-providers-vespa = false
apache-airflow-providers-weaviate = false
apache-airflow-providers-yandex = false
apache-airflow-providers-ydb = false
@@ -1591,6 +1598,7 @@ apache-airflow-providers-telegram = false
apache-airflow-providers-teradata = false
apache-airflow-providers-trino = false
apache-airflow-providers-vertica = false
+apache-airflow-providers-vespa = false
apache-airflow-providers-weaviate = false
apache-airflow-providers-yandex = false
apache-airflow-providers-ydb = false
@@ -1742,6 +1750,7 @@ apache-airflow-providers-telegram = { workspace = true }
apache-airflow-providers-teradata = { workspace = true }
apache-airflow-providers-trino = { workspace = true }
apache-airflow-providers-vertica = { workspace = true }
+apache-airflow-providers-vespa = { workspace = true }
apache-airflow-providers-weaviate = { workspace = true }
apache-airflow-providers-yandex = { workspace = true }
apache-airflow-providers-ydb = { workspace = true }
@@ -1875,6 +1884,7 @@ members = [
"providers/teradata",
"providers/trino",
"providers/vertica",
+ "providers/vespa",
"providers/weaviate",
"providers/yandex",
"providers/ydb",
diff --git a/scripts/ci/docker-compose/remove-sources.yml b/scripts/ci/docker-compose/remove-sources.yml
index a4287f26e46..40232b8c3e7 100644
--- a/scripts/ci/docker-compose/remove-sources.yml
+++ b/scripts/ci/docker-compose/remove-sources.yml
@@ -121,6 +121,7 @@ services:
- ../../../empty:/opt/airflow/providers/teradata/src
- ../../../empty:/opt/airflow/providers/trino/src
- ../../../empty:/opt/airflow/providers/vertica/src
+ - ../../../empty:/opt/airflow/providers/vespa/src
- ../../../empty:/opt/airflow/providers/weaviate/src
- ../../../empty:/opt/airflow/providers/yandex/src
- ../../../empty:/opt/airflow/providers/ydb/src
diff --git a/scripts/ci/docker-compose/tests-sources.yml b/scripts/ci/docker-compose/tests-sources.yml
index eb9da3caceb..0601a438522 100644
--- a/scripts/ci/docker-compose/tests-sources.yml
+++ b/scripts/ci/docker-compose/tests-sources.yml
@@ -134,6 +134,7 @@ services:
- ../../../providers/teradata/tests:/opt/airflow/providers/teradata/tests
- ../../../providers/trino/tests:/opt/airflow/providers/trino/tests
- ../../../providers/vertica/tests:/opt/airflow/providers/vertica/tests
+ - ../../../providers/vespa/tests:/opt/airflow/providers/vespa/tests
- ../../../providers/weaviate/tests:/opt/airflow/providers/weaviate/tests
- ../../../providers/yandex/tests:/opt/airflow/providers/yandex/tests
- ../../../providers/ydb/tests:/opt/airflow/providers/ydb/tests
diff --git a/uv.lock b/uv.lock
index 33d5b729890..02ac63b80a8 100644
--- a/uv.lock
+++ b/uv.lock
@@ -12,7 +12,7 @@ resolution-markers = [
]
[options]
-exclude-newer = "2026-04-03T22:16:12.267492Z"
+exclude-newer = "2026-04-04T09:38:26.261919Z"
exclude-newer-span = "P4D"
[options.exclude-newer-package]
@@ -252,6 +252,7 @@ members = [
"apache-airflow-providers-teradata",
"apache-airflow-providers-trino",
"apache-airflow-providers-vertica",
+ "apache-airflow-providers-vespa",
"apache-airflow-providers-weaviate",
"apache-airflow-providers-yandex",
"apache-airflow-providers-ydb",
@@ -819,7 +820,7 @@ wheels = [
[[package]]
name = "anthropic"
-version = "0.88.0"
+version = "0.89.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@@ -831,9 +832,9 @@ dependencies = [
{ name = "sniffio" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/86/68/565f13059c0a6a6fd5f96f306f2a0fb478a0e1174ec18a4df16b5fac9379/anthropic-0.88.0.tar.gz", hash = "sha256:f4c7f6863d08c869913516f08d658fe53caaf8bcc4fbea3218df343d2a876c58", size = 596654, upload-time = "2026-04-01T19:59:05.287Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/60/af/862e216dd6c5e9bc02fb374eeaaa19017c51b90ddfa5692668a3811947bd/anthropic-0.89.0.tar.gz", hash = "sha256:f3d75b8ccef4b35f3702639519e461eba437d4bcdfabb69378c65a02ab7bda66", size = 596758, upload-time = "2026-04-03T18:57:01.348Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ad/ac/68f646998160c9f2e6f9353a31dd87292ef02b915b455aaf70a52a059a75/anthropic-0.88.0-py3-none-any.whl", hash = "sha256:71898b32332bc75d9739bc10095288d40a29605da6d00da2fe832b1aa036552f", size = 478338, upload-time = "2026-04-01T19:59:03.832Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ba/9f973f22abb512d5d17428a76e4ecbc8d49b9dd1b5a1152576d48c24dc1d/anthropic-0.89.0-py3-none-any.whl", hash = "sha256:c6d23854af798f2471ca3bc653cca394d392cc272fe803d3da9d63575b8445f0", size = 478847, upload-time = "2026-04-03T18:56:59.54Z" },
]
[[package]]
@@ -966,6 +967,7 @@ all = [
{ name = "apache-airflow-providers-teradata" },
{ name = "apache-airflow-providers-trino" },
{ name = "apache-airflow-providers-vertica" },
+ { name = "apache-airflow-providers-vespa" },
{ name = "apache-airflow-providers-weaviate" },
{ name = "apache-airflow-providers-yandex" },
{ name = "apache-airflow-providers-ydb" },
@@ -1323,6 +1325,9 @@ uv = [
vertica = [
{ name = "apache-airflow-providers-vertica" },
]
+vespa = [
+ { name = "apache-airflow-providers-vespa" },
+]
weaviate = [
{ name = "apache-airflow-providers-weaviate" },
]
@@ -1584,6 +1589,8 @@ requires-dist = [
{ name = "apache-airflow-providers-trino", marker = "extra == 'trino'", editable = "providers/trino" },
{ name = "apache-airflow-providers-vertica", marker = "extra == 'all'", editable = "providers/vertica" },
{ name = "apache-airflow-providers-vertica", marker = "extra == 'vertica'", editable = "providers/vertica" },
+ { name = "apache-airflow-providers-vespa", marker = "extra == 'all'", editable = "providers/vespa" },
+ { name = "apache-airflow-providers-vespa", marker = "extra == 'vespa'", editable = "providers/vespa" },
{ name = "apache-airflow-providers-weaviate", marker = "extra == 'all'", editable = "providers/weaviate" },
{ name = "apache-airflow-providers-weaviate", marker = "extra == 'weaviate'", editable = "providers/weaviate" },
{ name = "apache-airflow-providers-yandex", marker = "extra == 'all'", editable = "providers/yandex" },
@@ -1602,7 +1609,7 @@ requires-dist = [
{ name = "sentry-sdk", marker = "extra == 'sentry'", specifier = ">=2.30.0" },
{ name = "uv", marker = "extra == 'uv'", specifier = ">=0.11.3" },
]
-provides-extras = ["all-core", "async", "graphviz", "gunicorn", "kerberos", "memray", "otel", "statsd", "all-task-sdk", "airbyte", "alibaba", "amazon", "apache-cassandra", "apache-drill", "apache-druid", "apache-flink", "apache-hdfs", "apache-hive", "apache-iceberg", "apache-impala", "apache-kafka", "apache-kylin", "apache-livy", "apache-pig", "apache-pinot", "apache-spark", "apache-tinkerpop", "apprise", "arangodb", "asana", "atlassian-jira", "celery", "cloudant", "cncf-kubernetes", "co [...]
+provides-extras = ["all-core", "async", "graphviz", "gunicorn", "kerberos", "memray", "otel", "statsd", "all-task-sdk", "airbyte", "alibaba", "amazon", "apache-cassandra", "apache-drill", "apache-druid", "apache-flink", "apache-hdfs", "apache-hive", "apache-iceberg", "apache-impala", "apache-kafka", "apache-kylin", "apache-livy", "apache-pig", "apache-pinot", "apache-spark", "apache-tinkerpop", "apprise", "arangodb", "asana", "atlassian-jira", "celery", "cloudant", "cncf-kubernetes", "co [...]
[package.metadata.requires-dev]
dev = [
@@ -6925,7 +6932,7 @@ requires-dist = [
{ name = "apache-airflow-providers-ssh", editable = "providers/ssh" },
{ name = "asyncssh", marker = "python_full_version < '3.14'", specifier = ">=2.12.0" },
{ name = "asyncssh", marker = "python_full_version >= '3.14'", specifier = ">=2.22.0" },
- { name = "paramiko", specifier = ">=3.4.0,<4.0.0" },
+ { name = "paramiko", specifier = ">=3.5.1,<4.0.0" },
{ name = "sshfs", marker = "extra == 'sshfs'", specifier = ">=2023.1.0" },
]
provides-extras = ["openlineage", "sshfs"]
@@ -7200,7 +7207,7 @@ requires-dist = [
{ name = "apache-airflow", editable = "." },
{ name = "apache-airflow-providers-common-compat", editable = "providers/common/compat" },
{ name = "asyncssh", specifier = ">=2.12.0" },
- { name = "paramiko", specifier = ">=3.4.0,<4.0.0" },
+ { name = "paramiko", specifier = ">=3.5.1,<4.0.0" },
]
[package.metadata.requires-dev]
@@ -7514,6 +7521,43 @@ dev = [
]
docs = [{ name = "apache-airflow-devel-common", extras = ["docs"], editable = "devel-common" }]
+[[package]]
+name = "apache-airflow-providers-vespa"
+version = "0.1.0"
+source = { editable = "providers/vespa" }
+dependencies = [
+ { name = "apache-airflow" },
+ { name = "apache-airflow-providers-common-compat" },
+ { name = "pyvespa" },
+]
+
+[package.dev-dependencies]
+dev = [
+ { name = "apache-airflow" },
+ { name = "apache-airflow-devel-common" },
+ { name = "apache-airflow-providers-common-compat" },
+ { name = "apache-airflow-task-sdk" },
+]
+docs = [
+ { name = "apache-airflow-devel-common", extra = ["docs"] },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "apache-airflow", editable = "." },
+ { name = "apache-airflow-providers-common-compat", editable = "providers/common/compat" },
+ { name = "pyvespa", specifier = ">=1.1.2" },
+]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "apache-airflow", editable = "." },
+ { name = "apache-airflow-devel-common", editable = "devel-common" },
+ { name = "apache-airflow-providers-common-compat", editable = "providers/common/compat" },
+ { name = "apache-airflow-task-sdk", editable = "task-sdk" },
+]
+docs = [{ name = "apache-airflow-devel-common", extras = ["docs"], editable = "devel-common" }]
+
[[package]]
name = "apache-airflow-providers-weaviate"
version = "3.3.3"
@@ -9790,14 +9834,14 @@ wheels = [
[[package]]
name = "click"
-version = "8.3.1"
+version = "8.3.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" },
]
[[package]]
@@ -10884,6 +10928,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fa/93/b44f67589e4d439913dab6720f7e3507b0fa8b8e56d06f6fc875ced26afb/fastavro-1.12.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:43ded16b3f4a9f1a42f5970c2aa618acb23ea59c4fcaa06680bdf470b255e5a8", size = 3386636, upload-time = "2025-10-10T15:42:18.974Z" },
]
+[[package]]
+name = "fastcore"
+version = "1.12.34"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/39/66/e2bf42b3cad563a7246cc8e61a49c88e611c4c4228244da6bb39909126ef/fastcore-1.12.34.tar.gz", hash = "sha256:24c06e40cf9444ee4cbfbb5ff331e59762c83f1f5e27a128beb90b46d95aa687", size = 94563, upload-time = "2026-04-01T09:43:17.953Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fa/89/bf2adbdbb90dab8b465d7a489e0a291e9153bf7b1db2b90ea4fa3c4bf16f/fastcore-1.12.34-py3-none-any.whl", hash = "sha256:917ed3559ef25cfbf3ec3327cb664cf75830832490ed1fb7be1425eb351783df", size = 98770, upload-time = "2026-04-01T09:43:16.288Z" },
+]
+
[[package]]
name = "fastjsonschema"
version = "2.21.2"
@@ -12941,6 +12994,44 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2f/90/fd509079dfcab01102c0fdd87f3a9506894bc70afcf9e9785ef6b2b3aff6/httplib2-0.31.2-py3-none-any.whl", hash = "sha256:dbf0c2fa3862acf3c55c078ea9c0bc4481d7dc5117cae71be9514912cf9f8349", size = 91099, upload-time = "2026-01-23T11:04:42.78Z" },
]
+[[package]]
+name = "httpr"
+version = "0.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/38/eb/644349f01f0e8af737a52aff18f69c8490922ed890ed2c256d8d5db0c6c4/httpr-0.4.1.tar.gz", hash = "sha256:4a592c26b775e3542ea0fea069dd2cd9e9dc5a80e24e1fa2a466d5ac374eb03c", size = 567261, upload-time = "2026-03-21T07:41:37.912Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/83/9e/6babaca23d526b8c32232919104def1d81cd503741896a2be9e8ba9f0d9a/httpr-0.4.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:134a8b0042ab6b102a141c717219994ae9aa89e88ad97a09c8702f790225a623", size = 2458573, upload-time = "2026-03-21T07:41:21.009Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/81/271cf119550d684cab56101807540458f801f8c2c51f44d287ea59047031/httpr-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:524216fd1d688c78b0a523c6fe5220198789ddc27f4576d2a43a34ff7b7dbfe7", size = 2284320, upload-time = "2026-03-21T07:41:17.336Z" },
+ { url = "https://files.pythonhosted.org/packages/36/f0/d093b7f779eb3cf97148c377c046d4408ff569332db3fb3ffa70a90978ff/httpr-0.4.1-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fd00861c9271056d4e270e8d8ca764ada592e4b1c451d3a5d7558f59fbae35d1", size = 2496084, upload-time = "2026-03-21T07:41:08.694Z" },
+ { url = "https://files.pythonhosted.org/packages/70/4d/19234dda67df6a552c0776c4987d875cb1a77190a0bd4e418cb558b44d63/httpr-0.4.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6fcd417f838cb05ccf538f08fb3ed91586dad2641f4285a2b36b312405bc85d", size = 2528763, upload-time = "2026-03-21T07:40:59.741Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/cc/ea799133ebae7922a10e161a5d0d50099035b999d03f11d5cbff7d8d3b10/httpr-0.4.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:724edde6bd87e86f64b6581bb94690adbf73f5eb6d5f207b2adeece53069fcbd", size = 2391113, upload-time = "2026-03-21T07:41:04.198Z" },
+ { url = "https://files.pythonhosted.org/packages/85/43/f48b3f7f064f06435f002d33298c8d2e09967c29135f959011daada6c3c6/httpr-0.4.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83a702fd09af35bc03b95edde0d1eafa5f2ed46750e0f240a75b370452ba6782", size = 2614235, upload-time = "2026-03-21T07:41:13.141Z" },
+ { url = "https://files.pythonhosted.org/packages/69/33/28787212ac8e0e166a7a06decaa9531d1840ed68698f2e10c3372ce13a60/httpr-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:91e96795f20b53e54cdb031013134de87b7995d3bfa51ecc4aa26de9311b3653", size = 2704440, upload-time = "2026-03-21T07:41:25.228Z" },
+ { url = "https://files.pythonhosted.org/packages/07/78/ce957ec65331b375a276471856bea3345c7e797e1f700e4f038527d9b3a8/httpr-0.4.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:46f9b006d3c61f597f3f7af03b69827bd1a14de6a6adfca28f6b9a5e3ad3dc23", size = 2649805, upload-time = "2026-03-21T07:41:28.814Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/93/c87b7e11e644948fd83f853b71e3d0989b217592cfc2efb37bbd4e835bd1/httpr-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4cb71034ef883243572debc078726e011a586932450951929d707e429aa509f6", size = 2859262, upload-time = "2026-03-21T07:41:33.622Z" },
+ { url = "https://files.pythonhosted.org/packages/85/1a/bbde0bac4410178b895100c986346681e7b216d6f09d367270b692cb8e2c/httpr-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:637d58b3338656ae68d04df938db3028cf83a217874da92d94a0fe85ae286ecc", size = 2075114, upload-time = "2026-03-21T07:41:43.364Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/05/f9652691effe48ac17c75372716cb6361245a4f8f7d70129dd7b72bd33e5/httpr-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:cd4497483a78f4a10eaa9d67211a44176b7aec7a7de288228b7f1e3e0b958b0d", size = 2334107, upload-time = "2026-03-21T07:41:39.248Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/ee/7f654baa4061a88e9f4f156bea797783a4ba28929cf620778e7813ce19d5/httpr-0.4.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:13f1e2768d2c1543c874119beef2f68e98a9452c7c076038833ca03a60fc2e5c", size = 2460211, upload-time = "2026-03-21T07:41:22.512Z" },
+ { url = "https://files.pythonhosted.org/packages/61/c4/3adb4b708cc2f5b44110f31dc217f292a0b33e71f6ed0d0c2211dcb03236/httpr-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:12db774b08250742183f9e4ae45695c64f832627f4c5f268cb59aba1e5c360b1", size = 2285243, upload-time = "2026-03-21T07:41:18.466Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/fc/c4c6755d38f4a97302992da1a5a9aa8d01e5ec180c09c47b93e2a7a9f85a/httpr-0.4.1-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1e610d73c813ea8c5b65b3830459f9aa8a5ce29afd4e50548e4706c1dc270405", size = 2496899, upload-time = "2026-03-21T07:41:09.968Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/16/2e62a445588f0a4f832c4f3a8cb3e3d24ade52fdf59ef5ad426ad8fcf090/httpr-0.4.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:567ad9d8d24e889a06863ac6108f7bac7960b0cacd74ead04608bb9baaf44444", size = 2530318, upload-time = "2026-03-21T07:41:01.137Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/bf/92a47a0ffdfc7729786b00743dbfce34d7d2bab3ee3bde3ca17aa3413d52/httpr-0.4.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d77c62647bf6308d308b8352ac5132d96ab58f58b93c39bdae340a2fc5de0d44", size = 2392462, upload-time = "2026-03-21T07:41:05.484Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/8f/1bfa9a6767244b79df82c202c2843b664f9fc1acff9dbe7f982c8627d2fe/httpr-0.4.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a070723b04c9e41b4b8d6450f97dbf54820288d33efe740525f691d47c4af967", size = 2616728, upload-time = "2026-03-21T07:41:14.327Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/92/ba4b17be7e4060bf36e7472e2afa998eb87eeb168cfb0ce5aec0313b37d3/httpr-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ece5a1207c654080af757364c7fa0c4bbd25ff910db0f240a4d4b89d4c1c88a6", size = 2705345, upload-time = "2026-03-21T07:41:26.455Z" },
+ { url = "https://files.pythonhosted.org/packages/11/d7/0d9fa9d695e2dde009d01d9380c1dfcbd6c1dc6b6e73c14c5ec615d36bb0/httpr-0.4.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d735b76da08fe88f57b914dbf03a790af7fc555eb2d343ee5faa27f90d0f07da", size = 2650374, upload-time = "2026-03-21T07:41:30.543Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/be/83219b05e6deb73164f3a1293dd1e534e1022bf20256f0908110d35338b8/httpr-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:14e6199419478540f98c66ea84c518623d1fa6a831a977020c3c7e0b68a5171f", size = 2861062, upload-time = "2026-03-21T07:41:35.19Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/af/1539ed1699e7d145f735fd1a9c726ee04c0bbeb092bf113d86320b1e269f/httpr-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:45c89be444b92bfad2e8d217045d07a1db7671b66df57f6bb1372e29db78ac2d", size = 2076326, upload-time = "2026-03-21T07:41:44.579Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/cf/07a68ebbae6397680bd6610fed4ad5658af7b42dabb3cc8b4bdb34efc850/httpr-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:419c4894a4547829636abb0ee98a8d27de4bf96555a354b8bc8c559f56e0e0a7", size = 2336305, upload-time = "2026-03-21T07:41:40.862Z" },
+ { url = "https://files.pythonhosted.org/packages/73/02/b41c7b05d8ed68895b300d7caf2d72c4b45dc535b4e881f2ab58373ff3b6/httpr-0.4.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8ac0781cf3811c4804d8fc8092e535350dd1f9ead1678d06c05bf41c027526b6", size = 2461678, upload-time = "2026-03-21T07:41:23.942Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ca/496a46b119b0c37fe245dd1986248343a62f31bf5a601a8b2bfefb21b3ba/httpr-0.4.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:e0eb815d995826c1f0a82f3bad837bc90c53ccdcc3aeced1e8b49afcec31cdec", size = 2287700, upload-time = "2026-03-21T07:41:19.838Z" },
+ { url = "https://files.pythonhosted.org/packages/30/3b/c9c31136156b35b0ad9db04dd2117ec5a13ad885ec51f3b0b3bd4fba43f4/httpr-0.4.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30889f270a2dbffdfa1575843c021909c2f38ceb576e89f7992a45ade097d262", size = 2394583, upload-time = "2026-03-21T07:41:07.221Z" },
+ { url = "https://files.pythonhosted.org/packages/71/56/11acf0ec67166ed5aa17ae53c563bbe9dc5781527ce03d23dbe7e580c4e8/httpr-0.4.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:81162019e2308af4f0694d154a8286f99ef8cef5f8ef9b08aa6699288e2059cb", size = 2706538, upload-time = "2026-03-21T07:41:27.661Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/9f/94e93f742d302dcb00ff4a11b2da012983e886ad71508bd7f07a51fb71f0/httpr-0.4.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:ad2b8c324f856e061e5b242cae9eaedaf460c166f1fd7ce456a5872860d3d81a", size = 2652945, upload-time = "2026-03-21T07:41:31.782Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/a0/2986108c3d5ff7ea39587fae0f3eac984d7fc03f1975fc129e1eaa779846/httpr-0.4.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0d61f8c95d18498d90052048f890b611dacf3aa646d5dea68bdaf295d5bdd0c0", size = 2861039, upload-time = "2026-03-21T07:41:36.366Z" },
+ { url = "https://files.pythonhosted.org/packages/94/da/084472a759715208f620424a08322b40569b40239c44a31e74b98e4c9a8a/httpr-0.4.1-cp39-abi3-win32.whl", hash = "sha256:5c6ea2a1151cc665faa6f6dae017a86d380590d5dbd0fd4321dbf1616f8d7660", size = 2077130, upload-time = "2026-03-21T07:41:45.774Z" },
+ { url = "https://files.pythonhosted.org/packages/55/c1/149c9b3071339da353422602c73562062b3f11f2a019cecd20df8bc9f2a0/httpr-0.4.1-cp39-abi3-win_amd64.whl", hash = "sha256:cc2da5229e200e08e7858b55df99ddd33e48fba450f484fde0a035ea627dbf20", size = 2335329, upload-time = "2026-03-21T07:41:42.198Z" },
+]
+
[[package]]
name = "httptools"
version = "0.7.1"
@@ -13015,7 +13106,7 @@ wheels = [
[[package]]
name = "huggingface-hub"
-version = "1.8.0"
+version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "filelock" },
@@ -13028,9 +13119,9 @@ dependencies = [
{ name = "typer" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/8e/2a/a847fd02261cd051da218baf99f90ee7c7040c109a01833db4f838f25256/huggingface_hub-1.8.0.tar.gz", hash = "sha256:c5627b2fd521e00caf8eff4ac965ba988ea75167fad7ee72e17f9b7183ec63f3", size = 735839, upload-time = "2026-03-25T16:01:28.152Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/88/bb/62c7aa86f63a05e2f9b96642fdef9b94526a23979820b09f5455deff4983/huggingface_hub-1.9.0.tar.gz", hash = "sha256:0ea5be7a56135c91797cae6ad726e38eaeb6eb4b77cefff5c9d38ba0ecf874f7", size = 750326, upload-time = "2026-04-03T08:35:55.888Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a9/ae/8a3a16ea4d202cb641b51d2681bdd3d482c1c592d7570b3fa264730829ce/huggingface_hub-1.8.0-py3-none-any.whl", hash = "sha256:d3eb5047bd4e33c987429de6020d4810d38a5bef95b3b40df9b17346b7f353f2", size = 625208, upload-time = "2026-03-25T16:01:26.603Z" },
+ { url = "https://files.pythonhosted.org/packages/73/37/0d15d16150e1829f3e90962c99f28257f6de9e526a680b4c6f5acdb54fd2/huggingface_hub-1.9.0-py3-none-any.whl", hash = "sha256:2999328c058d39fd19ab748dd09bd4da2fbaa4f4c1ddea823eab103051e14a1f", size = 637355, upload-time = "2026-04-03T08:35:53.897Z" },
]
[[package]]
@@ -16317,23 +16408,23 @@ wheels = [
[[package]]
name = "pagefind"
-version = "1.5.0b1"
+version = "1.5.0b2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/96/0b/e279dbd3565ae2202713dd2b90ccd231b2e1b115d9a2101fb6a1c67f014f/pagefind-1.5.0b1.tar.gz", hash = "sha256:1c36741162ef432ca7cc7fb6b4fc4c7c3a7da350d24a756948c65de395dbeed6", size = 46174, upload-time = "2026-01-11T10:21:37.877Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/c8/1b/d88d8d7fe3628e705c154b5b14bd4ddc5204b5fd155ce1aee48099fc6485/pagefind-1.5.0b2.tar.gz", hash = "sha256:b838d2c5a4ec5836294fa54af66b4210568a97635093c43b915d67ccb589378f", size = 46166, upload-time = "2026-04-03T23:51:23.398Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/07/af/2207e28bac820d43c70c63997d3821438859c050f2f683f91b9eac093041/pagefind-1.5.0b1-py3-none-any.whl", hash = "sha256:a884f75ba73b99d08ef13399b0e5ffe21b252c492cfe6ca75c8edaf067291bc2", size = 10120, upload-time = "2026-01-11T10:21:11.51Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/ff/7f5f9a551f53c0c2f247d216b0e9ba6f50954abb6266edc495a1e2f4082a/pagefind-1.5.0b2-py3-none-any.whl", hash = "sha256:2798b4f75134ba57197b93a6d7eb8e726be8d0eb86932f5ace72c0e5c8f1e637", size = 10121, upload-time = "2026-04-03T23:50:54.089Z" },
]
[[package]]
name = "pagefind-bin"
-version = "1.5.0b1"
+version = "1.5.0b2"
source = { registry = "https://pypi.org/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/af/86/e4c04dd78745b0d10904811d24118ded2b841c3fab6c1e83626921a0d542/pagefind_bin-1.5.0b1-py3-none-macosx_12_0_arm64.whl", hash = "sha256:923d7f2099e55ed7d039740ceb8d6eb353b6543f92e5e8c15318529e951083e9", size = 4538165, upload-time = "2026-01-11T10:21:12.85Z" },
- { url = "https://files.pythonhosted.org/packages/24/d4/fb32c253c188efdea9a56d76516015b77c25dcaf34d4671913c057170f42/pagefind_bin-1.5.0b1-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:5d9a5e6312bf312c82de20334df963b83a44d95cbe22c9be1029f4fdcfab5e8a", size = 4613600, upload-time = "2026-01-11T10:21:14.609Z" },
- { url = "https://files.pythonhosted.org/packages/00/9c/0948de0b57c221a87f9495a2dddfdf8df8ed9507c3c2addedb23bd3bea0f/pagefind_bin-1.5.0b1-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:80ad0fab0fdc38052963cc1e29cb100bab1d3fb5a7b608d3ca4024679e87e6e9", size = 4843566, upload-time = "2026-01-11T10:21:16.704Z" },
- { url = "https://files.pythonhosted.org/packages/50/b9/79b27685f8c2da78ad9ed4785e78c286a8f597611648236c244c985ad0d6/pagefind_bin-1.5.0b1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:f925de81a22861514ef717f5dfc3a3b852111561815b4b7d711eaf0b3bdb36e8", size = 4589188, upload-time = "2026-01-11T10:21:18.696Z" },
- { url = "https://files.pythonhosted.org/packages/e5/f5/ff7caecf7661f3e18005da5716a5e9cf40389b85f7ec9b2c21cefc918490/pagefind_bin-1.5.0b1-py3-none-win_amd64.whl", hash = "sha256:0a3d64c8ca671cb896914bdc74446285e0f74eb405ee234dfc4e016b89eef893", size = 4734213, upload-time = "2026-01-11T10:21:20.426Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/18/ed35f18d2cbcd1e49fec1c74ee88ee144cd38c5d01c794b567e9e274e723/pagefind_bin-1.5.0b2-py3-none-macosx_12_0_arm64.whl", hash = "sha256:19deda91cebca3dbff97760e878a582256adfe5068caaf47ee88a99b3c0a84c5", size = 4974364, upload-time = "2026-04-03T23:50:55.709Z" },
+ { url = "https://files.pythonhosted.org/packages/30/8c/69e32f7f3f96e3935356680fec29a1ab6beb505fe81f9a2be8cd28715229/pagefind_bin-1.5.0b2-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:358fcf9dfb4ab215a148fe72263cbb4d38d209d641616092eaa628d0ad3f0e19", size = 5049366, upload-time = "2026-04-03T23:50:57.559Z" },
+ { url = "https://files.pythonhosted.org/packages/87/2b/67b08d306998f8d238578b2dd487852e0a7c696a61b390c48ae70ecb6da1/pagefind_bin-1.5.0b2-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:77f04afd54c10f64c488e14f1e306ffaab3989dcb802db2312612699705c6fd6", size = 5269428, upload-time = "2026-04-03T23:50:59.222Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/c6/fb2ae4acb0e7670f6b02759efc3afa3052dec5e8a73f09763ec25858663e/pagefind_bin-1.5.0b2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:97c6c874b10414a0bf547b7bd9e33528fba98d3417c4003af9a84db3dc1880de", size = 5002912, upload-time = "2026-04-03T23:51:01.115Z" },
+ { url = "https://files.pythonhosted.org/packages/90/cc/76f22649048620b678ff167c952ce8701e8f3567f4dd8aa175be98d99412/pagefind_bin-1.5.0b2-py3-none-win_amd64.whl", hash = "sha256:568b84a1791d7552da814839044211a242530731a1ea6c09632f35edf6b4f39f", size = 5187054, upload-time = "2026-04-03T23:51:02.888Z" },
]
[[package]]
@@ -17470,7 +17561,7 @@ wheels = [
[[package]]
name = "pydantic-ai-slim"
-version = "1.76.0"
+version = "1.77.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
@@ -17482,9 +17573,9 @@ dependencies = [
{ name = "pydantic-graph" },
{ name = "typing-inspection" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a8/12/625331a88ea2db885e4cda4c2384f8dac9a876260ee3e6e982a950733e6c/pydantic_ai_slim-1.76.0.tar.gz", hash = "sha256:db82bc9a24f9c80d00be23f7a18e5cda8484d77c61a5cd8eedfc2fc8515657b2", size = 508214, upload-time = "2026-04-02T00:25:51.26Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/79/a7/ad011e626bed1f275fbaf933181573a50b05c2b9a0be927583d46fb8ff13/pydantic_ai_slim-1.77.0.tar.gz", hash = "sha256:a6e7006a4b048193d45b6ba816d301271e3f5ef1cdc4f9fb340617f382c6ce0d", size = 518781, upload-time = "2026-04-03T02:16:54.524Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/bd/c6/7801af6853502bd53f00e88560f60270d8a2ab3bd8e19732d5ae8f261503/pydantic_ai_slim-1.76.0-py3-none-any.whl", hash = "sha256:1932799ff46a03e83fca3fb194f580dcbf3b24c9d2571ef64d0789c950499e23", size = 651190, upload-time = "2026-04-02T00:25:43.987Z" },
+ { url = "https://files.pythonhosted.org/packages/96/c5/5913cc4ae99047901c602f0d8208e3a75a7952b7e57d76169547307d7cea/pydantic_ai_slim-1.77.0-py3-none-any.whl", hash = "sha256:110c516935de384f1beddc36fda04e8df36cdf5bee3a5bfd0da562726182e52b", size = 664494, upload-time = "2026-04-03T02:16:46.668Z" },
]
[package.optional-dependencies]
@@ -17638,7 +17729,7 @@ wheels = [
[[package]]
name = "pydantic-graph"
-version = "1.76.0"
+version = "1.77.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -17646,9 +17737,9 @@ dependencies = [
{ name = "pydantic" },
{ name = "typing-inspection" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/b7/3c/6dc8c19c9eba073884b861d88cc96658d38bde4dd49f4b07e9a87f589eec/pydantic_graph-1.76.0.tar.gz", hash = "sha256:e0f8f85ab08b0f896aed50bc888f946f7c2ef3f032b78fefc8dc1fd77a49406e", size = 58716, upload-time = "2026-04-02T00:25:53.605Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/60/40/a8b8e256bb90e4e284b35cc1c5e1a8e2724fa88ad89b7eac958fbf85852b/pydantic_graph-1.77.0.tar.gz", hash = "sha256:ba75dbdf221cd7e366e5c5d250f4d9f3138e05400ea52d3f36330772d989deee", size = 58689, upload-time = "2026-04-03T02:16:56.625Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/56/4f/60b018568a33c907734613fb089ff288168faa6affb634e05e1a53f1f9e8/pydantic_graph-1.76.0-py3-none-any.whl", hash = "sha256:eda23a36bcaf4ab09ce10e7860818c6eeb2f42b3429ba9a575cc3b713ef3bbd6", size = 72502, upload-time = "2026-04-02T00:25:47.148Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/09/3c9c3aba8031adbd21d1833e8e4edd749697e50a88fa9bdab641874abe4f/pydantic_graph-1.77.0-py3-none-any.whl", hash = "sha256:063803e87aec901919c2073ccf3fdd6e4fff84e8b05dbfbe8a6c1af63dd12c05", size = 72503, upload-time = "2026-04-03T02:16:49.97Z" },
]
[[package]]
@@ -18657,6 +18748,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" },
]
+[[package]]
+name = "pyvespa"
+version = "1.1.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "certifi" },
+ { name = "cryptography" },
+ { name = "docker" },
+ { name = "fastcore" },
+ { name = "httpr" },
+ { name = "httpx", extra = ["http2"] },
+ { name = "jinja2" },
+ { name = "lxml" },
+ { name = "python-dateutil" },
+ { name = "requests" },
+ { name = "requests-toolbelt" },
+ { name = "tenacity" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d5/aa/733a6e04652e34cb4c7184b818023d4a7e73a79a78074baa76a84b1290b3/pyvespa-1.1.2.tar.gz", hash = "sha256:da7d591db22d3e3021956a29bf3b4fab33e15cf6ccd71cc8c117d8b3b178781e", size = 153030, upload-time = "2026-03-04T09:08:59.504Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7a/02/9904b061aee06e1ac8e22f6ff619cc1c7c05310689a172c8e89abfddfc9c/pyvespa-1.1.2-py3-none-any.whl", hash = "sha256:3197e7c69c0bde53b663542f6b0a0d7c00420ef3cbbaa90d6af83cef44952ec4", size = 163077, upload-time = "2026-03-04T09:08:57.677Z" },
+]
+
[[package]]
name = "pywin32"
version = "311"
@@ -19006,123 +19122,123 @@ wheels = [
[[package]]
name = "regex"
-version = "2026.3.32"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/81/93/5ab3e899c47fa7994e524447135a71cd121685a35c8fe35029005f8b236f/regex-2026.3.32.tar.gz", hash = "sha256:f1574566457161678297a116fa5d1556c5a4159d64c5ff7c760e7c564bf66f16", size = 415605, upload-time = "2026-03-28T21:49:22.012Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6f/87/ae29a505fdfcec85978f35d30e6de7c0ae37eaf7c287f6e88abd04be27b3/regex-2026.3.32-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:462a041d2160090553572f6bb0be417ab9bb912a08de54cb692829c871ee88c1", size = 489575, upload-time = "2026-03-28T21:45:27.167Z" },
- { url = "https://files.pythonhosted.org/packages/f9/fd/7a56c6a86213e321a309161673667091991630287d7490c5e9ec3db29607/regex-2026.3.32-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3c6f6b027d10f84bfe65049028892b5740878edd9eae5fea0d1710b09b1d257", size = 291288, upload-time = "2026-03-28T21:45:30.886Z" },
- { url = "https://files.pythonhosted.org/packages/48/2f/ac2b481011b23f79994d4d80df03d9feccb64fbfc7bbe8dad2c3e8efc50c/regex-2026.3.32-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:879ae91f2928a13f01a55cfa168acedd2b02b11b4cd8b5bb9223e8cde777ca52", size = 289336, upload-time = "2026-03-28T21:45:32.631Z" },
- { url = "https://files.pythonhosted.org/packages/6e/a2/cf7dfef7a4182e84acbe8919ce7ff50e3545007c2743219e92271b2fbc1c/regex-2026.3.32-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:887a9fa74418d74d645281ee0edcf60694053bd1bc2ebc49eb5e66bfffc6d107", size = 786358, upload-time = "2026-03-28T21:45:34.025Z" },
- { url = "https://files.pythonhosted.org/packages/fb/cb/42bfeb4597206e3171e70c973ca1d39190b48f6cda7546c25f9cb283285f/regex-2026.3.32-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d571f0b2eec3513734ea31a16ce0f7840c0b85a98e7edfa0e328ed144f9ef78f", size = 854179, upload-time = "2026-03-28T21:45:35.713Z" },
- { url = "https://files.pythonhosted.org/packages/90/d8/9f4a7d7edffe7117de23b94696c52065b68e70267d71576d74429d598d9b/regex-2026.3.32-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ada7bd5bb6511d12177a7b00416ce55caee49fbf8c268f26b909497b534cacb", size = 898810, upload-time = "2026-03-28T21:45:37.435Z" },
- { url = "https://files.pythonhosted.org/packages/05/e6/80335c06ddf7fd7a28b97402ebe1ea4fe80a3aa162fba0f7364175f625d1/regex-2026.3.32-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:918db4e34a7ef3d0beee913fa54b34231cc3424676f1c19bdb85f01828d3cd37", size = 790605, upload-time = "2026-03-28T21:45:39.207Z" },
- { url = "https://files.pythonhosted.org/packages/38/0e/91436a89c1636090903d753d90b076784b11b8c67b79b3bde9851a45c4d7/regex-2026.3.32-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:69a847a6ffaa86e8af7b9e7037606e05a6f663deec516ad851e8e05d9908d16a", size = 786550, upload-time = "2026-03-28T21:45:40.993Z" },
- { url = "https://files.pythonhosted.org/packages/2b/fc/ea7364b5e9abd220cebf547f2f8a42044878e9d8b02b3a652f8b807c0cbc/regex-2026.3.32-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c8d402ea3dfe674288fe3962016affd33b5b27213d2b5db1823ffa4de524c57", size = 770223, upload-time = "2026-03-28T21:45:42.802Z" },
- { url = "https://files.pythonhosted.org/packages/3b/86/aff4ad741e914cc493e7500431cdf14e51bc808b14f1f205469d353a970b/regex-2026.3.32-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d6b39a2cc5625bbc4fda18919a891eab9aab934eecf83660a90ce20c53621a9a", size = 774436, upload-time = "2026-03-28T21:45:44.212Z" },
- { url = "https://files.pythonhosted.org/packages/bf/e7/060779f504c92320f75b90caab4e57324816020986c27f57414b0a1ebcc9/regex-2026.3.32-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f7cc00089b4c21847852c0ad76fb3680f9833b855a0d30bcec94211c435bff6b", size = 849400, upload-time = "2026-03-28T21:45:46.2Z" },
- { url = "https://files.pythonhosted.org/packages/c8/8e/6544b27f70bfd14e9c50ff5527027acc9b8f9830d352a746f843da7b0627/regex-2026.3.32-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:fd03e38068faeef937cc6761a250a4aaa015564bd0d61481fefcf15586d31825", size = 757934, upload-time = "2026-03-28T21:45:47.962Z" },
- { url = "https://files.pythonhosted.org/packages/bc/6f/abf2234b3f51da1e693f13bb85e7dbb3bbdd07c04e12e0e105b9bc6006a6/regex-2026.3.32-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e006ea703d5c0f3d112b51ba18af73b58209b954acfe3d8da42eacc9a00e4be6", size = 838479, upload-time = "2026-03-28T21:45:49.845Z" },
- { url = "https://files.pythonhosted.org/packages/db/3c/653f43c3a3643fd221bfaf61ed4a4c8f0ccc25e31a8faa8f1558a892c22c/regex-2026.3.32-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6980ceb5c1049d4878632f08ba0bf7234c30e741b0dc9081da0f86eca13189d3", size = 778478, upload-time = "2026-03-28T21:45:51.574Z" },
- { url = "https://files.pythonhosted.org/packages/88/dd/5e6bd702d7efc3f2a29bf65dfa46f5159653b3c6f846ddf693e1a7f9a739/regex-2026.3.32-cp310-cp310-win32.whl", hash = "sha256:6128dd0793a87287ea1d8bf16b4250dd96316c464ee15953d5b98875a284d41e", size = 266343, upload-time = "2026-03-28T21:45:53.548Z" },
- { url = "https://files.pythonhosted.org/packages/c4/89/39d04329e858956d2db1d08a10f02be8f6837c964663513ac4393158bef9/regex-2026.3.32-cp310-cp310-win_amd64.whl", hash = "sha256:5aa78c857c1731bdd9863923ffadc816d823edf475c7db6d230c28b53b7bdb5e", size = 278632, upload-time = "2026-03-28T21:45:55.604Z" },
- { url = "https://files.pythonhosted.org/packages/b6/d8/c7e9ff3c2648408f4cda7224e195ad7a0d68724225d8d9a55eca9055504f/regex-2026.3.32-cp310-cp310-win_arm64.whl", hash = "sha256:34c905a721ddee0f84c99e3e3b59dd4a5564a6fe338222bc89dd4d4df166115c", size = 270593, upload-time = "2026-03-28T21:45:56.994Z" },
- { url = "https://files.pythonhosted.org/packages/92/c1/c68163a6ce455996db71e249a65234b1c9f79a914ea2108c6c9af9e1812a/regex-2026.3.32-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d7855f5e59fcf91d0c9f4a51dc5d8847813832a2230c3e8e35912ccf20baaa2", size = 489568, upload-time = "2026-03-28T21:45:58.791Z" },
- { url = "https://files.pythonhosted.org/packages/96/9c/0bdd47733b832b5caa11e63df14dccdb311b41ab33c1221e249af4421f8f/regex-2026.3.32-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:18eb45f711e942c27dbed4109830bd070d8d618e008d0db39705f3f57070a4c6", size = 291287, upload-time = "2026-03-28T21:46:00.46Z" },
- { url = "https://files.pythonhosted.org/packages/e1/ff/1977a595f15f8dc355f9cebd875dab67f3faeca1f36b905fe53305bbcaed/regex-2026.3.32-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed3b8281c5d0944d939c82db4ec2300409dd69ee087f7a75a94f2e301e855fb4", size = 289325, upload-time = "2026-03-28T21:46:02.285Z" },
- { url = "https://files.pythonhosted.org/packages/0a/68/dfa21aef5af4a144702befeb5ff20ea9f9fbe40a4dfd08d56148b5b48b0a/regex-2026.3.32-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad5c53f2e8fcae9144009435ebe3d9832003508cf8935c04542a1b3b8deefa15", size = 790898, upload-time = "2026-03-28T21:46:04.079Z" },
- { url = "https://files.pythonhosted.org/packages/36/26/9424e43e0e31ac3ce1ba0e7232ee91e113a04a579c53331bc0f16a4a5bf7/regex-2026.3.32-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:70c634e39c5cda0da05c93d6747fdc957599f7743543662b6dbabdd8d3ba8a96", size = 862462, upload-time = "2026-03-28T21:46:05.923Z" },
- { url = "https://files.pythonhosted.org/packages/63/a8/06573154ac891c6b55b74a88e0fb7c10081c20916b82dd0abc8cef938e13/regex-2026.3.32-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1e0f6648fd48f4c73d801c55ab976cd602e2da87de99c07bff005b131f269c6a", size = 906522, upload-time = "2026-03-28T21:46:07.988Z" },
- { url = "https://files.pythonhosted.org/packages/e7/26/46673bb18448c51222c6272c850484a0092f364fae8d0315be9aa1e4baa7/regex-2026.3.32-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5e0fdb5744caf1036dec5510f543164f2144cb64932251f6dfd42fa872b7f9c", size = 798289, upload-time = "2026-03-28T21:46:09.959Z" },
- { url = "https://files.pythonhosted.org/packages/4d/cb/804f1bd5ff08687258e6a92b040aba9b770e626b8d3ba21fffdfa21db2db/regex-2026.3.32-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dab4178a0bc1ef13178832b12db7bc7f562e8f028b2b5be186e370090dc50652", size = 774823, upload-time = "2026-03-28T21:46:12.049Z" },
- { url = "https://files.pythonhosted.org/packages/e5/94/28a58258f8d822fb949c8ff87fc7e5f2a346922360ec084c193b3c95e51c/regex-2026.3.32-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f95bd07f301135771559101c060f558e2cf896c7df00bec050ca7f93bf11585a", size = 781381, upload-time = "2026-03-28T21:46:13.746Z" },
- { url = "https://files.pythonhosted.org/packages/c4/f3/71e69dbe0543586a3e3532cf36e8c9b38d6d93033161a9799c1e9090eb78/regex-2026.3.32-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2dcca2bceb823c9cc610e57b86a265d7ffc30e9fe98548c609eba8bd3c0c2488", size = 855968, upload-time = "2026-03-28T21:46:15.762Z" },
- { url = "https://files.pythonhosted.org/packages/6d/99/850feec404a02b62e048718ec1b4b98b5c3848cd9ca2316d0bdb65a53f6a/regex-2026.3.32-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:567b57eb987547a23306444e4f6f85d4314f83e65c71d320d898aa7550550443", size = 762785, upload-time = "2026-03-28T21:46:17.394Z" },
- { url = "https://files.pythonhosted.org/packages/40/04/808ab0462a2d19b295a3b42134f5183692f798addfe6a8b6aa5f7c7a35b2/regex-2026.3.32-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b6acb765e7c1f2fa08ac9057a33595e26104d7d67046becae184a8f100932dd9", size = 845797, upload-time = "2026-03-28T21:46:19.269Z" },
- { url = "https://files.pythonhosted.org/packages/06/53/8afcf0fd4bd55440b48442c86cddfe61b0d21c92d96e384c0c47d769f4c3/regex-2026.3.32-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1ed17104d1be7f807fdec35ec99777168dd793a09510d753f8710590ba54cdd", size = 785200, upload-time = "2026-03-28T21:46:20.939Z" },
- { url = "https://files.pythonhosted.org/packages/99/4d/23d992ab4115456fec520d6c3aae39e0e33739b244ddb39aa4102a0f7ef0/regex-2026.3.32-cp311-cp311-win32.whl", hash = "sha256:c60f1de066eb5a0fd8ee5974de4194bb1c2e7692941458807162ffbc39887303", size = 266351, upload-time = "2026-03-28T21:46:22.515Z" },
- { url = "https://files.pythonhosted.org/packages/62/74/27c3cdb3a3fbbf67f7231b872877416ec817ae84271573d2fd14bf8723d3/regex-2026.3.32-cp311-cp311-win_amd64.whl", hash = "sha256:8fe14e24124ef41220e5992a0f09432f890037df6f93fd3d6b7a0feff2db16b2", size = 278639, upload-time = "2026-03-28T21:46:24.016Z" },
- { url = "https://files.pythonhosted.org/packages/0a/12/6a67bd509f38aec021d63096dbc884f39473e92adeb1e35d6fb6d89cbd59/regex-2026.3.32-cp311-cp311-win_arm64.whl", hash = "sha256:ded4fc0edf3de792850cb8b04bbf3c5bd725eeaf9df4c27aad510f6eed9c4e19", size = 270594, upload-time = "2026-03-28T21:46:25.857Z" },
- { url = "https://files.pythonhosted.org/packages/38/94/69492c45b0e61b027109d8433a5c3d4f7a90709184c057c7cfc60acb1bfa/regex-2026.3.32-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ad8d372587e659940568afd009afeb72be939c769c552c9b28773d0337251391", size = 490572, upload-time = "2026-03-28T21:46:28.031Z" },
- { url = "https://files.pythonhosted.org/packages/92/0a/7dcffeebe0fcac45a1f9caf80712002d3cbd66d7d69d719315ee142b280f/regex-2026.3.32-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3f5747501b69299c6b0b047853771e4ed390510bada68cb16da9c9c2078343f7", size = 292078, upload-time = "2026-03-28T21:46:29.789Z" },
- { url = "https://files.pythonhosted.org/packages/e3/ec/988486058ef49eb931476419bae00f164c4ceb44787c45dc7a54b7de0ea4/regex-2026.3.32-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db976be51375bca900e008941639448d148c655c9545071965d0571ecc04f5d0", size = 289786, upload-time = "2026-03-28T21:46:31.415Z" },
- { url = "https://files.pythonhosted.org/packages/4a/cf/1955bb5567bc491bd63068e17f75ab0c9ff5e9d08466beec7e347f5e768d/regex-2026.3.32-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66a5083c3ffe5a5a95f8281ea47a88072d4f24001d562d1d9d28d4cdc005fec5", size = 796431, upload-time = "2026-03-28T21:46:33.101Z" },
- { url = "https://files.pythonhosted.org/packages/27/8a/67fcbca511b792107540181ee0690df6de877bfbcb41b7ecae7028025ca5/regex-2026.3.32-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e83ce8008b48762be296f1401f19afd9ea29f3d035d1974e0cecb74e9afbd1df", size = 865785, upload-time = "2026-03-28T21:46:35.053Z" },
- { url = "https://files.pythonhosted.org/packages/c2/59/0677bc44f2c28305edcabc11933777b9ad34e9e8ded7ba573d24e4bc3ee7/regex-2026.3.32-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3aa21bad31db904e0b9055e12c8282df62d43169c4a9d2929407060066ebc74", size = 913593, upload-time = "2026-03-28T21:46:36.835Z" },
- { url = "https://files.pythonhosted.org/packages/0a/fe/661043d1c263b0d9d10c6ff4e9c9745f3df9641c62b51f96a3473638e7ce/regex-2026.3.32-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f54840bea73541652f1170dc63402a5b776fc851ad36a842da9e5163c1f504a0", size = 801512, upload-time = "2026-03-28T21:46:38.587Z" },
- { url = "https://files.pythonhosted.org/packages/ff/27/74c986061380e1811a46cf04cdf9c939db9f8c0e63953eddfe37ffd633ea/regex-2026.3.32-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2ffbadc647325dd4e3118269bda93ded1eb5f5b0c3b7ba79a3da9fbd04f248e9", size = 776182, upload-time = "2026-03-28T21:46:40.69Z" },
- { url = "https://files.pythonhosted.org/packages/b6/c8/d833397b70cd1bacfcdc0a611f0e2c1f5b91fee8eedd88affcee770cbbb6/regex-2026.3.32-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:66d3126afe7eac41759cd5f0b3b246598086e88e70527c0d68c9e615b81771c4", size = 785837, upload-time = "2026-03-28T21:46:42.926Z" },
- { url = "https://files.pythonhosted.org/packages/e0/53/fa226b72989b5b93db6926fab5478115e085dfcf077e18d2cb386be0fd23/regex-2026.3.32-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f785f44a44702dea89b28bce5bc82552490694ce4e144e21a4f0545e364d2150", size = 860612, upload-time = "2026-03-28T21:46:44.8Z" },
- { url = "https://files.pythonhosted.org/packages/04/28/bdd2fc0c055a1b15702bd4084829bbb6b06095f27990e5bee52b2898ea03/regex-2026.3.32-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b7836aa13721dbdef658aebd11f60d00de633a95726521860fe1f6be75fa225a", size = 765285, upload-time = "2026-03-28T21:46:46.625Z" },
- { url = "https://files.pythonhosted.org/packages/b4/da/21f5e2a35a191b27e5a47cccb3914c99e139b49b1342d3f36e64e8cc60f7/regex-2026.3.32-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5336b1506142eb0f23c96fb4a34b37c4fefd4fed2a7042069f3c8058efe17855", size = 851963, upload-time = "2026-03-28T21:46:48.341Z" },
- { url = "https://files.pythonhosted.org/packages/18/f4/04ed04ebf335a44083695c22772be6a42efa31900415555563acf02cb4de/regex-2026.3.32-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b56993a7aeb4140c4770f4f7965c9e5af4f024457d06e23c01b0d47501cb18ed", size = 788332, upload-time = "2026-03-28T21:46:50.454Z" },
- { url = "https://files.pythonhosted.org/packages/21/25/5355908f479d0dc13d044f88270cdcabc8723efc12e4c2b19e5a94ff1a96/regex-2026.3.32-cp312-cp312-win32.whl", hash = "sha256:d363660f9ef8c734495598d2f3e527fb41f745c73159dc0d743402f049fb6836", size = 266847, upload-time = "2026-03-28T21:46:52.125Z" },
- { url = "https://files.pythonhosted.org/packages/00/e5/3be71c781a031db5df00735b613895ad5fdbf86c6e3bbea5fbbd7bfb5902/regex-2026.3.32-cp312-cp312-win_amd64.whl", hash = "sha256:c9f261ad3cd97257dc1d9355bfbaa7dd703e06574bffa0fa8fe1e31da915ee38", size = 278034, upload-time = "2026-03-28T21:46:54.096Z" },
- { url = "https://files.pythonhosted.org/packages/31/5f/27f1e0b1eea4faa99c66daca34130af20c44fae0237bbc98b87999dbc4a8/regex-2026.3.32-cp312-cp312-win_arm64.whl", hash = "sha256:89e50667e7e8c0e7903e4d644a2764fffe9a3a5d6578f72ab7a7b4205bf204b7", size = 270673, upload-time = "2026-03-28T21:46:56.046Z" },
- { url = "https://files.pythonhosted.org/packages/bd/ba/9c1819f302b42b5fbd4139ead6280e9ec37d19bbe33379df0039b2a57bb4/regex-2026.3.32-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c6d9c6e783b348f719b6118bb3f187b2e138e3112576c9679eb458cc8b2e164b", size = 490394, upload-time = "2026-03-28T21:46:58.112Z" },
- { url = "https://files.pythonhosted.org/packages/5b/0b/f62b0ce79eb83ca82fffea1736289d29bc24400355968301406789bcebd2/regex-2026.3.32-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f21ae18dfd15752cdd98d03cbd7a3640be826bfd58482a93f730dbd24d7b9fb", size = 291993, upload-time = "2026-03-28T21:47:00.198Z" },
- { url = "https://files.pythonhosted.org/packages/e7/d8/ba0f8f81f88cd20c0b27acc123561ac5495ea33f800f0b8ebed2038b23eb/regex-2026.3.32-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:844d88509c968dd44b30daeefac72b038b1bf31ac372d5106358ab01d393c48b", size = 289618, upload-time = "2026-03-28T21:47:02.269Z" },
- { url = "https://files.pythonhosted.org/packages/fd/0d/b47a0e68bc511c195ff129c0311a4cd79b954b8676193a9d03a97c623a91/regex-2026.3.32-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8fc918cd003ba0d066bf0003deb05a259baaaab4dc9bd4f1207bbbe64224857a", size = 796427, upload-time = "2026-03-28T21:47:04.096Z" },
- { url = "https://files.pythonhosted.org/packages/51/d7/32b05aa8fde7789ba316533c0f30e87b6b5d38d6d7f8765eadc5aab84671/regex-2026.3.32-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bbc458a292aee57d572075f22c035fa32969cdb7987d454e3e34d45a40a0a8b4", size = 865850, upload-time = "2026-03-28T21:47:05.982Z" },
- { url = "https://files.pythonhosted.org/packages/dc/67/828d8095501f237b83f630d4069eea8c0e5cb6a204e859cf0b67c223ce12/regex-2026.3.32-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:987cdfcfb97a249abc3601ad53c7de5c370529f1981e4c8c46793e4a1e1bfe8e", size = 913578, upload-time = "2026-03-28T21:47:08.172Z" },
- { url = "https://files.pythonhosted.org/packages/0f/f8/acf1eb80f58852e85bd39a6ddfa78ce2243ddc8de8da7582e6ba657da593/regex-2026.3.32-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5d88fa37ba5e8a80ca8d956b9ea03805cfa460223ac94b7d4854ee5e30f3173", size = 801536, upload-time = "2026-03-28T21:47:10.206Z" },
- { url = "https://files.pythonhosted.org/packages/9f/05/986cdf8d12693451f5889aaf4ea4f65b2c49b1152ae814fa1fb75439e40b/regex-2026.3.32-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d082be64e51671dd5ee1c208c92da2ddda0f2f20d8ef387e57634f7e97b6aae", size = 776226, upload-time = "2026-03-28T21:47:12.891Z" },
- { url = "https://files.pythonhosted.org/packages/32/02/945a6a2348ca1c6608cb1747275c8affd2ccd957d4885c25218a86377912/regex-2026.3.32-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c1d7fa44aece1fa02b8927441614c96520253a5cad6a96994e3a81e060feed55", size = 785933, upload-time = "2026-03-28T21:47:14.795Z" },
- { url = "https://files.pythonhosted.org/packages/53/12/c5bab6cc679ad79a45427a98c4e70809586ac963c5ad54a9217533c4763e/regex-2026.3.32-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d478a2ca902b6ef28ffc9521e5f0f728d036abe35c0b250ee8ae78cfe7c5e44e", size = 860671, upload-time = "2026-03-28T21:47:16.985Z" },
- { url = "https://files.pythonhosted.org/packages/bf/68/8d85f98c2443469facabef62b82b851d369b13f92bec2ca7a3808deaa47b/regex-2026.3.32-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2820d2231885e97aff0fcf230a19ebd5d2b5b8a1ba338c20deb34f16db1c7897", size = 765335, upload-time = "2026-03-28T21:47:18.872Z" },
- { url = "https://files.pythonhosted.org/packages/89/a7/d8a9c270916107a501fca63b748547c6c77e570d19f16a29b557ce734f3d/regex-2026.3.32-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc8ced733d6cd9af5e412f256a32f7c61cd2d7371280a65c689939ac4572499f", size = 851913, upload-time = "2026-03-28T21:47:20.793Z" },
- { url = "https://files.pythonhosted.org/packages/f4/8e/03d392b26679914ccf21f83d18ad4443232d2f8c3e2c30a962d4e3918d9c/regex-2026.3.32-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:847087abe98b3c1ebf1eb49d6ef320dbba75a83ee4f83c94704580f1df007dd4", size = 788447, upload-time = "2026-03-28T21:47:22.628Z" },
- { url = "https://files.pythonhosted.org/packages/cf/df/692227d23535a50604333068b39eb262626db780ab1e1b19d83fc66853aa/regex-2026.3.32-cp313-cp313-win32.whl", hash = "sha256:d21a07edddb3e0ca12a8b8712abc8452481c3d3db19ae87fc94e9842d005964b", size = 266834, upload-time = "2026-03-28T21:47:24.778Z" },
- { url = "https://files.pythonhosted.org/packages/b9/37/13e4e56adc16ba607cffa1fe880f233eb9ded8ab8a8580619683c9e4ce48/regex-2026.3.32-cp313-cp313-win_amd64.whl", hash = "sha256:3c054e39a9f85a3d76c62a1d50c626c5e9306964eaa675c53f61ff7ec1204bbb", size = 277972, upload-time = "2026-03-28T21:47:26.627Z" },
- { url = "https://files.pythonhosted.org/packages/ab/1c/80a86dbb2b416fec003b1801462bdcebbf1d43202ed5acb176e99c1ba369/regex-2026.3.32-cp313-cp313-win_arm64.whl", hash = "sha256:b2e9c2ea2e93223579308263f359eab8837dc340530b860cb59b713651889f14", size = 270649, upload-time = "2026-03-28T21:47:28.551Z" },
- { url = "https://files.pythonhosted.org/packages/58/08/e38372da599dc1c39c599907ec535016d110034bd3701ce36554f59767ef/regex-2026.3.32-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5d86e3fb08c94f084a625c8dc2132a79a3a111c8bf6e2bc59351fa61753c2f6e", size = 494495, upload-time = "2026-03-28T21:47:30.642Z" },
- { url = "https://files.pythonhosted.org/packages/5f/27/6e29ece8c9ce01001ece1137fa21c8707529c2305b22828f63623b0eb262/regex-2026.3.32-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b6f366a5ef66a2df4d9e68035cfe9f0eb8473cdfb922c37fac1d169b468607b0", size = 293988, upload-time = "2026-03-28T21:47:32.553Z" },
- { url = "https://files.pythonhosted.org/packages/e1/98/8752e18bb87a2fe728b73b0f83c082eb162a470766063f8028759fb26844/regex-2026.3.32-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b8fca73e16c49dd972ce3a88278dfa5b93bf91ddef332a46e9443abe21ca2f7c", size = 292634, upload-time = "2026-03-28T21:47:34.651Z" },
- { url = "https://files.pythonhosted.org/packages/7f/7b/d7729fe294e23e9c7c3871cb69d49059fa7d65fd11e437a2cbea43f6615d/regex-2026.3.32-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b953d9d496d19786f4d46e6ba4b386c6e493e81e40f9c5392332458183b0599d", size = 810532, upload-time = "2026-03-28T21:47:36.839Z" },
- { url = "https://files.pythonhosted.org/packages/fd/49/4dae7b000659f611b17b9c1541fba800b0569e4060debc4635ef1b23982c/regex-2026.3.32-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b565f25171e04d4fad950d1fa837133e3af6ea6f509d96166eed745eb0cf63bc", size = 871919, upload-time = "2026-03-28T21:47:39.192Z" },
- { url = "https://files.pythonhosted.org/packages/83/85/aa8ad3977b9399861db3df62b33fe5fef6932ee23a1b9f4f357f58f2094b/regex-2026.3.32-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f28eac18a8733a124444643a66ac96fef2c0ad65f50034e0a043b90333dc677f", size = 916550, upload-time = "2026-03-28T21:47:41.618Z" },
- { url = "https://files.pythonhosted.org/packages/c8/c0/6379d7f5b59ff0656ba49cf666d5013ecee55e83245275b310b0ffc79143/regex-2026.3.32-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cdd508664430dd51b8888deb6c5b416d8de046b2e11837254378d31febe4a98", size = 814988, upload-time = "2026-03-28T21:47:43.681Z" },
- { url = "https://files.pythonhosted.org/packages/2c/af/2dfddc64074bd9b70e27e170ee9db900542e2870210b489ad4471416ba86/regex-2026.3.32-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5c35d097f509cf7e40d20d5bee548d35d6049b36eb9965e8d43e4659923405b9", size = 786337, upload-time = "2026-03-28T21:47:46.076Z" },
- { url = "https://files.pythonhosted.org/packages/eb/2f/4eb8abd705236402b4fe0e130971634deffb1855e2028bf02a2b7c0e841c/regex-2026.3.32-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:85c9b0c131427470a6423baa0a9330be6fd8c3630cc3ee6fdee03360724cbec5", size = 800029, upload-time = "2026-03-28T21:47:48.356Z" },
- { url = "https://files.pythonhosted.org/packages/3e/2c/77d9ca2c9df483b51b4b1291c96d79c9ae301077841c4db39bc822f6b4c6/regex-2026.3.32-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:e50af656c15e2723eeb7279c0837e07accc594b95ec18b86821a4d44b51b24bf", size = 865843, upload-time = "2026-03-28T21:47:50.762Z" },
- { url = "https://files.pythonhosted.org/packages/48/10/306f477a509f4eed699071b1f031d89edd5a2b5fa28c8ede5b2638eaba82/regex-2026.3.32-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:4bc32b4dbdb4f9f300cf9f38f8ea2ce9511a068ffaa45ac1373ee7a943f1d810", size = 772473, upload-time = "2026-03-28T21:47:52.771Z" },
- { url = "https://files.pythonhosted.org/packages/f4/f6/54bd83ec46ac037de2beb049afc9dd5d2769c6ecaadf7856254ce610e62a/regex-2026.3.32-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e3e5d1802cba785210a4a800e63fcee7a228649a880f3bf7f2aadccb151a834b", size = 856805, upload-time = "2026-03-28T21:47:55.04Z" },
- { url = "https://files.pythonhosted.org/packages/37/e8/ee0e7d14de1fc6582d5782f072db6c61465a38a4142f88e175dda494b536/regex-2026.3.32-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ef250a3f5e93182193f5c927c5e9575b2cb14b80d03e258bc0b89cc5de076b60", size = 801875, upload-time = "2026-03-28T21:47:57.434Z" },
- { url = "https://files.pythonhosted.org/packages/8a/06/0fa9daca59d07b6aabd8e0468d3b86fd578576a157206fbcddbfc2298f7d/regex-2026.3.32-cp313-cp313t-win32.whl", hash = "sha256:9cf7036dfa2370ccc8651521fcbb40391974841119e9982fa312b552929e6c85", size = 269892, upload-time = "2026-03-28T21:47:59.674Z" },
- { url = "https://files.pythonhosted.org/packages/13/47/77f16b5ad9f10ca574f03d84a354b359b0ac33f85054f2f2daafc9f7b807/regex-2026.3.32-cp313-cp313t-win_amd64.whl", hash = "sha256:c940e00e8d3d10932c929d4b8657c2ea47d2560f31874c3e174c0d3488e8b865", size = 281318, upload-time = "2026-03-28T21:48:01.562Z" },
- { url = "https://files.pythonhosted.org/packages/c6/47/db4446faaea8d01c8315c9c89c7dc6abbb3305e8e712e9b23936095c4d58/regex-2026.3.32-cp313-cp313t-win_arm64.whl", hash = "sha256:ace48c5e157c1e58b7de633c5e257285ce85e567ac500c833349c363b3df69d4", size = 272366, upload-time = "2026-03-28T21:48:03.748Z" },
- { url = "https://files.pythonhosted.org/packages/32/68/ff024bf6131b7446a791a636dbbb7fa732d586f33b276d84b3460ea49393/regex-2026.3.32-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:a416ee898ecbc5d8b283223b4cf4d560f93244f6f7615c1bd67359744b00c166", size = 490430, upload-time = "2026-03-28T21:48:05.654Z" },
- { url = "https://files.pythonhosted.org/packages/61/72/039d9164817ee298f2a2d0246001afe662241dcbec0eedd1fe03e2a2555e/regex-2026.3.32-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d76d62909bfb14521c3f7cfd5b94c0c75ec94b0a11f647d2f604998962ec7b6c", size = 291948, upload-time = "2026-03-28T21:48:07.666Z" },
- { url = "https://files.pythonhosted.org/packages/06/9d/77f684d90ffe3e99b828d3cabb87a0f1601d2b9decd1333ff345809b1d02/regex-2026.3.32-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:631f7d95c83f42bccfe18946a38ad27ff6b6717fb4807e60cf24860b5eb277fc", size = 289786, upload-time = "2026-03-28T21:48:09.562Z" },
- { url = "https://files.pythonhosted.org/packages/83/70/bd76069a0304e924682b2efd8683a01617a7e1da9b651af73039d8da76a4/regex-2026.3.32-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12917c6c6813ffcdfb11680a04e4d63c5532b88cf089f844721c5f41f41a63ad", size = 796672, upload-time = "2026-03-28T21:48:11.568Z" },
- { url = "https://files.pythonhosted.org/packages/80/31/c2d7d9a5671e111a2c16d57e0cb03e1ce35b28a115901590528aa928bb5b/regex-2026.3.32-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e221b615f83b15887636fcb90ed21f1a19541366f8b7ba14ba1ad8304f4ded4", size = 866556, upload-time = "2026-03-28T21:48:14.081Z" },
- { url = "https://files.pythonhosted.org/packages/d7/b9/9921a31931d0bc3416ac30205471e0e2ed60dcbd16fc922bbd69b427322b/regex-2026.3.32-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f9ae4755fa90f1dc2d0d393d572ebc134c0fe30fcfc0ab7e67c1db15f192041", size = 912787, upload-time = "2026-03-28T21:48:16.548Z" },
- { url = "https://files.pythonhosted.org/packages/41/ab/2c1bc8ab99f63cdabdbc7823af8f4cfcd6ddbb2babf01861826c3f1ad44d/regex-2026.3.32-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a094e9dcafedfb9d333db5cf880304946683f43a6582bb86688f123335122929", size = 800879, upload-time = "2026-03-28T21:48:18.971Z" },
- { url = "https://files.pythonhosted.org/packages/49/e5/0be716eb2c0b2ae3a439e44432534e82b2f81848af64cb21c0473ad8ae46/regex-2026.3.32-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c1cecea3e477af105f32ef2119b8d895f297492e41d317e60d474bc4bffd62ff", size = 776332, upload-time = "2026-03-28T21:48:21.163Z" },
- { url = "https://files.pythonhosted.org/packages/26/80/114a61bd25dec7d1070930eaef82aadf9b05961a37629e7cca7bc3fc2257/regex-2026.3.32-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f26262900edd16272b6360014495e8d68379c6c6e95983f9b7b322dc928a1194", size = 786384, upload-time = "2026-03-28T21:48:23.277Z" },
- { url = "https://files.pythonhosted.org/packages/0c/78/be0a6531f8db426e8e60d6356aeef8e9cc3f541655a648c4968b63c87a88/regex-2026.3.32-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1cb22fa9ee6a0acb22fc9aecce5f9995fe4d2426ed849357d499d62608fbd7f9", size = 861381, upload-time = "2026-03-28T21:48:25.371Z" },
- { url = "https://files.pythonhosted.org/packages/45/b1/e5076fbe45b8fb39672584b1b606d512f5bd3a43155be68a95f6b88c1fc5/regex-2026.3.32-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:9b9118a78e031a2e4709cd2fcc3028432e89b718db70073a8da574c249b5b249", size = 765434, upload-time = "2026-03-28T21:48:27.494Z" },
- { url = "https://files.pythonhosted.org/packages/a3/da/fd65d68b897f8b52b1390d20d776fa753582484724a9cb4f4c26de657ae5/regex-2026.3.32-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:b193ed199848aa96618cd5959c1582a0bf23cd698b0b900cb0ffe81b02c8659c", size = 851501, upload-time = "2026-03-28T21:48:29.884Z" },
- { url = "https://files.pythonhosted.org/packages/e8/d6/1e9c991c32022a9312e9124cc974961b3a2501338de2cd1cce75a3612d7a/regex-2026.3.32-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:10fb2aaae1aaadf7d43c9f3c2450404253697bf8b9ce360bd5418d1d16292298", size = 788076, upload-time = "2026-03-28T21:48:32.025Z" },
- { url = "https://files.pythonhosted.org/packages/f0/5b/b23c72f6d607cbb24ef42acf0c7c2ef4eee1377a9f7ba43b312f889edfbb/regex-2026.3.32-cp314-cp314-win32.whl", hash = "sha256:110ba4920721374d16c4c8ea7ce27b09546d43e16aea1d7f43681b5b8f80ba61", size = 272255, upload-time = "2026-03-28T21:48:34.355Z" },
- { url = "https://files.pythonhosted.org/packages/2a/ec/32bbcc42366097a8cea2c481e02964be6c6fa5ccfb0fa9581686af0bec5f/regex-2026.3.32-cp314-cp314-win_amd64.whl", hash = "sha256:245667ad430745bae6a1e41081872d25819d86fbd9e0eec485ba00d9f78ad43d", size = 281160, upload-time = "2026-03-28T21:48:36.588Z" },
- { url = "https://files.pythonhosted.org/packages/6c/e4/89038a028cb68e719fa03ab1ad603649fc199bcda12270d2ac7b471b8f5d/regex-2026.3.32-cp314-cp314-win_arm64.whl", hash = "sha256:1ca02ff0ef33e9d8276a1fcd6d90ff6ea055a32c9149c0050b5b67e26c6d2c51", size = 273688, upload-time = "2026-03-28T21:48:38.976Z" },
- { url = "https://files.pythonhosted.org/packages/30/6e/87caccd608837a1fa4f8c7edc48e206103452b9bbc94fc724fa39340e807/regex-2026.3.32-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:51fb7e26f91f9091fd8ec6a946f99b15d3bc3667cb5ddc73dd6cb2222dd4a1cc", size = 494506, upload-time = "2026-03-28T21:48:41.327Z" },
- { url = "https://files.pythonhosted.org/packages/16/53/a922e6b24694d70bdd68fc3fd076950e15b1b418cff9d2cc362b3968d86f/regex-2026.3.32-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:51a93452034d671b0e21b883d48ea66c5d6a05620ee16a9d3f229e828568f3f0", size = 293986, upload-time = "2026-03-28T21:48:43.481Z" },
- { url = "https://files.pythonhosted.org/packages/60/e4/0cb32203c1aebad0577fcd5b9af1fe764869e617d5234bc6a0ad284299ea/regex-2026.3.32-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:03c2ebd15ff51e7b13bb3dc28dd5ac18cd39e59ebb40430b14ae1a19e833cff1", size = 292677, upload-time = "2026-03-28T21:48:45.772Z" },
- { url = "https://files.pythonhosted.org/packages/f0/f8/5006b70291469d4174dd66ad162802e2f68419c0f2a7952d0c76c1288cfa/regex-2026.3.32-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5bf2f3c2c5bd8360d335c7dcd4a9006cf1dabae063ee2558ee1b07bbc8a20d88", size = 810661, upload-time = "2026-03-28T21:48:48.147Z" },
- { url = "https://files.pythonhosted.org/packages/b2/9b/438763a20d22cd1f65f95c8f030dd25df2d80a941068a891d21a5f240456/regex-2026.3.32-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a4a3189a99ecdd1c13f42513ab3fc7fa8311b38ba7596dd98537acb8cd9acc3", size = 872156, upload-time = "2026-03-28T21:48:50.739Z" },
- { url = "https://files.pythonhosted.org/packages/6c/5b/1341287887ac982ed9f5f60125e440513ffe354aa7e3681940495af7c12a/regex-2026.3.32-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3c0bbfbd38506e1ea96a85da6782577f06239cb9fcf9696f1ea537c980c0680b", size = 916749, upload-time = "2026-03-28T21:48:53.57Z" },
- { url = "https://files.pythonhosted.org/packages/42/e2/1d2b48b8e94debfffc6fefb84d2a86a178cc208652a1d6493d5f29821c70/regex-2026.3.32-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8aaf8ee8f34b677f90742ca089b9c83d64bdc410528767273c816a863ed57327", size = 814788, upload-time = "2026-03-28T21:48:55.905Z" },
- { url = "https://files.pythonhosted.org/packages/a6/d9/7dacb34c43adaeb954518d851f3e5d3ce495ac00a9d6010e3b4b59917c4a/regex-2026.3.32-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ea568832eca219c2be1721afa073c1c9eb8f98a9733fdedd0a9747639fc22a5", size = 786594, upload-time = "2026-03-28T21:48:58.404Z" },
- { url = "https://files.pythonhosted.org/packages/ea/72/28295068c92dbd6d3ce4fd22554345cf504e957cc57dadeda4a64fa86a57/regex-2026.3.32-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e4c8fa46aad1a11ae2f8fcd1c90b9d55e18925829ac0d98c5bb107f93351745", size = 800167, upload-time = "2026-03-28T21:49:01.226Z" },
- { url = "https://files.pythonhosted.org/packages/ca/17/b10745adeca5b8d52da050e7c746137f5d01dabc6dbbe6e8d9d821dc65c1/regex-2026.3.32-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cec365d44835b043d7b3266487797639d07d621bec9dc0ea224b00775797cc1", size = 865906, upload-time = "2026-03-28T21:49:03.484Z" },
- { url = "https://files.pythonhosted.org/packages/45/9d/1acbcce765044ac0c87f453f4876e0897f7a61c10315262f960184310798/regex-2026.3.32-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:09e26cad1544d856da85881ad292797289e4406338afe98163f3db9f7fac816c", size = 772642, upload-time = "2026-03-28T21:49:06.811Z" },
- { url = "https://files.pythonhosted.org/packages/24/41/1ef8b4811355ad7b9d7579d3aeca00f18b7bc043ace26c8c609b9287346d/regex-2026.3.32-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:6062c4ef581a3e9e503dccf4e1b7f2d33fdc1c13ad510b287741ac73bc4c6b27", size = 856927, upload-time = "2026-03-28T21:49:09.373Z" },
- { url = "https://files.pythonhosted.org/packages/97/b1/0dc1d361be80ec1b8b707ada041090181133a7a29d438e432260a4b26f9a/regex-2026.3.32-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88ebc0783907468f17fca3d7821b30f9c21865a721144eb498cb0ff99a67bcac", size = 801910, upload-time = "2026-03-28T21:49:11.818Z" },
- { url = "https://files.pythonhosted.org/packages/b5/db/1a23f767fa250844772a9464306d34e0fafe2c317303b88a1415096b6324/regex-2026.3.32-cp314-cp314t-win32.whl", hash = "sha256:e480d3dac06c89bc2e0fd87524cc38c546ac8b4a38177650745e64acbbcfdeba", size = 275714, upload-time = "2026-03-28T21:49:14.528Z" },
- { url = "https://files.pythonhosted.org/packages/c2/2b/616d31b125ca76079d74d6b1d84ec0860ffdb41c379151135d06e35a8633/regex-2026.3.32-cp314-cp314t-win_amd64.whl", hash = "sha256:67015a8162d413af9e3309d9a24e385816666fbf09e48e3ec43342c8536f7df6", size = 285722, upload-time = "2026-03-28T21:49:16.642Z" },
- { url = "https://files.pythonhosted.org/packages/7e/91/043d9a00d6123c5fa22a3dc96b10445ce434a8110e1d5e53efb01f243c8b/regex-2026.3.32-cp314-cp314t-win_arm64.whl", hash = "sha256:1a6ac1ed758902e664e0d95c1ee5991aa6fb355423f378ed184c6ec47a1ec0e9", size = 275700, upload-time = "2026-03-28T21:49:19.348Z" },
+version = "2026.4.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/3a246dbf05666918bd3664d9d787f84a9108f6f43cc953a077e4a7dfdb7e/regex-2026.4.4.tar.gz", hash = "sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423", size = 416000, upload-time = "2026-04-03T20:56:28.155Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/59/fd98f8fd54b3feaa76a855324c676c17668c5a1121ec91b7ec96b01bf865/regex-2026.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:74fa82dcc8143386c7c0392e18032009d1db715c25f4ba22d23dc2e04d02a20f", size = 489403, upload-time = "2026-04-03T20:52:39.742Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/64/d0f222f68e3579d50babf0e4fcc9c9639ef0587fecc00b15e1e46bfc32fa/regex-2026.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a85b620a388d6c9caa12189233109e236b3da3deffe4ff11b84ae84e218a274f", size = 291208, upload-time = "2026-04-03T20:52:42.943Z" },
+ { url = "https://files.pythonhosted.org/packages/16/7f/3fab9709b0b0060ba81a04b8a107b34147cd14b9c5551b772154d6505504/regex-2026.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2895506ebe32cc63eeed8f80e6eae453171cfccccab35b70dc3129abec35a5b8", size = 289214, upload-time = "2026-04-03T20:52:44.648Z" },
+ { url = "https://files.pythonhosted.org/packages/14/bc/f5dcf04fd462139dcd75495c02eee22032ef741cfa151386a39c3f5fc9b5/regex-2026.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6780f008ee81381c737634e75c24e5a6569cc883c4f8e37a37917ee79efcafd9", size = 785505, upload-time = "2026-04-03T20:52:46.35Z" },
+ { url = "https://files.pythonhosted.org/packages/37/36/8a906e216d5b4de7ec3788c1d589b45db40c1c9580cd7b326835cfc976d4/regex-2026.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:88e9b048345c613f253bea4645b2fe7e579782b82cac99b1daad81e29cc2ed8e", size = 852129, upload-time = "2026-04-03T20:52:48.661Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/bb/bad2d79be0917a6ef31f5e0f161d9265cb56fd90a3ae1d2e8d991882a48b/regex-2026.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:be061028481186ba62a0f4c5f1cc1e3d5ab8bce70c89236ebe01023883bc903b", size = 899578, upload-time = "2026-04-03T20:52:50.61Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/b9/7cd0ceb58cd99c70806241636640ae15b4a3fe62e22e9b99afa67a0d7965/regex-2026.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d2228c02b368d69b724c36e96d3d1da721561fb9cc7faa373d7bf65e07d75cb5", size = 793634, upload-time = "2026-04-03T20:52:53Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/fb/c58e3ea40ed183806ccbac05c29a3e8c2f88c1d3a66ed27860d5cad7c62d/regex-2026.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0540e5b733618a2f84e9cb3e812c8afa82e151ca8e19cf6c4e95c5a65198236f", size = 786210, upload-time = "2026-04-03T20:52:54.713Z" },
+ { url = "https://files.pythonhosted.org/packages/54/a9/53790fc7a6c948a7be2bc7214fd9cabdd0d1ba561b0f401c91f4ff0357f0/regex-2026.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cf9b1b2e692d4877880388934ac746c99552ce6bf40792a767fd42c8c99f136d", size = 769930, upload-time = "2026-04-03T20:52:56.825Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/3c/29ca44729191c79f5476538cd0fa04fa2553b3c45508519ecea4c7afa8f6/regex-2026.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:011bb48bffc1b46553ac704c975b3348717f4e4aa7a67522b51906f99da1820c", size = 774892, upload-time = "2026-04-03T20:52:58.934Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/db/6ae74ef8a4cfead341c367e4eed45f71fb1aaba35827a775eed4f1ba4f74/regex-2026.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8512fcdb43f1bf18582698a478b5ab73f9c1667a5b7548761329ef410cd0a760", size = 848816, upload-time = "2026-04-03T20:53:00.684Z" },
+ { url = "https://files.pythonhosted.org/packages/53/9a/f7f2c1c6b610d7c6de1c3dc5951effd92c324b1fde761af2044b4721020f/regex-2026.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:867bddc63109a0276f5a31999e4c8e0eb7bbbad7d6166e28d969a2c1afeb97f9", size = 758363, upload-time = "2026-04-03T20:53:02.155Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/55/e5386d393bbf8b43c8b084703a46d635e7b2bdc6e0f5909a2619ea1125f1/regex-2026.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1b9a00b83f3a40e09859c78920571dcb83293c8004079653dd22ec14bbfa98c7", size = 837122, upload-time = "2026-04-03T20:53:03.727Z" },
+ { url = "https://files.pythonhosted.org/packages/01/da/cc78710ea2e60b10bacfcc9beb18c67514200ab03597b3b2b319995785c2/regex-2026.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e355be718caf838aa089870259cf1776dc2a4aa980514af9d02c59544d9a8b22", size = 782140, upload-time = "2026-04-03T20:53:05.608Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/5f/c7bcba41529105d6c2ca7080ecab7184cd00bee2e1ad1fdea80e618704ea/regex-2026.4.4-cp310-cp310-win32.whl", hash = "sha256:33bfda9684646d323414df7abe5692c61d297dbb0530b28ec66442e768813c59", size = 266225, upload-time = "2026-04-03T20:53:07.342Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/26/a745729c2c49354ec4f4bce168f29da932ca01b4758227686cc16c7dde1b/regex-2026.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:0709f22a56798457ae317bcce42aacee33c680068a8f14097430d9f9ba364bee", size = 278393, upload-time = "2026-04-03T20:53:08.65Z" },
+ { url = "https://files.pythonhosted.org/packages/87/8b/4327eeb9dbb4b098ebecaf02e9f82b79b6077beeb54c43d9a0660cf7c44c/regex-2026.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:ee9627de8587c1a22201cb16d0296ab92b4df5cdcb5349f4e9744d61db7c7c98", size = 270470, upload-time = "2026-04-03T20:53:10.018Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/7a/617356cbecdb452812a5d42f720d6d5096b360d4a4c1073af700ea140ad2/regex-2026.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4c36a85b00fadb85db9d9e90144af0a980e1a3d2ef9cd0f8a5bef88054657c6", size = 489415, upload-time = "2026-04-03T20:53:11.645Z" },
+ { url = "https://files.pythonhosted.org/packages/20/e6/bf057227144d02e3ba758b66649e87531d744dda5f3254f48660f18ae9d8/regex-2026.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dcb5453ecf9cd58b562967badd1edbf092b0588a3af9e32ee3d05c985077ce87", size = 291205, upload-time = "2026-04-03T20:53:13.289Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/3b/637181b787dd1a820ba1c712cee2b4144cd84a32dc776ca067b12b2d70c8/regex-2026.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6aa809ed4dc3706cc38594d67e641601bd2f36d5555b2780ff074edfcb136cf8", size = 289225, upload-time = "2026-04-03T20:53:16.002Z" },
+ { url = "https://files.pythonhosted.org/packages/05/21/bac05d806ed02cd4b39d9c8e5b5f9a2998c94c3a351b7792e80671fa5315/regex-2026.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33424f5188a7db12958246a54f59a435b6cb62c5cf9c8d71f7cc49475a5fdada", size = 792434, upload-time = "2026-04-03T20:53:17.414Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/17/c65d1d8ae90b772d5758eb4014e1e011bb2db353fc4455432e6cc9100df7/regex-2026.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d346fccdde28abba117cc9edc696b9518c3307fbfcb689e549d9b5979018c6d", size = 861730, upload-time = "2026-04-03T20:53:18.903Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/64/933321aa082a2c6ee2785f22776143ba89840189c20d3b6b1d12b6aae16b/regex-2026.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:415a994b536440f5011aa77e50a4274d15da3245e876e5c7f19da349caaedd87", size = 906495, upload-time = "2026-04-03T20:53:20.561Z" },
+ { url = "https://files.pythonhosted.org/packages/01/ea/4c8d306e9c36ac22417336b1e02e7b358152c34dc379673f2d331143725f/regex-2026.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21e5eb86179b4c67b5759d452ea7c48eb135cd93308e7a260aa489ed2eb423a4", size = 799810, upload-time = "2026-04-03T20:53:22.961Z" },
+ { url = "https://files.pythonhosted.org/packages/29/ce/7605048f00e1379eba89d610c7d644d8f695dc9b26d3b6ecfa3132b872ff/regex-2026.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:312ec9dd1ae7d96abd8c5a36a552b2139931914407d26fba723f9e53c8186f86", size = 774242, upload-time = "2026-04-03T20:53:25.015Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/77/283e0d5023fde22cd9e86190d6d9beb21590a452b195ffe00274de470691/regex-2026.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0d2b28aa1354c7cd7f71b7658c4326f7facac106edd7f40eda984424229fd59", size = 781257, upload-time = "2026-04-03T20:53:26.918Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/fb/7f3b772be101373c8626ed34c5d727dcbb8abd42a7b1219bc25fd9a3cc04/regex-2026.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:349d7310eddff40429a099c08d995c6d4a4bfaf3ff40bd3b5e5cb5a5a3c7d453", size = 854490, upload-time = "2026-04-03T20:53:29.065Z" },
+ { url = "https://files.pythonhosted.org/packages/85/30/56547b80f34f4dd2986e1cdd63b1712932f63b6c4ce2f79c50a6cd79d1c2/regex-2026.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:e7ab63e9fe45a9ec3417509e18116b367e89c9ceb6219222a3396fa30b147f80", size = 763544, upload-time = "2026-04-03T20:53:30.917Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/2f/ce060fdfea8eff34a8997603532e44cdb7d1f35e3bc253612a8707a90538/regex-2026.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fe896e07a5a2462308297e515c0054e9ec2dd18dfdc9427b19900b37dfe6f40b", size = 844442, upload-time = "2026-04-03T20:53:32.463Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/44/810cb113096a1dacbe82789fbfab2823f79d19b7f1271acecb7009ba9b88/regex-2026.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eb59c65069498dbae3c0ef07bbe224e1eaa079825a437fb47a479f0af11f774f", size = 789162, upload-time = "2026-04-03T20:53:34.039Z" },
+ { url = "https://files.pythonhosted.org/packages/20/96/9647dd7f2ecf6d9ce1fb04dfdb66910d094e10d8fe53e9c15096d8aa0bd2/regex-2026.4.4-cp311-cp311-win32.whl", hash = "sha256:2a5d273181b560ef8397c8825f2b9d57013de744da9e8257b8467e5da8599351", size = 266227, upload-time = "2026-04-03T20:53:35.601Z" },
+ { url = "https://files.pythonhosted.org/packages/33/80/74e13262460530c3097ff343a17de9a34d040a5dc4de9cf3a8241faab51c/regex-2026.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:9542ccc1e689e752594309444081582f7be2fdb2df75acafea8a075108566735", size = 278399, upload-time = "2026-04-03T20:53:37.021Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/3c/39f19f47f19dcefa3403f09d13562ca1c0fd07ab54db2bc03148f3f6b46a/regex-2026.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:b5f9fb784824a042be3455b53d0b112655686fdb7a91f88f095f3fee1e2a2a54", size = 270473, upload-time = "2026-04-03T20:53:38.633Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/28/b972a4d3df61e1d7bcf1b59fdb3cddef22f88b6be43f161bb41ebc0e4081/regex-2026.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52", size = 490434, upload-time = "2026-04-03T20:53:40.219Z" },
+ { url = "https://files.pythonhosted.org/packages/84/20/30041446cf6dc3e0eab344fc62770e84c23b6b68a3b657821f9f80cb69b4/regex-2026.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb", size = 292061, upload-time = "2026-04-03T20:53:41.862Z" },
+ { url = "https://files.pythonhosted.org/packages/62/c8/3baa06d75c98c46d4cc4262b71fd2edb9062b5665e868bca57859dadf93a/regex-2026.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76", size = 289628, upload-time = "2026-04-03T20:53:43.701Z" },
+ { url = "https://files.pythonhosted.org/packages/31/87/3accf55634caad8c0acab23f5135ef7d4a21c39f28c55c816ae012931408/regex-2026.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be", size = 796651, upload-time = "2026-04-03T20:53:45.379Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/0c/aaa2c83f34efedbf06f61cb1942c25f6cf1ee3b200f832c4d05f28306c2e/regex-2026.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1", size = 865916, upload-time = "2026-04-03T20:53:47.064Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/f6/8c6924c865124643e8f37823eca845dc27ac509b2ee58123685e71cd0279/regex-2026.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13", size = 912287, upload-time = "2026-04-03T20:53:49.422Z" },
+ { url = "https://files.pythonhosted.org/packages/11/0e/a9f6f81013e0deaf559b25711623864970fe6a098314e374ccb1540a4152/regex-2026.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9", size = 801126, upload-time = "2026-04-03T20:53:51.096Z" },
+ { url = "https://files.pythonhosted.org/packages/71/61/3a0cc8af2dc0c8deb48e644dd2521f173f7e6513c6e195aad9aa8dd77ac5/regex-2026.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d", size = 776788, upload-time = "2026-04-03T20:53:52.889Z" },
+ { url = "https://files.pythonhosted.org/packages/64/0b/8bb9cbf21ef7dee58e49b0fdb066a7aded146c823202e16494a36777594f/regex-2026.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3", size = 785184, upload-time = "2026-04-03T20:53:55.627Z" },
+ { url = "https://files.pythonhosted.org/packages/99/c2/d3e80e8137b25ee06c92627de4e4d98b94830e02b3e6f81f3d2e3f504cf5/regex-2026.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0", size = 859913, upload-time = "2026-04-03T20:53:57.249Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/e6/9d5d876157d969c804622456ef250017ac7a8f83e0e14f903b9e6df5ce95/regex-2026.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043", size = 765732, upload-time = "2026-04-03T20:53:59.428Z" },
+ { url = "https://files.pythonhosted.org/packages/82/80/b568935b4421388561c8ed42aff77247285d3ae3bb2a6ca22af63bae805e/regex-2026.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244", size = 852152, upload-time = "2026-04-03T20:54:01.505Z" },
+ { url = "https://files.pythonhosted.org/packages/39/29/f0f81217e21cd998245da047405366385d5c6072048038a3d33b37a79dc0/regex-2026.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73", size = 789076, upload-time = "2026-04-03T20:54:03.323Z" },
+ { url = "https://files.pythonhosted.org/packages/49/1d/1d957a61976ab9d4e767dd4f9d04b66cc0c41c5e36cf40e2d43688b5ae6f/regex-2026.4.4-cp312-cp312-win32.whl", hash = "sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f", size = 266700, upload-time = "2026-04-03T20:54:05.639Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/5c/bf575d396aeb58ea13b06ef2adf624f65b70fafef6950a80fc3da9cae3bc/regex-2026.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b", size = 277768, upload-time = "2026-04-03T20:54:07.312Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/27/049df16ec6a6828ccd72add3c7f54b4df029669bea8e9817df6fff58be90/regex-2026.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983", size = 270568, upload-time = "2026-04-03T20:54:09.484Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/83/c4373bc5f31f2cf4b66f9b7c31005bd87fe66f0dce17701f7db4ee79ee29/regex-2026.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943", size = 490273, upload-time = "2026-04-03T20:54:11.202Z" },
+ { url = "https://files.pythonhosted.org/packages/46/f8/fe62afbcc3cf4ad4ac9adeaafd98aa747869ae12d3e8e2ac293d0593c435/regex-2026.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031", size = 291954, upload-time = "2026-04-03T20:54:13.412Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/92/4712b9fe6a33d232eeb1c189484b80c6c4b8422b90e766e1195d6e758207/regex-2026.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7", size = 289487, upload-time = "2026-04-03T20:54:15.824Z" },
+ { url = "https://files.pythonhosted.org/packages/88/2c/f83b93f85e01168f1070f045a42d4c937b69fdb8dd7ae82d307253f7e36e/regex-2026.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17", size = 796646, upload-time = "2026-04-03T20:54:18.229Z" },
+ { url = "https://files.pythonhosted.org/packages/df/55/61a2e17bf0c4dc57e11caf8dd11771280d8aaa361785f9e3bc40d653f4a7/regex-2026.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17", size = 865904, upload-time = "2026-04-03T20:54:20.019Z" },
+ { url = "https://files.pythonhosted.org/packages/45/32/1ac8ed1b5a346b5993a3d256abe0a0f03b0b73c8cc88d928537368ac65b6/regex-2026.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae", size = 912304, upload-time = "2026-04-03T20:54:22.403Z" },
+ { url = "https://files.pythonhosted.org/packages/26/47/2ee5c613ab546f0eddebf9905d23e07beb933416b1246c2d8791d01979b4/regex-2026.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e", size = 801126, upload-time = "2026-04-03T20:54:24.308Z" },
+ { url = "https://files.pythonhosted.org/packages/75/cd/41dacd129ca9fd20bd7d02f83e0fad83e034ac8a084ec369c90f55ef37e2/regex-2026.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d", size = 776772, upload-time = "2026-04-03T20:54:26.319Z" },
+ { url = "https://files.pythonhosted.org/packages/89/6d/5af0b588174cb5f46041fa7dd64d3fd5cd2fe51f18766703d1edc387f324/regex-2026.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27", size = 785228, upload-time = "2026-04-03T20:54:28.387Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/3b/f5a72b7045bd59575fc33bf1345f156fcfd5a8484aea6ad84b12c5a82114/regex-2026.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf", size = 860032, upload-time = "2026-04-03T20:54:30.641Z" },
+ { url = "https://files.pythonhosted.org/packages/39/a4/72a317003d6fcd7a573584a85f59f525dfe8f67e355ca74eb6b53d66a5e2/regex-2026.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0", size = 765714, upload-time = "2026-04-03T20:54:32.789Z" },
+ { url = "https://files.pythonhosted.org/packages/25/1e/5672e16f34dbbcb2560cc7e6a2fbb26dfa8b270711e730101da4423d3973/regex-2026.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa", size = 852078, upload-time = "2026-04-03T20:54:34.546Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/0d/c813f0af7c6cc7ed7b9558bac2e5120b60ad0fa48f813e4d4bd55446f214/regex-2026.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b", size = 789181, upload-time = "2026-04-03T20:54:36.642Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/6d/a344608d1adbd2a95090ddd906cec09a11be0e6517e878d02a5123e0917f/regex-2026.4.4-cp313-cp313-win32.whl", hash = "sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62", size = 266690, upload-time = "2026-04-03T20:54:38.343Z" },
+ { url = "https://files.pythonhosted.org/packages/31/07/54049f89b46235ca6f45cd6c88668a7050e77d4a15555e47dd40fde75263/regex-2026.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81", size = 277733, upload-time = "2026-04-03T20:54:40.11Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/21/61366a8e20f4d43fb597708cac7f0e2baadb491ecc9549b4980b2be27d16/regex-2026.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427", size = 270565, upload-time = "2026-04-03T20:54:41.883Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/1e/3a2b9672433bef02f5d39aa1143ca2c08f311c1d041c464a42be9ae648dc/regex-2026.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c", size = 494126, upload-time = "2026-04-03T20:54:43.602Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/4b/c132a4f4fe18ad3340d89fcb56235132b69559136036b845be3c073142ed/regex-2026.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141", size = 293882, upload-time = "2026-04-03T20:54:45.41Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/5f/eaa38092ce7a023656280f2341dbbd4ad5f05d780a70abba7bb4f4bea54c/regex-2026.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717", size = 292334, upload-time = "2026-04-03T20:54:47.051Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/f6/dd38146af1392dac33db7074ab331cec23cced3759167735c42c5460a243/regex-2026.4.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07", size = 811691, upload-time = "2026-04-03T20:54:49.074Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/f0/dc54c2e69f5eeec50601054998ec3690d5344277e782bd717e49867c1d29/regex-2026.4.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca", size = 871227, upload-time = "2026-04-03T20:54:51.035Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/af/cb16bd5dc61621e27df919a4449bbb7e5a1034c34d307e0a706e9cc0f3e3/regex-2026.4.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520", size = 917435, upload-time = "2026-04-03T20:54:52.994Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/71/8b260897f22996b666edd9402861668f45a2ca259f665ac029e6104a2d7d/regex-2026.4.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883", size = 816358, upload-time = "2026-04-03T20:54:54.884Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/60/775f7f72a510ef238254906c2f3d737fc80b16ca85f07d20e318d2eea894/regex-2026.4.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b", size = 785549, upload-time = "2026-04-03T20:54:57.01Z" },
+ { url = "https://files.pythonhosted.org/packages/58/42/34d289b3627c03cf381e44da534a0021664188fa49ba41513da0b4ec6776/regex-2026.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1", size = 801364, upload-time = "2026-04-03T20:54:58.981Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/20/f6ecf319b382a8f1ab529e898b222c3f30600fcede7834733c26279e7465/regex-2026.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b", size = 866221, upload-time = "2026-04-03T20:55:00.88Z" },
+ { url = "https://files.pythonhosted.org/packages/92/6a/9f16d3609d549bd96d7a0b2aee1625d7512ba6a03efc01652149ef88e74d/regex-2026.4.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff", size = 772530, upload-time = "2026-04-03T20:55:03.213Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/f6/aa9768bc96a4c361ac96419fbaf2dcdc33970bb813df3ba9b09d5d7b6d96/regex-2026.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb", size = 856989, upload-time = "2026-04-03T20:55:05.087Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/b4/c671db3556be2473ae3e4bb7a297c518d281452871501221251ea4ecba57/regex-2026.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4", size = 803241, upload-time = "2026-04-03T20:55:07.162Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/5c/83e3b1d89fa4f6e5a1bc97b4abd4a9a97b3c1ac7854164f694f5f0ba98a0/regex-2026.4.4-cp313-cp313t-win32.whl", hash = "sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa", size = 269921, upload-time = "2026-04-03T20:55:09.62Z" },
+ { url = "https://files.pythonhosted.org/packages/28/07/077c387121f42cdb4d92b1301133c0d93b5709d096d1669ab847dda9fe2e/regex-2026.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0", size = 281240, upload-time = "2026-04-03T20:55:11.521Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/22/ead4a4abc7c59a4d882662aa292ca02c8b617f30b6e163bc1728879e9353/regex-2026.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe", size = 272440, upload-time = "2026-04-03T20:55:13.365Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/f5/ed97c2dc47b5fbd4b73c0d7d75f9ebc8eca139f2bbef476bba35f28c0a77/regex-2026.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7", size = 490343, upload-time = "2026-04-03T20:55:15.241Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e9/de4828a7385ec166d673a5790ad06ac48cdaa98bc0960108dd4b9cc1aef7/regex-2026.4.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752", size = 291909, upload-time = "2026-04-03T20:55:17.558Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/d6/5cfbfc97f3201a4d24b596a77957e092030dcc4205894bc035cedcfce62f/regex-2026.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951", size = 289692, upload-time = "2026-04-03T20:55:20.561Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/ac/f2212d9fd56fe897e36d0110ba30ba2d247bd6410c5bd98499c7e5a1e1f2/regex-2026.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f", size = 796979, upload-time = "2026-04-03T20:55:22.56Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/e3/a016c12675fbac988a60c7e1c16e67823ff0bc016beb27bd7a001dbdabc6/regex-2026.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8", size = 866744, upload-time = "2026-04-03T20:55:24.646Z" },
+ { url = "https://files.pythonhosted.org/packages/af/a4/0b90ca4cf17adc3cb43de80ec71018c37c88ad64987e8d0d481a95ca60b5/regex-2026.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4", size = 911613, upload-time = "2026-04-03T20:55:27.033Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/3b/2b3dac0b82d41ab43aa87c6ecde63d71189d03fe8854b8ca455a315edac3/regex-2026.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9", size = 800551, upload-time = "2026-04-03T20:55:29.532Z" },
+ { url = "https://files.pythonhosted.org/packages/25/fe/5365eb7aa0e753c4b5957815c321519ecab033c279c60e1b1ae2367fa810/regex-2026.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83", size = 776911, upload-time = "2026-04-03T20:55:31.526Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/b3/7fb0072156bba065e3b778a7bc7b0a6328212be5dd6a86fd207e0c4f2dab/regex-2026.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb", size = 785751, upload-time = "2026-04-03T20:55:33.797Z" },
+ { url = "https://files.pythonhosted.org/packages/02/1a/9f83677eb699273e56e858f7bd95acdbee376d42f59e8bfca2fd80d79df3/regex-2026.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465", size = 860484, upload-time = "2026-04-03T20:55:35.745Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/7a/93937507b61cfcff8b4c5857f1b452852b09f741daa9acae15c971d8554e/regex-2026.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4", size = 765939, upload-time = "2026-04-03T20:55:37.972Z" },
+ { url = "https://files.pythonhosted.org/packages/86/ea/81a7f968a351c6552b1670ead861e2a385be730ee28402233020c67f9e0f/regex-2026.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566", size = 851417, upload-time = "2026-04-03T20:55:39.92Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/7e/323c18ce4b5b8f44517a36342961a0306e931e499febbd876bb149d900f0/regex-2026.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95", size = 789056, upload-time = "2026-04-03T20:55:42.303Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/af/e7510f9b11b1913b0cd44eddb784b2d650b2af6515bfce4cffcc5bfd1d38/regex-2026.4.4-cp314-cp314-win32.whl", hash = "sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8", size = 272130, upload-time = "2026-04-03T20:55:44.995Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/51/57dae534c915e2d3a21490e88836fa2ae79dde3b66255ecc0c0a155d2c10/regex-2026.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4", size = 280992, upload-time = "2026-04-03T20:55:47.316Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/5e/abaf9f4c3792e34edb1434f06717fae2b07888d85cb5cec29f9204931bf8/regex-2026.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f", size = 273563, upload-time = "2026-04-03T20:55:49.273Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/06/35da85f9f217b9538b99cbb170738993bcc3b23784322decb77619f11502/regex-2026.4.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3", size = 494191, upload-time = "2026-04-03T20:55:51.258Z" },
+ { url = "https://files.pythonhosted.org/packages/54/5b/1bc35f479eef8285c4baf88d8c002023efdeebb7b44a8735b36195486ae7/regex-2026.4.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e", size = 293877, upload-time = "2026-04-03T20:55:53.214Z" },
+ { url = "https://files.pythonhosted.org/packages/39/5b/f53b9ad17480b3ddd14c90da04bfb55ac6894b129e5dea87bcaf7d00e336/regex-2026.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6", size = 292410, upload-time = "2026-04-03T20:55:55.736Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/56/52377f59f60a7c51aa4161eecf0b6032c20b461805aca051250da435ffc9/regex-2026.4.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359", size = 811831, upload-time = "2026-04-03T20:55:57.802Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/63/8026310bf066f702a9c361f83a8c9658f3fe4edb349f9c1e5d5273b7c40c/regex-2026.4.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a", size = 871199, upload-time = "2026-04-03T20:56:00.333Z" },
+ { url = "https://files.pythonhosted.org/packages/20/9f/a514bbb00a466dbb506d43f187a04047f7be1505f10a9a15615ead5080ee/regex-2026.4.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55", size = 917649, upload-time = "2026-04-03T20:56:02.445Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/6b/8399f68dd41a2030218839b9b18360d79b86d22b9fab5ef477c7f23ca67c/regex-2026.4.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99", size = 816388, upload-time = "2026-04-03T20:56:04.595Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/9c/103963f47c24339a483b05edd568594c2be486188f688c0170fd504b2948/regex-2026.4.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790", size = 785746, upload-time = "2026-04-03T20:56:07.13Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/ee/7f6054c0dec0cee3463c304405e4ff42e27cff05bf36fcb34be549ab17bd/regex-2026.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc", size = 801483, upload-time = "2026-04-03T20:56:09.365Z" },
+ { url = "https://files.pythonhosted.org/packages/30/c2/51d3d941cf6070dc00c3338ecf138615fc3cce0421c3df6abe97a08af61a/regex-2026.4.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f", size = 866331, upload-time = "2026-04-03T20:56:12.039Z" },
+ { url = "https://files.pythonhosted.org/packages/16/e8/76d50dcc122ac33927d939f350eebcfe3dbcbda96913e03433fc36de5e63/regex-2026.4.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863", size = 772673, upload-time = "2026-04-03T20:56:14.558Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/6e/5f6bf75e20ea6873d05ba4ec78378c375cbe08cdec571c83fbb01606e563/regex-2026.4.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a", size = 857146, upload-time = "2026-04-03T20:56:16.663Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/33/3c76d9962949e487ebba353a18e89399f292287204ac8f2f4cfc3a51c233/regex-2026.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81", size = 803463, upload-time = "2026-04-03T20:56:18.923Z" },
+ { url = "https://files.pythonhosted.org/packages/19/eb/ef32dcd2cb69b69bc0c3e55205bce94a7def48d495358946bc42186dcccc/regex-2026.4.4-cp314-cp314t-win32.whl", hash = "sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74", size = 275709, upload-time = "2026-04-03T20:56:20.996Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/86/c291bf740945acbf35ed7dbebf8e2eea2f3f78041f6bd7cdab80cb274dc0/regex-2026.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45", size = 285622, upload-time = "2026-04-03T20:56:23.641Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/e7/ec846d560ae6a597115153c02ca6138a7877a1748b2072d9521c10a93e58/regex-2026.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d", size = 275773, upload-time = "2026-04-03T20:56:26.07Z" },
]
[[package]]
@@ -19538,7 +19654,7 @@ wheels = [
[[package]]
name = "sagemaker-studio"
-version = "1.0.25"
+version = "1.0.26"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "boto3" },
@@ -19550,9 +19666,9 @@ dependencies = [
{ name = "setuptools" },
{ name = "urllib3" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/94/62/5ed5bdad5968e08d2a95b9aa6b907c62982763da418589b139158ece1886/sagemaker_studio-1.0.25.tar.gz", hash = "sha256:71f72b2c3b936bea85c14ed673bf87bb650bc2f9b8be5c17bf8dd7291c458824", size = 531676, upload-time = "2026-03-10T22:59:44.497Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/07/f7/30728df902393d789820e9707c633514a7f6a375027996c19c1e422cc10f/sagemaker_studio-1.0.26.tar.gz", hash = "sha256:d16e54710ac17d232d2d5ba6735d158d072074b076c0c50b9d4b57dc5c01b0af", size = 533284, upload-time = "2026-04-02T23:52:17.674Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/18/13/64507f53aa9d514b10988d3b538de113f83ce68cebdd1c5e4732f18995e6/sagemaker_studio-1.0.25-py3-none-any.whl", hash = "sha256:a5d06ceb7c3fa30cce700f724f622ee450b25ae5b42d631dbf1d24ddf37e0c54", size = 306552, upload-time = "2026-03-10T22:59:42.795Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/7c/826c8f40de9f0f6f7717c1bd3f45d139c4cfa271fb1daa8a3b14a7d98311/sagemaker_studio-1.0.26-py3-none-any.whl", hash = "sha256:0961f9983d93f9710d2a11bc2af69ae99af0f295db80112b792428c884cd167c", size = 307327, upload-time = "2026-04-02T23:52:16.157Z" },
]
[[package]]
@@ -19828,8 +19944,8 @@ name = "secretstorage"
version = "3.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "cryptography", marker = "(python_full_version < '3.14' and sys_platform == 'emscripten') or (python_full_version < '3.14' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" },
- { name = "jeepney", marker = "(python_full_version < '3.14' and sys_platform == 'emscripten') or (python_full_version < '3.14' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" },
+ { name = "cryptography" },
+ { name = "jeepney" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" }
wheels = [
@@ -20599,62 +20715,62 @@ wheels = [
[[package]]
name = "sqlalchemy"
-version = "2.0.48"
+version = "2.0.49"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/1f/73/b4a9737255583b5fa858e0bb8e116eb94b88c910164ed2ed719147bde3de/sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7", size = 9886075, upload-time = "2026-03-02T15:28:51.474Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9a/67/1235676e93dd3b742a4a8eddfae49eea46c85e3eed29f0da446a8dd57500/sqlalchemy-2.0.48-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89", size = 2157384, upload-time = "2026-03-02T15:38:26.781Z" },
- { url = "https://files.pythonhosted.org/packages/4d/d7/fa728b856daa18c10e1390e76f26f64ac890c947008284387451d56ca3d0/sqlalchemy-2.0.48-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0", size = 3236981, upload-time = "2026-03-02T15:58:53.53Z" },
- { url = "https://files.pythonhosted.org/packages/5c/ad/6c4395649a212a6c603a72c5b9ab5dce3135a1546cfdffa3c427e71fd535/sqlalchemy-2.0.48-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd", size = 3235232, upload-time = "2026-03-02T15:52:25.654Z" },
- { url = "https://files.pythonhosted.org/packages/01/f4/58f845e511ac0509765a6f85eb24924c1ef0d54fb50de9d15b28c3601458/sqlalchemy-2.0.48-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29", size = 3188106, upload-time = "2026-03-02T15:58:55.193Z" },
- { url = "https://files.pythonhosted.org/packages/3f/f9/6dcc7bfa5f5794c3a095e78cd1de8269dfb5584dfd4c2c00a50d3c1ade44/sqlalchemy-2.0.48-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0", size = 3209522, upload-time = "2026-03-02T15:52:27.407Z" },
- { url = "https://files.pythonhosted.org/packages/d7/5a/b632875ab35874d42657f079529f0745410604645c269a8c21fb4272ff7a/sqlalchemy-2.0.48-cp310-cp310-win32.whl", hash = "sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018", size = 2117695, upload-time = "2026-03-02T15:46:51.389Z" },
- { url = "https://files.pythonhosted.org/packages/de/03/9752eb2a41afdd8568e41ac3c3128e32a0a73eada5ab80483083604a56d1/sqlalchemy-2.0.48-cp310-cp310-win_amd64.whl", hash = "sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76", size = 2140928, upload-time = "2026-03-02T15:46:52.992Z" },
- { url = "https://files.pythonhosted.org/packages/d7/6d/b8b78b5b80f3c3ab3f7fa90faa195ec3401f6d884b60221260fd4d51864c/sqlalchemy-2.0.48-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc", size = 2157184, upload-time = "2026-03-02T15:38:28.161Z" },
- { url = "https://files.pythonhosted.org/packages/21/4b/4f3d4a43743ab58b95b9ddf5580a265b593d017693df9e08bd55780af5bb/sqlalchemy-2.0.48-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c", size = 3313555, upload-time = "2026-03-02T15:58:57.21Z" },
- { url = "https://files.pythonhosted.org/packages/21/dd/3b7c53f1dbbf736fd27041aee68f8ac52226b610f914085b1652c2323442/sqlalchemy-2.0.48-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7", size = 3313057, upload-time = "2026-03-02T15:52:29.366Z" },
- { url = "https://files.pythonhosted.org/packages/d9/cc/3e600a90ae64047f33313d7d32e5ad025417f09d2ded487e8284b5e21a15/sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d", size = 3265431, upload-time = "2026-03-02T15:58:59.096Z" },
- { url = "https://files.pythonhosted.org/packages/8b/19/780138dacfe3f5024f4cf96e4005e91edf6653d53d3673be4844578faf1d/sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571", size = 3287646, upload-time = "2026-03-02T15:52:31.569Z" },
- { url = "https://files.pythonhosted.org/packages/40/fd/f32ced124f01a23151f4777e4c705f3a470adc7bd241d9f36a7c941a33bf/sqlalchemy-2.0.48-cp311-cp311-win32.whl", hash = "sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617", size = 2116956, upload-time = "2026-03-02T15:46:54.535Z" },
- { url = "https://files.pythonhosted.org/packages/58/d5/dd767277f6feef12d05651538f280277e661698f617fa4d086cce6055416/sqlalchemy-2.0.48-cp311-cp311-win_amd64.whl", hash = "sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c", size = 2141627, upload-time = "2026-03-02T15:46:55.849Z" },
- { url = "https://files.pythonhosted.org/packages/ef/91/a42ae716f8925e9659df2da21ba941f158686856107a61cc97a95e7647a3/sqlalchemy-2.0.48-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b", size = 2155737, upload-time = "2026-03-02T15:49:13.207Z" },
- { url = "https://files.pythonhosted.org/packages/b9/52/f75f516a1f3888f027c1cfb5d22d4376f4b46236f2e8669dcb0cddc60275/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb", size = 3337020, upload-time = "2026-03-02T15:50:34.547Z" },
- { url = "https://files.pythonhosted.org/packages/37/9a/0c28b6371e0cdcb14f8f1930778cb3123acfcbd2c95bb9cf6b4a2ba0cce3/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894", size = 3349983, upload-time = "2026-03-02T15:53:25.542Z" },
- { url = "https://files.pythonhosted.org/packages/1c/46/0aee8f3ff20b1dcbceb46ca2d87fcc3d48b407925a383ff668218509d132/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9", size = 3279690, upload-time = "2026-03-02T15:50:36.277Z" },
- { url = "https://files.pythonhosted.org/packages/ce/8c/a957bc91293b49181350bfd55e6dfc6e30b7f7d83dc6792d72043274a390/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e", size = 3314738, upload-time = "2026-03-02T15:53:27.519Z" },
- { url = "https://files.pythonhosted.org/packages/4b/44/1d257d9f9556661e7bdc83667cc414ba210acfc110c82938cb3611eea58f/sqlalchemy-2.0.48-cp312-cp312-win32.whl", hash = "sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99", size = 2115546, upload-time = "2026-03-02T15:54:31.591Z" },
- { url = "https://files.pythonhosted.org/packages/f2/af/c3c7e1f3a2b383155a16454df62ae8c62a30dd238e42e68c24cebebbfae6/sqlalchemy-2.0.48-cp312-cp312-win_amd64.whl", hash = "sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a", size = 2142484, upload-time = "2026-03-02T15:54:34.072Z" },
- { url = "https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4", size = 2152599, upload-time = "2026-03-02T15:49:14.41Z" },
- { url = "https://files.pythonhosted.org/packages/6d/ff/f4e04a4bd5a24304f38cb0d4aa2ad4c0fb34999f8b884c656535e1b2b74c/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f", size = 3278825, upload-time = "2026-03-02T15:50:38.269Z" },
- { url = "https://files.pythonhosted.org/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed", size = 3295200, upload-time = "2026-03-02T15:53:29.366Z" },
- { url = "https://files.pythonhosted.org/packages/87/dc/1609a4442aefd750ea2f32629559394ec92e89ac1d621a7f462b70f736ff/sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658", size = 3226876, upload-time = "2026-03-02T15:50:39.802Z" },
- { url = "https://files.pythonhosted.org/packages/37/c3/6ae2ab5ea2fa989fbac4e674de01224b7a9d744becaf59bb967d62e99bed/sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8", size = 3265045, upload-time = "2026-03-02T15:53:31.421Z" },
- { url = "https://files.pythonhosted.org/packages/6f/82/ea4665d1bb98c50c19666e672f21b81356bd6077c4574e3d2bbb84541f53/sqlalchemy-2.0.48-cp313-cp313-win32.whl", hash = "sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131", size = 2113700, upload-time = "2026-03-02T15:54:35.825Z" },
- { url = "https://files.pythonhosted.org/packages/b7/2b/b9040bec58c58225f073f5b0c1870defe1940835549dafec680cbd58c3c3/sqlalchemy-2.0.48-cp313-cp313-win_amd64.whl", hash = "sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2", size = 2139487, upload-time = "2026-03-02T15:54:37.079Z" },
- { url = "https://files.pythonhosted.org/packages/f4/f4/7b17bd50244b78a49d22cc63c969d71dc4de54567dc152a9b46f6fae40ce/sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae", size = 3558851, upload-time = "2026-03-02T15:57:48.607Z" },
- { url = "https://files.pythonhosted.org/packages/20/0d/213668e9aca61d370f7d2a6449ea4ec699747fac67d4bda1bb3d129025be/sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb", size = 3525525, upload-time = "2026-03-02T16:04:38.058Z" },
- { url = "https://files.pythonhosted.org/packages/85/d7/a84edf412979e7d59c69b89a5871f90a49228360594680e667cb2c46a828/sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b", size = 3466611, upload-time = "2026-03-02T15:57:50.759Z" },
- { url = "https://files.pythonhosted.org/packages/86/55/42404ce5770f6be26a2b0607e7866c31b9a4176c819e9a7a5e0a055770be/sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121", size = 3475812, upload-time = "2026-03-02T16:04:40.092Z" },
- { url = "https://files.pythonhosted.org/packages/ae/ae/29b87775fadc43e627cf582fe3bda4d02e300f6b8f2747c764950d13784c/sqlalchemy-2.0.48-cp313-cp313t-win32.whl", hash = "sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485", size = 2141335, upload-time = "2026-03-02T15:52:51.518Z" },
- { url = "https://files.pythonhosted.org/packages/91/44/f39d063c90f2443e5b46ec4819abd3d8de653893aae92df42a5c4f5843de/sqlalchemy-2.0.48-cp313-cp313t-win_amd64.whl", hash = "sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79", size = 2173095, upload-time = "2026-03-02T15:52:52.79Z" },
- { url = "https://files.pythonhosted.org/packages/f7/b3/f437eaa1cf028bb3c927172c7272366393e73ccd104dcf5b6963f4ab5318/sqlalchemy-2.0.48-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd", size = 2154401, upload-time = "2026-03-02T15:49:17.24Z" },
- { url = "https://files.pythonhosted.org/packages/6c/1c/b3abdf0f402aa3f60f0df6ea53d92a162b458fca2321d8f1f00278506402/sqlalchemy-2.0.48-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f", size = 3274528, upload-time = "2026-03-02T15:50:41.489Z" },
- { url = "https://files.pythonhosted.org/packages/f2/5e/327428a034407651a048f5e624361adf3f9fbac9d0fa98e981e9c6ff2f5e/sqlalchemy-2.0.48-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b", size = 3279523, upload-time = "2026-03-02T15:53:32.962Z" },
- { url = "https://files.pythonhosted.org/packages/2a/ca/ece73c81a918add0965b76b868b7b5359e068380b90ef1656ee995940c02/sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0", size = 3224312, upload-time = "2026-03-02T15:50:42.996Z" },
- { url = "https://files.pythonhosted.org/packages/88/11/fbaf1ae91fa4ee43f4fe79661cead6358644824419c26adb004941bdce7c/sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2", size = 3246304, upload-time = "2026-03-02T15:53:34.937Z" },
- { url = "https://files.pythonhosted.org/packages/fa/a8/5fb0deb13930b4f2f698c5541ae076c18981173e27dd00376dbaea7a9c82/sqlalchemy-2.0.48-cp314-cp314-win32.whl", hash = "sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6", size = 2116565, upload-time = "2026-03-02T15:54:38.321Z" },
- { url = "https://files.pythonhosted.org/packages/95/7e/e83615cb63f80047f18e61e31e8e32257d39458426c23006deeaf48f463b/sqlalchemy-2.0.48-cp314-cp314-win_amd64.whl", hash = "sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0", size = 2142205, upload-time = "2026-03-02T15:54:39.831Z" },
- { url = "https://files.pythonhosted.org/packages/83/e3/69d8711b3f2c5135e9cde5f063bc1605860f0b2c53086d40c04017eb1f77/sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241", size = 3563519, upload-time = "2026-03-02T15:57:52.387Z" },
- { url = "https://files.pythonhosted.org/packages/f8/4f/a7cce98facca73c149ea4578981594aaa5fd841e956834931de503359336/sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0", size = 3528611, upload-time = "2026-03-02T16:04:42.097Z" },
- { url = "https://files.pythonhosted.org/packages/cd/7d/5936c7a03a0b0cb0fa0cc425998821c6029756b0855a8f7ee70fba1de955/sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3", size = 3472326, upload-time = "2026-03-02T15:57:54.423Z" },
- { url = "https://files.pythonhosted.org/packages/f4/33/cea7dfc31b52904efe3dcdc169eb4514078887dff1f5ae28a7f4c5d54b3c/sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b", size = 3478453, upload-time = "2026-03-02T16:04:44.584Z" },
- { url = "https://files.pythonhosted.org/packages/c8/95/32107c4d13be077a9cae61e9ae49966a35dc4bf442a8852dd871db31f62e/sqlalchemy-2.0.48-cp314-cp314t-win32.whl", hash = "sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f", size = 2147209, upload-time = "2026-03-02T15:52:54.274Z" },
- { url = "https://files.pythonhosted.org/packages/d2/d7/1e073da7a4bc645eb83c76067284a0374e643bc4be57f14cc6414656f92c/sqlalchemy-2.0.48-cp314-cp314t-win_amd64.whl", hash = "sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933", size = 2182198, upload-time = "2026-03-02T15:52:55.606Z" },
- { url = "https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096", size = 1940202, upload-time = "2026-03-02T15:52:43.285Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/09/45/461788f35e0364a8da7bda51a1fe1b09762d0c32f12f63727998d85a873b/sqlalchemy-2.0.49.tar.gz", hash = "sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f", size = 9898221, upload-time = "2026-04-03T16:38:11.704Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/96/76/f908955139842c362aa877848f42f9249642d5b69e06cee9eae5111da1bd/sqlalchemy-2.0.49-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f", size = 2159321, upload-time = "2026-04-03T16:50:11.8Z" },
+ { url = "https://files.pythonhosted.org/packages/24/e2/17ba0b7bfbd8de67196889b6d951de269e8a46057d92baca162889beb16d/sqlalchemy-2.0.49-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b", size = 3238937, upload-time = "2026-04-03T16:54:45.731Z" },
+ { url = "https://files.pythonhosted.org/packages/90/1e/410dd499c039deacff395eec01a9da057125fcd0c97e3badc252c6a2d6a7/sqlalchemy-2.0.49-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1", size = 3237188, upload-time = "2026-04-03T16:56:53.217Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/06/e797a8b98a3993ac4bc785309b9b6d005457fc70238ee6cefa7c8867a92e/sqlalchemy-2.0.49-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339", size = 3190061, upload-time = "2026-04-03T16:54:47.489Z" },
+ { url = "https://files.pythonhosted.org/packages/44/d3/5a9f7ef580af1031184b38235da6ac58c3b571df01c9ec061c44b2b0c5a6/sqlalchemy-2.0.49-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d", size = 3211477, upload-time = "2026-04-03T16:56:55.056Z" },
+ { url = "https://files.pythonhosted.org/packages/69/ec/7be8c8cb35f038e963a203e4fe5a028989167cc7299927b7cf297c271e37/sqlalchemy-2.0.49-cp310-cp310-win32.whl", hash = "sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3", size = 2119965, upload-time = "2026-04-03T17:00:50.009Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/31/0defb93e3a10b0cf7d1271aedd87251a08c3a597ee4f353281769b547b5a/sqlalchemy-2.0.49-cp310-cp310-win_amd64.whl", hash = "sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75", size = 2142935, upload-time = "2026-04-03T17:00:51.675Z" },
+ { url = "https://files.pythonhosted.org/packages/60/b5/e3617cc67420f8f403efebd7b043128f94775e57e5b84e7255203390ceae/sqlalchemy-2.0.49-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe", size = 2159126, upload-time = "2026-04-03T16:50:13.242Z" },
+ { url = "https://files.pythonhosted.org/packages/20/9b/91ca80403b17cd389622a642699e5f6564096b698e7cdcbcbb6409898bc4/sqlalchemy-2.0.49-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014", size = 3315509, upload-time = "2026-04-03T16:54:49.332Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/61/0722511d98c54de95acb327824cb759e8653789af2b1944ab1cc69d32565/sqlalchemy-2.0.49-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536", size = 3315014, upload-time = "2026-04-03T16:56:56.376Z" },
+ { url = "https://files.pythonhosted.org/packages/46/55/d514a653ffeb4cebf4b54c47bec32ee28ad89d39fafba16eeed1d81dccd5/sqlalchemy-2.0.49-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88", size = 3267388, upload-time = "2026-04-03T16:54:51.272Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/16/0dcc56cb6d3335c1671a2258f5d2cb8267c9a2260e27fde53cbfb1b3540a/sqlalchemy-2.0.49-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700", size = 3289602, upload-time = "2026-04-03T16:56:57.63Z" },
+ { url = "https://files.pythonhosted.org/packages/51/6c/f8ab6fb04470a133cd80608db40aa292e6bae5f162c3a3d4ab19544a67af/sqlalchemy-2.0.49-cp311-cp311-win32.whl", hash = "sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a", size = 2119044, upload-time = "2026-04-03T17:00:53.455Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/59/55a6d627d04b6ebb290693681d7683c7da001eddf90b60cfcc41ee907978/sqlalchemy-2.0.49-cp311-cp311-win_amd64.whl", hash = "sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af", size = 2143642, upload-time = "2026-04-03T17:00:54.769Z" },
+ { url = "https://files.pythonhosted.org/packages/49/b3/2de412451330756aaaa72d27131db6dde23995efe62c941184e15242a5fa/sqlalchemy-2.0.49-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b", size = 2157681, upload-time = "2026-04-03T16:53:07.132Z" },
+ { url = "https://files.pythonhosted.org/packages/50/84/b2a56e2105bd11ebf9f0b93abddd748e1a78d592819099359aa98134a8bf/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982", size = 3338976, upload-time = "2026-04-03T17:07:40Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/fa/65fcae2ed62f84ab72cf89536c7c3217a156e71a2c111b1305ab6f0690e2/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672", size = 3351937, upload-time = "2026-04-03T17:12:23.374Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/2f/6fd118563572a7fe475925742eb6b3443b2250e346a0cc27d8d408e73773/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e", size = 3281646, upload-time = "2026-04-03T17:07:41.949Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/d7/410f4a007c65275b9cf82354adb4bb8ba587b176d0a6ee99caa16fe638f8/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750", size = 3316695, upload-time = "2026-04-03T17:12:25.642Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/95/81f594aa60ded13273a844539041ccf1e66c5a7bed0a8e27810a3b52d522/sqlalchemy-2.0.49-cp312-cp312-win32.whl", hash = "sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0", size = 2117483, upload-time = "2026-04-03T17:05:40.896Z" },
+ { url = "https://files.pythonhosted.org/packages/47/9e/fd90114059175cac64e4fafa9bf3ac20584384d66de40793ae2e2f26f3bb/sqlalchemy-2.0.49-cp312-cp312-win_amd64.whl", hash = "sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4", size = 2144494, upload-time = "2026-04-03T17:05:42.282Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/81/81755f50eb2478eaf2049728491d4ea4f416c1eb013338682173259efa09/sqlalchemy-2.0.49-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120", size = 2154547, upload-time = "2026-04-03T16:53:08.64Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/bc/3494270da80811d08bcfa247404292428c4fe16294932bce5593f215cad9/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2", size = 3280782, upload-time = "2026-04-03T17:07:43.508Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/f5/038741f5e747a5f6ea3e72487211579d8cbea5eb9827a9cbd61d0108c4bd/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3", size = 3297156, upload-time = "2026-04-03T17:12:27.697Z" },
+ { url = "https://files.pythonhosted.org/packages/88/50/a6af0ff9dc954b43a65ca9b5367334e45d99684c90a3d3413fc19a02d43c/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7", size = 3228832, upload-time = "2026-04-03T17:07:45.38Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/d1/5f6bdad8de0bf546fc74370939621396515e0cdb9067402d6ba1b8afbe9a/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33", size = 3267000, upload-time = "2026-04-03T17:12:29.657Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/30/ad62227b4a9819a5e1c6abff77c0f614fa7c9326e5a3bdbee90f7139382b/sqlalchemy-2.0.49-cp313-cp313-win32.whl", hash = "sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b", size = 2115641, upload-time = "2026-04-03T17:05:43.989Z" },
+ { url = "https://files.pythonhosted.org/packages/17/3a/7215b1b7d6d49dc9a87211be44562077f5f04f9bb5a59552c1c8e2d98173/sqlalchemy-2.0.49-cp313-cp313-win_amd64.whl", hash = "sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148", size = 2141498, upload-time = "2026-04-03T17:05:45.7Z" },
+ { url = "https://files.pythonhosted.org/packages/28/4b/52a0cb2687a9cd1648252bb257be5a1ba2c2ded20ba695c65756a55a15a4/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518", size = 3560807, upload-time = "2026-04-03T16:58:31.666Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/d8/fda95459204877eed0458550d6c7c64c98cc50c2d8d618026737de9ed41a/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d", size = 3527481, upload-time = "2026-04-03T17:06:00.155Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0a/2aac8b78ac6487240cf7afef8f203ca783e8796002dc0cf65c4ee99ff8bb/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0", size = 3468565, upload-time = "2026-04-03T16:58:33.414Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/3d/ce71cfa82c50a373fd2148b3c870be05027155ce791dc9a5dcf439790b8b/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08", size = 3477769, upload-time = "2026-04-03T17:06:02.787Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/e8/0a9f5c1f7c6f9ca480319bf57c2d7423f08d31445974167a27d14483c948/sqlalchemy-2.0.49-cp313-cp313t-win32.whl", hash = "sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d", size = 2143319, upload-time = "2026-04-03T17:02:04.328Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/51/fb5240729fbec73006e137c4f7a7918ffd583ab08921e6ff81a999d6517a/sqlalchemy-2.0.49-cp313-cp313t-win_amd64.whl", hash = "sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba", size = 2175104, upload-time = "2026-04-03T17:02:05.989Z" },
+ { url = "https://files.pythonhosted.org/packages/55/33/bf28f618c0a9597d14e0b9ee7d1e0622faff738d44fe986ee287cdf1b8d0/sqlalchemy-2.0.49-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e", size = 2156356, upload-time = "2026-04-03T16:53:09.914Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/a7/5f476227576cb8644650eff68cc35fa837d3802b997465c96b8340ced1e2/sqlalchemy-2.0.49-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a", size = 3276486, upload-time = "2026-04-03T17:07:46.9Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/84/efc7c0bf3a1c5eef81d397f6fddac855becdbb11cb38ff957888603014a7/sqlalchemy-2.0.49-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066", size = 3281479, upload-time = "2026-04-03T17:12:32.226Z" },
+ { url = "https://files.pythonhosted.org/packages/91/68/bb406fa4257099c67bd75f3f2261b129c63204b9155de0d450b37f004698/sqlalchemy-2.0.49-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187", size = 3226269, upload-time = "2026-04-03T17:07:48.678Z" },
+ { url = "https://files.pythonhosted.org/packages/67/84/acb56c00cca9f251f437cb49e718e14f7687505749ea9255d7bd8158a6df/sqlalchemy-2.0.49-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401", size = 3248260, upload-time = "2026-04-03T17:12:34.381Z" },
+ { url = "https://files.pythonhosted.org/packages/56/19/6a20ea25606d1efd7bd1862149bb2a22d1451c3f851d23d887969201633f/sqlalchemy-2.0.49-cp314-cp314-win32.whl", hash = "sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5", size = 2118463, upload-time = "2026-04-03T17:05:47.093Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/4f/8297e4ed88e80baa1f5aa3c484a0ee29ef3c69c7582f206c916973b75057/sqlalchemy-2.0.49-cp314-cp314-win_amd64.whl", hash = "sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5", size = 2144204, upload-time = "2026-04-03T17:05:48.694Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/33/95e7216df810c706e0cd3655a778604bbd319ed4f43333127d465a46862d/sqlalchemy-2.0.49-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977", size = 3565474, upload-time = "2026-04-03T16:58:35.128Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/a4/ed7b18d8ccf7f954a83af6bb73866f5bc6f5636f44c7731fbb741f72cc4f/sqlalchemy-2.0.49-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01", size = 3530567, upload-time = "2026-04-03T17:06:04.587Z" },
+ { url = "https://files.pythonhosted.org/packages/73/a3/20faa869c7e21a827c4a2a42b41353a54b0f9f5e96df5087629c306df71e/sqlalchemy-2.0.49-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61", size = 3474282, upload-time = "2026-04-03T16:58:37.131Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/50/276b9a007aa0764304ad467eceb70b04822dc32092492ee5f322d559a4dc/sqlalchemy-2.0.49-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a", size = 3480406, upload-time = "2026-04-03T17:06:07.176Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/c3/c80fcdb41905a2df650c2a3e0337198b6848876e63d66fe9188ef9003d24/sqlalchemy-2.0.49-cp314-cp314t-win32.whl", hash = "sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158", size = 2149151, upload-time = "2026-04-03T17:02:07.281Z" },
+ { url = "https://files.pythonhosted.org/packages/05/52/9f1a62feab6ed368aff068524ff414f26a6daebc7361861035ae00b05530/sqlalchemy-2.0.49-cp314-cp314t-win_amd64.whl", hash = "sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7", size = 2184178, upload-time = "2026-04-03T17:02:08.623Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/30/8519fdde58a7bdf155b714359791ad1dc018b47d60269d5d160d311fdc36/sqlalchemy-2.0.49-py3-none-any.whl", hash = "sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0", size = 1942158, upload-time = "2026-04-03T16:53:44.135Z" },
]
[package.optional-dependencies]
@@ -21503,11 +21619,11 @@ wheels = [
[[package]]
name = "types-protobuf"
-version = "7.34.1.20260402"
+version = "7.34.1.20260403"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/40/43/19261b537cfc8db9f39f92b31dba961462a034412aafafa630aa54191db7/types_protobuf-7.34.1.20260402.tar.gz", hash = "sha256:7270594b4bc28dfe85602d97849b28a2fe7905d77b1d91b418924ed6f83f857c", size = 68767, upload-time = "2026-04-02T04:18:25.025Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/ae/b3/c2e407ea36e0e4355c135127cee1b88a2cc9a2c92eafca50a360ab9f2708/types_protobuf-7.34.1.20260403.tar.gz", hash = "sha256:8d7881867888e667eb9563c08a916fccdc12bdb5f9f34c31d217cce876e36765", size = 68782, upload-time = "2026-04-03T04:18:09.428Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/15/34/9bc4fcb26be938a4d678a1e5364e6a6b36e84c1d9b6aaf4d6bb5082ad86a/types_protobuf-7.34.1.20260402-py3-none-any.whl", hash = "sha256:5a1094907ca0d19202f7bca6db06ff69779873db695ddd73f2dde718cd8df1fa", size = 86007, upload-time = "2026-04-02T04:18:23.673Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/95/24fb0f6fe37b41cf94f9b9912712645e17d8048d4becaf37c1607ddd8e32/types_protobuf-7.34.1.20260403-py3-none-any.whl", hash = "sha256:16d9bbca52ab0f306279958878567df2520f3f5579059419b0ce149a0ad1e332", size = 86011, upload-time = "2026-04-03T04:18:08.245Z" },
]
[[package]]
@@ -21656,11 +21772,11 @@ wheels = [
[[package]]
name = "tzdata"
-version = "2025.3"
+version = "2026.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" },
]
[[package]]
@@ -21764,16 +21880,16 @@ wheels = [
[[package]]
name = "uvicorn"
-version = "0.42.0"
+version = "0.43.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "h11" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/e3/ad/4a96c425be6fb67e0621e62d86c402b4a17ab2be7f7c055d9bd2f638b9e2/uvicorn-0.42.0.tar.gz", hash = "sha256:9b1f190ce15a2dd22e7758651d9b6d12df09a13d51ba5bf4fc33c383a48e1775", size = 85393, upload-time = "2026-03-16T06:19:50.077Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/62/f2/368268300fb8af33743508d738ef7bb4d56afdb46c6d9c0fa3dd515df171/uvicorn-0.43.0.tar.gz", hash = "sha256:ab1652d2fb23abf124f36ccc399828558880def222c3cb3d98d24021520dc6e8", size = 85686, upload-time = "2026-04-03T18:37:48.984Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/0a/89/f8827ccff89c1586027a105e5630ff6139a64da2515e24dafe860bd9ae4d/uvicorn-0.42.0-py3-none-any.whl", hash = "sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359", size = 68830, upload-time = "2026-03-16T06:19:48.325Z" },
+ { url = "https://files.pythonhosted.org/packages/55/df/0cf5b0c451602748fdc7a702d4667f6e209bf96aa6e3160d754234445f2a/uvicorn-0.43.0-py3-none-any.whl", hash = "sha256:46fac64f487fd968cd999e5e49efbbe64bd231b5bd8b4a0b482a23ebce499620", size = 68591, upload-time = "2026-04-03T18:37:47.64Z" },
]
[package.optional-dependencies]