From 6a24b3ca0f9459817039e317f22d04db25c77368 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 16:56:19 +0900 Subject: [PATCH 01/36] add: add `SQLAlchemy` and `alembic` as deps --- poetry.lock | 198 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 + 2 files changed, 199 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index a475288..d5f2021 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,24 @@ # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + [[package]] name = "black" version = "23.12.1" @@ -163,6 +182,77 @@ files = [ [package.dependencies] python-dateutil = ">=2.7" +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -205,6 +295,25 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "mako" +version = "1.3.0" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.0-py3-none-any.whl", hash = "sha256:57d4e997349f1a92035aa25c17ace371a4213f2ca42f99bee9a602500cfd54d9"}, + {file = "Mako-1.3.0.tar.gz", hash = "sha256:e3a9d388fd00e87043edbe8792f45880ac0114e9c4adc69f6e9bfb2c55e3b11b"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + [[package]] name = "markupsafe" version = "2.1.3" @@ -645,6 +754,93 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "sqlalchemy" +version = "2.0.25" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-win32.whl", hash = "sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-win_amd64.whl", hash = "sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-win32.whl", hash = "sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-win32.whl", hash = "sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-win_amd64.whl", hash = "sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-win32.whl", hash = "sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-win_amd64.whl", hash = "sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-win32.whl", hash = "sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-win_amd64.whl", hash = "sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-win32.whl", hash = "sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-win_amd64.whl", hash = "sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7"}, + {file = "SQLAlchemy-2.0.25-py3-none-any.whl", hash = "sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3"}, + {file = "SQLAlchemy-2.0.25.tar.gz", hash = "sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "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\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + [[package]] name = "typing-extensions" version = "4.9.0" @@ -670,4 +866,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "20d3e064dab961246dbe38294b81208e682af71cc33a924bdfb94751a9697694" +content-hash = "d470c56d34ca4f6651e641995a6fc6ff13e5e09cc0d7777686a3f57704521631" diff --git a/pyproject.toml b/pyproject.toml index fce751b..f228ab8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,8 @@ readme = "README.md" setuptools = "^69.0.3" python = "^3.12" python-dotenv = "^1.0.0" +sqlalchemy = "^2.0.25" +alembic = "^1.13.1" [tool.poetry.group.dev.dependencies] mypy = "^1.8.0" From 2e8cf065902dfbd1f2ba47a70d6563a66fb71aea Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 16:57:13 +0900 Subject: [PATCH 02/36] add: add `alembic.ini` --- alembic.ini | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 alembic.ini diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..3c14307 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,114 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = migration + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python-dateutil library that can be +# installed by adding `alembic[tz]` to the pip requirements +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to migration/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:migration/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S From 8aab865b6f55e6155f7fafab42162609cc755286 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 16:57:48 +0900 Subject: [PATCH 03/36] add: add version template of alembic, `script.py.mako` --- migration/script.py.mako | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 migration/script.py.mako diff --git a/migration/script.py.mako b/migration/script.py.mako new file mode 100644 index 0000000..e4c96cc --- /dev/null +++ b/migration/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +import src + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} From 449ac380cce7304ac62ed7ba602c0f52e0696329 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 16:59:03 +0900 Subject: [PATCH 04/36] add: add `pydantic` and `pydantic-settings` as deps --- poetry.lock | 164 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 + 2 files changed, 165 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index d5f2021..b89ad44 100644 --- a/poetry.lock +++ b/poetry.lock @@ -19,6 +19,17 @@ typing-extensions = ">=4" [package.extras] tz = ["backports.zoneinfo"] +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + [[package]] name = "black" version = "23.12.1" @@ -548,6 +559,157 @@ files = [ {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, ] +[[package]] +name = "pydantic" +version = "2.5.3" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, + {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.14.6" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.14.6" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, + {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, + {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, + {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, + {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, + {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, + {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, + {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, + {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, + {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, + {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, + {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, + {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, + {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, + {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, + {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, + {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, + {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, + {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, + {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, + {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, + {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, + {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, + {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, + {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, + {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, + {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, + {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, + {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, + {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, + {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, + {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, + {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, + {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, + {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-settings" +version = "2.1.0" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.1.0-py3-none-any.whl", hash = "sha256:7621c0cb5d90d1140d2f0ef557bdf03573aac7035948109adf2574770b77605a"}, + {file = "pydantic_settings-2.1.0.tar.gz", hash = "sha256:26b1492e0a24755626ac5e6d715e9077ab7ad4fb5f19a8b7ed7011d52f36141c"}, +] + +[package.dependencies] +pydantic = ">=2.3.0" +python-dotenv = ">=0.21.0" + [[package]] name = "pyflakes" version = "3.1.0" @@ -866,4 +1028,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "d470c56d34ca4f6651e641995a6fc6ff13e5e09cc0d7777686a3f57704521631" +content-hash = "41dc064a5c99f5949715631737683e93fb479f1555c3e398790f2f528f23dcb9" diff --git a/pyproject.toml b/pyproject.toml index f228ab8..a3d7851 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,8 @@ readme = "README.md" setuptools = "^69.0.3" python = "^3.12" python-dotenv = "^1.0.0" +pydantic = "^2.5.3" +pydantic-settings = "^2.1.0" sqlalchemy = "^2.0.25" alembic = "^1.13.1" From 18908073a4dc470e1d3c51ec245022f987598b05 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 17:01:07 +0900 Subject: [PATCH 05/36] add: add `GenkaiEraConfig` for config --- config.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 config.py diff --git a/config.py b/config.py new file mode 100644 index 0000000..d6aead6 --- /dev/null +++ b/config.py @@ -0,0 +1,42 @@ +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class GenkaiEraConfig(BaseSettings): + model_config = SettingsConfigDict(env_prefix="GENKAIERA_", case_sensitive=True) + + IS_CI: int = 0 + IS_TEST: int = 0 + + POSTGRES_HOST: str + POSTGRES_PORT: str + POSTGRES_DB_NAME: str = "genkaiera" + POSTGRES_USER: str + POSTGRES_PASSWORD: str + + POSTGRES_CONNECT_ARGS: dict[str, str] = {} + POSTGRES_EXPIRE_ON_COMMIT: bool = False + POSTGRES_AUTOCOMMIT: bool = False + POSTGRES_AUTOFLUSH: bool = False + + def get_postgres_dsn(self) -> str: + result: str = ( + f"postgresql+asyncpg://" + f"{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@" + f"{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/" + f"{self.POSTGRES_DB_NAME}" + ) + + return result + + +test_settings = SettingsConfigDict( + env_prefix="GENKAIERA_", + case_sensitive=True, + env_file="./test.env", +) + + +class GenkaiEraTestConfig(GenkaiEraConfig): + model_config = test_settings + + POSTGRES_DB_NAME: str = "genkaiera_test" From 95a513360fcd65f96d9a0d02d95de283560625ef Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 17:02:19 +0900 Subject: [PATCH 06/36] add: add `/script/database.py` for database check --- script/database.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 script/database.py diff --git a/script/database.py b/script/database.py new file mode 100644 index 0000000..7ee0c98 --- /dev/null +++ b/script/database.py @@ -0,0 +1,68 @@ +import argparse +import os +import subprocess +import time + +from load_env import load_dotenv + +RUNCOMMAND_PATH = "script/run_command.py" + +parser = argparse.ArgumentParser() +parser.add_argument("--online", action="store_true") +parser.add_argument("--max_retries", type=int, default=5) +parser.add_argument("positional_arg", nargs="*") + +args = parser.parse_args() +load_dotenv() + +if args.online: + print("Online mode. The `--online` flag is enabled.") +else: + print("Offline mode. The `--online` flag is disabled.") + + +USER = "genkaiera_api" +DB = "genkaiera" +PASSWORD = os.environ.get("GENKAIERA_POSTGRES_PASSWORD") +HOST = os.environ.get("GENKAIERA_POSTGRES_HOST") +PORT = os.environ.get("GENKAIERA_POSTGRES_PORT") + +max_retries = args.max_retries + +for i in range(max_retries): + try: + subprocess.check_call( + [ + "python", + RUNCOMMAND_PATH, + "psql", + f"user={USER} password={PASSWORD} host={HOST} port={PORT} dbname={DB}", + "-c", + r"\q", + ] + ) + print("DB is ready!") + break + except subprocess.CalledProcessError: + if i < max_retries: + print( + f"DB is not ready yet. Waiting for 5 seconds... (Retry: {i}/{max_retries})" + ) + time.sleep(5) + else: + print("DB is unavailable after maximum retries. Exiting...") + exit(1) + +target_dbs = [f"{DB}_test", DB] + +for target_db in target_dbs: + print(f"Applying migrations for {target_db}...") + env = os.environ.copy() + env["GENKAIERA_POSTGRES_DB_NAME"] = target_db + + if args.online: + subprocess.check_call(["alembic", "upgrade", "head"], env=env) + else: + subprocess.check_call(["alembic", "upgrade", "head", "--sql"], env=env) + + print(f"Migrations applied for {target_db}!") From e2bb74301312c8d6efbc884a5e4322cd4cd941af Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 17:04:05 +0900 Subject: [PATCH 07/36] add: add migration related commands in `Makefile` --- Makefile | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Makefile b/Makefile index dda54fc..32e62bb 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,10 @@ build: up: docker compose up -d --build +.PHONY: init +init: + $(MAKE) online_migrate + .PHONY: down down: docker compose down @@ -61,6 +65,18 @@ logs: shell: docker compose run --rm app bash +.PHONY: online_migrate +online_migrate: + docker compose run --rm app bash -c "python ./script/database.py --online" + +.PHONY: offline_migrate +offline_migrate: + docker compose run --rm app bash -c "python ./script/database.py" + +.PHONY: make_migration +make_migration: + docker compose run --rm app bash -c "python r./script/un_command.py alembic revision --autogenerate" + .PHONY: flake8 flake8: docker compose run --rm app bash -c "python ./script/run_command.py flake8 ./" @@ -87,13 +103,16 @@ isort_check: .PHONY: pytest_html pytest_html: + $(MAKE) online_migrate docker compose run --rm app bash -c "python ./script/run_command.py pytest -v ./test/ --cov=./src/ --cov-report=html --html=report.html" .PHONY: pytest_xml pytest_xml: + $(MAKE) online_migrate docker compose run --rm app bash -c "python ./script/run_command.py pytest -v ./test/ --cov=./src/ --cov-report=xml" .PHONY: pytest_ci pytest_ci: $(MAKE) up + $(MAKE) online_migrate docker compose run --rm app bash -c "python r./script/un_command.py pytest -v ./test/ --cov=./src/ --junitxml=pytest.xml --cov-report=term-missing:skip-covered | tee pytest-coverage.txt" From 3e19a7e7620167202221d2c7bceae609bd63feca Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:29:48 +0900 Subject: [PATCH 08/36] feat: add new util function, `utcnow()` --- src/common/__init__.py | 0 src/common/util/__init__.py | 0 src/common/util/utcnow.py | 10 ++++++++++ 3 files changed, 10 insertions(+) create mode 100644 src/common/__init__.py create mode 100644 src/common/util/__init__.py create mode 100644 src/common/util/utcnow.py diff --git a/src/common/__init__.py b/src/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/util/__init__.py b/src/common/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/util/utcnow.py b/src/common/util/utcnow.py new file mode 100644 index 0000000..59e2600 --- /dev/null +++ b/src/common/util/utcnow.py @@ -0,0 +1,10 @@ +import datetime + + +def utcnow() -> datetime.datetime: + """ + Return the current time in UTC. + """ + now_in_utc: datetime.datetime = datetime.datetime.now(tz=datetime.timezone.utc) + + return now_in_utc From 9959726533dd5f23ba1699897e58090b70bddc88 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:30:37 +0900 Subject: [PATCH 09/36] feat: add a new type decorator for sqlalchemy, `SAUUID` --- src/common/library/__init__.py | 0 .../library/sqlalchemy/field/__init__.py | 0 src/common/library/sqlalchemy/field/sauuid.py | 47 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/common/library/__init__.py create mode 100644 src/common/library/sqlalchemy/field/__init__.py create mode 100644 src/common/library/sqlalchemy/field/sauuid.py diff --git a/src/common/library/__init__.py b/src/common/library/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/library/sqlalchemy/field/__init__.py b/src/common/library/sqlalchemy/field/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/library/sqlalchemy/field/sauuid.py b/src/common/library/sqlalchemy/field/sauuid.py new file mode 100644 index 0000000..c070cca --- /dev/null +++ b/src/common/library/sqlalchemy/field/sauuid.py @@ -0,0 +1,47 @@ +import uuid +from typing import Any, Optional +from uuid import UUID + +from sqlalchemy import Dialect, String, types + + +# Name this class "SAUUID" to avoid name conflicts with the standard package uuid.UUID +class SAUUID(types.TypeDecorator[uuid.UUID]): + impl = String + cache_ok: bool = True + + def __init__(self, *args: Any, **kwargs: Any) -> None: + _ = args, kwargs + + self.impl.length = 36 + types.TypeDecorator.__init__(self, length=self.impl.length) + + def process_bind_param( + self, value: Optional[UUID], dialect: Optional[Dialect] = None + ) -> Optional[str]: + if value is None: + return None + + if isinstance(value, UUID): + return str(value) + + raise ValueError( + f"ValueError: value '{value}' is not a valid instance of 'uuid.UUID'." + ) + + def process_result_value( + self, value: Optional[str], dialect: Optional[Dialect] = None + ) -> Optional[UUID]: + if value is None: + return None + + if isinstance(value, str): + return UUID(value) + + raise ValueError( + f"ValueError: value '{value}' is not 'None' or a valid instance of 'uuid.UUID'." + ) + + # noinspection PyMethodMayBeStatic + def is_mutable(self) -> bool: + return False From 0276411de3d0d5286ccafb2a5d063476645e9481 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:31:13 +0900 Subject: [PATCH 10/36] feat: add a new mixin for sqlalchemy, `UIDMixin` --- src/common/library/sqlalchemy/mixin/__init__.py | 0 src/common/library/sqlalchemy/mixin/uid.py | 17 +++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/common/library/sqlalchemy/mixin/__init__.py create mode 100644 src/common/library/sqlalchemy/mixin/uid.py diff --git a/src/common/library/sqlalchemy/mixin/__init__.py b/src/common/library/sqlalchemy/mixin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/library/sqlalchemy/mixin/uid.py b/src/common/library/sqlalchemy/mixin/uid.py new file mode 100644 index 0000000..3866928 --- /dev/null +++ b/src/common/library/sqlalchemy/mixin/uid.py @@ -0,0 +1,17 @@ +import uuid + +from sqlalchemy.orm import Mapped, mapped_column + +from src.common.library.sqlalchemy.field.sauuid import SAUUID + +DEFAULT_UID_COLUMN_NAME: str = "uid" + + +class UIDMixin(object): + uid: Mapped[uuid.UUID] = mapped_column( + name=DEFAULT_UID_COLUMN_NAME, + type_=SAUUID, + primary_key=True, + default=uuid.uuid4, + nullable=False, + ) From b61a2415d1e5dcf374c91adee74f84dbd9b68a28 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:31:41 +0900 Subject: [PATCH 11/36] feat: add a new mixin for sqlalchemy, `TimeStampMixin` --- .../library/sqlalchemy/mixin/timestamp.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/common/library/sqlalchemy/mixin/timestamp.py diff --git a/src/common/library/sqlalchemy/mixin/timestamp.py b/src/common/library/sqlalchemy/mixin/timestamp.py new file mode 100644 index 0000000..99c148e --- /dev/null +++ b/src/common/library/sqlalchemy/mixin/timestamp.py @@ -0,0 +1,44 @@ +import datetime + +from sqlalchemy import DateTime, func +from sqlalchemy.orm import Mapped, mapped_column + +DEFAULT_TIMEZONE: datetime.timezone = datetime.timezone.utc + +DEFAULT_CREATED_AT_COLUMN_NAME: str = "created_at" +DEFAULT_UPDATED_AT_COLUMN_NAME: str = "updated_at" + + +class CreatedAtMixin(object): + created_at: Mapped[datetime.datetime] = mapped_column( + name=DEFAULT_CREATED_AT_COLUMN_NAME, + type_=DateTime(timezone=True), + server_default=func.now(), + nullable=False, + ) + + +class UpdatedAtMixin(object): + updated_at: Mapped[datetime.datetime] = mapped_column( + name=DEFAULT_UPDATED_AT_COLUMN_NAME, + type_=DateTime(timezone=True), + onupdate=func.now(), + nullable=False, + ) + + +# We get an error when we do 'class TimestampMixin(CreatedAtMixin, UpdatedAtMixin)' +class TimestampMixin(object): + created_at: Mapped[datetime.datetime] = mapped_column( + name=DEFAULT_CREATED_AT_COLUMN_NAME, + type_=DateTime(timezone=True), + server_default=func.now(), + nullable=False, + ) + + updated_at: Mapped[datetime.datetime] = mapped_column( + name=DEFAULT_UPDATED_AT_COLUMN_NAME, + type_=DateTime(timezone=True), + onupdate=func.now(), + nullable=False, + ) From d6a245171abda8ea7158586197f0ce69b1ad85ad Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:34:54 +0900 Subject: [PATCH 12/36] feat: add a new utils for migration, `migrate.py` --- src/common/library/sqlalchemy/__init__.py | 0 src/common/library/sqlalchemy/util/__init__.py | 0 src/common/library/sqlalchemy/util/migrate.py | 18 ++++++++++++++++++ 3 files changed, 18 insertions(+) create mode 100644 src/common/library/sqlalchemy/__init__.py create mode 100644 src/common/library/sqlalchemy/util/__init__.py create mode 100644 src/common/library/sqlalchemy/util/migrate.py diff --git a/src/common/library/sqlalchemy/__init__.py b/src/common/library/sqlalchemy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/library/sqlalchemy/util/__init__.py b/src/common/library/sqlalchemy/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/library/sqlalchemy/util/migrate.py b/src/common/library/sqlalchemy/util/migrate.py new file mode 100644 index 0000000..2b3f974 --- /dev/null +++ b/src/common/library/sqlalchemy/util/migrate.py @@ -0,0 +1,18 @@ +from sqlalchemy.ext.asyncio import AsyncEngine +from sqlalchemy.orm.decl_api import DeclarativeMeta + + +async def create_all(engine_to_bind: AsyncEngine, base: DeclarativeMeta) -> None: + async with engine_to_bind.begin() as conn: + await conn.run_sync(base.metadata.create_all) + + +async def drop_all(engine_to_bind: AsyncEngine, base: DeclarativeMeta) -> None: + async with engine_to_bind.begin() as conn: + await conn.run_sync(base.metadata.drop_all) + + +async def reset_all(engine_to_bind: AsyncEngine, base: DeclarativeMeta) -> None: + async with engine_to_bind.begin() as conn: + await conn.run_sync(base.metadata.drop_all) + await conn.run_sync(base.metadata.create_all) From 869055995641fb0f165d4c5c3d7e64c2e6ebe5b9 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:35:26 +0900 Subject: [PATCH 13/36] feat: add `ConfigDict`s for pydantic --- src/common/library/pydantic/__init__.py | 0 src/common/library/pydantic/config.py | 16 ++++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/common/library/pydantic/__init__.py create mode 100644 src/common/library/pydantic/config.py diff --git a/src/common/library/pydantic/__init__.py b/src/common/library/pydantic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/library/pydantic/config.py b/src/common/library/pydantic/config.py new file mode 100644 index 0000000..42472a3 --- /dev/null +++ b/src/common/library/pydantic/config.py @@ -0,0 +1,16 @@ +from pydantic.config import ConfigDict + +BaseConfig = ConfigDict( + validate_default=True, + validate_assignment=True, + extra="forbid", + use_enum_values=True, +) + +FrozenConfig = ConfigDict( + validate_default=True, + validate_assignment=True, + extra="forbid", + use_enum_values=True, + frozen=True, +) From 49c8e679c2103ddc6f18b51d137e5ff7bed6a338 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:35:49 +0900 Subject: [PATCH 14/36] feat: add a new validator for pydantic, `datetime_must_be_utc()` --- src/common/library/pydantic/validator/__init__.py | 0 src/common/library/pydantic/validator/utc.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 src/common/library/pydantic/validator/__init__.py create mode 100644 src/common/library/pydantic/validator/utc.py diff --git a/src/common/library/pydantic/validator/__init__.py b/src/common/library/pydantic/validator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/library/pydantic/validator/utc.py b/src/common/library/pydantic/validator/utc.py new file mode 100644 index 0000000..eeb42f9 --- /dev/null +++ b/src/common/library/pydantic/validator/utc.py @@ -0,0 +1,15 @@ +import datetime + + +def datetime_must_be_utc(value: datetime.datetime) -> None: + if not hasattr(value, "tzinfo"): + raise ValueError( + "ValueError: `datetime.datetime` value must be UTC, " + "but the object doesn't have attribute 'tzinfo'.\n" + ) + + if value.tzinfo != datetime.timezone.utc: + raise ValueError( + f"ValueError: `datetime.datetime` value must be UTC, but got {value.tzinfo}.\n" + f"(tzinfo: {value.tzinfo})" + ) From 3f9aabfa6355820a70330ffbff69182a47344c3c Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:36:16 +0900 Subject: [PATCH 15/36] feat: add `SingletonMeta` --- src/common/design/__init__.py | 0 src/common/design/singleton.py | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 src/common/design/__init__.py create mode 100644 src/common/design/singleton.py diff --git a/src/common/design/__init__.py b/src/common/design/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/common/design/singleton.py b/src/common/design/singleton.py new file mode 100644 index 0000000..658a5c5 --- /dev/null +++ b/src/common/design/singleton.py @@ -0,0 +1,8 @@ +class SingletonMeta(type): + _instances = {} # type: ignore + + def __call__(cls, *args, **kwargs): # type: ignore + if cls not in cls._instances: + cls._instances[cls] = super(SingletonMeta, cls).__call__(*args, **kwargs) + + return cls._instances[cls] From 797abec9dba278fbdcadf014c7d02e9e1471dffa Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:36:32 +0900 Subject: [PATCH 16/36] feat: add `Interface` and `InterfaceMeta` --- src/common/design/interface.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/common/design/interface.py diff --git a/src/common/design/interface.py b/src/common/design/interface.py new file mode 100644 index 0000000..ca24b4c --- /dev/null +++ b/src/common/design/interface.py @@ -0,0 +1,4 @@ +from abc import ABC, ABCMeta + +Interface = ABC +InterfaceMeta = ABCMeta From 22ca98b599b0af12922438c0237ac29ff02f2ec5 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:38:47 +0900 Subject: [PATCH 17/36] feat: add `AsyncSessionWrapperInterface` and `AsyncSessionManagerInterface` --- src/adapter/__init__.py | 0 src/adapter/session.py | 47 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/adapter/__init__.py create mode 100644 src/adapter/session.py diff --git a/src/adapter/__init__.py b/src/adapter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/adapter/session.py b/src/adapter/session.py new file mode 100644 index 0000000..48fd532 --- /dev/null +++ b/src/adapter/session.py @@ -0,0 +1,47 @@ +from abc import abstractmethod +from contextlib import asynccontextmanager +from typing import AsyncIterator + +from src.common.design.interface import Interface + + +class AsyncSessionWrapperInterface[RawSessionType](Interface): + @abstractmethod + async def commit( + self, + ) -> None: + raise NotImplementedError() + + @abstractmethod + async def rollback( + self, + ) -> None: + raise NotImplementedError() + + @abstractmethod + async def close( + self, + ) -> None: + raise NotImplementedError() + + @abstractmethod + def get_raw_session( + self, + ) -> RawSessionType: + raise NotImplementedError() + + @abstractmethod + def set_raw_session( + self, + raw_session: RawSessionType, + ) -> None: + raise NotImplementedError() + + +class AsyncSessionManagerInterface[RawSessionType](Interface): + @asynccontextmanager + @abstractmethod + async def get_session( + self, + ) -> AsyncIterator[AsyncSessionWrapperInterface[RawSessionType]]: + yield NotImplementedError() # type: ignore From 0fa3c1c6dfea121952732e7765efe8180c07c0a1 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:39:16 +0900 Subject: [PATCH 18/36] feat: add `Repository` --- src/adapter/repository/__init__.py | 0 src/adapter/repository/repository.py | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/adapter/repository/__init__.py create mode 100644 src/adapter/repository/repository.py diff --git a/src/adapter/repository/__init__.py b/src/adapter/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/adapter/repository/repository.py b/src/adapter/repository/repository.py new file mode 100644 index 0000000..04eee53 --- /dev/null +++ b/src/adapter/repository/repository.py @@ -0,0 +1,27 @@ +from typing import Optional + +from src.adapter.session import AsyncSessionWrapperInterface +from src.common.design.interface import Interface + + +class Repository[RawSessionType](Interface): + __session_wrapper: Optional[AsyncSessionWrapperInterface[RawSessionType]] = None + + def set_session_wrapper( + self, + session: AsyncSessionWrapperInterface[RawSessionType], + ) -> None: + self.__session_wrapper: AsyncSessionWrapperInterface[RawSessionType] = session + + def get_session_wrapper( + self, + ) -> AsyncSessionWrapperInterface[RawSessionType]: + if self.__session_wrapper is None: + raise RuntimeError("'self.__session' is not set") + + return self.__session_wrapper + + def get_raw_session( + self, + ) -> RawSessionType: + return self.get_session_wrapper().get_raw_session() From 3f22b7900dcd147487e4932f6d3c779e1b7460a0 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:41:19 +0900 Subject: [PATCH 19/36] add(deps): add `injectpr` as a dep --- poetry.lock | 16 +++++++++++++++- pyproject.toml | 3 ++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index b89ad44..bb1a66d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -275,6 +275,20 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "injector" +version = "0.21.0" +description = "Injector - Python dependency injection framework, inspired by Guice" +optional = false +python-versions = "*" +files = [ + {file = "injector-0.21.0-py2.py3-none-any.whl", hash = "sha256:3942c5e4c501d390d5ad1b8d7d486ef93b844934afeeb17211acb6c4ca29eeb4"}, + {file = "injector-0.21.0.tar.gz", hash = "sha256:919eb6b9f96f40bf98fda34c79762b217bd1544d9adc35805ff2948e92356c9c"}, +] + +[package.extras] +dev = ["black (==23.3.0)", "build (==0.10.0)", "check-manifest (==0.49)", "click (==8.1.3)", "coverage (==7.2.7)", "exceptiongroup (==1.1.1)", "iniconfig (==2.0.0)", "mypy (==1.4.1)", "mypy-extensions (==1.0.0)", "packaging (==23.1)", "pathspec (==0.11.1)", "platformdirs (==3.8.0)", "pluggy (==1.2.0)", "pyproject-hooks (==1.0.0)", "pytest (==7.4.0)", "pytest-cov (==4.1.0)", "tomli (==2.0.1)", "typing-extensions (==4.7.0)"] + [[package]] name = "isort" version = "5.13.2" @@ -1028,4 +1042,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "41dc064a5c99f5949715631737683e93fb479f1555c3e398790f2f528f23dcb9" +content-hash = "7abf377221abb0ed8fc8d5e576e50b56d01d50dc0372c90831450637f4c73577" diff --git a/pyproject.toml b/pyproject.toml index a3d7851..aa4c2e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,11 +9,12 @@ readme = "README.md" [tool.poetry.dependencies] setuptools = "^69.0.3" python = "^3.12" -python-dotenv = "^1.0.0" +injector = "^0.21.0" pydantic = "^2.5.3" pydantic-settings = "^2.1.0" sqlalchemy = "^2.0.25" alembic = "^1.13.1" +python-dotenv = "^1.0.0" [tool.poetry.group.dev.dependencies] mypy = "^1.8.0" From bb50c56583de15ad9a19ab4a100c74208ab18d9b Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:43:13 +0900 Subject: [PATCH 20/36] feat: add `SABaseModel` --- src/__init__.py | 0 src/infrastructure/__init__.py | 0 src/infrastructure/database/__init__.py | 0 src/infrastructure/database/sqlalchemy/__init__.py | 0 src/infrastructure/database/sqlalchemy/model/__init__.py | 0 src/infrastructure/database/sqlalchemy/model/base/__init__.py | 0 src/infrastructure/database/sqlalchemy/model/base/model.py | 3 +++ 7 files changed, 3 insertions(+) create mode 100644 src/__init__.py create mode 100644 src/infrastructure/__init__.py create mode 100644 src/infrastructure/database/__init__.py create mode 100644 src/infrastructure/database/sqlalchemy/__init__.py create mode 100644 src/infrastructure/database/sqlalchemy/model/__init__.py create mode 100644 src/infrastructure/database/sqlalchemy/model/base/__init__.py create mode 100644 src/infrastructure/database/sqlalchemy/model/base/model.py diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/infrastructure/__init__.py b/src/infrastructure/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/infrastructure/database/__init__.py b/src/infrastructure/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/infrastructure/database/sqlalchemy/__init__.py b/src/infrastructure/database/sqlalchemy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/infrastructure/database/sqlalchemy/model/__init__.py b/src/infrastructure/database/sqlalchemy/model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/infrastructure/database/sqlalchemy/model/base/__init__.py b/src/infrastructure/database/sqlalchemy/model/base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/infrastructure/database/sqlalchemy/model/base/model.py b/src/infrastructure/database/sqlalchemy/model/base/model.py new file mode 100644 index 0000000..9f9bbe3 --- /dev/null +++ b/src/infrastructure/database/sqlalchemy/model/base/model.py @@ -0,0 +1,3 @@ +from sqlalchemy.orm import declarative_base + +SABaseModel = declarative_base() From 2c5e6c3dff00bb83fbb1ac2e29cf956ef8cfe1ff Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:44:11 +0900 Subject: [PATCH 21/36] feat: load `SABaseModel` in `env.py` --- migration/env.py | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 migration/env.py diff --git a/migration/env.py b/migration/env.py new file mode 100644 index 0000000..4a060ae --- /dev/null +++ b/migration/env.py @@ -0,0 +1,92 @@ +import asyncio +from logging.config import fileConfig + +from alembic import context +from sqlalchemy.engine import Connection + +from config import GenkaiEraConfig +from src.di.container import container +from src.infrastructure.database.sqlalchemy.model.base.model import SABaseModel +from src.infrastructure.database.sqlalchemy.session import SAAsyncSessionManager + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = (SABaseModel.metadata,) + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + +_ = () + +sa_manager: SAAsyncSessionManager = container.get(SAAsyncSessionManager) + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + + url = GenkaiEraConfig().get_postgres_dsn() # type: ignore + context.configure( + url=url, + target_metadata=target_metadata, # type: ignore + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = sa_manager.get_async_engine() + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() From 6dbda08162316c0e7bd2a15d8e058b8e88f52647 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:45:04 +0900 Subject: [PATCH 22/36] feat: add `SATableNames` --- src/infrastructure/database/sqlalchemy/table_name.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/infrastructure/database/sqlalchemy/table_name.py diff --git a/src/infrastructure/database/sqlalchemy/table_name.py b/src/infrastructure/database/sqlalchemy/table_name.py new file mode 100644 index 0000000..29b8cae --- /dev/null +++ b/src/infrastructure/database/sqlalchemy/table_name.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel + + +class SATableNames(BaseModel): + # Order: alphabetically + pass From 263a0c543f1d8b49a2a97e87b71f8c4c63249467 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:47:10 +0900 Subject: [PATCH 23/36] feat: implement `define_sa_models()` --- src/infrastructure/database/sqlalchemy/model/define.py | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/infrastructure/database/sqlalchemy/model/define.py diff --git a/src/infrastructure/database/sqlalchemy/model/define.py b/src/infrastructure/database/sqlalchemy/model/define.py new file mode 100644 index 0000000..0bbaf69 --- /dev/null +++ b/src/infrastructure/database/sqlalchemy/model/define.py @@ -0,0 +1,5 @@ +def define_sa_models() -> None: + """ + Define models for SQLAlchemy by importing them. + """ + _ = () From 15d8e20294834c6f64f98403ca3ea188d049e49f Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:58:24 +0900 Subject: [PATCH 24/36] feat: add a new `TypeVar`, `RawSessionType` --- src/adapter/type.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/adapter/type.py diff --git a/src/adapter/type.py b/src/adapter/type.py new file mode 100644 index 0000000..f7a3ed5 --- /dev/null +++ b/src/adapter/type.py @@ -0,0 +1,3 @@ +from typing import TypeVar + +RawSessionType = TypeVar("RawSessionType") From 75e0a52883869d711b433750f57f3b8e74875883 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 18:58:57 +0900 Subject: [PATCH 25/36] fix: fix mypy problems --- src/adapter/repository/repository.py | 8 ++++++-- src/adapter/session.py | 13 ++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/adapter/repository/repository.py b/src/adapter/repository/repository.py index 04eee53..6ff0284 100644 --- a/src/adapter/repository/repository.py +++ b/src/adapter/repository/repository.py @@ -1,10 +1,14 @@ -from typing import Optional +from typing import Optional, Generic from src.adapter.session import AsyncSessionWrapperInterface +from src.adapter.type import RawSessionType from src.common.design.interface import Interface -class Repository[RawSessionType](Interface): +class Repository( + Interface, + Generic[RawSessionType], +): __session_wrapper: Optional[AsyncSessionWrapperInterface[RawSessionType]] = None def set_session_wrapper( diff --git a/src/adapter/session.py b/src/adapter/session.py index 48fd532..5fe6a17 100644 --- a/src/adapter/session.py +++ b/src/adapter/session.py @@ -1,11 +1,15 @@ from abc import abstractmethod from contextlib import asynccontextmanager -from typing import AsyncIterator +from typing import AsyncIterator, Generic +from src.adapter.type import RawSessionType from src.common.design.interface import Interface -class AsyncSessionWrapperInterface[RawSessionType](Interface): +class AsyncSessionWrapperInterface( + Generic[RawSessionType], + Interface, +): @abstractmethod async def commit( self, @@ -38,7 +42,10 @@ def set_raw_session( raise NotImplementedError() -class AsyncSessionManagerInterface[RawSessionType](Interface): +class AsyncSessionManagerInterface( + Generic[RawSessionType], + Interface, +): @asynccontextmanager @abstractmethod async def get_session( From efbd83282a3b737e0d2057670088da593a51f47e Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 19:00:37 +0900 Subject: [PATCH 26/36] feat: add a new di module, `GenkaiEraConfigModule` --- src/di/module/__init__.py | 0 src/di/module/config.py | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/di/module/__init__.py create mode 100644 src/di/module/config.py diff --git a/src/di/module/__init__.py b/src/di/module/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/di/module/config.py b/src/di/module/config.py new file mode 100644 index 0000000..10fa9b0 --- /dev/null +++ b/src/di/module/config.py @@ -0,0 +1,20 @@ +from injector import Binder, InstanceProvider, Module + +from config import GenkaiEraConfig + + +class GenkaiEraConfigModule(Module): + def __init__(self) -> None: + is_test: int = GenkaiEraConfig().IS_TEST # type: ignore + + self.__config: GenkaiEraConfig + if is_test == 1: + self.__config = GenkaiEraTestConfig() # type: ignore + else: + self.__config = GenkaiEraConfig() # type: ignore + + def configure(self, binder: Binder) -> None: + binder.bind( + GenkaiEraConfig, + InstanceProvider(self.__config), + ) From 9e376bca3ca01350e157b61d5fa17df27506e6b7 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 19:00:56 +0900 Subject: [PATCH 27/36] feat: add a new di module, `GenkaiEraSADatabaseModule` --- src/di/module/database.py | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/di/module/database.py diff --git a/src/di/module/database.py b/src/di/module/database.py new file mode 100644 index 0000000..54e2aee --- /dev/null +++ b/src/di/module/database.py @@ -0,0 +1,40 @@ +from injector import Binder, ClassProvider, Module +from sqlalchemy.ext.asyncio import AsyncSession + +from src.infrastructure.database.sqlalchemy.model.define import define_sa_models +from src.infrastructure.database.sqlalchemy.session import ( + SAAsyncSessionManager, + SAAsyncSessionWrapper, +) + + +class GenkaiEraSADatabaseModule(Module): + def __init__( + self, + ) -> None: + # Required for type checking + define_sa_models() + + def configure( + self, + binder: Binder, + ) -> None: + binder.bind( + AsyncSessionWrapperInterface[AsyncSession], # type: ignore + ClassProvider(SAAsyncSessionManager), + ) + + binder.bind( + AsyncSessionWrapperInterface[AsyncSession], # type: ignore + ClassProvider(SAAsyncSessionWrapper), + ) + + binder.bind( + AsyncSessionManagerInterface, # type: ignore + ClassProvider(SAAsyncSessionManager), + ) + + binder.bind( + AsyncSessionWrapperInterface, # type: ignore + ClassProvider(SAAsyncSessionWrapper), + ) From 7fda316c19cc3b13bb7c9d40ec721befe5df9cac Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 19:01:13 +0900 Subject: [PATCH 28/36] feat: add container of `injector` --- src/di/__init__.py | 0 src/di/container.py | 11 +++++++++++ 2 files changed, 11 insertions(+) create mode 100644 src/di/__init__.py create mode 100644 src/di/container.py diff --git a/src/di/__init__.py b/src/di/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/di/container.py b/src/di/container.py new file mode 100644 index 0000000..8a72b0c --- /dev/null +++ b/src/di/container.py @@ -0,0 +1,11 @@ +from injector import Injector + +from src.di.module.config import GenkaiEraConfigModule +from src.di.module.database import GenkaiEraSADatabaseModule + +container = Injector( + [ + GenkaiEraConfigModule, + GenkaiEraSADatabaseModule, + ] +) From 1bb1039391dbb6c3961e0948dac48cdf8ca744d0 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 19:01:36 +0900 Subject: [PATCH 29/36] feat: add `SAAsyncSessionWrapper` and `SAAsyncSessionManager` --- .../database/sqlalchemy/session.py | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/infrastructure/database/sqlalchemy/session.py diff --git a/src/infrastructure/database/sqlalchemy/session.py b/src/infrastructure/database/sqlalchemy/session.py new file mode 100644 index 0000000..b045b5c --- /dev/null +++ b/src/infrastructure/database/sqlalchemy/session.py @@ -0,0 +1,133 @@ +from contextlib import asynccontextmanager +from typing import AsyncIterator, Optional + +from injector import inject +from sqlalchemy.ext.asyncio import ( + AsyncEngine, + AsyncSession, + async_sessionmaker, + create_async_engine, +) + +from config import GenkaiEraConfig +from src.adapter.session import ( + AsyncSessionManagerInterface, + AsyncSessionWrapperInterface, +) + + +class SAAsyncSessionWrapper(AsyncSessionWrapperInterface[AsyncSession]): + __raw_session: Optional[AsyncSession] = None + + async def commit( + self, + ) -> None: + session: AsyncSession = self.get_raw_session() + + await session.commit() + + async def rollback( + self, + ) -> None: + session: AsyncSession = self.get_raw_session() + + await session.rollback() + + async def close( + self, + ) -> None: + session: AsyncSession = self.get_raw_session() + + await session.close() + + def get_raw_session( + self, + ) -> AsyncSession: + if self.__raw_session is None: + raise RuntimeError(f"{self.__name__}().__raw_session is not set.") # type: ignore + + return self.__raw_session + + def set_raw_session( + self, + raw_session: AsyncSession, + ) -> None: + self.__raw_session = raw_session + + +class SAAsyncSessionManager(AsyncSessionManagerInterface[AsyncSession]): + __config: GenkaiEraConfig + + @staticmethod + def create_engine( + dsn: str, + connect_args: dict[str, str], + ) -> AsyncEngine: + engine: AsyncEngine = create_async_engine( + dsn, + connect_args=connect_args, + ) + + return engine + + @staticmethod + def create_session_maker( + engine: AsyncEngine, + expire_on_commit: bool = False, + autocommit: bool = False, + autoflush: bool = False, + ) -> async_sessionmaker: # type: ignore + session_maker: async_sessionmaker = async_sessionmaker( # type: ignore + engine, + expire_on_commit=expire_on_commit, + autocommit=autocommit, + autoflush=autoflush, + class_=AsyncSession, + ) + + return session_maker + + @inject + def __init__( + self, + config: GenkaiEraConfig, + ) -> None: + self.__config: GenkaiEraConfig = config + + self.__async_engine: AsyncEngine = self.create_engine( + dsn=self.__config.get_postgres_dsn(), + connect_args=self.__config.POSTGRES_CONNECT_ARGS, + ) + + self.__async_session_maker: async_sessionmaker = self.create_session_maker( # type: ignore + engine=self.__async_engine, + expire_on_commit=self.__config.POSTGRES_EXPIRE_ON_COMMIT, + autocommit=self.__config.POSTGRES_AUTOCOMMIT, + autoflush=self.__config.POSTGRES_AUTOFLUSH, + ) + + def get_async_engine(self) -> AsyncEngine: + return self.__async_engine + + def get_async_session_maker(self) -> async_sessionmaker: # type: ignore + return self.__async_session_maker + + @asynccontextmanager + async def get_session( + self, + ) -> AsyncIterator[SAAsyncSessionWrapper]: + raw_async_session: AsyncSession = self.get_async_session_maker()() + + session_wrapper: SAAsyncSessionWrapper = SAAsyncSessionWrapper() + session_wrapper.set_raw_session(raw_async_session) + + try: + yield session_wrapper + await session_wrapper.commit() + + except Exception as e: + await session_wrapper.rollback() + raise e + + finally: + await session_wrapper.close() From 74ecf8f5e7d3de136e1010ad4008d514e5d94cf0 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 19:04:11 +0900 Subject: [PATCH 30/36] fix: fix flake8 problems --- src/di/module/config.py | 4 ++-- src/di/module/database.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/di/module/config.py b/src/di/module/config.py index 10fa9b0..60d58aa 100644 --- a/src/di/module/config.py +++ b/src/di/module/config.py @@ -1,6 +1,6 @@ from injector import Binder, InstanceProvider, Module -from config import GenkaiEraConfig +from config import GenkaiEraConfig, GenkaiEraTestConfig class GenkaiEraConfigModule(Module): @@ -11,7 +11,7 @@ def __init__(self) -> None: if is_test == 1: self.__config = GenkaiEraTestConfig() # type: ignore else: - self.__config = GenkaiEraConfig() # type: ignore + self.__config = GenkaiEraConfig() def configure(self, binder: Binder) -> None: binder.bind( diff --git a/src/di/module/database.py b/src/di/module/database.py index 54e2aee..d9e145c 100644 --- a/src/di/module/database.py +++ b/src/di/module/database.py @@ -1,6 +1,10 @@ from injector import Binder, ClassProvider, Module from sqlalchemy.ext.asyncio import AsyncSession +from src.adapter.session import ( + AsyncSessionWrapperInterface, + AsyncSessionManagerInterface, +) from src.infrastructure.database.sqlalchemy.model.define import define_sa_models from src.infrastructure.database.sqlalchemy.session import ( SAAsyncSessionManager, From b67a54bfd09e53879d24061f63d1c843220f3aa9 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 19:04:36 +0900 Subject: [PATCH 31/36] fix: fix mypy problems --- src/di/module/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/di/module/config.py b/src/di/module/config.py index 60d58aa..9a44c60 100644 --- a/src/di/module/config.py +++ b/src/di/module/config.py @@ -11,7 +11,7 @@ def __init__(self) -> None: if is_test == 1: self.__config = GenkaiEraTestConfig() # type: ignore else: - self.__config = GenkaiEraConfig() + self.__config = GenkaiEraConfig() # type: ignore def configure(self, binder: Binder) -> None: binder.bind( From 6d5a67a63caef22c126a253d29aff36743b736df Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 19:07:52 +0900 Subject: [PATCH 32/36] fix: fix invalid file path in `Makefile` --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 32e62bb..e52fdda 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,7 @@ offline_migrate: .PHONY: make_migration make_migration: - docker compose run --rm app bash -c "python r./script/un_command.py alembic revision --autogenerate" + docker compose run --rm app bash -c "python ./script/run_command.py alembic revision --autogenerate" .PHONY: flake8 flake8: @@ -115,4 +115,4 @@ pytest_xml: pytest_ci: $(MAKE) up $(MAKE) online_migrate - docker compose run --rm app bash -c "python r./script/un_command.py pytest -v ./test/ --cov=./src/ --junitxml=pytest.xml --cov-report=term-missing:skip-covered | tee pytest-coverage.txt" + docker compose run --rm app bash -c "python r./script/run_command.py pytest -v ./test/ --cov=./src/ --junitxml=pytest.xml --cov-report=term-missing:skip-covered | tee pytest-coverage.txt" From 1d20511f67f45a0fb755fff3f9d16e16c3232970 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 19:08:23 +0900 Subject: [PATCH 33/36] add(deps): add `asyncpg` as a dep --- poetry.lock | 56 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index bb1a66d..799b6bf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -30,6 +30,60 @@ files = [ {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, ] +[[package]] +name = "asyncpg" +version = "0.29.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"}, + {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"}, + {file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"}, + {file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"}, + {file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"}, + {file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"}, + {file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"}, + {file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"}, + {file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"}, + {file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"}, + {file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"}, + {file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"}, + {file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"}, +] + +[package.extras] +docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"] + [[package]] name = "black" version = "23.12.1" @@ -1042,4 +1096,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "7abf377221abb0ed8fc8d5e576e50b56d01d50dc0372c90831450637f4c73577" +content-hash = "ed7e84b01299d07e60ab75a8c1b5aa33545a1eb3a2212854e72c3808d74a9b34" diff --git a/pyproject.toml b/pyproject.toml index aa4c2e8..273124c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ injector = "^0.21.0" pydantic = "^2.5.3" pydantic-settings = "^2.1.0" sqlalchemy = "^2.0.25" +asyncpg = "^0.29.0" alembic = "^1.13.1" python-dotenv = "^1.0.0" From e98317a10ab9ed7de8b36ec1b38f1f6a1dad926d Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 19:11:48 +0900 Subject: [PATCH 34/36] feat: add initial migration file --- migration/versions/fbab55528ed7_.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 migration/versions/fbab55528ed7_.py diff --git a/migration/versions/fbab55528ed7_.py b/migration/versions/fbab55528ed7_.py new file mode 100644 index 0000000..17acffa --- /dev/null +++ b/migration/versions/fbab55528ed7_.py @@ -0,0 +1,26 @@ +"""empty message + +Revision ID: fbab55528ed7 +Revises: +Create Date: 2024-01-04 19:11:09.055615 + +""" +from typing import Sequence, Union + +# revision identifiers, used by Alembic. +revision: str = "fbab55528ed7" +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### From 426c417545979f67134398a9643c161560755a28 Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Thu, 4 Jan 2024 19:12:24 +0900 Subject: [PATCH 35/36] fix: fix invalid file path in `Makefile` --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e52fdda..368d466 100644 --- a/Makefile +++ b/Makefile @@ -115,4 +115,4 @@ pytest_xml: pytest_ci: $(MAKE) up $(MAKE) online_migrate - docker compose run --rm app bash -c "python r./script/run_command.py pytest -v ./test/ --cov=./src/ --junitxml=pytest.xml --cov-report=term-missing:skip-covered | tee pytest-coverage.txt" + docker compose run --rm app bash -c "python ./script/run_command.py pytest -v ./test/ --cov=./src/ --junitxml=pytest.xml --cov-report=term-missing:skip-covered | tee pytest-coverage.txt" From 43b1045698550fb5ecea68b3772ce2ea9397770b Mon Sep 17 00:00:00 2001 From: Colk-tech Date: Wed, 10 Jan 2024 21:40:18 +0900 Subject: [PATCH 36/36] rename: rename `GENKAIERA_POSTGRES_USER`, `genkaiera_api` -> `genkaiera_app` --- .env.ci | 2 +- .env.example | 2 +- .env.local | 2 +- .env.test | 2 +- script/database.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.env.ci b/.env.ci index ea0308c..a6050ee 100644 --- a/.env.ci +++ b/.env.ci @@ -15,5 +15,5 @@ GENKAIERA_IS_TEST=1 # Database GENKAIERA_POSTGRES_HOST=postgres GENKAIERA_POSTGRES_PORT=5432 -GENKAIERA_POSTGRES_USER=genkaiera_api +GENKAIERA_POSTGRES_USER=genkaiera_app GENKAIERA_POSTGRES_PASSWORD=hM7UCyG61f5aKKy8LOuWlfEhZffuD9gCRia3w+rKrYg0I3sa3PaKP4e7IDrXNfmd diff --git a/.env.example b/.env.example index 0cf3ec2..2d0f573 100644 --- a/.env.example +++ b/.env.example @@ -15,5 +15,5 @@ GENKAIERA_IS_TEST=0 # Database GENKAIERA_POSTGRES_HOST=postgres GENKAIERA_POSTGRES_PORT=5432 -GENKAIERA_POSTGRES_USER=genkaiera_api +GENKAIERA_POSTGRES_USER=genkaiera_app GENKAIERA_POSTGRES_PASSWORD=hM7UCyG61f5aKKy8LOuWlfEhZffuD9gCRia3w+rKrYg0I3sa3PaKP4e7IDrXNfmd diff --git a/.env.local b/.env.local index 0cf3ec2..2d0f573 100644 --- a/.env.local +++ b/.env.local @@ -15,5 +15,5 @@ GENKAIERA_IS_TEST=0 # Database GENKAIERA_POSTGRES_HOST=postgres GENKAIERA_POSTGRES_PORT=5432 -GENKAIERA_POSTGRES_USER=genkaiera_api +GENKAIERA_POSTGRES_USER=genkaiera_app GENKAIERA_POSTGRES_PASSWORD=hM7UCyG61f5aKKy8LOuWlfEhZffuD9gCRia3w+rKrYg0I3sa3PaKP4e7IDrXNfmd diff --git a/.env.test b/.env.test index 4141362..ee62a48 100644 --- a/.env.test +++ b/.env.test @@ -15,5 +15,5 @@ GENKAIERA_IS_TEST=1 # Database GENKAIERA_POSTGRES_HOST=postgres GENKAIERA_POSTGRES_PORT=5432 -GENKAIERA_POSTGRES_USER=genkaiera_api +GENKAIERA_POSTGRES_USER=genkaiera_app GENKAIERA_POSTGRES_PASSWORD=hM7UCyG61f5aKKy8LOuWlfEhZffuD9gCRia3w+rKrYg0I3sa3PaKP4e7IDrXNfmd diff --git a/script/database.py b/script/database.py index 7ee0c98..9a5e090 100644 --- a/script/database.py +++ b/script/database.py @@ -21,7 +21,7 @@ print("Offline mode. The `--online` flag is disabled.") -USER = "genkaiera_api" +USER = "genkaiera_app" DB = "genkaiera" PASSWORD = os.environ.get("GENKAIERA_POSTGRES_PASSWORD") HOST = os.environ.get("GENKAIERA_POSTGRES_HOST")