From 73d92645ca463e1c65b8e27825ebfee7f7656f93 Mon Sep 17 00:00:00 2001 From: pro100ton Date: Sat, 18 Jan 2025 11:27:28 +0300 Subject: [PATCH] Playing with asyncio and context managers --- dbapi/context_managers.py | 0 dbapi/migrator.py | 15 ++++++++ main.py | 66 +++++++++++++++++++------------ playground/README.md | 3 ++ playground/contexts.py | 66 +++++++++++++++++++++++++++++++ poetry.lock | 81 ++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 7 files changed, 207 insertions(+), 25 deletions(-) create mode 100644 dbapi/context_managers.py create mode 100644 dbapi/migrator.py create mode 100644 playground/README.md create mode 100644 playground/contexts.py diff --git a/dbapi/context_managers.py b/dbapi/context_managers.py new file mode 100644 index 0000000..e69de29 diff --git a/dbapi/migrator.py b/dbapi/migrator.py new file mode 100644 index 0000000..1c805f3 --- /dev/null +++ b/dbapi/migrator.py @@ -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) diff --git a/main.py b/main.py index b2098f1..dfc586e 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,17 @@ +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 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 obsidian.notes_parser import parse_training_data, remap_unique_exercises from apple.notes_parser import parse_training_data as apple_parse_training_data @@ -22,6 +29,13 @@ engine = create_engine( 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'")) @@ -44,8 +58,8 @@ engine = create_engine( # TODO: Check how migrations are done # NOTE: Drop all Tables from database -metadata_obj.drop_all(engine) -metadata_obj.create_all(engine) +# 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) @@ -55,27 +69,27 @@ metadata_obj.create_all(engine) # 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) -ExerciseRepository(engine=engine).get_exercises_from_training(1) +# 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) +# ExerciseRepository(engine=engine).get_exercises_from_training(1) # ----- # Calculating unique exercises for obsidian @@ -170,3 +184,7 @@ ExerciseRepository(engine=engine).get_exercises_from_training(1) # unique_exercise_parsed_names[exr.name] += 1 # pprint(unique_exercise_parsed_names) # print(len(combined_trainings)) + +fbm = FitnessDatabseMigrator(async_engine=async_engine) + +asyncio.run(fbm.reset_database()) diff --git a/playground/README.md b/playground/README.md new file mode 100644 index 0000000..a7cb4ba --- /dev/null +++ b/playground/README.md @@ -0,0 +1,3 @@ +# Directory for testing hypotheses +## context.py +File for testing context operations diff --git a/playground/contexts.py b/playground/contexts.py new file mode 100644 index 0000000..f0b52c1 --- /dev/null +++ b/playground/contexts.py @@ -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 caller’s 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()) + diff --git a/poetry.lock b/poetry.lock index acc6f3a..968e47a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -12,6 +12,85 @@ files = [ {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]] name = "colorama" version = "0.4.6" @@ -511,4 +590,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "19f999486896e58ce5aad4e148e87fb8a81e4178b8dd86ad7d40049b4f892131" +content-hash = "bee1ff3c639641805049c37fc57b55a4ef93de7f9dd1585e6bf6c9530852df5f" diff --git a/pyproject.toml b/pyproject.toml index b5017b4..1962a1f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ pytest = "^8.3.4" sqlalchemy = "^2.0.37" psycopg2 = "^2.9.10" python-dotenv = "^1.0.1" +asyncpg = "^0.30.0" [build-system]