Playing with asyncio and context managers

This commit is contained in:
pro100ton 2025-01-18 11:27:28 +03:00
parent 1f067d71d7
commit 73d92645ca
7 changed files with 207 additions and 25 deletions

View file

15
dbapi/migrator.py Normal file
View file

@ -0,0 +1,15 @@
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
from dbapi.tables import metadata_obj
class FitnessDatabseMigrator:
"""Class for performing management operations with database"""
def __init__(self, async_engine: AsyncEngine) -> None:
self.engine = async_engine
async def reset_database(self):
"""Method for dropping all tables and create them from tables metadata"""
async with self.engine.begin() as conn:
await conn.run_sync(metadata_obj.drop_all)
await conn.run_sync(metadata_obj.create_all)

66
main.py
View file

@ -1,10 +1,17 @@
import asyncio
from collections import defaultdict from collections import defaultdict
from pprint import pprint from pprint import pprint
import os import os
from typing import Dict, List from typing import Dict, List
from sqlalchemy import Table, create_engine, text, insert from sqlalchemy import Table, create_engine, text, insert
from dotenv import load_dotenv from dotenv import load_dotenv
from dbapi.repositories import ApproachRepository, ExerciseRepository, TrainingRepository from sqlalchemy.ext.asyncio import create_async_engine
from dbapi.migrator import FitnessDatabseMigrator
from dbapi.repositories import (
ApproachRepository,
ExerciseRepository,
TrainingRepository,
)
from dbapi.tables import metadata_obj, training, exercise, approach from dbapi.tables import metadata_obj, training, exercise, approach
from obsidian.notes_parser import parse_training_data, remap_unique_exercises 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 parse_training_data as apple_parse_training_data
@ -22,6 +29,13 @@ engine = create_engine(
echo=True, 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 # NOTE: "Begin once" style - using `.begin` as context creator for SQLAlchemy
# with engine.begin() as conn: # with engine.begin() as conn:
# result = conn.execute(text("select 'hello world'")) # result = conn.execute(text("select 'hello world'"))
@ -44,8 +58,8 @@ engine = create_engine(
# TODO: Check how migrations are done # TODO: Check how migrations are done
# NOTE: Drop all Tables from database # NOTE: Drop all Tables from database
metadata_obj.drop_all(engine) # metadata_obj.drop_all(engine)
metadata_obj.create_all(engine) # metadata_obj.create_all(engine)
# NOTE: Table reflection - generating table object from existing tables (only tables, that are stored in metadata) # 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) # some_table = Table("some_table", metadata_obj, autoload_with=engine)
@ -55,27 +69,27 @@ metadata_obj.create_all(engine)
# Inserting training values into database # Inserting training values into database
trainings: List[Training] = parse_training_data() # trainings: List[Training] = parse_training_data()
for train in trainings: # for train in trainings:
if not train: # if not train:
continue # continue
else: # else:
print(train) # print(train)
new_training_pk: int = TrainingRepository(engine).create_training(train.date) # new_training_pk: int = TrainingRepository(engine).create_training(train.date)
for exr in train.exercises: # for exr in train.exercises:
approach_statements = [] # approach_statements = []
new_exercise_pk: int = ExerciseRepository(engine).create_exercise( # new_exercise_pk: int = ExerciseRepository(engine).create_exercise(
training_pk=new_training_pk, exercise_name=exr.name # training_pk=new_training_pk, exercise_name=exr.name
) # )
for appr in exr.approaches: # for appr in exr.approaches:
new_approach_pk: int = ApproachRepository(engine).create_approach( # new_approach_pk: int = ApproachRepository(engine).create_approach(
exercise_pk=new_exercise_pk, weight=appr.weight, reps=appr.reps # exercise_pk=new_exercise_pk, weight=appr.weight, reps=appr.reps
) # )
#
#
print("-------------------------\n" * 2) # print("-------------------------\n" * 2)
print("-------------------------\n" * 2) # print("-------------------------\n" * 2)
ExerciseRepository(engine=engine).get_exercises_from_training(1) # ExerciseRepository(engine=engine).get_exercises_from_training(1)
# ----- # -----
# Calculating unique exercises for obsidian # Calculating unique exercises for obsidian
@ -170,3 +184,7 @@ ExerciseRepository(engine=engine).get_exercises_from_training(1)
# unique_exercise_parsed_names[exr.name] += 1 # unique_exercise_parsed_names[exr.name] += 1
# pprint(unique_exercise_parsed_names) # pprint(unique_exercise_parsed_names)
# print(len(combined_trainings)) # print(len(combined_trainings))
fbm = FitnessDatabseMigrator(async_engine=async_engine)
asyncio.run(fbm.reset_database())

3
playground/README.md Normal file
View file

@ -0,0 +1,3 @@
# Directory for testing hypotheses
## context.py
File for testing context operations

66
playground/contexts.py Normal file
View file

@ -0,0 +1,66 @@
import asyncio
class ContextPlayground:
def __init__(self) -> None:
self.keka = "Hi, i am keka"
def __enter__(self):
print("Entered context")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
print(f"\tException raised. Type: {exc_type}")
if exc_val:
print(f"\tException raised. Value: {exc_val}")
if exc_tb:
print(f"\tException raised. Traceback: {exc_tb}")
print("\tExiting context")
# Returning True means that context manager will supress the exception and it will not be propagated outside
# the with block. IOW it indicated, that exception is going to be handled inside context
return True
# Returning False means that the exception will be propagated and it will be raised outside the with block
# IOW it means that the exception will not be handled inside context manager and must be handled by the caller
# Note from documentation: __exit__() methods should not reraise the passed-in exception;
# this is the callers responsibility.
# return False # Or return None
def main():
with ContextPlayground() as kek:
print(f"\t\t{kek.keka}")
print("\t\tInside context")
print("Outside context")
class AsyncContextPlayground:
"""Asyncio version of ContextPlayground"""
def __init__(self) -> None:
self.keka = "Hi, i am async Keka"
async def __aenter__(self) -> None:
print("\tEntered async context")
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if exc_type:
print(f"\t\tException raised. Type: {exc_type}")
if exc_val:
print(f"\t\tException raised. Value: {exc_val}")
if exc_tb:
print(f"\t\tException raised. Traceback: {exc_tb}")
print("\tExiting async context")
return True
async def async_main():
async with AsyncContextPlayground() as kek:
print(f"\t\t{kek.keka}")
print("\t\tInside async context")
raise ValueError("An error occurred") # Uncomment to test exception handling
print("Outside async context")
if __name__ == "__main__":
asyncio.run(async_main())

81
poetry.lock generated
View file

@ -12,6 +12,85 @@ files = [
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
] ]
[[package]]
name = "async-timeout"
version = "5.0.1"
description = "Timeout context manager for asyncio programs"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
{file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"},
{file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"},
]
[[package]]
name = "asyncpg"
version = "0.30.0"
description = "An asyncio PostgreSQL driver"
category = "main"
optional = false
python-versions = ">=3.8.0"
files = [
{file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"},
{file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"},
{file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3152fef2e265c9c24eec4ee3d22b4f4d2703d30614b0b6753e9ed4115c8a146f"},
{file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7255812ac85099a0e1ffb81b10dc477b9973345793776b128a23e60148dd1af"},
{file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:578445f09f45d1ad7abddbff2a3c7f7c291738fdae0abffbeb737d3fc3ab8b75"},
{file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c42f6bb65a277ce4d93f3fba46b91a265631c8df7250592dd4f11f8b0152150f"},
{file = "asyncpg-0.30.0-cp310-cp310-win32.whl", hash = "sha256:aa403147d3e07a267ada2ae34dfc9324e67ccc4cdca35261c8c22792ba2b10cf"},
{file = "asyncpg-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb622c94db4e13137c4c7f98834185049cc50ee01d8f657ef898b6407c7b9c50"},
{file = "asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a"},
{file = "asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed"},
{file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a"},
{file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956"},
{file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056"},
{file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454"},
{file = "asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d"},
{file = "asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f"},
{file = "asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e"},
{file = "asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a"},
{file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3"},
{file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737"},
{file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a"},
{file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af"},
{file = "asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e"},
{file = "asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305"},
{file = "asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70"},
{file = "asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3"},
{file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33"},
{file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4"},
{file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4"},
{file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba"},
{file = "asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590"},
{file = "asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e"},
{file = "asyncpg-0.30.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29ff1fc8b5bf724273782ff8b4f57b0f8220a1b2324184846b39d1ab4122031d"},
{file = "asyncpg-0.30.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64e899bce0600871b55368b8483e5e3e7f1860c9482e7f12e0a771e747988168"},
{file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b290f4726a887f75dcd1b3006f484252db37602313f806e9ffc4e5996cfe5cb"},
{file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f86b0e2cd3f1249d6fe6fd6cfe0cd4538ba994e2d8249c0491925629b9104d0f"},
{file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:393af4e3214c8fa4c7b86da6364384c0d1b3298d45803375572f415b6f673f38"},
{file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fd4406d09208d5b4a14db9a9dbb311b6d7aeeab57bded7ed2f8ea41aeef39b34"},
{file = "asyncpg-0.30.0-cp38-cp38-win32.whl", hash = "sha256:0b448f0150e1c3b96cb0438a0d0aa4871f1472e58de14a3ec320dbb2798fb0d4"},
{file = "asyncpg-0.30.0-cp38-cp38-win_amd64.whl", hash = "sha256:f23b836dd90bea21104f69547923a02b167d999ce053f3d502081acea2fba15b"},
{file = "asyncpg-0.30.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f4e83f067b35ab5e6371f8a4c93296e0439857b4569850b178a01385e82e9ad"},
{file = "asyncpg-0.30.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5df69d55add4efcd25ea2a3b02025b669a285b767bfbf06e356d68dbce4234ff"},
{file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3479a0d9a852c7c84e822c073622baca862d1217b10a02dd57ee4a7a081f708"},
{file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26683d3b9a62836fad771a18ecf4659a30f348a561279d6227dab96182f46144"},
{file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1b982daf2441a0ed314bd10817f1606f1c28b1136abd9e4f11335358c2c631cb"},
{file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1c06a3a50d014b303e5f6fc1e5f95eb28d2cee89cf58384b700da621e5d5e547"},
{file = "asyncpg-0.30.0-cp39-cp39-win32.whl", hash = "sha256:1b11a555a198b08f5c4baa8f8231c74a366d190755aa4f99aacec5970afe929a"},
{file = "asyncpg-0.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:8b684a3c858a83cd876f05958823b68e8d14ec01bb0c0d14a6704c5bf9711773"},
{file = "asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851"},
]
[package.dependencies]
async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.11.0\""}
[package.extras]
docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"]
gssauth = ["gssapi", "sspilib"]
test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"]
[[package]] [[package]]
name = "colorama" name = "colorama"
version = "0.4.6" version = "0.4.6"
@ -511,4 +590,4 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "19f999486896e58ce5aad4e148e87fb8a81e4178b8dd86ad7d40049b4f892131" content-hash = "bee1ff3c639641805049c37fc57b55a4ef93de7f9dd1585e6bf6c9530852df5f"

View file

@ -12,6 +12,7 @@ pytest = "^8.3.4"
sqlalchemy = "^2.0.37" sqlalchemy = "^2.0.37"
psycopg2 = "^2.9.10" psycopg2 = "^2.9.10"
python-dotenv = "^1.0.1" python-dotenv = "^1.0.1"
asyncpg = "^0.30.0"
[build-system] [build-system]