Compare commits

...

3 commits

Author SHA1 Message Date
30f1d9addb Describe other jobs
Some checks failed
/ setup (push) Has been cancelled
2025-07-30 21:53:25 -07:00
e4db350833 Add start of test application 2025-07-30 21:22:19 -07:00
e1200eda46 Add python protobuf dataclass generation 2025-07-30 20:54:36 -07:00
21 changed files with 757 additions and 17 deletions

View file

@ -26,3 +26,53 @@ compile_pip_requirements(
src = "requirements.in",
requirements_txt = "requirements_lock.txt",
)
# Ruff
config_setting(
name = "macos_aarch64",
constraint_values = [
"@platforms//os:macos",
"@platforms//cpu:aarch64",
],
)
config_setting(
name = "macos_x86_64",
constraint_values = [
"@platforms//os:macos",
"@platforms//cpu:x86_64",
],
)
config_setting(
name = "linux_aarch64",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:aarch64",
],
)
config_setting(
name = "linux_x86_64",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
)
# Extract and expose the ruff binary
genrule(
name = "ruff_binary",
srcs = select({
":macos_aarch64": ["@ruff_macos_aarch64//file"],
":macos_x86_64": ["@ruff_macos_x86_64//file"],
":linux_aarch64": ["@ruff_linux_aarch64//file"],
":linux_x86_64": ["@ruff_linux_x86_64//file"],
}),
outs = ["ruff"],
cmd = """
tar -xzf $< -O > $@
chmod +x $@
""",
visibility = ["//visibility:public"],
)

View file

@ -214,3 +214,32 @@ pip.parse(
requirements_lock = "//:requirements_lock.txt",
)
use_repo(pip, "pypi")
# Ruff
# macOS ARM64 (Apple Silicon)
http_file(
name = "ruff_macos_aarch64",
sha256 = "86b39b4002bb12588af972ad56cfddc1eaa0879c4badb07f0021fae77b5cd958", # Update this
urls = ["https://github.com/astral-sh/ruff/releases/download/0.12.7/ruff-aarch64-apple-darwin.tar.gz"],
)
# macOS x86_64 (Intel)
http_file(
name = "ruff_macos_x86_64",
sha256 = "f0179a587d5509f32ab16bd95cdf64ddcebc80d653e3674161d366312a4eaf7a", # Update this
urls = ["https://github.com/astral-sh/ruff/releases/download/0.12.7/ruff-x86_64-apple-darwin.tar.gz"],
)
# Linux x86_64
http_file(
name = "ruff_linux_x86_64",
sha256 = "65b1ec7ba8feda6cbe52aec168f32e5c276577065914fca922b9a8b3f42db433", # Update this
urls = ["https://github.com/astral-sh/ruff/releases/download/0.12.7/ruff-x86_64-unknown-linux-gnu.tar.gz"],
)
# Linux ARM64
http_file(
name = "ruff_linux_aarch64",
sha256 = "0d4bb492a02cf191a2e1e058cf819c2ed86c05ea444de5e3895ba75c890a9804", # Update this
urls = ["https://github.com/astral-sh/ruff/releases/download/0.12.7/ruff-aarch64-unknown-linux-gnu.tar.gz"],
)

View file

