Trying to implement gRPC server

This commit is contained in:
pro100ton 2025-01-25 18:41:45 +03:00
parent 90242d770d
commit 2cb868aecc
11 changed files with 581 additions and 188 deletions

0
api/__init__.py Normal file
View file

5
api/proto/README.md Normal file
View file

@ -0,0 +1,5 @@
# Команда для генерации файлов
*Запускать надо из корня приложения*
```
python3 -m grpc_tools.protoc -Iapi/proto=api/proto --python_out=. --pyi_out=. --grpc_python_out=. api/proto/routes.proto
```

0
api/proto/__init__.py Normal file
View file

14
api/proto/routes.proto Normal file
View file

@ -0,0 +1,14 @@
syntax = "proto3";
service RouteApproaches {
rpc GetApproach(ApproachID) returns (Approach) {}
}
message ApproachID {
int32 approachID = 1;
}
message Approach {
int32 reps = 1;
double weight = 2;
}

40
api/proto/routes_pb2.py Normal file
View file

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# NO CHECKED-IN PROTOBUF GENCODE
# source: api/proto/routes.proto
# Protobuf Python Version: 5.29.0
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
5,
29,
0,
'',
'api/proto/routes.proto'
)
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61pi/proto/routes.proto\" \n\nApproachID\x12\x12\n\napproachID\x18\x01 \x01(\x05\"(\n\x08\x41pproach\x12\x0c\n\x04reps\x18\x01 \x01(\x05\x12\x0e\n\x06weight\x18\x02 \x01(\x01\x32:\n\x0fRouteApproaches\x12\'\n\x0bGetApproach\x12\x0b.ApproachID\x1a\t.Approach\"\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'api.proto.routes_pb2', _globals)
if not _descriptor._USE_C_DESCRIPTORS:
DESCRIPTOR._loaded_options = None
_globals['_APPROACHID']._serialized_start=26
_globals['_APPROACHID']._serialized_end=58
_globals['_APPROACH']._serialized_start=60
_globals['_APPROACH']._serialized_end=100
_globals['_ROUTEAPPROACHES']._serialized_start=102
_globals['_ROUTEAPPROACHES']._serialized_end=160
# @@protoc_insertion_point(module_scope)

19
api/proto/routes_pb2.pyi Normal file
View file

@ -0,0 +1,19 @@
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from typing import ClassVar as _ClassVar, Optional as _Optional
DESCRIPTOR: _descriptor.FileDescriptor
class ApproachID(_message.Message):
__slots__ = ("approachID",)
APPROACHID_FIELD_NUMBER: _ClassVar[int]
approachID: int
def __init__(self, approachID: _Optional[int] = ...) -> None: ...
class Approach(_message.Message):
__slots__ = ("reps", "weight")
REPS_FIELD_NUMBER: _ClassVar[int]
WEIGHT_FIELD_NUMBER: _ClassVar[int]
reps: int
weight: float
def __init__(self, reps: _Optional[int] = ..., weight: _Optional[float] = ...) -> None: ...

View file

