This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git
The following commit(s) were added to refs/heads/main by this push:
new 910e8a9 Improve some utilities associated with importing keys
910e8a9 is described below
commit 910e8a9a8a3d199f6ebbb018802b0864fba4ae5c
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri Jun 13 20:26:12 2025 +0100
Improve some utilities associated with importing keys
---
atr/db/interaction.py | 28 +----------
atr/util.py | 72 ++++++++++++++++++++++++++
poetry.lock | 136 +++++++++++++++++++++++++++++++++++++++++++++++++-
pyproject.toml | 1 +
4 files changed, 209 insertions(+), 28 deletions(-)
diff --git a/atr/db/interaction.py b/atr/db/interaction.py
index 193e635..7360a37 100644
--- a/atr/db/interaction.py
+++ b/atr/db/interaction.py
@@ -31,7 +31,6 @@ import sqlmodel
import atr.analysis as analysis
import atr.db as db
import atr.db.models as models
-import atr.ldap as ldap
import atr.schema as schema
import atr.user as user
import atr.util as util
@@ -78,18 +77,7 @@ async def key_user_add(asf_uid: str | None, public_key: str,
selected_committees
added_keys = []
for key in keys:
- # Determine ASF UID if not provided
- if asf_uid is None:
- for uid_str in key["uids"]:
- if match := re.search(r"([A-Za-z0-9]+)@apache.org", uid_str):
- asf_uid = match.group(1).lower()
- break
- else:
- # _LOGGER.warning(f"key_user_add called with no ASF UID found
in key UIDs: {key.get('uids')}")
- for uid_str in key.get("uids", []):
- if asf_uid := await
asyncio.to_thread(_asf_uid_from_uid_str, uid_str):
- break
-
+ asf_uid = await util.asf_uid_from_uids(key.get("uids", []))
# Store key in database
async with db.session() as data:
added = await key_user_session_add(asf_uid, public_key, key,
selected_committees, data)
@@ -316,20 +304,6 @@ async def upload_keys(
return results, success_count, error_count, submitted_committees
-def _asf_uid_from_uid_str(uid_str: str) -> str | None:
- if not (email_match := re.search(r"<([^>]+)>", uid_str)):
- return None
- email = email_match.group(1)
- if email.endswith("@apache.org"):
- return None
- ldap_params = ldap.SearchParameters(email_query=email)
- ldap.search(ldap_params)
- if not (ldap_params.results_list and ("uid" in
ldap_params.results_list[0])):
- return None
- ldap_uid_val = ldap_params.results_list[0]["uid"]
- return ldap_uid_val[0] if isinstance(ldap_uid_val, list) else ldap_uid_val
-
-
def _key_latest_self_signature(key: dict) -> datetime.datetime | None:
fingerprint = key["fingerprint"]
# TODO: Only 64 bits, which is not at all secure
diff --git a/atr/util.py b/atr/util.py
index 98f7923..114b85c 100644
--- a/atr/util.py
+++ b/atr/util.py
@@ -37,6 +37,7 @@ import aioshutil
import asfquart
import asfquart.base as base
import asfquart.session as session
+import httpx
import jinja2
import quart
import quart_wtf
@@ -47,6 +48,7 @@ import wtforms
# Therefore, this module must not import atr.db
import atr.config as config
import atr.db.models as models
+import atr.ldap as ldap
import atr.user as user
F = TypeVar("F", bound="QuartFormTyped")
@@ -139,6 +141,32 @@ def as_url(func: Callable, **kwargs: Any) -> str:
return quart.url_for(annotations["endpoint"], **kwargs)
+def asf_uid_from_email(email: str) -> str | None:
+ ldap_params = ldap.SearchParameters(email_query=email)
+ ldap.search(ldap_params)
+ if not (ldap_params.results_list and ("uid" in
ldap_params.results_list[0])):
+ return None
+ ldap_uid_val = ldap_params.results_list[0]["uid"]
+ return ldap_uid_val[0] if isinstance(ldap_uid_val, list) else ldap_uid_val
+
+
+async def asf_uid_from_uids(uids: list[str]) -> str | None:
+ # Determine ASF UID if not provided
+ emails = []
+ for uid_str in uids:
+ if match := re.search(r"<([^>]+)>", uid_str):
+ email = match.group(1).lower()
+ if email.endswith("@apache.org"):
+ return email.removesuffix("@apache.org")
+ emails.append(email)
+ # We did not find a direct @apache.org email address
+ # Therefore, search LDAP
+ for email in emails:
+ if asf_uid := await asyncio.to_thread(asf_uid_from_email, email):
+ return asf_uid
+ return None
+
+
@contextlib.asynccontextmanager
async def async_temporary_directory(
suffix: str | None = None, prefix: str | None = None, dir: str |
pathlib.Path | None = None
@@ -373,6 +401,30 @@ def get_unfinished_dir() -> pathlib.Path:
return pathlib.Path(config.get().UNFINISHED_STORAGE_DIR)
+async def get_urls_as_completed(urls: Sequence[str]) ->
AsyncGenerator[tuple[str, int | str | None, bytes]]:
+ """GET a list of URLs in parallel and yield (url, status, content_bytes)
as they become available."""
+ async with httpx.AsyncClient() as client:
+ tasks = [asyncio.create_task(client.get(url)) for url in urls]
+ for future in asyncio.as_completed(tasks):
+ try:
+ response = await future
+ except Exception as e:
+ yield ("", str(e), b"")
+ continue
+ url = str(response.url)
+ try:
+ response.raise_for_status()
+ yield (url, response.status_code, await response.aread())
+ except httpx.HTTPStatusError as e:
+ if e.response.status_code == 200:
+ # This should not happen
+ yield (url, str(e), b"")
+ else:
+ yield (url, e.response.status_code, b"")
+ except Exception as e:
+ yield (url, str(e), b"")
+
+
async def is_dir_resolve(path: pathlib.Path) -> pathlib.Path | None:
try:
resolved_path = await asyncio.to_thread(path.resolve)
@@ -433,6 +485,26 @@ def parse_key_blocks(keys_text: str) -> list[str]:
return key_blocks
+def parse_key_blocks_bytes(keys_data: bytes) -> list[str]:
+ """Extract GPG key blocks from a KEYS file."""
+ key_blocks = []
+ current_block = []
+ in_key_block = False
+
+ for line in keys_data.splitlines():
+ if line.strip() == b"-----BEGIN PGP PUBLIC KEY BLOCK-----":
+ in_key_block = True
+ current_block = [line]
+ elif (line.strip() == b"-----END PGP PUBLIC KEY BLOCK-----") and
in_key_block:
+ current_block.append(line)
+ key_blocks.append(b"\n".join(current_block))
+ in_key_block = False
+ elif in_key_block:
+ current_block.append(line)
+
+ return key_blocks
+
+
async def paths_recursive(base_path: pathlib.Path) ->
AsyncGenerator[pathlib.Path]:
"""Yield all file paths recursively within a base path, relative to the
base path."""
if (resolved_base_path := await is_dir_resolve(base_path)) is None:
diff --git a/poetry.lock b/poetry.lock
index 57f38af..f719cd0 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2302,6 +2302,140 @@ all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"]
dev = ["twine (>=3.4.1)"]
nodejs = ["nodejs-wheel-binaries"]
+[[package]]
+name = "pysequoia"
+version = "0.1.29"
+description = "Provides OpenPGP facilities using Sequoia-PGP library"
+optional = false
+python-versions = ">=3.7"
+groups = ["main"]
+files = [
+ {file =
"pysequoia-0.1.29-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:7f01e4d3110a4a53686f48e7d473c56e0d0c864d2eb5431eac1f8cd52e89f574"},
+ {file =
"pysequoia-0.1.29-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:7c2eb0da2e190367b8f145397f1a1c8d1749e18b3d552876be4816557c3b5483"},
+ {file =
"pysequoia-0.1.29-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:dc1223d7a43651c229cac7a912fbd255f628f6b31a2d692c0567644c69dd81c1"},
+ {file =
"pysequoia-0.1.29-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl",
hash =
"sha256:ce360808437982d98c549dd7c01570e525be0103988d37ca25b9b407322caa5d"},
+ {file =
"pysequoia-0.1.29-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
hash =
"sha256:085ddafa1aa269dc30e75110ee712a0e23165111480bb156894ad63a261e6628"},
+ {file =
"pysequoia-0.1.29-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash =
"sha256:4e36e1800f3bcf446ba7262de19bacf21dd1ffc1785b3bf11313de65b10a35b0"},
+ {file = "pysequoia-0.1.29-cp310-cp310-musllinux_1_2_aarch64.whl", hash =
"sha256:6fd15bfbe384fdafd95b73daf185a4346cd0b9b3764ff0840227d8872e5b538e"},
+ {file = "pysequoia-0.1.29-cp310-cp310-musllinux_1_2_armv7l.whl", hash =
"sha256:d17e0d337428fe1ab4559e946d48163c831bd94fd0fd174aeee774959e47d1a3"},
+ {file = "pysequoia-0.1.29-cp310-cp310-musllinux_1_2_i686.whl", hash =
"sha256:c00457c3664918b22b112f8e56bf2647dfee0aaefbebeda3c77734b1e7b44111"},
+ {file = "pysequoia-0.1.29-cp310-cp310-musllinux_1_2_x86_64.whl", hash =
"sha256:b4138b987ce64474d3219e4f65794e22c1743be1705cd2b83f75a381eefe770d"},
+ {file = "pysequoia-0.1.29-cp310-cp310-win32.whl", hash =
"sha256:87309b6f77a9b3b3b0a35a6dfbb2d928c067410cac2b785a05dbaf4e955eb790"},
+ {file = "pysequoia-0.1.29-cp310-cp310-win_amd64.whl", hash =
"sha256:6e823616db7247c64a26983504479fea7508b5cc179664ca656d576201f8656e"},
+ {file = "pysequoia-0.1.29-cp311-cp311-macosx_10_12_x86_64.whl", hash =
"sha256:42f8046e66049825b10cbe13da7546c94d7f63ea7623f874730b0fffc9b5e522"},
+ {file = "pysequoia-0.1.29-cp311-cp311-macosx_11_0_arm64.whl", hash =
"sha256:91c3abf93beed35081d1ed61d74b5d3bf7eb247df34b4aa4bda989455f0df382"},
+ {file =
"pysequoia-0.1.29-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:1d759b373147a1642dcf99c48d11f14859acd0b458243e2833dbb0fa0b0cc5ec"},
+ {file =
"pysequoia-0.1.29-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:dd8440ad35ccf1cc72e92c626062a6a3549f8d629e9d45b8eba70e8aac4edfda"},
+ {file =
"pysequoia-0.1.29-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:440cbd4cdbd71eb7e687b273188e0661c914c5bce4c9bd79a6f0b116435fa4e7"},
+ {file =
"pysequoia-0.1.29-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl",
hash =
"sha256:3b20603358e1309a73b1b109198829465c88d64af7c2b4349ef38bc7bc230a26"},
+ {file =
"pysequoia-0.1.29-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
hash =
"sha256:46417d14d7e30cf9beb2a63391bdecb1389538a8c6803562f33c52248ea0df6c"},
+ {file =
"pysequoia-0.1.29-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash =
"sha256:4fd1e9cd746162f4d765c404a55a8f1c8121c02319fdd73e10fc49c7b37893eb"},
+ {file = "pysequoia-0.1.29-cp311-cp311-musllinux_1_2_aarch64.whl", hash =
"sha256:76ce026f03ccfd7057284550faab05a724d1557480d6850d9fcf6c798b810530"},
+ {file = "pysequoia-0.1.29-cp311-cp311-musllinux_1_2_armv7l.whl", hash =
"sha256:c7f70c8cbb1ee94ca205dded1472596135dc1db78eeb0d29a9a9483ebab63c38"},
+ {file = "pysequoia-0.1.29-cp311-cp311-musllinux_1_2_i686.whl", hash =
"sha256:44ec69ae02d77811085a3a2af990e5bf33611e9f65fc517fb2543ad8c2456b1f"},
+ {file = "pysequoia-0.1.29-cp311-cp311-musllinux_1_2_x86_64.whl", hash =
"sha256:98e9a808c6a1dc8806c8b6ed32099e8cb95f09c27d0a9129e68759a2dd6a90a6"},
+ {file = "pysequoia-0.1.29-cp311-cp311-win32.whl", hash =
"sha256:3a4a4ff85d720cb185f4af55c28bcefe6117b76b932db977fb6d611738c671e1"},
+ {file = "pysequoia-0.1.29-cp311-cp311-win_amd64.whl", hash =
"sha256:22fcf180fa00a653ab2edeb5ef5f2e0e16881bb7d75faf7cc67963fb1b595bc3"},
+ {file = "pysequoia-0.1.29-cp312-cp312-macosx_10_12_x86_64.whl", hash =
"sha256:fd535cd68cf38db10209dcc2ac6f02e6259e44160f0b55d03038d320102ab1eb"},
+ {file = "pysequoia-0.1.29-cp312-cp312-macosx_11_0_arm64.whl", hash =
"sha256:67afdc183aab650529e54898782b758dae364b228dbe8a8663acd12e1754f3c8"},
+ {file =
"pysequoia-0.1.29-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:22757e27f0c24ec9dee0942c35f2205ed7aaf46ca76cc99872bb1dad6484d098"},
+ {file =
"pysequoia-0.1.29-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:6f5d8b80ff695b4428652754a6f5a477ce87cddfc76541c47c4a2f8ec33c18b2"},
+ {file =
"pysequoia-0.1.29-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:5f4cbbea46809fc4b50648ecce1e419c67bb9c03eed7a25afde803298ce633c6"},
+ {file =
"pysequoia-0.1.29-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl",
hash =
"sha256:4e180503ba06836ad3de1e0b5329f548f9ef77077ef63d5d6a02c4a1f2bab744"},
+ {file =
"pysequoia-0.1.29-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
hash =
"sha256:a50e8ed06a1c849104ad5ac8d5feb2e9d1a96d3d0b2370bccfcf4892375f3f23"},
+ {file =
"pysequoia-0.1.29-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash =
"sha256:65f4b335950776aed6ca9c9509c074863ad83dcee5aa4c16cc51cfda37ce9860"},
+ {file = "pysequoia-0.1.29-cp312-cp312-musllinux_1_2_aarch64.whl", hash =
"sha256:f989c65bed9d1bbbd324b1e8de23a74c99dc2f838144ed468e639785c63bf6ab"},
+ {file = "pysequoia-0.1.29-cp312-cp312-musllinux_1_2_armv7l.whl", hash =
"sha256:60dcf853858b94ab468d10789100f9767d6b8ee24892ba14bdcf9a4725e88634"},
+ {file = "pysequoia-0.1.29-cp312-cp312-musllinux_1_2_i686.whl", hash =
"sha256:f622053e787531e51c3295515e1c4dcd766318ed0479b7ae4b0c593566e298fa"},
+ {file = "pysequoia-0.1.29-cp312-cp312-musllinux_1_2_x86_64.whl", hash =
"sha256:d42e5eab63357758da5661cb5f35ff78c1cddeb4a00b055ef74ca72e3e294c89"},
+ {file = "pysequoia-0.1.29-cp312-cp312-win32.whl", hash =
"sha256:462486eb0dc68f8b0e385eac52623de35088a81c5998543f0db0f009e2e2493f"},
+ {file = "pysequoia-0.1.29-cp312-cp312-win_amd64.whl", hash =
"sha256:f0c0f8fae955467a23fa3421c3d5694a7ed66f0d173a64000f181f87aad46c41"},
+ {file = "pysequoia-0.1.29-cp313-cp313-macosx_10_12_x86_64.whl", hash =
"sha256:f9e422365d004a1cb74e95cce7c9f793781ce0833ac3f4e66133541901307151"},
+ {file = "pysequoia-0.1.29-cp313-cp313-macosx_11_0_arm64.whl", hash =
"sha256:3b7d7a59ccf8f68fced8431ba73f5746faeb70cbd1634d660544f814ae12c9be"},
+ {file =
"pysequoia-0.1.29-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:58f91b08d3d8aeb8abd51e6048f3d0660fc99b2d0d1d1bb8eb74d44b9f788e25"},
+ {file =
"pysequoia-0.1.29-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:24666d0752f0cf70ff0eaa38424413db0a587c5b77e1cf23e1b4064ec35d3632"},
+ {file =
"pysequoia-0.1.29-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:4b1feabbd9177c82eb84f4f87d58eb2ea3a59fa02b50626337b170d2cbb7ae0c"},
+ {file =
"pysequoia-0.1.29-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl",
hash =
"sha256:06b35189c8819b3c432917ceaad7b1581074f3c8c71c0488288c7dabfadb69a2"},
+ {file =
"pysequoia-0.1.29-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
hash =
"sha256:ae898f13c6b754f5ad4c7fc158dd5bd40cb65a51ca22f93e9aeb84eb2faa429b"},
+ {file =
"pysequoia-0.1.29-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash =
"sha256:6d8278141262b233ae91c7f50fb88d989830bf7ed4cfa7b4abdd04e1f1f1df7f"},
+ {file = "pysequoia-0.1.29-cp313-cp313-musllinux_1_2_aarch64.whl", hash =
"sha256:9791b531f116e2fcb16e3f033be62700f336060f59e1b7c8efca6f097f33c1d4"},
+ {file = "pysequoia-0.1.29-cp313-cp313-musllinux_1_2_armv7l.whl", hash =
"sha256:ff37f55f2acc1f4d3dd890ac4946e8cda29219fedaf6d02f9c3c6c12adf6be4c"},
+ {file = "pysequoia-0.1.29-cp313-cp313-musllinux_1_2_i686.whl", hash =
"sha256:77e225e0a1c268a06f701fa88eed33d3cd5d225301ce04534e365605254aa61d"},
+ {file = "pysequoia-0.1.29-cp313-cp313-musllinux_1_2_x86_64.whl", hash =
"sha256:886c692bb2b293a4a094a981eca73c22e7e2eb1da05a73b67e8571339dc6a36b"},
+ {file = "pysequoia-0.1.29-cp313-cp313-win32.whl", hash =
"sha256:feb9fa7f7690f3b788ab74dade98dae5d9d94c4d14d32ba5cc80d25c27378a78"},
+ {file = "pysequoia-0.1.29-cp313-cp313-win_amd64.whl", hash =
"sha256:0b68e1d66ca1af71ca11bf1ded8f76cab828584659d09883a4552411f6f63611"},
+ {file =
"pysequoia-0.1.29-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:978425a689f093e6f48303f56997576243036f4974a0edad91e146679a3986e9"},
+ {file =
"pysequoia-0.1.29-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:e797bf9e47cb929ed487449877374a87807335a1728441f895d54277fccfd1cb"},
+ {file =
"pysequoia-0.1.29-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:9d53365ead887504394ae988c7a51cf2772eaefcde913cc5efa0197ef0cf9bc9"},
+ {file =
"pysequoia-0.1.29-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl",
hash =
"sha256:3aea4e351d4451354e4a22f9deeb3fad6a9432b8c7e7a5eb0a7fff63ef19cc10"},
+ {file = "pysequoia-0.1.29-cp313-cp313t-musllinux_1_2_aarch64.whl", hash =
"sha256:ee1cd6300ff9cf063e4e3772bfe1e7b3934fdcc2b9c91bf32da02f43c2e30573"},
+ {file = "pysequoia-0.1.29-cp313-cp313t-musllinux_1_2_armv7l.whl", hash =
"sha256:f2826056511cf6e4e0f14b3b13410705a4eb2e878dc617cc4ba63909e7d718f9"},
+ {file = "pysequoia-0.1.29-cp313-cp313t-musllinux_1_2_i686.whl", hash =
"sha256:c14bf7026e285948660c0cd68a644b43d1b5717b8e5c7b07d0fcb416c6375cb1"},
+ {file = "pysequoia-0.1.29-cp313-cp313t-musllinux_1_2_x86_64.whl", hash =
"sha256:749e0d11c83109c5e6ac85bb291d2bb5a6a81545eca73a31d01a85d8353a0cea"},
+ {file =
"pysequoia-0.1.29-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
hash =
"sha256:8a7489772acc70db9f4bea72e0796ff0706526e8851e5dae0f71b24dc49c27ed"},
+ {file =
"pysequoia-0.1.29-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash =
"sha256:4502515826df73f30bc62c47e2100b80361b4ac32e69514bed051a7547c8dac3"},
+ {file =
"pysequoia-0.1.29-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:0fb8796ee9ee05cd7673df5f2cbd2ab9af1bf176f86f654c6478019397711e9e"},
+ {file =
"pysequoia-0.1.29-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:ef799eb16fde51ff9e7f2dcbcab9f45ba30a4fa9a3b33073776bb048430248eb"},
+ {file =
"pysequoia-0.1.29-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:b91d5802b578d61b5afc259592ba527715568a1d52f040eb600f85564b1a0b4a"},
+ {file =
"pysequoia-0.1.29-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl",
hash =
"sha256:15b7f3dc196018df905eb47bb404306e1e461942b5d115b0d97f2dc5d912dec0"},
+ {file = "pysequoia-0.1.29-cp37-cp37m-musllinux_1_2_aarch64.whl", hash =
"sha256:eb21fad6adec34f24ed748b4da4d219cfefc77a2eb83f751b38da32393fd8ca4"},
+ {file = "pysequoia-0.1.29-cp37-cp37m-musllinux_1_2_armv7l.whl", hash =
"sha256:944d87caa16455d2d4984d221d3aea640b542263dadefde0a0544d08b9256acc"},
+ {file = "pysequoia-0.1.29-cp37-cp37m-musllinux_1_2_i686.whl", hash =
"sha256:d2899f81c2e3a6daeb6bbb9e3d1c3c841f40a881da5043aef9f997216d75113c"},
+ {file = "pysequoia-0.1.29-cp37-cp37m-musllinux_1_2_x86_64.whl", hash =
"sha256:07d5d6a427c496aa3032e50866d31ea7672e18c81d42f8134fb12bbf89cf3e77"},
+ {file =
"pysequoia-0.1.29-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:b7e0271395c1164b941db155ab12b03100f779567a0c2d5f4ed8718542174681"},
+ {file =
"pysequoia-0.1.29-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:7d8758569723daa08049f958b80251f01cfb33fd05458976915c190084e8b5ec"},
+ {file =
"pysequoia-0.1.29-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:cbfecc9c910381cdbeab226687439d48af93f07860f65ab0d6232bafa515ef0b"},
+ {file =
"pysequoia-0.1.29-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash
= "sha256:b9bfb036a482ea012150bcbd5d77ecd058650c87dc4eb1532f6c9b822a6cae3b"},
+ {file =
"pysequoia-0.1.29-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
hash =
"sha256:753a4789aee59f4ff049c3ceb4e772ab4aa0176f9cb0d6eadbbc63e69900c8e9"},
+ {file =
"pysequoia-0.1.29-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash =
"sha256:6736a3304d22e1e2e9f14c626a0adc79edddb76de252eb0b91feaf01c118449b"},
+ {file = "pysequoia-0.1.29-cp38-cp38-musllinux_1_2_aarch64.whl", hash =
"sha256:2e176724e80483a131340d8a54fb057181568bebe3f51f94b2efa9eb318d35d3"},
+ {file = "pysequoia-0.1.29-cp38-cp38-musllinux_1_2_armv7l.whl", hash =
"sha256:5a5126389e9eff69afb5aaf0105d17f5d05ad85bfb9d9242b3dd40bcc717ded7"},
+ {file = "pysequoia-0.1.29-cp38-cp38-musllinux_1_2_i686.whl", hash =
"sha256:2f904f20faee6cb6eb2996d140a0ba42e957fb2cf28c5c5130a55cf22403dd07"},
+ {file = "pysequoia-0.1.29-cp38-cp38-musllinux_1_2_x86_64.whl", hash =
"sha256:a569110ef0dc810cedb559f00c5b707918b7e33407599adc4904b70897358fa2"},
+ {file = "pysequoia-0.1.29-cp38-cp38-win32.whl", hash =
"sha256:a778842fc0d9e13124dcd4dba427f8e8d0de9923824176d4f246595dae3e2238"},
+ {file =
"pysequoia-0.1.29-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:b2ef102900801a2695a3e25cdc3f9e84e5e78eb7bcf9db00b9065657d14dba49"},
+ {file =
"pysequoia-0.1.29-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:1b9cdacf7e3af31c217be78b309b235845a20f9fe00e4f79a1090141dd0d0719"},
+ {file =
"pysequoia-0.1.29-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:f272babb43418872e48c4b5d1129cf19e5e443fd88b0809c180e47eaa3e7190c"},
+ {file =
"pysequoia-0.1.29-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash
= "sha256:3d29530a956fe33b20a7f7ffdb9f3d5a8933d98c634e5fb866b26e871165260c"},
+ {file =
"pysequoia-0.1.29-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
hash =
"sha256:36c0d321938372124e9b0bfe8e63ef623c1cd15703c3856b116fadcaff23fb8a"},
+ {file =
"pysequoia-0.1.29-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash =
"sha256:1679898fb1f56d8b0419c59cee92ce3a53c61592d7f0113c95e9d86966148932"},
+ {file = "pysequoia-0.1.29-cp39-cp39-musllinux_1_2_aarch64.whl", hash =
"sha256:cdbd7f92832cc4f75d442702c7178e5e27b96de77fefc3e870b314d3668ebb09"},
+ {file = "pysequoia-0.1.29-cp39-cp39-musllinux_1_2_armv7l.whl", hash =
"sha256:f0d26ab62bcd1a00374ec8dbe8c393ed01ba930d8395722c17908e4521ef63fe"},
+ {file = "pysequoia-0.1.29-cp39-cp39-musllinux_1_2_i686.whl", hash =
"sha256:3a90d71f7731d092ed0f60d22a2a01a24762850f10de7d5d3e9e6b602093a1ae"},
+ {file = "pysequoia-0.1.29-cp39-cp39-musllinux_1_2_x86_64.whl", hash =
"sha256:13e994133bb8aa837cfa70b0b61339e5d8602a123534315dd292263838a685a4"},
+ {file = "pysequoia-0.1.29-cp39-cp39-win32.whl", hash =
"sha256:b11f2a37c1d74b22123c9ce175cde37f2229250d8a9cac25e839cbee3125f22d"},
+ {file = "pysequoia-0.1.29-cp39-cp39-win_amd64.whl", hash =
"sha256:4bf7d8731626074e71cbea6ee9132fbddcd2c9c982af59fa50738cf3eb154d56"},
+ {file =
"pysequoia-0.1.29-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:1207995ae91ed6440c0cfd3186b38262abaf4c78f4a90c45972e7462b47056dc"},
+ {file =
"pysequoia-0.1.29-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:501e236b94d5a3a731dfc6c3db492b9d5456a62ad1e1322cbfa47b663b4a799f"},
+ {file =
"pysequoia-0.1.29-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:4fbde22d58c682d4aa6f5b7d7176938f4465fcd268ed84766033d373d44de3c9"},
+ {file =
"pysequoia-0.1.29-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl",
hash =
"sha256:77d554fe7af43f82d183971dcc0b90b809936a5ebf5174bcdac58bd869ae05a5"},
+ {file =
"pysequoia-0.1.29-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
hash =
"sha256:2151dd7f3296e3c3e4f1c512bab0cd8fcf8b03b6453c878d635ff6f5b3c5dda3"},
+ {file =
"pysequoia-0.1.29-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl",
hash =
"sha256:aff6270affdcc0ff83aa880197782e38073726766de3670ccf02d693a13be447"},
+ {file = "pysequoia-0.1.29-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl",
hash =
"sha256:3d4a32160c7ac321389afaf25775e3f34637ae8b1036bb2fc958dede90e65907"},
+ {file = "pysequoia-0.1.29-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl",
hash =
"sha256:6c5bd1d5c1ee885de7351c88210a0856c0cc4554ff2706979b21baa680fe1d9f"},
+ {file = "pysequoia-0.1.29-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash
= "sha256:f8c0fa3d458e3587da3f63ab5b457dd2d8aa9982ea93ce3622625bb8c5fac6b6"},
+ {file = "pysequoia-0.1.29-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl",
hash =
"sha256:4b5d59b23e2be53fe0f6fd726a250cc6ce06ca5946f2f02f0f1843a01fa6ffcc"},
+ {file =
"pysequoia-0.1.29-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:002b98573627bc9f3c7758948476c76303f50cc1adf190df52b0788b68c2afdd"},
+ {file =
"pysequoia-0.1.29-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:e8e422822c6662ba16c41f7ab9e3a61ac0484b2ee299b41f5efe0b93aaae52d3"},
+ {file =
"pysequoia-0.1.29-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:577fad1f6d612552feb1380cf293203c04e153d8718944debdcec637f385802f"},
+ {file =
"pysequoia-0.1.29-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl",
hash =
"sha256:a95080a37b447feea41068f2154d948a91ad4bc8d80617e0984ea2db647bf01b"},
+ {file =
"pysequoia-0.1.29-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
hash =
"sha256:dd14391592e1e1db288cddf7c13b3a85f7f68c3605ca7968feb5fdbd88634fa9"},
+ {file =
"pysequoia-0.1.29-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl",
hash =
"sha256:b6be7819ade45a196b1531dd42db9115ab05114ae48bc0019663cc10b97499c2"},
+ {file = "pysequoia-0.1.29-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl",
hash =
"sha256:06817388d55823cb9c4c9aabb7ebaa8c3d23316e4c97a6e346cfe843fc2a682a"},
+ {file = "pysequoia-0.1.29-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl",
hash =
"sha256:21aaa5ead84916951d2df41d80807daecc08e49bd06aab1410de780d71b0c9d5"},
+ {file = "pysequoia-0.1.29-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash
= "sha256:cf63ffc35ff7b88110e2b12aa5756a8e49734c842eed99f383533f48209589b0"},
+ {file = "pysequoia-0.1.29-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl",
hash =
"sha256:4ffa1937ffe341b9b9a127b48bff98283a7dc7db6131c83c3deed3a78e8c180b"},
+ {file =
"pysequoia-0.1.29-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
hash =
"sha256:13117d3863b853297fddc67e300908efe354321863787d3c43ca2c290fa95365"},
+ {file =
"pysequoia-0.1.29-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
hash =
"sha256:0602548135dfc8baa1756f21639845b8ce99092e37c8559ac2fffcd54dfc0b90"},
+ {file =
"pysequoia-0.1.29-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
hash =
"sha256:b6632f24abedcd9b2b8170667f698c63d428b1ffb9b417020c06ea9657bc1fb9"},
+ {file =
"pysequoia-0.1.29-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl",
hash =
"sha256:8a14b2376cbca0db39211fe93fb935195315d6b491f9c988e1e442bd5c0d0a03"},
+ {file = "pysequoia-0.1.29-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl",
hash =
"sha256:74821e397b2885833d6120c3cf544fc8aab6584f2eed62f7ad3770558d0c76c2"},
+ {file = "pysequoia-0.1.29-pp39-pypy39_pp73-musllinux_1_2_armv7l.whl", hash
= "sha256:af9ec8f920f58d10afe72dd6e69e51b90ff8f0a42987279e4c41ac49934edfe1"},
+ {file = "pysequoia-0.1.29-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash =
"sha256:5bcb3d29982fec8a26c8026fbaef292eca7e6cf0b5889b2ad85a26b1f1fdd080"},
+ {file = "pysequoia-0.1.29-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash
= "sha256:14b2529adda3275cdcf9fbb199938594517f1159985e272e67337ecc2b6890b3"},
+ {file = "pysequoia-0.1.29.tar.gz", hash =
"sha256:3d6cd75c100aad565a16d64260ec8c1d7e43222724a8df5e47a0d8843341a09a"},
+]
+
[[package]]
name = "pytest"
version = "8.4.0"
@@ -3178,4 +3312,4 @@ propcache = ">=0.2.1"
[metadata]
lock-version = "2.1"
python-versions = "~=3.13"
-content-hash =
"b79174b297f7612b074db005aa6399eace902e49302d56e0168540befd71a8f1"
+content-hash =
"8dd458139228d185e9815f8ce7f69b683a64b3afc6a2c4801eacca61eeaa84bb"
diff --git a/pyproject.toml b/pyproject.toml
index 19f5ef1..bd9f7e9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -35,6 +35,7 @@ dependencies = [
"quart-wtforms~=1.0.3",
"rich~=14.0.0",
"sqlmodel~=0.0.24",
+ "pysequoia (>=0.1.29,<0.2.0)",
]
[dependency-groups]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]