@ -10,27 +10,12 @@ rust_binary(
deps = [
"@crates//:prost",
"@crates//:prost-build",
"@crates//:serde",
"@crates//:schemars",
"@crates//:serde",
"@crates//:tempfile",
],
)
# Generate Rust code for databuild proto
genrule(
name = "generate_databuild_rust",
srcs = [
"databuild.proto",
],
outs = ["databuild.rs"],
cmd = "PROTOC=$(location @com_google_protobuf//:protoc) $(location :prost_generator) $(location databuild.proto) /dev/null $@",
tools = [
"//databuild:prost_generator",
"@com_google_protobuf//:protoc",
],
visibility = ["//visibility:public"],
)
# DataBuild library using generated prost code
rust_library(
name = "databuild",
@ -45,9 +30,9 @@ rust_library(
"lib.rs",
"log_access.rs",
"log_collector.rs",
"mermaid_utils.rs",
"metric_templates.rs",
"metrics_aggregator.rs",
"mermaid_utils.rs",
"orchestration/error.rs",
"orchestration/events.rs",
"orchestration/mod.rs",
@ -143,3 +128,67 @@ filegroup(
srcs = ["databuild.proto"],
visibility = ["//visibility:public"],
)
# Generate Rust code for databuild proto
genrule(
name = "generate_databuild_rust",
srcs = [
"databuild.proto",
],
outs = ["databuild.rs"],
cmd = "PROTOC=$(location @com_google_protobuf//:protoc) $(location :prost_generator) $(location databuild.proto) /dev/null $@",
tools = [
"//databuild:prost_generator",
"@com_google_protobuf//:protoc",
],
visibility = ["//visibility:public"],
)
# Python proto dataclass codegen
py_binary(
name = "protoc-gen-python_betterproto2",
srcs = ["proto_wrapper.py"],
main = "proto_wrapper.py",
deps = [
"@pypi//betterproto2_compiler",
],
)
genrule(
name = "py_protobuf_dataclasses",
srcs = [
"databuild.proto",
],
outs = [
"py_proto_out/__init__.py",
"py_proto_out/databuild/__init__.py",
"py_proto_out/databuild/v1/__init__.py",
"py_proto_out/message_pool.py",
],
cmd = """
mkdir -p $(GENDIR)/databuild/py_proto_out
export PATH=$$PATH:$$(dirname $(location :protoc-gen-python_betterproto2))
export PATH=$$PATH:$$(dirname $(location //:ruff_binary))
$(location @com_google_protobuf//:protoc) --python_betterproto2_out=$(GENDIR)/databuild/py_proto_out $(location databuild.proto)
""",
tools = [
":protoc-gen-python_betterproto2",
"//:ruff_binary",
"@com_google_protobuf//:protoc",
"@pypi//betterproto2_compiler",
],
)
py_library(
name = "py_proto",
srcs = [
"proto.py",
":py_protobuf_dataclasses",
],
visibility = ["//visibility:public"],
deps = [
"@pypi//betterproto2_compiler",
"@pypi//grpcio",
"@pypi//pytest",
],
)

1
databuild/proto.py Normal file
View file

@ -0,0 +1 @@
from databuild.py_proto_out.databuild.v1 import *

View file

@ -0,0 +1,5 @@
# betterproto_wrapper.py
from betterproto2_compiler.plugin.main import main
if __name__ == "__main__":
main()

View file

@ -51,3 +51,11 @@ rust_test(
"@crates//:serde_json",
],
)
py_test(
name = "py_proto_test",
srcs = ["py_proto_test.py"],
deps = [
"//databuild:py_proto",
],
)

View file

@ -0,0 +1,25 @@
load("//databuild:rules.bzl", "databuild_graph", "databuild_job")
py_library(
name = "job_src",
srcs = glob(["**/*.py"]),
visibility = ["//visibility:public"],
deps = ["//databuild:py_proto"],
)
# Tests
py_test(
name = "test",
srcs = glob(["**/test.py"]),
deps = [":job_src"],
)
# Bazel-defined
#databuild_job(
# name = "ingest_color_votes",
#)
# Python-DSL-defined
# TODO

View file

@ -0,0 +1,34 @@
# Test DataBuild App
This directory contains common job components for testing databuild apps described via different methods, e.g. the core bazel targets, the python DSL, etc.
## Structure
The fictitious use case is "daily color votes". The underlying input data is votes per color per day, which we combine and aggregate in ways that help us test different aspects of databuild. Job exec contents should be trivial, as the purpose is to test composition. Types of partition relationships:
- Time-range: 1 day depending on N prior days
- Multi-partition-output jobs
- Always output multiple, e.g. producing per type
- Consume different inputs based on desired output
- Produce multiple of the same type depending on input
```mermaid
flowchart TD
daily_color_votes[(daily_color_votes/$date/$color)]
color_votes_1w[(color_votes_1w/$date/$color)]
color_votes_1m[(color_votes_1m/$date/$color)]
daily_votes[(daily_votes/$date)]
votes_1w[(votes_1w/$date)]
votes_1m[(votes_1m/$date)]
color_vote_report[(color_vote_report/$date/$color)]
ingest_color_votes --> daily_color_votes
daily_color_votes --> trailing_color_votes --> color_votes_1w & color_votes_1m
daily_color_votes --> aggregate_color_votes --> daily_votes
color_votes_1w --> aggregate_color_votes --> votes_1w
color_votes_1m --> aggregate_color_votes --> votes_1m
daily_votes & votes_1w & votes_1m & color_votes_1w & color_votes_1m --> color_vote_report_calc --> color_vote_report
```
## Data Access
Data access is implemented in [`dal.py`](./dal.py), with data written as lists of dicts in JSON. Partition fields are stored as values in those dicts.

View file

@ -0,0 +1,2 @@
COLORS = ["red", "blue", "green", "yellow", "cerulean", "cucumber", "sage", "forest"]

30
databuild/test/app/dal.py Normal file
View file

@ -0,0 +1,30 @@
from databuild.proto import PartitionRef
import json
from pathlib import Path
def ref_path(ref: PartitionRef) -> str:
assert isinstance(ref, PartitionRef), f"Wanted PartitionRef, got `{type(ref)}`"
return "data/" + ref.str.lstrip("/") + "/data.json"
def read(*refs: PartitionRef, empty_ok: bool=True) -> list[dict]:
results = []
for ref in refs:
try:
with open(ref_path(ref)) as infile:
results.extend(json.load(infile))
except FileNotFoundError:
if not empty_ok:
raise
return []
return results
def write(ref: PartitionRef, data: list[dict]) -> None:
# mkdirs before writing in case path doesn't exist
path = ref_path(ref)
Path(path.rsplit("/", 1)[0]).mkdir(parents=True, exist_ok=True)
with open(path, "w") as outfile:
json.dump(data, outfile)

View file

@ -0,0 +1,10 @@
# Aggregate Color Votes
This job adds up votes across colors for the same date. It uses an arbitrary list of colors from [`colors.py`](../../colors.py).
## Configure
When requested for a given date, it creates a job config for each date and type of aggregation, handling daily, weekly,
and monthly aggregates. Declares data deps based on date and the colors in [`colors.py`](../../colors.py).
## Execute
Simply sums the `votes` from the referenced partitions and writes them.

View file

@ -0,0 +1,18 @@
# Color Vote Report Calc
Calculates some metrics based on data calculated by other aggregates:
- Total votes
- On this day
- In last week
- In last month
- Percent of total votes going to this color
- On this day
- In last week
- In last month
## Configure
This job tests the "produce multiple partitions based on requested inputs in one run" mode. It only ever produces a
single job config, which produces all requested outputs.
## Execute
Iterates over requested partitions and performs calculations described above.

View file

@ -0,0 +1,9 @@
# Ingest Color Votes
This job simply generates a random number between 0 and 1000 and writes it to the output.
## Configure
The job has no inputs, and communicates params via env var. It generates a single job config per color date requested.
## Execute
Generates data with `votes` being a number between 0 and 1000.

View file

@ -0,0 +1,13 @@
from databuild.proto import PartitionRef, JobConfigureResponse, JobConfig
from datetime import date
def configure(outputs: list[PartitionRef]) -> JobConfigureResponse:
configs = []
for output in outputs:
prefix, data_date, color = output.str.split("/")
date.fromisoformat(data_date) # Should be able to parse date
assert prefix == "daily_color_votes"
configs.append(JobConfig(outputs = [output], inputs=[], args=[], env={"DATA_DATE": data_date, "COLOR": color}))
return JobConfigureResponse(configs=configs)

View file

@ -0,0 +1,10 @@
from databuild.test.app import dal
from databuild.proto import PartitionRef
import random
def execute(data_date: str, color: str):
random.seed(hash((data_date, color)))
ref = PartitionRef(str=f"daily_color_votes/{data_date}/{color}")
dal.write(ref, [{"color": color, "data_date": data_date, "votes": random.randint(0, 1000)}])

View file

@ -0,0 +1,18 @@
"""Main entrypoint for the ingest_color_votes job for use with bazel-defined graph."""
import sys
import os
from databuild.proto import PartitionRef
from databuild.test.app.jobs.ingest_color_votes.config import configure
from databuild.test.app.jobs.ingest_color_votes.execute import execute
if __name__ == "__main__":
if sys.argv[1] == "config":
configure([
PartitionRef(str=raw_ref)
for raw_ref in sys.argv[2:]
])
elif sys.argv[1] == "execute":
execute(os.environ["DATA_DATE"], os.environ["COLOR"])
else:
raise Exception(f"Invalid command `{sys.argv[1]}`")

View file

@ -0,0 +1,43 @@
from databuild.test.app.jobs.ingest_color_votes.config import configure
from databuild.test.app.jobs.ingest_color_votes.execute import execute
from databuild.test.app import dal
from databuild.proto import PartitionRef
def test_ingest_color_votes_configure():
refs_single = [PartitionRef(str="daily_color_votes/2025-01-01/red")]
config_single = configure(refs_single)
assert len(config_single.configs) == 1
assert config_single.configs[0].outputs[0].str == "daily_color_votes/2025-01-01/red"
assert config_single.configs[0].env["COLOR"] == "red"
assert config_single.configs[0].env["DATA_DATE"] == "2025-01-01"
refs_multiple = [
PartitionRef(str="daily_color_votes/2025-01-02/red"),
PartitionRef(str="daily_color_votes/2025-01-02/blue"),
]
config_multiple = configure(refs_multiple)
assert len(config_multiple.configs) == 2
assert len(config_multiple.configs[0].outputs) == 1
assert config_multiple.configs[0].outputs[0].str == "daily_color_votes/2025-01-02/red"
assert config_multiple.configs[0].env["COLOR"] == "red"
assert config_multiple.configs[0].env["DATA_DATE"] == "2025-01-02"
assert len(config_multiple.configs[1].outputs) == 1
assert config_multiple.configs[1].outputs[0].str == "daily_color_votes/2025-01-02/blue"
assert config_multiple.configs[1].env["COLOR"] == "blue"
assert config_multiple.configs[1].env["DATA_DATE"] == "2025-01-02"
def test_ingest_color_votes():
execute("2025-01-01", "red")
results = dal.read(PartitionRef(str="daily_color_votes/2025-01-01/red"))
assert len(results) == 1
assert results[0]["color"] == "red"
assert results[0]["data_date"] == "2025-01-01"
assert isinstance(results[0]["votes"], int)
if __name__ == '__main__':
import pytest
raise SystemExit(pytest.main([__file__]))

View file

@ -0,0 +1,11 @@
# Trailing Color Votes
This job adds up votes from trailing days. For week granularity, it's 7 days, for month granularity its 28 days.
## Configure
Produces a job config for every color and date. Uses "WEEKLY" and "MONTHLY" env vars to signal when those aggregates
should be calculated and written, e.g. when both weekly and monthly are requested for the same color + date, a single
job config is produced that configures the job to produce both.
## Execute
Just reads trailing data for the specified color and date and adds it up, writing `votes` with the sum.

View file

@ -0,0 +1,10 @@
def test_import():
from databuild.proto import PartitionRef
ref = PartitionRef(str="foo_bar")
assert(ref.str) == "foo_bar"
if __name__ == "__main__":
import pytest
raise SystemExit(pytest.main([__file__]))

View file

@ -1 +1,4 @@
pytest
grpcio-tools
betterproto2-compiler
grpcio

View file

@ -4,10 +4,321 @@
#
# bazel run //:requirements.update
#
betterproto2[grpclib]==0.7.1 \
--hash=sha256:7302b81de5d96845ff8113b4c7850798ae59ff003a7643309b4ee02ff9febc54 \
--hash=sha256:9fb9cac431d0478fbb1e4dd66823981e41c91d3af8bc58d85c0bdb9e005a167d
# via betterproto2-compiler
betterproto2-compiler==0.7.1 \
--hash=sha256:2ddf6aea2df78bb948ccd2f39820cda4b797b751fb1a43d6c27de0ffcaa8f20b \
--hash=sha256:f1f96431b06a0777db4e7e92e927ad73573ecbfb47d1bef42ee0960e3dd909ea
# via -r requirements.in
grpcio==1.74.0 \
--hash=sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f \
--hash=sha256:136b53c91ac1d02c8c24201bfdeb56f8b3ac3278668cbb8e0ba49c88069e1bdc \
--hash=sha256:1733969040989f7acc3d94c22f55b4a9501a30f6aaacdbccfaba0a3ffb255ab7 \
--hash=sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7 \
--hash=sha256:1a2b06afe2e50ebfd46247ac3ba60cac523f54ec7792ae9ba6073c12daf26f0a \
--hash=sha256:1bf949792cee20d2078323a9b02bacbbae002b9e3b9e2433f2741c15bdeba1c4 \
--hash=sha256:22b834cef33429ca6cc28303c9c327ba9a3fafecbf62fae17e9a7b7163cc43ac \
--hash=sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6 \
--hash=sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89 \
--hash=sha256:2f609a39f62a6f6f05c7512746798282546358a37ea93c1fcbadf8b2fed162e3 \
--hash=sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49 \
--hash=sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20 \
--hash=sha256:3d14e3c4d65e19d8430a4e28ceb71ace4728776fd6c3ce34016947474479683f \
--hash=sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc \
--hash=sha256:4bc5fca10aaf74779081e16c2bcc3d5ec643ffd528d9e7b1c9039000ead73bae \
--hash=sha256:4e4181bfc24413d1e3a37a0b7889bea68d973d4b45dd2bc68bb766c140718f82 \
--hash=sha256:55b453812fa7c7ce2f5c88be3018fb4a490519b6ce80788d5913f3f9d7da8c7b \
--hash=sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91 \
--hash=sha256:5f251c355167b2360537cf17bea2cf0197995e551ab9da6a0a59b3da5e8704f9 \
--hash=sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5 \
--hash=sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362 \
--hash=sha256:655726919b75ab3c34cdad39da5c530ac6fa32696fb23119e36b64adcfca174a \
--hash=sha256:662456c4513e298db6d7bd9c3b8df6f75f8752f0ba01fb653e252ed4a59b5a5d \
--hash=sha256:68c8ebcca945efff9d86d8d6d7bfb0841cf0071024417e2d7f45c5e46b5b08eb \
--hash=sha256:69e1a8180868a2576f02356565f16635b99088da7df3d45aaa7e24e73a054e31 \
--hash=sha256:6bab67d15ad617aff094c382c882e0177637da73cbc5532d52c07b4ee887a87b \
--hash=sha256:7d95d71ff35291bab3f1c52f52f474c632db26ea12700c2ff0ea0532cb0b5854 \
--hash=sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1 \
--hash=sha256:834988b6c34515545b3edd13e902c1acdd9f2465d386ea5143fb558f153a7176 \
--hash=sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8 \
--hash=sha256:85bd5cdf4ed7b2d6438871adf6afff9af7096486fcf51818a81b77ef4dd30907 \
--hash=sha256:86ad489db097141a907c559988c29718719aa3e13370d40e20506f11b4de0d11 \
--hash=sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c \
--hash=sha256:8efe72fde5500f47aca1ef59495cb59c885afe04ac89dd11d810f2de87d935d4 \
--hash=sha256:8f7b5882fb50632ab1e48cb3122d6df55b9afabc265582808036b6e51b9fd6b7 \
--hash=sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707 \
--hash=sha256:9e912d3c993a29df6c627459af58975b2e5c897d93287939b9d5065f000249b5 \
--hash=sha256:a8f0302f9ac4e9923f98d8e243939a6fb627cd048f5cd38595c97e38020dffce \
--hash=sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa \
--hash=sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01 \
--hash=sha256:c3d7bd6e3929fd2ea7fbc3f562e4987229ead70c9ae5f01501a46701e08f1ad9 \
--hash=sha256:c98e0b7434a7fa4e3e63f250456eaef52499fba5ae661c58cc5b5477d11e7182 \
--hash=sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b \
--hash=sha256:e154d230dc1bbbd78ad2fdc3039fa50ad7ffcf438e4eb2fa30bce223a70c7486 \
--hash=sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249 \
--hash=sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3 \
--hash=sha256:e8978003816c7b9eabe217f88c78bc26adc8f9304bf6a594b02e5a49b2ef9c11 \
--hash=sha256:ecde9ab49f58433abe02f9ed076c7b5be839cf0153883a6d23995937a82392fa \
--hash=sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e \
--hash=sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24 \
--hash=sha256:fe0f540750a13fd8e5da4b3eaba91a785eea8dca5ccd2bc2ffe978caa403090e
# via
# -r requirements.in
# grpcio-tools
grpcio-tools==1.74.0 \
--hash=sha256:03787990b56f5c3b3f72c722a7e74fbc5a3b769bbc31ad426e2c6f6a28a9d7c8 \
--hash=sha256:051ce925b0b99ae2daf61b3cba19962b8655cc2a72758ce4081b89272206f5a3 \
--hash=sha256:0cab5a2c6ae75b555fee8a1a9a9b575205171e1de392fe2d4139a29e67d8f5bb \
--hash=sha256:0e8c22e390800175417ec646fac99acaadcbd2f5cdb1a27694995ca86d3bbfd3 \
--hash=sha256:187f99fd22de6e63fbf4f30b2e054a2e3c4fb80beec73b1f4716ea86192050f5 \
--hash=sha256:1bdf91eb722f2990085b1342c277e212ec392e37bd493a2a21d9eb9238f28c3e \
--hash=sha256:1e23ff54dea7f6e9543dcebd2c0f4b7c9af39812966c05e1c5289477cb2bf2f7 \
--hash=sha256:1fdc013118e4e9054b6e1a64d16a0d4a17a4071042e674ada8673406ddb26e59 \
--hash=sha256:333003e6a9dc304da9e6b086294a8d25212c542284e60699a72b456c515f114c \
--hash=sha256:39045d07f2582b35685858e1616761b7ad45085e446941c8f9f7c6da523f83c3 \
--hash=sha256:406ec87e2fd4cb6a40229fbecebcd11973afd4747484bfd5c2bc2ebe81545b7a \
--hash=sha256:41040eb1b5d1e582687f6f19cf2efc4c191b6eab56b16f6fba50ac085c5ca4dd \
--hash=sha256:4b6c5efb331ae9e5f614437f4a5938459a8a5a1ab3dfe133d2bbdeaba39b894d \
--hash=sha256:519d7cae085ae6695a8031bb990bf7766a922332b0a531e51342abc5431b78b5 \
--hash=sha256:5274a4f227e4bd244e3890a9238bda47b169765421ea87f157e4955ea39b4326 \
--hash=sha256:536f53a6a8d1ba1c469d085066cfa0dd3bb51f07013b71857bc3ad1eabe3ab49 \
--hash=sha256:5ec661f3bb41f0d2a30125ea382f4d5c874bf4f26d4d8e3839bb7e3b3c037b3e \
--hash=sha256:61d84f6050d7170712600f7ee1dac8849f5dc0bfe0044dd71132ee1e7aa2b373 \
--hash=sha256:6b61337b47d981b4d270e3caa83607a900169617478c034e6f6baf16ab22d333 \
--hash=sha256:6f56d67b04790f84e216353341c6b298f1aeb591e1797fe955f606516c640936 \
--hash=sha256:700d8933684f66dd8edc0324590fa61930bed8f9fb66322a48f5c7ba08386810 \
--hash=sha256:70725de8cf724c54040502f199ea28df0e8bc480175eacbed8c999c9ad4c0ffe \
--hash=sha256:76072dee9fa99b33eb0c334a16e70d694df762df705c7a2481f702af33d81a28 \
--hash=sha256:77b400d3c87b1f85be505366e299e00214e2266f604ab58616fc77d016336a24 \
--hash=sha256:796796b4d7e83a9cdd03bb95c6774fca060fd209d83fb9af5f043e9c6f06a1fa \
--hash=sha256:7970a9cf3002bec2eff5a449ac7398b77e5d171cbb534c47258c72409d0aea74 \
--hash=sha256:7e920982b4eaab253affbd45ec6d5ec12d895f5c143374ef4c3eadef49162373 \
--hash=sha256:88ab9eb18b6ac1b4872add6b394073bd8d44eee7c32e4dc60a022e25ffaffb95 \
--hash=sha256:88e535c1cf349e57e371529ea9918f811c5eff88161f322bbc06d6222bad6d50 \
--hash=sha256:98c7b8eb0de6984cd7fa7335ce3383b3bb9a1559edc238c811df88008d5d3593 \
--hash=sha256:9b18afca48b55832402a716ea4634ef2b68927a8a17ddf4038f51812299255c9 \
--hash=sha256:9d9e28fbbab9b9e923c3d286949e8ff81ebbb402458698f0a2b1183b539779db \
--hash=sha256:a036cd2a4223901e7a9f6a9b394326a9352a4ad70bdd3f1d893f1b231fcfdf7e \
--hash=sha256:b63e250da44b15c67b9a34c5c30c81059bde528fc8af092d7f43194469f7c719 \
--hash=sha256:b8324cd67f61f7900d227b36913ee5f0302ba3ba8777c8bc705afa8174098d28 \
--hash=sha256:b966f3b93f9d24151591d096ecf9c3fdb419a50d486761f7d28a9a69b028b627 \
--hash=sha256:bef8a16c34e68aaa2d246cd358629f8103730cb96cfc521f720378995f218282 \
--hash=sha256:c3cf9401ce72bc49582c2d80e0a2ee0e573e1c3c998c8bc5f739db8845e8e148 \
--hash=sha256:d1fdf245178158a92a2dc78e3545b6d13b6c917d9b80931fc85cfb3e9534a07d \
--hash=sha256:d576b7786207359b63c2c2e3c387639b4177cf53b1e43d020b005deead32049e \
--hash=sha256:d73686934bfdd868be0dbfbfcba2a5f50a8b0b71362e86a133e8efcbdc5cad5d \
--hash=sha256:db08b91ea0cd66dc4b1b929100e7aa84c9c10c51573c8282ec1ba05b41f887ef \
--hash=sha256:e2e22460355adbd0f25fdd7ed8b9ae53afb3875b9d5f34cdf1cf12559418245e \
--hash=sha256:e3d0c33cc984d21525f190cb1af479f8da46370df5f2ced1a4e50769ababd0c0 \
--hash=sha256:e41084adbae7176097aa9d08a13d98c189895ec8c967f5461975750d3537625a \
--hash=sha256:e85f442a9e89e276bf89a0c9c76ea71647a927d967759333c1fa40300c27f7bd \
--hash=sha256:f0129a62711dbc1f1efd51d069d2ce0631d69e033bf3a046606c623acf935e08 \
--hash=sha256:f037414c527a2c4a3af15451d9e58d7856d0a62b3f6dd3f5b969ecba82f5e843 \
--hash=sha256:f476f1ec637888a49402a1acff52bb641ec01a8672f60b57c5ee0a1d0e0763d2 \
--hash=sha256:f8f7d17b7573b9a2a6b4183fa4a56a2ab17370c8d0541e1424cf0c9c6f863434 \
--hash=sha256:fc572f8af2d8f13db4b0091dcf518d6ca5c82ea6f59e8716683bd8aeb729b203
# via -r requirements.in
grpclib==0.4.8 \
--hash=sha256:a5047733a7acc1c1cee6abf3c841c7c6fab67d2844a45a853b113fa2e6cd2654 \
--hash=sha256:d8823763780ef94fed8b2c562f7485cf0bbee15fc7d065a640673667f7719c9a
# via betterproto2
h2==4.2.0 \
--hash=sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0 \
--hash=sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f
# via grpclib
hpack==4.1.0 \
--hash=sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496 \
--hash=sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca
# via h2
hyperframe==6.1.0 \
--hash=sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5 \
--hash=sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08
# via h2
iniconfig==2.1.0 \
--hash=sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7 \
--hash=sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760
# via pytest
jinja2==3.1.6 \
--hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \
--hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67
# via betterproto2-compiler
markupsafe==3.0.2 \
--hash=sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4 \
--hash=sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30 \
--hash=sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0 \
--hash=sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9 \
--hash=sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 \
--hash=sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13 \
--hash=sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028 \
--hash=sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca \
--hash=sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557 \
--hash=sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832 \
--hash=sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0 \
--hash=sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b \
--hash=sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579 \
--hash=sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a \
--hash=sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c \
--hash=sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff \
--hash=sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c \
--hash=sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22 \
--hash=sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094 \
--hash=sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb \
--hash=sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e \
--hash=sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5 \
--hash=sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a \
--hash=sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d \
--hash=sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a \
--hash=sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b \
--hash=sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8 \
--hash=sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225 \
--hash=sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c \
--hash=sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144 \
--hash=sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f \
--hash=sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87 \
--hash=sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d \
--hash=sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93 \
--hash=sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf \
--hash=sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158 \
--hash=sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 \
--hash=sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb \
--hash=sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48 \
--hash=sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171 \
--hash=sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c \
--hash=sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6 \
--hash=sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd \
--hash=sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d \
--hash=sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1 \
--hash=sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d \
--hash=sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca \
--hash=sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a \
--hash=sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29 \
--hash=sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe \
--hash=sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798 \
--hash=sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c \
--hash=sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 \
--hash=sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f \
--hash=sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f \
--hash=sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a \
--hash=sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178 \
--hash=sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0 \
--hash=sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79 \
--hash=sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430 \
--hash=sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50
# via jinja2
multidict==6.6.3 \
--hash=sha256:02fd8f32d403a6ff13864b0851f1f523d4c988051eea0471d4f1fd8010f11134 \
--hash=sha256:04cbcce84f63b9af41bad04a54d4cc4e60e90c35b9e6ccb130be2d75b71f8c17 \
--hash=sha256:056bebbeda16b2e38642d75e9e5310c484b7c24e3841dc0fb943206a72ec89d6 \
--hash=sha256:05db2f66c9addb10cfa226e1acb363450fab2ff8a6df73c622fefe2f5af6d4e7 \
--hash=sha256:0b9e59946b49dafaf990fd9c17ceafa62976e8471a14952163d10a7a630413a9 \
--hash=sha256:0db58da8eafb514db832a1b44f8fa7906fdd102f7d982025f816a93ba45e3dcb \
--hash=sha256:0f1130b896ecb52d2a1e615260f3ea2af55fa7dc3d7c3003ba0c3121a759b18b \
--hash=sha256:10bea2ee839a759ee368b5a6e47787f399b41e70cf0c20d90dfaf4158dfb4e55 \
--hash=sha256:12f4581d2930840295c461764b9a65732ec01250b46c6b2c510d7ee68872b140 \
--hash=sha256:1328201ee930f069961ae707d59c6627ac92e351ed5b92397cf534d1336ce557 \
--hash=sha256:135631cb6c58eac37d7ac0df380294fecdc026b28837fa07c02e459c7fb9c54e \
--hash=sha256:14117a41c8fdb3ee19c743b1c027da0736fdb79584d61a766da53d399b71176c \
--hash=sha256:15332783596f227db50fb261c2c251a58ac3873c457f3a550a95d5c0aa3c770d \
--hash=sha256:159ca68bfd284a8860f8d8112cf0521113bffd9c17568579e4d13d1f1dc76b65 \
--hash=sha256:18f4eba0cbac3546b8ae31e0bbc55b02c801ae3cbaf80c247fcdd89b456ff58c \
--hash=sha256:1bf99b4daf908c73856bd87ee0a2499c3c9a3d19bb04b9c6025e66af3fd07462 \
--hash=sha256:1c8082e5814b662de8589d6a06c17e77940d5539080cbab9fe6794b5241b76d9 \
--hash=sha256:208b9b9757060b9faa6f11ab4bc52846e4f3c2fb8b14d5680c8aac80af3dc751 \
--hash=sha256:20c5a0c3c13a15fd5ea86c42311859f970070e4e24de5a550e99d7c271d76318 \
--hash=sha256:2334cfb0fa9549d6ce2c21af2bfbcd3ac4ec3646b1b1581c88e3e2b1779ec92b \
--hash=sha256:233ad16999afc2bbd3e534ad8dbe685ef8ee49a37dbc2cdc9514e57b6d589ced \
--hash=sha256:274d416b0df887aef98f19f21578653982cfb8a05b4e187d4a17103322eeaf8f \
--hash=sha256:295adc9c0551e5d5214b45cf29ca23dbc28c2d197a9c30d51aed9e037cb7c578 \
--hash=sha256:2e4cc8d848cd4fe1cdee28c13ea79ab0ed37fc2e89dd77bac86a2e7959a8c3bc \
--hash=sha256:346055630a2df2115cd23ae271910b4cae40f4e336773550dca4889b12916e75 \
--hash=sha256:35712f1748d409e0707b165bf49f9f17f9e28ae85470c41615778f8d4f7d9609 \
--hash=sha256:3713303e4a6663c6d01d648a68f2848701001f3390a030edaaf3fc949c90bf7c \
--hash=sha256:37b09ca60998e87734699e88c2363abfd457ed18cfbf88e4009a4e83788e63ed \
--hash=sha256:3893a0d7d28a7fe6ca7a1f760593bc13038d1d35daf52199d431b61d2660602b \
--hash=sha256:41bb9d1d4c303886e2d85bade86e59885112a7f4277af5ad47ab919a2251f306 \
--hash=sha256:42ca5aa9329a63be8dc49040f63817d1ac980e02eeddba763a9ae5b4027b9c9c \
--hash=sha256:43571f785b86afd02b3855c5ac8e86ec921b760298d6f82ff2a61daf5a35330b \
--hash=sha256:448e4a9afccbf297577f2eaa586f07067441e7b63c8362a3540ba5a38dc0f14a \
--hash=sha256:4ef421045f13879e21c994b36e728d8e7d126c91a64b9185810ab51d474f27e7 \
--hash=sha256:500b84f51654fdc3944e936f2922114349bf8fdcac77c3092b03449f0e5bc2b3 \
--hash=sha256:531e331a2ee53543ab32b16334e2deb26f4e6b9b28e41f8e0c87e99a6c8e2d69 \
--hash=sha256:53becb01dd8ebd19d1724bebe369cfa87e4e7f29abbbe5c14c98ce4c383e16cd \
--hash=sha256:540d3c06d48507357a7d57721e5094b4f7093399a0106c211f33540fdc374d55 \
--hash=sha256:555ff55a359302b79de97e0468e9ee80637b0de1fce77721639f7cd9440b3a10 \
--hash=sha256:5633a82fba8e841bc5c5c06b16e21529573cd654f67fd833650a215520a6210e \
--hash=sha256:5bd8d6f793a787153956cd35e24f60485bf0651c238e207b9a54f7458b16d539 \
--hash=sha256:61af8a4b771f1d4d000b3168c12c3120ccf7284502a94aa58c68a81f5afac090 \
--hash=sha256:639ecc9fe7cd73f2495f62c213e964843826f44505a3e5d82805aa85cac6f89e \
--hash=sha256:67c92ed673049dec52d7ed39f8cf9ebbadf5032c774058b4406d18c8f8fe7063 \
--hash=sha256:68e9e12ed00e2089725669bdc88602b0b6f8d23c0c95e52b95f0bc69f7fe9b55 \
--hash=sha256:6c1e61bb4f80895c081790b6b09fa49e13566df8fbff817da3f85b3a8192e36b \
--hash=sha256:70b72e749a4f6e7ed8fb334fa8d8496384840319512746a5f42fa0aec79f4d61 \
--hash=sha256:70d974eaaa37211390cd02ef93b7e938de564bbffa866f0b08d07e5e65da783d \
--hash=sha256:712b348f7f449948e0a6c4564a21c7db965af900973a67db432d724619b3c680 \
--hash=sha256:72d8815f2cd3cf3df0f83cac3f3ef801d908b2d90409ae28102e0553af85545a \
--hash=sha256:7394888236621f61dcdd25189b2768ae5cc280f041029a5bcf1122ac63df79f9 \
--hash=sha256:73ab034fb8d58ff85c2bcbadc470efc3fafeea8affcf8722855fb94557f14cc5 \
--hash=sha256:766a4a5996f54361d8d5a9050140aa5362fe48ce51c755a50c0bc3706460c430 \
--hash=sha256:769841d70ca8bdd140a715746199fc6473414bd02efd678d75681d2d6a8986c5 \
--hash=sha256:775b464d31dac90f23192af9c291dc9f423101857e33e9ebf0020a10bfcf4144 \
--hash=sha256:798a9eb12dab0a6c2e29c1de6f3468af5cb2da6053a20dfa3344907eed0937cc \
--hash=sha256:7af039820cfd00effec86bda5d8debef711a3e86a1d3772e85bea0f243a4bd65 \
--hash=sha256:7c6df517cf177da5d47ab15407143a89cd1a23f8b335f3a28d57e8b0a3dbb884 \
--hash=sha256:81ef2f64593aba09c5212a3d0f8c906a0d38d710a011f2f42759704d4557d3f2 \
--hash=sha256:877443eaaabcd0b74ff32ebeed6f6176c71850feb7d6a1d2db65945256ea535c \
--hash=sha256:8db10f29c7541fc5da4defd8cd697e1ca429db743fa716325f236079b96f775a \
--hash=sha256:8df25594989aebff8a130f7899fa03cbfcc5d2b5f4a461cf2518236fe6f15961 \
--hash=sha256:900eb9f9da25ada070f8ee4a23f884e0ee66fe4e1a38c3af644256a508ad81ca \
--hash=sha256:934796c81ea996e61914ba58064920d6cad5d99140ac3167901eb932150e2e56 \
--hash=sha256:94c47ea3ade005b5976789baaed66d4de4480d0a0bf31cef6edaa41c1e7b56a6 \
--hash=sha256:9c19cea2a690f04247d43f366d03e4eb110a0dc4cd1bbeee4d445435428ed35b \
--hash=sha256:9e236a7094b9c4c1b7585f6b9cca34b9d833cf079f7e4c49e6a4a6ec9bfdc68f \
--hash=sha256:9e864486ef4ab07db5e9cb997bad2b681514158d6954dd1958dfb163b83d53e6 \
--hash=sha256:9ed948328aec2072bc00f05d961ceadfd3e9bfc2966c1319aeaf7b7c21219183 \
--hash=sha256:9f5b28c074c76afc3e4c610c488e3493976fe0e596dd3db6c8ddfbb0134dcac5 \
--hash=sha256:9f97e181f344a0ef3881b573d31de8542cc0dbc559ec68c8f8b5ce2c2e91646d \
--hash=sha256:a2be5b7b35271f7fff1397204ba6708365e3d773579fe2a30625e16c4b4ce817 \
--hash=sha256:ab0a34a007704c625e25a9116c6770b4d3617a071c8a7c30cd338dfbadfe6485 \
--hash=sha256:acf6b97bd0884891af6a8b43d0f586ab2fcf8e717cbd47ab4bdddc09e20652d8 \
--hash=sha256:b1db4d2093d6b235de76932febf9d50766cf49a5692277b2c28a501c9637f616 \
--hash=sha256:b24576f208793ebae00280c59927c3b7c2a3b1655e443a25f753c4611bc1c373 \
--hash=sha256:b8fee016722550a2276ca2cb5bb624480e0ed2bd49125b2b73b7010b9090e888 \
--hash=sha256:b9cbc60010de3562545fa198bfc6d3825df430ea96d2cc509c39bd71e2e7d648 \
--hash=sha256:b9fe5a0e57c6dbd0e2ce81ca66272282c32cd11d31658ee9553849d91289e1c1 \
--hash=sha256:bb933c891cd4da6bdcc9733d048e994e22e1883287ff7540c2a0f3b117605092 \
--hash=sha256:bc7f6fbc61b1c16050a389c630da0b32fc6d4a3d191394ab78972bf5edc568c2 \
--hash=sha256:bd0578596e3a835ef451784053cfd327d607fc39ea1a14812139339a18a0dbc3 \
--hash=sha256:bf9bd1fd5eec01494e0f2e8e446a74a85d5e49afb63d75a9934e4a5423dba21d \
--hash=sha256:c60b401f192e79caec61f166da9c924e9f8bc65548d4246842df91651e83d600 \
--hash=sha256:c8161b5a7778d3137ea2ee7ae8a08cce0010de3b00ac671c5ebddeaa17cefd22 \
--hash=sha256:cdf22e4db76d323bcdc733514bf732e9fb349707c98d341d40ebcc6e9318ef3d \
--hash=sha256:ce8b7693da41a3c4fde5871c738a81490cea5496c671d74374c8ab889e1834fb \
--hash=sha256:d04d01f0a913202205a598246cf77826fe3baa5a63e9f6ccf1ab0601cf56eca0 \
--hash=sha256:d25594d3b38a2e6cabfdcafef339f754ca6e81fbbdb6650ad773ea9775af35ab \
--hash=sha256:d4e47d8faffaae822fb5cba20937c048d4f734f43572e7079298a6c39fb172cb \
--hash=sha256:dbc7cf464cc6d67e83e136c9f55726da3a30176f020a36ead246eceed87f1cd8 \
--hash=sha256:dd7793bab517e706c9ed9d7310b06c8672fd0aeee5781bfad612f56b8e0f7d14 \
--hash=sha256:e098c17856a8c9ade81b4810888c5ad1914099657226283cab3062c0540b0643 \
--hash=sha256:e0cb0ab69915c55627c933f0b555a943d98ba71b4d1c57bc0d0a66e2567c7471 \
--hash=sha256:e252017a817fad7ce05cafbe5711ed40faeb580e63b16755a3a24e66fa1d87c0 \
--hash=sha256:e2db616467070d0533832d204c54eea6836a5e628f2cb1e6dfd8cd6ba7277cb7 \
--hash=sha256:e4e15d2138ee2694e038e33b7c3da70e6b0ad8868b9f8094a72e1414aeda9c1a \
--hash=sha256:e5511cb35f5c50a2db21047c875eb42f308c5583edf96bd8ebf7d770a9d68f6d \
--hash=sha256:e5e8523bb12d7623cd8300dbd91b9e439a46a028cd078ca695eb66ba31adee3c \
--hash=sha256:e5f481cccb3c5c5e5de5d00b5141dc589c1047e60d07e85bbd7dea3d4580d63f \
--hash=sha256:e924fb978615a5e33ff644cc42e6aa241effcf4f3322c09d4f8cebde95aff5f8 \
--hash=sha256:e93089c1570a4ad54c3714a12c2cef549dc9d58e97bcded193d928649cab78e9 \
--hash=sha256:e995a34c3d44ab511bfc11aa26869b9d66c2d8c799fa0e74b28a473a692532d6 \
--hash=sha256:ef43b5dd842382329e4797c46f10748d8c2b6e0614f46b4afe4aee9ac33159df \
--hash=sha256:ef58340cc896219e4e653dade08fea5c55c6df41bcc68122e3be3e9d873d9a7b \
--hash=sha256:f114d8478733ca7388e7c7e0ab34b72547476b97009d643644ac33d4d3fe1821 \
--hash=sha256:f3aa090106b1543f3f87b2041eef3c156c8da2aed90c63a2fbed62d875c49c37 \
--hash=sha256:f3fc723ab8a5c5ed6c50418e9bfcd8e6dceba6c271cee6728a10a4ed8561520c \
--hash=sha256:f54cb79d26d0cd420637d184af38f0668558f3c4bbe22ab7ad830e67249f2e0b \
--hash=sha256:fc9dc435ec8699e7b602b94fe0cd4703e69273a01cbc34409af29e7820f777f1
# via grpclib
packaging==25.0 \
--hash=sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 \
--hash=sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f
@ -16,6 +327,17 @@ pluggy==1.6.0 \
--hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \
--hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746
# via pytest
protobuf==6.31.1 \
--hash=sha256:0414e3aa5a5f3ff423828e1e6a6e907d6c65c1d5b7e6e975793d5590bdeecc16 \
--hash=sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447 \
--hash=sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6 \
--hash=sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402 \
--hash=sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e \
--hash=sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9 \
--hash=sha256:8764cf4587791e7564051b35524b72844f845ad0bb011704c3736cce762d8fe9 \
--hash=sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39 \
--hash=sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a
# via grpcio-tools
pygments==2.19.2 \
--hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \
--hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b
@ -24,3 +346,43 @@ pytest==8.4.1 \
--hash=sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7 \
--hash=sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c
# via -r requirements.in
python-dateutil==2.9.0.post0 \
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
--hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
# via betterproto2
ruff==0.9.10 \
--hash=sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d \
--hash=sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c \
--hash=sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12 \
--hash=sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d \
--hash=sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8 \
--hash=sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69 \
--hash=sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e \
--hash=sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7 \
--hash=sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c \
--hash=sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029 \
--hash=sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c \
--hash=sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1 \
--hash=sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43 \
--hash=sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52 \
--hash=sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16 \
--hash=sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d \
--hash=sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1 \
--hash=sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5
# via betterproto2-compiler
six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
# via python-dateutil
typing-extensions==4.14.1 \
--hash=sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36 \
--hash=sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76
# via
# betterproto2
# betterproto2-compiler
# The following packages are considered to be unsafe in a requirements file:
setuptools==80.9.0 \
--hash=sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922 \
--hash=sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c
# via grpcio-tools