@ -0,0 +1,97 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import warnings
from api.proto import routes_pb2 as api_dot_proto_dot_routes__pb2
GRPC_GENERATED_VERSION = '1.70.0'
GRPC_VERSION = grpc.__version__
_version_not_supported = False
try:
from grpc._utilities import first_version_is_lower
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
except ImportError:
_version_not_supported = True
if _version_not_supported:
raise RuntimeError(
f'The grpc package installed is at version {GRPC_VERSION},'
+ f' but the generated code in api/proto/routes_pb2_grpc.py depends on'
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
)
class RouteApproachesStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.GetApproach = channel.unary_unary(
'/RouteApproaches/GetApproach',
request_serializer=api_dot_proto_dot_routes__pb2.ApproachID.SerializeToString,
response_deserializer=api_dot_proto_dot_routes__pb2.Approach.FromString,
_registered_method=True)
class RouteApproachesServicer(object):
"""Missing associated documentation comment in .proto file."""
def GetApproach(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_RouteApproachesServicer_to_server(servicer, server):
rpc_method_handlers = {
'GetApproach': grpc.unary_unary_rpc_method_handler(
servicer.GetApproach,
request_deserializer=api_dot_proto_dot_routes__pb2.ApproachID.FromString,
response_serializer=api_dot_proto_dot_routes__pb2.Approach.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'RouteApproaches', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
server.add_registered_method_handlers('RouteApproaches', rpc_method_handlers)
# This class is part of an EXPERIMENTAL API.
class RouteApproaches(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def GetApproach(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/RouteApproaches/GetApproach',
api_dot_proto_dot_routes__pb2.ApproachID.SerializeToString,
api_dot_proto_dot_routes__pb2.Approach.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)

199
main.py
View file

@ -1,189 +1,18 @@
import asyncio
from collections import defaultdict
from pprint import pprint
import os
from typing import Dict, List
from sqlalchemy import Table, create_engine, text, insert
from dotenv import load_dotenv
from sqlalchemy.ext.asyncio import create_async_engine
from dbapi.migrator import FitnessDatabseMigrator
from dbapi.repositories.approach_repo import ApproachRepository
from dbapi.repositories.exercise_repo import ExerciseRepository
from dbapi.repositories.training_repo import TrainingRepository
from dbapi.tables import metadata_obj, training, exercise, approach
from obsidian.notes_parser import parse_training_data, remap_unique_exercises
from apple.notes_parser import parse_training_data as apple_parse_training_data
from apple.notes_parser import remap_unique_exercises as apple_remaper
from obsidian.py_models import Training
from concurrent.futures import ThreadPoolExecutor
import grpc
from api.proto import routes_pb2, routes_pb2_grpc
load_dotenv()
class RouteApproachesServicer(routes_pb2_grpc.RouteApproachesServicer):
def GetApproach(self, request, context):
print("Hello")
return routes_pb2.Approach(weight=100, reps=12)
DB_USERNAME = os.getenv("POSTGRES_USER")
DB_PASS = os.getenv("POSTGRES_PASSWORD")
def serve():
server = grpc.server(ThreadPoolExecutor(max_workers=10))
routes_pb2_grpc.add_RouteApproachesServicer_to_server(RouteApproachesServicer(), server)
server.add_insecure_port("[::]:50051")
server.start()
server.wait_for_termination()
engine = create_engine(
f"postgresql+psycopg2://{DB_USERNAME}:{DB_PASS}@localhost:5433/fitness_database",
echo=True,
)
# Creating async engine for Database connection
async_engine = create_async_engine(
f"postgresql+asyncpg://{DB_USERNAME}:{DB_PASS}@localhost:5433/fitness_database",
echo=True,
)
# NOTE: "Begin once" style - using `.begin` as context creator for SQLAlchemy
# with engine.begin() as conn:
# result = conn.execute(text("select 'hello world'"))
# print(result.all())
# conn.execute(
# text("INSERT INTO some_table(x, y) VALUES (:x, :y)"),
# [{"x": 6, "y": 7}, {"x": 9, "y": 10}],
# )
# NOTE: "Commit as you go" style - after managing transactions we need to call Connection.commit(). Otherwise ROLLBACK
# will be executed
# with engine.connect() as conn:
# result = conn.execute(text("SELECT x, y FROM some_table"))
# for row in result:
# print(f"x: {row.x} -- y: {row.y}")
# NOTE : Create all tables from metadata object
# metadata_obj.create_all(engine)
# TODO: Check how psycopg2 handles duplication of tables
# TODO: Check how migrations are done
# NOTE: Drop all Tables from database
metadata_obj.drop_all(engine)
metadata_obj.create_all(engine)
# NOTE: Table reflection - generating table object from existing tables (only tables, that are stored in metadata)
# some_table = Table("some_table", metadata_obj, autoload_with=engine)
# print(some_table.c)
# -----
# Inserting training values into database
trainings: List[Training] = parse_training_data()
for train in trainings:
if not train:
continue
else:
print(train)
new_training_pk: int = TrainingRepository(engine).create_training(train.date)
for exr in train.exercises:
approach_statements = []
new_exercise_pk: int = ExerciseRepository(engine).create_exercise(
training_pk=new_training_pk, exercise_name=exr.name
)
for appr in exr.approaches:
new_approach_pk: int = ApproachRepository(engine).create_approach(
exercise_pk=new_exercise_pk, weight=appr.weight, reps=appr.reps
)
print("-------------------------\n" * 2)
print("-------------------------\n" * 2)
# -----
# Calculating unique exercises for obsidian
# trainings: List[Training] = parse_training_data()
#
#
# unique_exercise_names = defaultdict(int)
# counter = 0
#
# for train in trainings:
# if not train:
# continue
# if train.exercises:
# for exr in train.exercises:
# counter += 1
# unique_exercise_names[exr.name] += 1
#
# pprint(unique_exercise_names)
# print(counter)
# parsed_trainings = remap_unique_exercises(trainings)
#
# print("\n" * 3)
#
# unique_exercise_parsed_names = defaultdict(int)
# p_counter = 0
# for train in parsed_trainings:
# if not train:
# continue
# if train.exercises:
# for exr in train.exercises:
# p_counter += 1
# unique_exercise_parsed_names[exr.name] += 1
# pprint(unique_exercise_parsed_names)
# print(p_counter)
# Apple notes playground
# trainings: List[Training] = apple_parse_training_data()
#
#
# unique_exercise_names = defaultdict(int)
# counter = 0
#
# for train in trainings:
# if not train:
# continue
# if train.exercises:
# for exr in train.exercises:
# if exr:
# counter += 1
# unique_exercise_names[exr.name] += 1
#
# pprint(unique_exercise_names)
# print(counter)
#
# parsed_trainings = apple_remaper(trainings)
#
# print("\n" * 3)
#
# unique_exercise_parsed_names = defaultdict(int)
# p_counter = 0
# for train in parsed_trainings:
# if not train:
# continue
# if train.exercises:
# for exr in train.exercises:
# if exr:
# p_counter += 1
# unique_exercise_parsed_names[exr.name] += 1
# pprint(unique_exercise_parsed_names)
# print(p_counter)
# Combined trainings
# obsidian_trainings: List[Training] = parse_training_data()
# obsidian_parsed_trainings = remap_unique_exercises(obsidian_trainings)
#
# apple_trainings: List[Training] = apple_parse_training_data()
# apple_parsed_trainings = apple_remaper(apple_trainings)
#
#
# combined_trainings = obsidian_trainings + apple_trainings
# unique_exercise_parsed_names = defaultdict(int)
# for train in combined_trainings:
# if not train:
# continue
# if train.exercises:
# for exr in train.exercises:
# if exr:
# unique_exercise_parsed_names[exr.name] += 1
# pprint(unique_exercise_parsed_names)
# print(len(combined_trainings))
# Async engine playground
# fbm = FitnessDatabseMigrator(async_engine=async_engine)
#
# asyncio.run(fbm.reset_database())
serve()

190
main_backup.py Normal file
View file

@ -0,0 +1,190 @@
import asyncio
from collections import defaultdict
from pprint import pprint
import os
from typing import Dict, List
from sqlalchemy import Table, create_engine, text, insert
from dotenv import load_dotenv
from sqlalchemy.ext.asyncio import create_async_engine
from dbapi.migrator import FitnessDatabseMigrator
from dbapi.repositories.approach_repo import ApproachRepository
from dbapi.repositories.exercise_repo import ExerciseRepository
from dbapi.repositories.training_repo import TrainingRepository
from dbapi.tables import metadata_obj, training, exercise, approach
from obsidian.notes_parser import parse_training_data, remap_unique_exercises
from apple.notes_parser import parse_training_data as apple_parse_training_data
from apple.notes_parser import remap_unique_exercises as apple_remaper
from obsidian.py_models import Training
# LOADING DATA
# load_dotenv()
#
# DB_USERNAME = os.getenv("POSTGRES_USER")
# DB_PASS = os.getenv("POSTGRES_PASSWORD")
#
# engine = create_engine(
# f"postgresql+psycopg2://{DB_USERNAME}:{DB_PASS}@localhost:5433/fitness_database",
# echo=True,
# )
#
# # Creating async engine for Database connection
# async_engine = create_async_engine(
# f"postgresql+asyncpg://{DB_USERNAME}:{DB_PASS}@localhost:5433/fitness_database",
# echo=True,
# )
# NOTE: "Begin once" style - using `.begin` as context creator for SQLAlchemy
# with engine.begin() as conn:
# result = conn.execute(text("select 'hello world'"))
# print(result.all())
# conn.execute(
# text("INSERT INTO some_table(x, y) VALUES (:x, :y)"),
# [{"x": 6, "y": 7}, {"x": 9, "y": 10}],
# )
# NOTE: "Commit as you go" style - after managing transactions we need to call Connection.commit(). Otherwise ROLLBACK
# will be executed
# with engine.connect() as conn:
# result = conn.execute(text("SELECT x, y FROM some_table"))
# for row in result:
# print(f"x: {row.x} -- y: {row.y}")
# NOTE : Create all tables from metadata object
# metadata_obj.create_all(engine)
# TODO: Check how psycopg2 handles duplication of tables
# TODO: Check how migrations are done
# NOTE: Drop all Tables from database
metadata_obj.drop_all(engine)
metadata_obj.create_all(engine)
# NOTE: Table reflection - generating table object from existing tables (only tables, that are stored in metadata)
# some_table = Table("some_table", metadata_obj, autoload_with=engine)
# print(some_table.c)
# -----
# Inserting training values into database
trainings: List[Training] = parse_training_data()
for train in trainings:
if not train:
continue
else:
print(train)
new_training_pk: int = TrainingRepository(engine).create_training(train.date)
for exr in train.exercises:
approach_statements = []
new_exercise_pk: int = ExerciseRepository(engine).create_exercise(
training_pk=new_training_pk, exercise_name=exr.name
)
for appr in exr.approaches:
new_approach_pk: int = ApproachRepository(engine).create_approach(
exercise_pk=new_exercise_pk, weight=appr.weight, reps=appr.reps
)
print("-------------------------\n" * 2)
print("-------------------------\n" * 2)
# -----
# Calculating unique exercises for obsidian
# trainings: List[Training] = parse_training_data()
#
#
# unique_exercise_names = defaultdict(int)
# counter = 0
#
# for train in trainings:
# if not train:
# continue
# if train.exercises:
# for exr in train.exercises:
# counter += 1
# unique_exercise_names[exr.name] += 1
#
# pprint(unique_exercise_names)
# print(counter)
# parsed_trainings = remap_unique_exercises(trainings)
#
# print("\n" * 3)
#
# unique_exercise_parsed_names = defaultdict(int)
# p_counter = 0
# for train in parsed_trainings:
# if not train:
# continue
# if train.exercises:
# for exr in train.exercises:
# p_counter += 1
# unique_exercise_parsed_names[exr.name] += 1
# pprint(unique_exercise_parsed_names)
# print(p_counter)
# Apple notes playground
# trainings: List[Training] = apple_parse_training_data()
#
#
# unique_exercise_names = defaultdict(int)
# counter = 0
#
# for train in trainings:
# if not train:
# continue
# if train.exercises:
# for exr in train.exercises:
# if exr:
# counter += 1
# unique_exercise_names[exr.name] += 1
#
# pprint(unique_exercise_names)
# print(counter)
#
# parsed_trainings = apple_remaper(trainings)
#
# print("\n" * 3)
#
# unique_exercise_parsed_names = defaultdict(int)
# p_counter = 0
# for train in parsed_trainings:
# if not train:
# continue
# if train.exercises:
# for exr in train.exercises:
# if exr:
# p_counter += 1
# unique_exercise_parsed_names[exr.name] += 1
# pprint(unique_exercise_parsed_names)
# print(p_counter)
# Combined trainings
# obsidian_trainings: List[Training] = parse_training_data()
# obsidian_parsed_trainings = remap_unique_exercises(obsidian_trainings)
#
# apple_trainings: List[Training] = apple_parse_training_data()
# apple_parsed_trainings = apple_remaper(apple_trainings)
#
#
# combined_trainings = obsidian_trainings + apple_trainings
# unique_exercise_parsed_names = defaultdict(int)
# for train in combined_trainings:
# if not train:
# continue
# if train.exercises:
# for exr in train.exercises:
# if exr:
# unique_exercise_parsed_names[exr.name] += 1
# pprint(unique_exercise_parsed_names)
# print(len(combined_trainings))
# Async engine playground
# fbm = FitnessDatabseMigrator(async_engine=async_engine)
#
# asyncio.run(fbm.reset_database())

203
poetry.lock generated
View file

@ -1,9 +1,10 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
[[package]]
name = "annotated-types"
version = "0.7.0"
description = "Reusable constraint types to use with typing.Annotated"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -15,6 +16,7 @@ files = [
name = "async-timeout"
version = "5.0.1"
description = "Timeout context manager for asyncio programs"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -26,6 +28,7 @@ files = [
name = "asyncpg"
version = "0.30.0"
description = "An asyncio PostgreSQL driver"
category = "main"
optional = false
python-versions = ">=3.8.0"
files = [
@ -92,6 +95,7 @@ test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
@ -103,6 +107,7 @@ files = [
name = "exceptiongroup"
version = "1.2.2"
description = "Backport of PEP 654 (exception groups)"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -117,6 +122,7 @@ test = ["pytest (>=6)"]
name = "greenlet"
version = "3.1.1"
description = "Lightweight in-process concurrent programming"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -199,10 +205,149 @@ files = [
docs = ["Sphinx", "furo"]
test = ["objgraph", "psutil"]
[[package]]
name = "grpcio"
version = "1.70.0"
description = "HTTP/2-based RPC framework"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
{file = "grpcio-1.70.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:95469d1977429f45fe7df441f586521361e235982a0b39e33841549143ae2851"},
{file = "grpcio-1.70.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:ed9718f17fbdb472e33b869c77a16d0b55e166b100ec57b016dc7de9c8d236bf"},
{file = "grpcio-1.70.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:374d014f29f9dfdb40510b041792e0e2828a1389281eb590df066e1cc2b404e5"},
{file = "grpcio-1.70.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2af68a6f5c8f78d56c145161544ad0febbd7479524a59c16b3e25053f39c87f"},
{file = "grpcio-1.70.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7df14b2dcd1102a2ec32f621cc9fab6695effef516efbc6b063ad749867295"},
{file = "grpcio-1.70.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c78b339869f4dbf89881e0b6fbf376313e4f845a42840a7bdf42ee6caed4b11f"},
{file = "grpcio-1.70.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58ad9ba575b39edef71f4798fdb5c7b6d02ad36d47949cd381d4392a5c9cbcd3"},
{file = "grpcio-1.70.0-cp310-cp310-win32.whl", hash = "sha256:2b0d02e4b25a5c1f9b6c7745d4fa06efc9fd6a611af0fb38d3ba956786b95199"},
{file = "grpcio-1.70.0-cp310-cp310-win_amd64.whl", hash = "sha256:0de706c0a5bb9d841e353f6343a9defc9fc35ec61d6eb6111802f3aa9fef29e1"},
{file = "grpcio-1.70.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:17325b0be0c068f35770f944124e8839ea3185d6d54862800fc28cc2ffad205a"},
{file = "grpcio-1.70.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:dbe41ad140df911e796d4463168e33ef80a24f5d21ef4d1e310553fcd2c4a386"},
{file = "grpcio-1.70.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5ea67c72101d687d44d9c56068328da39c9ccba634cabb336075fae2eab0d04b"},
{file = "grpcio-1.70.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb5277db254ab7586769e490b7b22f4ddab3876c490da0a1a9d7c695ccf0bf77"},
{file = "grpcio-1.70.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7831a0fc1beeeb7759f737f5acd9fdcda520e955049512d68fda03d91186eea"},
{file = "grpcio-1.70.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:27cc75e22c5dba1fbaf5a66c778e36ca9b8ce850bf58a9db887754593080d839"},
{file = "grpcio-1.70.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d63764963412e22f0491d0d32833d71087288f4e24cbcddbae82476bfa1d81fd"},
{file = "grpcio-1.70.0-cp311-cp311-win32.whl", hash = "sha256:bb491125103c800ec209d84c9b51f1c60ea456038e4734688004f377cfacc113"},
{file = "grpcio-1.70.0-cp311-cp311-win_amd64.whl", hash = "sha256:d24035d49e026353eb042bf7b058fb831db3e06d52bee75c5f2f3ab453e71aca"},
{file = "grpcio-1.70.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:ef4c14508299b1406c32bdbb9fb7b47612ab979b04cf2b27686ea31882387cff"},
{file = "grpcio-1.70.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:aa47688a65643afd8b166928a1da6247d3f46a2784d301e48ca1cc394d2ffb40"},
{file = "grpcio-1.70.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:880bfb43b1bb8905701b926274eafce5c70a105bc6b99e25f62e98ad59cb278e"},
{file = "grpcio-1.70.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e654c4b17d07eab259d392e12b149c3a134ec52b11ecdc6a515b39aceeec898"},
{file = "grpcio-1.70.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2394e3381071045a706ee2eeb6e08962dd87e8999b90ac15c55f56fa5a8c9597"},
{file = "grpcio-1.70.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b3c76701428d2df01964bc6479422f20e62fcbc0a37d82ebd58050b86926ef8c"},
{file = "grpcio-1.70.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac073fe1c4cd856ebcf49e9ed6240f4f84d7a4e6ee95baa5d66ea05d3dd0df7f"},
{file = "grpcio-1.70.0-cp312-cp312-win32.whl", hash = "sha256:cd24d2d9d380fbbee7a5ac86afe9787813f285e684b0271599f95a51bce33528"},
{file = "grpcio-1.70.0-cp312-cp312-win_amd64.whl", hash = "sha256:0495c86a55a04a874c7627fd33e5beaee771917d92c0e6d9d797628ac40e7655"},
{file = "grpcio-1.70.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa573896aeb7d7ce10b1fa425ba263e8dddd83d71530d1322fd3a16f31257b4a"},
{file = "grpcio-1.70.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:d405b005018fd516c9ac529f4b4122342f60ec1cee181788249372524e6db429"},
{file = "grpcio-1.70.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f32090238b720eb585248654db8e3afc87b48d26ac423c8dde8334a232ff53c9"},
{file = "grpcio-1.70.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfa089a734f24ee5f6880c83d043e4f46bf812fcea5181dcb3a572db1e79e01c"},
{file = "grpcio-1.70.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f19375f0300b96c0117aca118d400e76fede6db6e91f3c34b7b035822e06c35f"},
{file = "grpcio-1.70.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7c73c42102e4a5ec76608d9b60227d917cea46dff4d11d372f64cbeb56d259d0"},
{file = "grpcio-1.70.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0a5c78d5198a1f0aa60006cd6eb1c912b4a1520b6a3968e677dbcba215fabb40"},
{file = "grpcio-1.70.0-cp313-cp313-win32.whl", hash = "sha256:fe9dbd916df3b60e865258a8c72ac98f3ac9e2a9542dcb72b7a34d236242a5ce"},
{file = "grpcio-1.70.0-cp313-cp313-win_amd64.whl", hash = "sha256:4119fed8abb7ff6c32e3d2255301e59c316c22d31ab812b3fbcbaf3d0d87cc68"},
{file = "grpcio-1.70.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:8058667a755f97407fca257c844018b80004ae8035565ebc2812cc550110718d"},
{file = "grpcio-1.70.0-cp38-cp38-macosx_10_14_universal2.whl", hash = "sha256:879a61bf52ff8ccacbedf534665bb5478ec8e86ad483e76fe4f729aaef867cab"},
{file = "grpcio-1.70.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:0ba0a173f4feacf90ee618fbc1a27956bfd21260cd31ced9bc707ef551ff7dc7"},
{file = "grpcio-1.70.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558c386ecb0148f4f99b1a65160f9d4b790ed3163e8610d11db47838d452512d"},
{file = "grpcio-1.70.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:412faabcc787bbc826f51be261ae5fa996b21263de5368a55dc2cf824dc5090e"},
{file = "grpcio-1.70.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3b0f01f6ed9994d7a0b27eeddea43ceac1b7e6f3f9d86aeec0f0064b8cf50fdb"},
{file = "grpcio-1.70.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7385b1cb064734005204bc8994eed7dcb801ed6c2eda283f613ad8c6c75cf873"},
{file = "grpcio-1.70.0-cp38-cp38-win32.whl", hash = "sha256:07269ff4940f6fb6710951116a04cd70284da86d0a4368fd5a3b552744511f5a"},
{file = "grpcio-1.70.0-cp38-cp38-win_amd64.whl", hash = "sha256:aba19419aef9b254e15011b230a180e26e0f6864c90406fdbc255f01d83bc83c"},
{file = "grpcio-1.70.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:4f1937f47c77392ccd555728f564a49128b6a197a05a5cd527b796d36f3387d0"},
{file = "grpcio-1.70.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:0cd430b9215a15c10b0e7d78f51e8a39d6cf2ea819fd635a7214fae600b1da27"},
{file = "grpcio-1.70.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:e27585831aa6b57b9250abaf147003e126cd3a6c6ca0c531a01996f31709bed1"},
{file = "grpcio-1.70.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1af8e15b0f0fe0eac75195992a63df17579553b0c4af9f8362cc7cc99ccddf4"},
{file = "grpcio-1.70.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbce24409beaee911c574a3d75d12ffb8c3e3dd1b813321b1d7a96bbcac46bf4"},
{file = "grpcio-1.70.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ff4a8112a79464919bb21c18e956c54add43ec9a4850e3949da54f61c241a4a6"},
{file = "grpcio-1.70.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5413549fdf0b14046c545e19cfc4eb1e37e9e1ebba0ca390a8d4e9963cab44d2"},
{file = "grpcio-1.70.0-cp39-cp39-win32.whl", hash = "sha256:b745d2c41b27650095e81dea7091668c040457483c9bdb5d0d9de8f8eb25e59f"},
{file = "grpcio-1.70.0-cp39-cp39-win_amd64.whl", hash = "sha256:a31d7e3b529c94e930a117b2175b2efd179d96eb3c7a21ccb0289a8ab05b645c"},
{file = "grpcio-1.70.0.tar.gz", hash = "sha256:8d1584a68d5922330025881e63a6c1b54cc8117291d382e4fa69339b6d914c56"},
]
[package.extras]
protobuf = ["grpcio-tools (>=1.70.0)"]
[[package]]
name = "grpcio-tools"
version = "1.70.0"
description = "Protobuf code generator for gRPC"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
{file = "grpcio_tools-1.70.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:4d456521290e25b1091975af71604facc5c7db162abdca67e12a0207b8bbacbe"},
{file = "grpcio_tools-1.70.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:d50080bca84f53f3a05452e06e6251cbb4887f5a1d1321d1989e26d6e0dc398d"},
{file = "grpcio_tools-1.70.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02e3bf55fb569fe21b54a32925979156e320f9249bb247094c4cbaa60c23a80d"},
{file = "grpcio_tools-1.70.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88a3ec6fa2381f616d567f996503e12ca353777941b61030fd9733fd5772860e"},
{file = "grpcio_tools-1.70.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6034a0579fab2aed8685fa1a558de084668b1e9b01a82a4ca7458b9bedf4654c"},
{file = "grpcio_tools-1.70.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:701bbb1ff406a21a771f5b1df6be516c0a59236774b6836eaad7696b1d128ea8"},
{file = "grpcio_tools-1.70.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6eeb86864e1432fc1ab61e03395a2a4c04e9dd9c89db07e6fe68c7c2ac8ec24f"},
{file = "grpcio_tools-1.70.0-cp310-cp310-win32.whl", hash = "sha256:d53c8c45e843b5836781ad6b82a607c72c2f9a3f556e23d703a0e099222421fa"},
{file = "grpcio_tools-1.70.0-cp310-cp310-win_amd64.whl", hash = "sha256:22024caee36ab65c2489594d718921dcbb5bd18d61c5417a9ede94fd8dc8a589"},
{file = "grpcio_tools-1.70.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:5f5aba12d98d25c7ab2dd983939e2c21556a7d15f903b286f24d88d2c6e30c0a"},
{file = "grpcio_tools-1.70.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:d47a6c6cfc526b290b7b53a37dd7e6932983f7a168b56aab760b4b597c47f30f"},
{file = "grpcio_tools-1.70.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b5a9beadd1e24772ffa2c70f07d72f73330d356b78b246e424f4f2ed6c6713f3"},
{file = "grpcio_tools-1.70.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb8135eef160a62505f074bf7a3d62f3b13911c3c14037c5392bf877114213b5"},
{file = "grpcio_tools-1.70.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7ac9b3e13ace8467a586c53580ee22f9732c355583f3c344ef8c6c0666219cc"},
{file = "grpcio_tools-1.70.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:63f367363a4a1489a0046b19f9d561216ea0d206c40a6f1bf07a58ccfb7be480"},
{file = "grpcio_tools-1.70.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54ceffef59a059d2c7304554a8bbb20eedb05a3f937159ab1c332c1b28e12c9f"},
{file = "grpcio_tools-1.70.0-cp311-cp311-win32.whl", hash = "sha256:7a90a66a46821140a2a2b0be787dfabe42e22e9a5ba9cc70726b3e5c71a3b785"},
{file = "grpcio_tools-1.70.0-cp311-cp311-win_amd64.whl", hash = "sha256:4ebf09733545a69c166b02caa14c34451e38855544820dab7fdde5c28e2dbffe"},
{file = "grpcio_tools-1.70.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:ec5d6932c3173d7618267b3b3fd77b9243949c5ec04302b7338386d4f8544e0b"},
{file = "grpcio_tools-1.70.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:f22852da12f53b02a3bdb29d0c32fcabab9c7c8f901389acffec8461083f110d"},
{file = "grpcio_tools-1.70.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:7d45067e6efd20881e98a0e1d7edd7f207b1625ad7113321becbfe0a6ebee46c"},
{file = "grpcio_tools-1.70.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3020c97f03b30eee3c26aa2a55fbe003f1729c6f879a378507c2c78524db7c12"},
{file = "grpcio_tools-1.70.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7fd472fce3b33bdf7fbc24d40da7ab10d7a088bcaf59c37433c2c57330fbcb6"},
{file = "grpcio_tools-1.70.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3875543d74ce1a698a11f498f83795216ce929cb29afa5fac15672c7ba1d6dd2"},
{file = "grpcio_tools-1.70.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a130c24d617a3a57369da784080dfa8848444d41b7ae1250abc06e72e706a8d9"},
{file = "grpcio_tools-1.70.0-cp312-cp312-win32.whl", hash = "sha256:8eae17c920d14e2e451dbb18f5d8148f884e10228061941b33faa8fceee86e73"},
{file = "grpcio_tools-1.70.0-cp312-cp312-win_amd64.whl", hash = "sha256:99caa530242a0a832d8b6a6ab94b190c9b449d3e237f953911b4d56207569436"},
{file = "grpcio_tools-1.70.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:f024688d04e7a9429489ed695b85628075c3c6d655198ba3c6ccbd1d8b7c333b"},
{file = "grpcio_tools-1.70.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:1fa9a81621d7178498dedcf94eb8f276a7594327faf3dd5fd1935ce2819a2bdb"},
{file = "grpcio_tools-1.70.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c6da2585c0950cdb650df1ff6d85b3fe31e22f8370b9ee11f8fe641d5b4bf096"},
{file = "grpcio_tools-1.70.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70234b592af17050ec30cf35894790cef52aeae87639efe6db854a7fa783cc8c"},
{file = "grpcio_tools-1.70.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c021b040d0a9f5bb96a725c4d2b95008aad127d6bed124a7bbe854973014f5b"},
{file = "grpcio_tools-1.70.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:114a42e566e5b16a47e98f7910a6c0074b37e2d1faacaae13222e463d0d0d43c"},
{file = "grpcio_tools-1.70.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:4cae365d7e3ba297256216a9a256458b286f75c64603f017972b3ad1ee374437"},
{file = "grpcio_tools-1.70.0-cp313-cp313-win32.whl", hash = "sha256:ae139a8d3ddd8353f62af3af018e99ebcd2f4a237bd319cb4b6f58dd608aaa54"},
{file = "grpcio_tools-1.70.0-cp313-cp313-win_amd64.whl", hash = "sha256:04bf30c0eb2741defe3ab6e0a6102b022d69cfd39d68fab9b954993ceca8d346"},
{file = "grpcio_tools-1.70.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:076f71c6d5adcf237ebca63f1ed51098293261dab9f301e3dfd180e896e5fa89"},
{file = "grpcio_tools-1.70.0-cp38-cp38-macosx_10_14_universal2.whl", hash = "sha256:d1fc2112e9c40167086e2e6a929b253e5281bffd070fab7cd1ae019317ffc11d"},
{file = "grpcio_tools-1.70.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:904f13d2d04f88178b09d8ef89549b90cbf8792b684a7c72540fc1a9887697e2"},
{file = "grpcio_tools-1.70.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1de6c71833d36fb8cc8ac10539681756dc2c5c67e5d4aa4d05adb91ecbdd8474"},
{file = "grpcio_tools-1.70.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ab788afced2d2c59bef86479967ce0b28485789a9f2cc43793bb7aa67f9528b"},
{file = "grpcio_tools-1.70.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:836293dcbb1e59fa52aa8aa890bd7a32a8eea7651cd614e96d86de4f3032fe73"},
{file = "grpcio_tools-1.70.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:740b3741d124c5f390dd50ad1c42c11788882baf3c202cd3e69adee0e3dde559"},
{file = "grpcio_tools-1.70.0-cp38-cp38-win32.whl", hash = "sha256:b9e4a12b862ba5e42d8028da311e8d4a2c307362659b2f4141d0f940f8c12b49"},
{file = "grpcio_tools-1.70.0-cp38-cp38-win_amd64.whl", hash = "sha256:fd04c93af460b1456cd12f8f85502503e1db6c4adc1b7d4bd775b12c1fd94fee"},
{file = "grpcio_tools-1.70.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:52d7e7ef11867fe7de577076b1f2ac6bf106b2325130e3de66f8c364c96ff332"},
{file = "grpcio_tools-1.70.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:0f7ed0372afd9f5eb938334e84681396257015ab92e03de009aa3170e64b24d0"},
{file = "grpcio_tools-1.70.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:24a5b0328ffcfe0c4a9024f302545abdb8d6f24921409a5839f2879555b96fea"},
{file = "grpcio_tools-1.70.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9387b30f3b2f46942fb5718624d7421875a6ce458620d6e15817172d78db1e1a"},
{file = "grpcio_tools-1.70.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4545264e06e1cd7fb21b9447bb5126330bececb4bc626c98f793fda2fd910bf8"},
{file = "grpcio_tools-1.70.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79b723ce30416e8e1d7ff271f97ade79aaf30309a595d80c377105c07f5b20fd"},
{file = "grpcio_tools-1.70.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1c0917dce12af04529606d437def83962d51c59dcde905746134222e94a2ab1b"},
{file = "grpcio_tools-1.70.0-cp39-cp39-win32.whl", hash = "sha256:5cb0baa52d4d44690fac6b1040197c694776a291a90e2d3c369064b4d5bc6642"},
{file = "grpcio_tools-1.70.0-cp39-cp39-win_amd64.whl", hash = "sha256:840ec536ab933db2ef8d5acaa6b712d0e9e8f397f62907c852ec50a3f69cdb78"},
{file = "grpcio_tools-1.70.0.tar.gz", hash = "sha256:e578fee7c1c213c8e471750d92631d00f178a15479fb2cb3b939a07fc125ccd3"},
]
[package.dependencies]
grpcio = ">=1.70.0"
protobuf = ">=5.26.1,<6.0dev"
setuptools = "*"
[[package]]
name = "iniconfig"
version = "2.0.0"
description = "brain-dead simple config-ini parsing"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -214,6 +359,7 @@ files = [
name = "packaging"
version = "24.2"
description = "Core utilities for Python packages"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -225,6 +371,7 @@ files = [
name = "pluggy"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -236,10 +383,32 @@ files = [
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "protobuf"
version = "5.29.3"
description = ""
category = "main"
optional = false
python-versions = ">=3.8"
files = [
{file = "protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888"},
{file = "protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a"},
{file = "protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e"},
{file = "protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84"},
{file = "protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f"},
{file = "protobuf-5.29.3-cp38-cp38-win32.whl", hash = "sha256:84a57163a0ccef3f96e4b6a20516cedcf5bb3a95a657131c5c3ac62200d23252"},
{file = "protobuf-5.29.3-cp38-cp38-win_amd64.whl", hash = "sha256:b89c115d877892a512f79a8114564fb435943b59067615894c3b13cd3e1fa107"},
{file = "protobuf-5.29.3-cp39-cp39-win32.whl", hash = "sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7"},
{file = "protobuf-5.29.3-cp39-cp39-win_amd64.whl", hash = "sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da"},
{file = "protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f"},
{file = "protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620"},
]
[[package]]
name = "psycopg2"
version = "2.9.10"
description = "psycopg2 - Python-PostgreSQL Database Adapter"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -259,6 +428,7 @@ files = [
name = "pydantic"
version = "2.10.5"
description = "Data validation using Python type hints"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -279,6 +449,7 @@ timezone = ["tzdata"]
name = "pydantic-core"
version = "2.27.2"
description = "Core functionality for Pydantic validation and serialization"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -391,6 +562,7 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
name = "pytest"
version = "8.3.4"
description = "pytest: simple powerful testing with Python"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -413,6 +585,7 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments
name = "python-dotenv"
version = "1.0.1"
description = "Read key-value pairs from a .env file and set them as environment variables"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -423,10 +596,32 @@ files = [
[package.extras]
cli = ["click (>=5.0)"]
[[package]]
name = "setuptools"
version = "75.8.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false
python-versions = ">=3.9"
files = [
{file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"},
{file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"},
]
[package.extras]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"]
core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
enabler = ["pytest-enabler (>=2.2)"]
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.14.0,<1.15.0)", "pytest-mypy"]
[[package]]
name = "sqlalchemy"
version = "2.0.37"
description = "Database Abstraction Library"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -490,7 +685,7 @@ files = [
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
greenlet = {version = "!=0.4.17", markers = "python_version < \"3.14\" and platform_machine == \"aarch64\" or python_version < \"3.14\" and platform_machine == \"ppc64le\" or python_version < \"3.14\" and platform_machine == \"x86_64\" or python_version < \"3.14\" and platform_machine == \"amd64\" or python_version < \"3.14\" and platform_machine == \"AMD64\" or python_version < \"3.14\" and platform_machine == \"win32\" or python_version < \"3.14\" and platform_machine == \"WIN32\""}
typing-extensions = ">=4.6.0"
[package.extras]
@ -522,6 +717,7 @@ sqlcipher = ["sqlcipher3_binary"]
name = "tomli"
version = "2.2.1"
description = "A lil' TOML parser"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -563,6 +759,7 @@ files = [
name = "typing-extensions"
version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -573,4 +770,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "bee1ff3c639641805049c37fc57b55a4ef93de7f9dd1585e6bf6c9530852df5f"
content-hash = "f590636643c34629bbb6f7b9bc0b20975fe3c6f86807ebb6408da99aae5a90b6"

View file

@ -13,6 +13,8 @@ sqlalchemy = "^2.0.37"
psycopg2 = "^2.9.10"
python-dotenv = "^1.0.1"
asyncpg = "^0.30.0"
grpcio = "^1.70.0"
grpcio-tools = "^1.70.0"
[build-system]