From e81da835405bd4261d77a700354250e82318bc00 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Fri, 10 Jan 2025 15:31:29 -0500 Subject: [PATCH 01/14] Update the TCH rules to TC --- ruff-shared.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ruff-shared.toml b/ruff-shared.toml index 0702eaf..e5e4cdb 100644 --- a/ruff-shared.toml +++ b/ruff-shared.toml @@ -58,9 +58,9 @@ ignore = [ "S607", # using PATH is not a security vulnerability "SIM102", # sometimes the formatting of nested if statements is clearer "SIM117", # sometimes nested with contexts are clearer - "TCH001", # we decided to not maintain separate TYPE_CHECKING blocks - "TCH002", # we decided to not maintain separate TYPE_CHECKING blocks - "TCH003", # we decided to not maintain separate TYPE_CHECKING blocks + "TC001", # we decided to not maintain separate TYPE_CHECKING blocks + "TC002", # we decided to not maintain separate TYPE_CHECKING blocks + "TC003", # we decided to not maintain separate TYPE_CHECKING blocks "TD003", # we don't require issues be created for TODOs "TID252", # if we're going to use relative imports, use them always "TRY003", # good general advice but lint is way too aggressive From ece2a03f1cbfc93768c1497ae5d5febca405a09e Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Fri, 10 Jan 2025 14:26:23 -0500 Subject: [PATCH 02/14] Refresh dependencies --- .pre-commit-config.yaml | 2 +- requirements/dev.txt | 136 ++++++++++---------- requirements/main.txt | 278 ++++++++++++++++++++-------------------- requirements/tox.txt | 36 +++--- 4 files changed, 226 insertions(+), 226 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5dec526..c18bbbe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: check-toml - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.8.6 + rev: v0.9.1 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/requirements/dev.txt b/requirements/dev.txt index b26564c..98d360c 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -392,7 +392,7 @@ gitpython==3.1.44 \ --hash=sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110 \ --hash=sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269 # via documenteer -greenlet==3.1.1 ; (python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64') \ +greenlet==3.1.1 ; (python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64') \ --hash=sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e \ --hash=sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7 \ --hash=sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01 \ @@ -841,9 +841,9 @@ pycparser==2.22 ; implementation_name == 'pypy' \ # via # -c requirements/main.txt # cffi -pydantic==2.10.4 \ - --hash=sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d \ - --hash=sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06 +pydantic==2.10.5 \ + --hash=sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff \ + --hash=sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53 # via # -c requirements/main.txt # documenteer @@ -975,9 +975,9 @@ pytest==8.3.4 \ # pytest-asyncio # pytest-cov # pytest-mock -pytest-asyncio==0.25.1 \ - --hash=sha256:79be8a72384b0c917677e00daa711e07db15259f4d23203c59012bcd989d4aee \ - --hash=sha256:c84878849ec63ff2ca509423616e071ef9cd8cc93c053aa33b5b8fb70a990671 +pytest-asyncio==0.25.2 \ + --hash=sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075 \ + --hash=sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f # via -r requirements/dev.in pytest-cov==6.0.0 \ --hash=sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35 \ @@ -1324,9 +1324,9 @@ scriv==1.5.1 \ --hash=sha256:30ae9ff8d144f8e0cf394c4e1d379542f1b3823767642955b54ec40dc00b32b6 \ --hash=sha256:a3adc657733b4124fcb54527a5f3daab0d3c300de82d0fd2b9b297b243151b78 # via -r requirements/dev.in -setuptools==75.7.0 \ - --hash=sha256:84fb203f278ebcf5cd08f97d3fb96d3fbed4b629d500b29ad60d11e00769b183 \ - --hash=sha256:886ff7b16cd342f1d1defc16fc98c9ce3fde69e087a4e1983d7ab634e5f41f4f +setuptools==75.8.0 \ + --hash=sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6 \ + --hash=sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3 # via # documenteer # sphinxcontrib-bibtex @@ -1456,64 +1456,64 @@ sphinxext-rediraffe==0.2.7 \ --hash=sha256:651dcbfae5ffda9ffd534dfb8025f36120e5efb6ea1a33f5420023862b9f725d \ --hash=sha256:9e430a52d4403847f4ffb3a8dd6dfc34a9fe43525305131f52ed899743a5fd8c # via documenteer -sqlalchemy==2.0.36 \ - --hash=sha256:03e08af7a5f9386a43919eda9de33ffda16b44eb11f3b313e6822243770e9763 \ - --hash=sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436 \ - --hash=sha256:07b441f7d03b9a66299ce7ccf3ef2900abc81c0db434f42a5694a37bd73870f2 \ - --hash=sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588 \ - --hash=sha256:1e0d612a17581b6616ff03c8e3d5eff7452f34655c901f75d62bd86449d9750e \ - --hash=sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959 \ - --hash=sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d \ - --hash=sha256:28120ef39c92c2dd60f2721af9328479516844c6b550b077ca450c7d7dc68575 \ - --hash=sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908 \ - --hash=sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8 \ - --hash=sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8 \ - --hash=sha256:3d6718667da04294d7df1670d70eeddd414f313738d20a6f1d1f379e3139a545 \ - --hash=sha256:3dbb986bad3ed5ceaf090200eba750b5245150bd97d3e67343a3cfed06feecf7 \ - --hash=sha256:4557e1f11c5f653ebfdd924f3f9d5ebfc718283b0b9beebaa5dd6b77ec290971 \ - --hash=sha256:46331b00096a6db1fdc052d55b101dbbfc99155a548e20a0e4a8e5e4d1362855 \ - --hash=sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c \ - --hash=sha256:4f5e9cd989b45b73bd359f693b935364f7e1f79486e29015813c338450aa5a71 \ - --hash=sha256:50aae840ebbd6cdd41af1c14590e5741665e5272d2fee999306673a1bb1fdb4d \ - --hash=sha256:59b1ee96617135f6e1d6f275bbe988f419c5178016f3d41d3c0abb0c819f75bb \ - --hash=sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72 \ - --hash=sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f \ - --hash=sha256:69f93723edbca7342624d09f6704e7126b152eaed3cdbb634cb657a54332a3c5 \ - --hash=sha256:6a440293d802d3011028e14e4226da1434b373cbaf4a4bbb63f845761a708346 \ - --hash=sha256:72c28b84b174ce8af8504ca28ae9347d317f9dba3999e5981a3cd441f3712e24 \ - --hash=sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e \ - --hash=sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5 \ - --hash=sha256:8318f4776c85abc3f40ab185e388bee7a6ea99e7fa3a30686580b209eaa35c08 \ - --hash=sha256:8958b10490125124463095bbdadda5aa22ec799f91958e410438ad6c97a7b793 \ - --hash=sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88 \ - --hash=sha256:90812a8933df713fdf748b355527e3af257a11e415b613dd794512461eb8a686 \ - --hash=sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b \ - --hash=sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2 \ - --hash=sha256:9fe53b404f24789b5ea9003fc25b9a3988feddebd7e7b369c8fac27ad6f52f28 \ - --hash=sha256:a4e46a888b54be23d03a89be510f24a7652fe6ff660787b96cd0e57a4ebcb46d \ - --hash=sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5 \ - --hash=sha256:ac9dfa18ff2a67b09b372d5db8743c27966abf0e5344c555d86cc7199f7ad83a \ - --hash=sha256:af148a33ff0349f53512a049c6406923e4e02bf2f26c5fb285f143faf4f0e46a \ - --hash=sha256:b11d0cfdd2b095e7b0686cf5fabeb9c67fae5b06d265d8180715b8cfa86522e3 \ - --hash=sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf \ - --hash=sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5 \ - --hash=sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef \ - --hash=sha256:b817d41d692bf286abc181f8af476c4fbef3fd05e798777492618378448ee689 \ - --hash=sha256:b81ee3d84803fd42d0b154cb6892ae57ea6b7c55d8359a02379965706c7efe6c \ - --hash=sha256:be9812b766cad94a25bc63bec11f88c4ad3629a0cec1cd5d4ba48dc23860486b \ - --hash=sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07 \ - --hash=sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa \ - --hash=sha256:c4ae3005ed83f5967f961fd091f2f8c5329161f69ce8480aa8168b2d7fe37f06 \ - --hash=sha256:c54a1e53a0c308a8e8a7dffb59097bff7facda27c70c286f005327f21b2bd6b1 \ - --hash=sha256:d0ddd9db6e59c44875211bc4c7953a9f6638b937b0a88ae6d09eb46cced54eff \ - --hash=sha256:dc022184d3e5cacc9579e41805a681187650e170eb2fd70e28b86192a479dcaa \ - --hash=sha256:e32092c47011d113dc01ab3e1d3ce9f006a47223b18422c5c0d150af13a00687 \ - --hash=sha256:f7b64e6ec3f02c35647be6b4851008b26cff592a95ecb13b6788a54ef80bbdd4 \ - --hash=sha256:f942a799516184c855e1a32fbc7b29d7e571b52612647866d4ec1c3242578fcb \ - --hash=sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44 \ - --hash=sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c \ - --hash=sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e \ - --hash=sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53 +sqlalchemy==2.0.37 \ + --hash=sha256:03f0528c53ca0b67094c4764523c1451ea15959bbf0a8a8a3096900014db0278 \ + --hash=sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6 \ + --hash=sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb \ + --hash=sha256:1b2690456528a87234a75d1a1644cdb330a6926f455403c8e4f6cad6921f9098 \ + --hash=sha256:1cdba1f73b64530c47b27118b7053b8447e6d6f3c8104e3ac59f3d40c33aa9fd \ + --hash=sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2 \ + --hash=sha256:2952748ecd67ed3b56773c185e85fc084f6bdcdec10e5032a7c25a6bc7d682ef \ + --hash=sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94 \ + --hash=sha256:2fa2c0913f02341d25fb858e4fb2031e6b0813494cca1ba07d417674128ce11b \ + --hash=sha256:3151822aa1db0eb5afd65ccfafebe0ef5cda3a7701a279c8d0bf17781a793bb4 \ + --hash=sha256:35bd2df269de082065d4b23ae08502a47255832cc3f17619a5cea92ce478b02b \ + --hash=sha256:41296bbcaa55ef5fdd32389a35c710133b097f7b2609d8218c0eabded43a1d84 \ + --hash=sha256:44f569d0b1eb82301b92b72085583277316e7367e038d97c3a1a899d9a05e342 \ + --hash=sha256:46954173612617a99a64aee103bcd3f078901b9a8dcfc6ae80cbf34ba23df989 \ + --hash=sha256:4b12885dc85a2ab2b7d00995bac6d967bffa8594123b02ed21e8eb2205a7584b \ + --hash=sha256:4f581d365af9373a738c49e0c51e8b18e08d8a6b1b15cc556773bcd8a192fa8b \ + --hash=sha256:51bc9cfef83e0ac84f86bf2b10eaccb27c5a3e66a1212bef676f5bee6ef33ebb \ + --hash=sha256:521ef85c04c33009166777c77e76c8a676e2d8528dc83a57836b63ca9c69dcd1 \ + --hash=sha256:5bc3339db84c5fb9130ac0e2f20347ee77b5dd2596ba327ce0d399752f4fce39 \ + --hash=sha256:635d8a21577341dfe4f7fa59ec394b346da12420b86624a69e466d446de16aff \ + --hash=sha256:648ec5acf95ad59255452ef759054f2176849662af4521db6cb245263ae4aa33 \ + --hash=sha256:650dcb70739957a492ad8acff65d099a9586b9b8920e3507ca61ec3ce650bb72 \ + --hash=sha256:6b788f14c5bb91db7f468dcf76f8b64423660a05e57fe277d3f4fad7b9dcb7ce \ + --hash=sha256:6c67415258f9f3c69867ec02fea1bf6508153709ecbd731a982442a590f2b7e4 \ + --hash=sha256:74bbd1d0a9bacf34266a7907d43260c8d65d31d691bb2356f41b17c2dca5b1d0 \ + --hash=sha256:75311559f5c9881a9808eadbeb20ed8d8ba3f7225bef3afed2000c2a9f4d49b9 \ + --hash=sha256:78361be6dc9073ed17ab380985d1e45e48a642313ab68ab6afa2457354ff692c \ + --hash=sha256:7b7e772dc4bc507fdec4ee20182f15bd60d2a84f1e087a8accf5b5b7a0dcf2ba \ + --hash=sha256:82df02816c14f8dc9f4d74aea4cb84a92f4b0620235daa76dde002409a3fbb5a \ + --hash=sha256:84b9f23b0fa98a6a4b99d73989350a94e4a4ec476b9a7dfe9b79ba5939f5e80b \ + --hash=sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658 \ + --hash=sha256:8e47f1af09444f87c67b4f1bb6231e12ba6d4d9f03050d7fc88df6d075231a49 \ + --hash=sha256:93d1543cd8359040c02b6614421c8e10cd7a788c40047dbc507ed46c29ae5636 \ + --hash=sha256:94b564e38b344d3e67d2e224f0aec6ba09a77e4582ced41e7bfd0f757d926ec9 \ + --hash=sha256:955a2a765aa1bd81aafa69ffda179d4fe3e2a3ad462a736ae5b6f387f78bfeb8 \ + --hash=sha256:9d087663b7e1feabea8c578d6887d59bb00388158e8bff3a76be11aa3f748ca2 \ + --hash=sha256:9df21b8d9e5c136ea6cde1c50d2b1c29a2b5ff2b1d610165c23ff250e0704087 \ + --hash=sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1 \ + --hash=sha256:b2eae3423e538c10d93ae3e87788c6a84658c3ed6db62e6a61bb9495b0ad16bb \ + --hash=sha256:b661b49d0cb0ab311a189b31e25576b7ac3e20783beb1e1817d72d9d02508bf5 \ + --hash=sha256:bedee60385c1c0411378cbd4dc486362f5ee88deceea50002772912d798bb00f \ + --hash=sha256:c505edd429abdfe3643fa3b2e83efb3445a34a9dc49d5f692dd087be966020e0 \ + --hash=sha256:cce918ada64c956b62ca2c2af59b125767097ec1dca89650a6221e887521bfd7 \ + --hash=sha256:cf5ae8a9dcf657fd72144a7fd01f243236ea39e7344e579a121c4205aedf07bb \ + --hash=sha256:cf95a60b36997dad99692314c4713f141b61c5b0b4cc5c3426faad570b31ca01 \ + --hash=sha256:d57bafbab289e147d064ffbd5cca2d7b1394b63417c0636cea1f2e93d16eb9e8 \ + --hash=sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2 \ + --hash=sha256:d75ead7dd4d255068ea0f21492ee67937bd7c90964c8f3c2bea83c7b7f81b95f \ + --hash=sha256:da36c3b0e891808a7542c5c89f224520b9a16c7f5e4d6a1156955605e54aef0e \ + --hash=sha256:db18ff6b8c0f1917f8b20f8eca35c28bbccb9f83afa94743e03d40203ed83de9 \ + --hash=sha256:dfff7be361048244c3aa0f60b5e63221c5e0f0e509f4e47b8910e22b57d10ae7 \ + --hash=sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb \ + --hash=sha256:e56a139bfe136a22c438478a86f8204c1eb5eed36f4e15c4224e4b9db01cb3e4 \ + --hash=sha256:e6f5d254a22394847245f411a2956976401e84da4288aa70cbcd5190744062c1 \ + --hash=sha256:e7402ff96e2b073a98ef6d6142796426d705addd27b9d26c3b32dbaa06d7d069 \ + --hash=sha256:ea308cec940905ba008291d93619d92edaf83232ec85fbd514dcb329f3192761 \ + --hash=sha256:eaa8039b6d20137a4e02603aba37d12cd2dde7887500b8855356682fc33933f4 # via # -c requirements/main.txt # -r requirements/dev.in diff --git a/requirements/main.txt b/requirements/main.txt index ad4dffb..ecc636d 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -55,9 +55,9 @@ anyio==4.8.0 \ # sse-starlette # starlette # watchfiles -arq==0.26.1 \ - --hash=sha256:789d12ca7d69919bd2e641e44f3f14a38bd854daa7ded22cdd725796e8c65352 \ - --hash=sha256:a4db5395c3aaefe0610074b0aa125ff3392aa90a2214973be566796a5819d9cf +arq==0.26.3 \ + --hash=sha256:362063ea3c726562fb69c723d5b8ee80827fdefda782a8547da5be3d380ac4b1 \ + --hash=sha256:9f4b78149a58c9dc4b88454861a254b7c4e7a159f2c973c89b548288b77e9005 # via safir-arq async-timeout==5.0.1 \ --hash=sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c \ @@ -322,9 +322,9 @@ fastjsonschema==2.21.1 \ --hash=sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4 \ --hash=sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667 # via nbformat -faststream==0.5.33 \ - --hash=sha256:24b3b48dff4195c63141864fae24608c4e3e08c9172faccc74b0c46d6d88f665 \ - --hash=sha256:d40761c9f5db9b2cee69748832c4ead20d61805c439d3fbe3107fe4c9a066b2d +faststream==0.5.34 \ + --hash=sha256:84615968c5768ebaa89b72ae66b53e5302c08e7d18b341ef5193e54cb6ba8623 \ + --hash=sha256:aa7f61d6968a68f13ebf755cce9e8bf11b00717c28b2ef66e896b5d652a6c6a2 # via safir gidgethub==5.3.0 \ --hash=sha256:4dd92f2252d12756b13f9dd15cde322bfb0d625b6fb5d680da1567ec74b462c0 \ @@ -772,9 +772,9 @@ pycparser==2.22 ; implementation_name == 'pypy' or platform_python_implementatio --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \ --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc # via cffi -pydantic==2.10.4 \ - --hash=sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d \ - --hash=sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06 +pydantic==2.10.5 \ + --hash=sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff \ + --hash=sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53 # via # -r requirements/main.in # fast-depends @@ -1250,64 +1250,64 @@ soupsieve==2.6 \ --hash=sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb \ --hash=sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9 # via beautifulsoup4 -sqlalchemy==2.0.36 \ - --hash=sha256:03e08af7a5f9386a43919eda9de33ffda16b44eb11f3b313e6822243770e9763 \ - --hash=sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436 \ - --hash=sha256:07b441f7d03b9a66299ce7ccf3ef2900abc81c0db434f42a5694a37bd73870f2 \ - --hash=sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588 \ - --hash=sha256:1e0d612a17581b6616ff03c8e3d5eff7452f34655c901f75d62bd86449d9750e \ - --hash=sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959 \ - --hash=sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d \ - --hash=sha256:28120ef39c92c2dd60f2721af9328479516844c6b550b077ca450c7d7dc68575 \ - --hash=sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908 \ - --hash=sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8 \ - --hash=sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8 \ - --hash=sha256:3d6718667da04294d7df1670d70eeddd414f313738d20a6f1d1f379e3139a545 \ - --hash=sha256:3dbb986bad3ed5ceaf090200eba750b5245150bd97d3e67343a3cfed06feecf7 \ - --hash=sha256:4557e1f11c5f653ebfdd924f3f9d5ebfc718283b0b9beebaa5dd6b77ec290971 \ - --hash=sha256:46331b00096a6db1fdc052d55b101dbbfc99155a548e20a0e4a8e5e4d1362855 \ - --hash=sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c \ - --hash=sha256:4f5e9cd989b45b73bd359f693b935364f7e1f79486e29015813c338450aa5a71 \ - --hash=sha256:50aae840ebbd6cdd41af1c14590e5741665e5272d2fee999306673a1bb1fdb4d \ - --hash=sha256:59b1ee96617135f6e1d6f275bbe988f419c5178016f3d41d3c0abb0c819f75bb \ - --hash=sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72 \ - --hash=sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f \ - --hash=sha256:69f93723edbca7342624d09f6704e7126b152eaed3cdbb634cb657a54332a3c5 \ - --hash=sha256:6a440293d802d3011028e14e4226da1434b373cbaf4a4bbb63f845761a708346 \ - --hash=sha256:72c28b84b174ce8af8504ca28ae9347d317f9dba3999e5981a3cd441f3712e24 \ - --hash=sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e \ - --hash=sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5 \ - --hash=sha256:8318f4776c85abc3f40ab185e388bee7a6ea99e7fa3a30686580b209eaa35c08 \ - --hash=sha256:8958b10490125124463095bbdadda5aa22ec799f91958e410438ad6c97a7b793 \ - --hash=sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88 \ - --hash=sha256:90812a8933df713fdf748b355527e3af257a11e415b613dd794512461eb8a686 \ - --hash=sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b \ - --hash=sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2 \ - --hash=sha256:9fe53b404f24789b5ea9003fc25b9a3988feddebd7e7b369c8fac27ad6f52f28 \ - --hash=sha256:a4e46a888b54be23d03a89be510f24a7652fe6ff660787b96cd0e57a4ebcb46d \ - --hash=sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5 \ - --hash=sha256:ac9dfa18ff2a67b09b372d5db8743c27966abf0e5344c555d86cc7199f7ad83a \ - --hash=sha256:af148a33ff0349f53512a049c6406923e4e02bf2f26c5fb285f143faf4f0e46a \ - --hash=sha256:b11d0cfdd2b095e7b0686cf5fabeb9c67fae5b06d265d8180715b8cfa86522e3 \ - --hash=sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf \ - --hash=sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5 \ - --hash=sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef \ - --hash=sha256:b817d41d692bf286abc181f8af476c4fbef3fd05e798777492618378448ee689 \ - --hash=sha256:b81ee3d84803fd42d0b154cb6892ae57ea6b7c55d8359a02379965706c7efe6c \ - --hash=sha256:be9812b766cad94a25bc63bec11f88c4ad3629a0cec1cd5d4ba48dc23860486b \ - --hash=sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07 \ - --hash=sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa \ - --hash=sha256:c4ae3005ed83f5967f961fd091f2f8c5329161f69ce8480aa8168b2d7fe37f06 \ - --hash=sha256:c54a1e53a0c308a8e8a7dffb59097bff7facda27c70c286f005327f21b2bd6b1 \ - --hash=sha256:d0ddd9db6e59c44875211bc4c7953a9f6638b937b0a88ae6d09eb46cced54eff \ - --hash=sha256:dc022184d3e5cacc9579e41805a681187650e170eb2fd70e28b86192a479dcaa \ - --hash=sha256:e32092c47011d113dc01ab3e1d3ce9f006a47223b18422c5c0d150af13a00687 \ - --hash=sha256:f7b64e6ec3f02c35647be6b4851008b26cff592a95ecb13b6788a54ef80bbdd4 \ - --hash=sha256:f942a799516184c855e1a32fbc7b29d7e571b52612647866d4ec1c3242578fcb \ - --hash=sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44 \ - --hash=sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c \ - --hash=sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e \ - --hash=sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53 +sqlalchemy==2.0.37 \ + --hash=sha256:03f0528c53ca0b67094c4764523c1451ea15959bbf0a8a8a3096900014db0278 \ + --hash=sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6 \ + --hash=sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb \ + --hash=sha256:1b2690456528a87234a75d1a1644cdb330a6926f455403c8e4f6cad6921f9098 \ + --hash=sha256:1cdba1f73b64530c47b27118b7053b8447e6d6f3c8104e3ac59f3d40c33aa9fd \ + --hash=sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2 \ + --hash=sha256:2952748ecd67ed3b56773c185e85fc084f6bdcdec10e5032a7c25a6bc7d682ef \ + --hash=sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94 \ + --hash=sha256:2fa2c0913f02341d25fb858e4fb2031e6b0813494cca1ba07d417674128ce11b \ + --hash=sha256:3151822aa1db0eb5afd65ccfafebe0ef5cda3a7701a279c8d0bf17781a793bb4 \ + --hash=sha256:35bd2df269de082065d4b23ae08502a47255832cc3f17619a5cea92ce478b02b \ + --hash=sha256:41296bbcaa55ef5fdd32389a35c710133b097f7b2609d8218c0eabded43a1d84 \ + --hash=sha256:44f569d0b1eb82301b92b72085583277316e7367e038d97c3a1a899d9a05e342 \ + --hash=sha256:46954173612617a99a64aee103bcd3f078901b9a8dcfc6ae80cbf34ba23df989 \ + --hash=sha256:4b12885dc85a2ab2b7d00995bac6d967bffa8594123b02ed21e8eb2205a7584b \ + --hash=sha256:4f581d365af9373a738c49e0c51e8b18e08d8a6b1b15cc556773bcd8a192fa8b \ + --hash=sha256:51bc9cfef83e0ac84f86bf2b10eaccb27c5a3e66a1212bef676f5bee6ef33ebb \ + --hash=sha256:521ef85c04c33009166777c77e76c8a676e2d8528dc83a57836b63ca9c69dcd1 \ + --hash=sha256:5bc3339db84c5fb9130ac0e2f20347ee77b5dd2596ba327ce0d399752f4fce39 \ + --hash=sha256:635d8a21577341dfe4f7fa59ec394b346da12420b86624a69e466d446de16aff \ + --hash=sha256:648ec5acf95ad59255452ef759054f2176849662af4521db6cb245263ae4aa33 \ + --hash=sha256:650dcb70739957a492ad8acff65d099a9586b9b8920e3507ca61ec3ce650bb72 \ + --hash=sha256:6b788f14c5bb91db7f468dcf76f8b64423660a05e57fe277d3f4fad7b9dcb7ce \ + --hash=sha256:6c67415258f9f3c69867ec02fea1bf6508153709ecbd731a982442a590f2b7e4 \ + --hash=sha256:74bbd1d0a9bacf34266a7907d43260c8d65d31d691bb2356f41b17c2dca5b1d0 \ + --hash=sha256:75311559f5c9881a9808eadbeb20ed8d8ba3f7225bef3afed2000c2a9f4d49b9 \ + --hash=sha256:78361be6dc9073ed17ab380985d1e45e48a642313ab68ab6afa2457354ff692c \ + --hash=sha256:7b7e772dc4bc507fdec4ee20182f15bd60d2a84f1e087a8accf5b5b7a0dcf2ba \ + --hash=sha256:82df02816c14f8dc9f4d74aea4cb84a92f4b0620235daa76dde002409a3fbb5a \ + --hash=sha256:84b9f23b0fa98a6a4b99d73989350a94e4a4ec476b9a7dfe9b79ba5939f5e80b \ + --hash=sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658 \ + --hash=sha256:8e47f1af09444f87c67b4f1bb6231e12ba6d4d9f03050d7fc88df6d075231a49 \ + --hash=sha256:93d1543cd8359040c02b6614421c8e10cd7a788c40047dbc507ed46c29ae5636 \ + --hash=sha256:94b564e38b344d3e67d2e224f0aec6ba09a77e4582ced41e7bfd0f757d926ec9 \ + --hash=sha256:955a2a765aa1bd81aafa69ffda179d4fe3e2a3ad462a736ae5b6f387f78bfeb8 \ + --hash=sha256:9d087663b7e1feabea8c578d6887d59bb00388158e8bff3a76be11aa3f748ca2 \ + --hash=sha256:9df21b8d9e5c136ea6cde1c50d2b1c29a2b5ff2b1d610165c23ff250e0704087 \ + --hash=sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1 \ + --hash=sha256:b2eae3423e538c10d93ae3e87788c6a84658c3ed6db62e6a61bb9495b0ad16bb \ + --hash=sha256:b661b49d0cb0ab311a189b31e25576b7ac3e20783beb1e1817d72d9d02508bf5 \ + --hash=sha256:bedee60385c1c0411378cbd4dc486362f5ee88deceea50002772912d798bb00f \ + --hash=sha256:c505edd429abdfe3643fa3b2e83efb3445a34a9dc49d5f692dd087be966020e0 \ + --hash=sha256:cce918ada64c956b62ca2c2af59b125767097ec1dca89650a6221e887521bfd7 \ + --hash=sha256:cf5ae8a9dcf657fd72144a7fd01f243236ea39e7344e579a121c4205aedf07bb \ + --hash=sha256:cf95a60b36997dad99692314c4713f141b61c5b0b4cc5c3426faad570b31ca01 \ + --hash=sha256:d57bafbab289e147d064ffbd5cca2d7b1394b63417c0636cea1f2e93d16eb9e8 \ + --hash=sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2 \ + --hash=sha256:d75ead7dd4d255068ea0f21492ee67937bd7c90964c8f3c2bea83c7b7f81b95f \ + --hash=sha256:da36c3b0e891808a7542c5c89f224520b9a16c7f5e4d6a1156955605e54aef0e \ + --hash=sha256:db18ff6b8c0f1917f8b20f8eca35c28bbccb9f83afa94743e03d40203ed83de9 \ + --hash=sha256:dfff7be361048244c3aa0f60b5e63221c5e0f0e509f4e47b8910e22b57d10ae7 \ + --hash=sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb \ + --hash=sha256:e56a139bfe136a22c438478a86f8204c1eb5eed36f4e15c4224e4b9db01cb3e4 \ + --hash=sha256:e6f5d254a22394847245f411a2956976401e84da4288aa70cbcd5190744062c1 \ + --hash=sha256:e7402ff96e2b073a98ef6d6142796426d705addd27b9d26c3b32dbaa06d7d069 \ + --hash=sha256:ea308cec940905ba008291d93619d92edaf83232ec85fbd514dcb329f3192761 \ + --hash=sha256:eaa8039b6d20137a4e02603aba37d12cd2dde7887500b8855356682fc33933f4 # via # alembic # safir @@ -1420,78 +1420,78 @@ uvloop==0.21.0 ; platform_python_implementation != 'PyPy' and sys_platform != 'c --hash=sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816 \ --hash=sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2 # via uvicorn -watchfiles==1.0.3 \ - --hash=sha256:0179252846be03fa97d4d5f8233d1c620ef004855f0717712ae1c558f1974a16 \ - --hash=sha256:06ce08549e49ba69ccc36fc5659a3d0ff4e3a07d542b895b8a9013fcab46c2dc \ - --hash=sha256:0b90651b4cf9e158d01faa0833b073e2e37719264bcee3eac49fc3c74e7d304b \ - --hash=sha256:0d1ec043f02ca04bf21b1b32cab155ce90c651aaf5540db8eb8ad7f7e645cba8 \ - --hash=sha256:0fe4e740ea94978b2b2ab308cbf9270a246bcbb44401f77cc8740348cbaeac3d \ - --hash=sha256:127de3883bdb29dbd3b21f63126bb8fa6e773b74eaef46521025a9ce390e1073 \ - --hash=sha256:1550be1a5cb3be08a3fb84636eaafa9b7119b70c71b0bed48726fd1d5aa9b868 \ - --hash=sha256:160eff7d1267d7b025e983ca8460e8cc67b328284967cbe29c05f3c3163711a3 \ - --hash=sha256:16db2d7e12f94818cbf16d4c8938e4d8aaecee23826344addfaaa671a1527b07 \ - --hash=sha256:1c6cf7709ed3e55704cc06f6e835bf43c03bc8e3cb8ff946bf69a2e0a78d9d77 \ - --hash=sha256:1da46bb1eefb5a37a8fb6fd52ad5d14822d67c498d99bda8754222396164ae42 \ - --hash=sha256:1df924ba82ae9e77340101c28d56cbaff2c991bd6fe8444a545d24075abb0a87 \ - --hash=sha256:1e263cc718545b7f897baeac1f00299ab6fabe3e18caaacacb0edf6d5f35513c \ - --hash=sha256:228e2247de583475d4cebf6b9af5dc9918abb99d1ef5ee737155bb39fb33f3c0 \ - --hash=sha256:275c1b0e942d335fccb6014d79267d1b9fa45b5ac0639c297f1e856f2f532552 \ - --hash=sha256:29b9cb35b7f290db1c31fb2fdf8fc6d3730cfa4bca4b49761083307f441cac5a \ - --hash=sha256:2b4691234d31686dca133c920f94e478b548a8e7c750f28dbbc2e4333e0d3da9 \ - --hash=sha256:2b961b86cd3973f5822826017cad7f5a75795168cb645c3a6b30c349094e02e3 \ - --hash=sha256:2dcc3f60c445f8ce14156854a072ceb36b83807ed803d37fdea2a50e898635d6 \ - --hash=sha256:2f492d2907263d6d0d52f897a68647195bc093dafed14508a8d6817973586b6b \ - --hash=sha256:310505ad305e30cb6c5f55945858cdbe0eb297fc57378f29bacceb534ac34199 \ - --hash=sha256:34e87c7b3464d02af87f1059fedda5484e43b153ef519e4085fe1a03dd94801e \ - --hash=sha256:418c5ce332f74939ff60691e5293e27c206c8164ce2b8ce0d9abf013003fb7fe \ - --hash=sha256:46e86ed457c3486080a72bc837300dd200e18d08183f12b6ca63475ab64ed651 \ - --hash=sha256:48681c86f2cb08348631fed788a116c89c787fdf1e6381c5febafd782f6c3b44 \ - --hash=sha256:489b80812f52a8d8c7b0d10f0d956db0efed25df2821c7a934f6143f76938bd6 \ - --hash=sha256:48c9f3bc90c556a854f4cab6a79c16974099ccfa3e3e150673d82d47a4bc92c9 \ - --hash=sha256:49bc1bc26abf4f32e132652f4b3bfeec77d8f8f62f57652703ef127e85a3e38d \ - --hash=sha256:52bb50a4c4ca2a689fdba84ba8ecc6a4e6210f03b6af93181bb61c4ec3abaf86 \ - --hash=sha256:5691340f259b8f76b45fb31b98e594d46c36d1dc8285efa7975f7f50230c9093 \ - --hash=sha256:62691f1c0894b001c7cde1195c03b7801aaa794a837bd6eef24da87d1542838d \ - --hash=sha256:632a52dcaee44792d0965c17bdfe5dc0edad5b86d6a29e53d6ad4bf92dc0ff49 \ - --hash=sha256:65ab1fb635476f6170b07e8e21db0424de94877e4b76b7feabfe11f9a5fc12b5 \ - --hash=sha256:6a5bc3ca468bb58a2ef50441f953e1f77b9a61bd1b8c347c8223403dc9b4ac9a \ - --hash=sha256:6a76494d2c5311584f22416c5a87c1e2cb954ff9b5f0988027bc4ef2a8a67181 \ - --hash=sha256:6f8dc09ae69af50bead60783180f656ad96bd33ffbf6e7a6fce900f6d53b08f1 \ - --hash=sha256:703aa5e50e465be901e0e0f9d5739add15e696d8c26c53bc6fc00eb65d7b9469 \ - --hash=sha256:713f67132346bdcb4c12df185c30cf04bdf4bf6ea3acbc3ace0912cab6b7cb8c \ - --hash=sha256:75d3bcfa90454dba8df12adc86b13b6d85fda97d90e708efc036c2760cc6ba44 \ - --hash=sha256:7ca05cacf2e5c4a97d02a2878a24020daca21dbb8823b023b978210a75c79098 \ - --hash=sha256:80bf4b459d94a0387617a1b499f314aa04d8a64b7a0747d15d425b8c8b151da0 \ - --hash=sha256:84fac88278f42d61c519a6c75fb5296fd56710b05bbdcc74bdf85db409a03780 \ - --hash=sha256:889a37e2acf43c377b5124166bece139b4c731b61492ab22e64d371cce0e6e80 \ - --hash=sha256:8af4b582d5fc1b8465d1d2483e5e7b880cc1a4e99f6ff65c23d64d070867ac58 \ - --hash=sha256:90b0fe1fcea9bd6e3084b44875e179b4adcc4057a3b81402658d0eb58c98edf8 \ - --hash=sha256:93436ed550e429da007fbafb723e0769f25bae178fbb287a94cb4ccdf42d3af3 \ - --hash=sha256:995c374e86fa82126c03c5b4630c4e312327ecfe27761accb25b5e1d7ab50ec8 \ - --hash=sha256:9af037d3df7188ae21dc1c7624501f2f90d81be6550904e07869d8d0e6766655 \ - --hash=sha256:9e080cf917b35b20c889225a13f290f2716748362f6071b859b60b8847a6aa43 \ - --hash=sha256:a2ec98e31e1844eac860e70d9247db9d75440fc8f5f679c37d01914568d18721 \ - --hash=sha256:abd85de513eb83f5ec153a802348e7a5baa4588b818043848247e3e8986094e8 \ - --hash=sha256:ac1be85fe43b4bf9a251978ce5c3bb30e1ada9784290441f5423a28633a958a7 \ - --hash=sha256:be37f9b1f8934cd9e7eccfcb5612af9fb728fecbe16248b082b709a9d1b348bf \ - --hash=sha256:bfcae6aecd9e0cb425f5145afee871465b98b75862e038d42fe91fd753ddd780 \ - --hash=sha256:c05b021f7b5aa333124f2a64d56e4cb9963b6efdf44e8d819152237bbd93ba15 \ - --hash=sha256:c14a07bdb475eb696f85c715dbd0f037918ccbb5248290448488a0b4ef201aad \ - --hash=sha256:c18f3502ad0737813c7dad70e3e1cc966cc147fbaeef47a09463bbffe70b0a00 \ - --hash=sha256:c2e9fe695ff151b42ab06501820f40d01310fbd58ba24da8923ace79cf6d702d \ - --hash=sha256:c68be72b1666d93b266714f2d4092d78dc53bd11cf91ed5a3c16527587a52e29 \ - --hash=sha256:ca94c85911601b097d53caeeec30201736ad69a93f30d15672b967558df02885 \ - --hash=sha256:cf745cbfad6389c0e331786e5fe9ae3f06e9d9c2ce2432378e1267954793975c \ - --hash=sha256:d9dd2b89a16cf7ab9c1170b5863e68de6bf83db51544875b25a5f05a7269e678 \ - --hash=sha256:ddff3f8b9fa24a60527c137c852d0d9a7da2a02cf2151650029fdc97c852c974 \ - --hash=sha256:e153a690b7255c5ced17895394b4f109d5dcc2a4f35cb809374da50f0e5c456a \ - --hash=sha256:ea2b51c5f38bad812da2ec0cd7eec09d25f521a8b6b6843cbccedd9a1d8a5c15 \ - --hash=sha256:ef9ec8068cf23458dbf36a08e0c16f0a2df04b42a8827619646637be1769300a \ - --hash=sha256:f280b02827adc9d87f764972fbeb701cf5611f80b619c20568e1982a277d6146 \ - --hash=sha256:f3ff7da165c99a5412fe5dd2304dd2dbaaaa5da718aad942dcb3a178eaa70c56 \ - --hash=sha256:f58d3bfafecf3d81c15d99fc0ecf4319e80ac712c77cf0ce2661c8cf8bf84066 \ - --hash=sha256:f79fe7993e230a12172ce7d7c7db061f046f672f2b946431c81aff8f60b2758b \ - --hash=sha256:ffe709b1d0bc2e9921257569675674cafb3a5f8af689ab9f3f2b3f88775b960f +watchfiles==1.0.4 \ + --hash=sha256:02a526ee5b5a09e8168314c905fc545c9bc46509896ed282aeb5a8ba9bd6ca27 \ + --hash=sha256:05d341c71f3d7098920f8551d4df47f7b57ac5b8dad56558064c3431bdfc0b74 \ + --hash=sha256:076f293100db3b0b634514aa0d294b941daa85fc777f9c698adb1009e5aca0b1 \ + --hash=sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712 \ + --hash=sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1 \ + --hash=sha256:0bc80d91ddaf95f70258cf78c471246846c1986bcc5fd33ccc4a1a67fcb40f9a \ + --hash=sha256:13c2ce7b72026cfbca120d652f02c7750f33b4c9395d79c9790b27f014c8a5a2 \ + --hash=sha256:1941b4e39de9b38b868a69b911df5e89dc43767feeda667b40ae032522b9b5f1 \ + --hash=sha256:1eacd91daeb5158c598fe22d7ce66d60878b6294a86477a4715154990394c9b3 \ + --hash=sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2 \ + --hash=sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90 \ + --hash=sha256:278aaa395f405972e9f523bd786ed59dfb61e4b827856be46a42130605fd0899 \ + --hash=sha256:2a9f93f8439639dc244c4d2902abe35b0279102bca7bbcf119af964f51d53c19 \ + --hash=sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303 \ + --hash=sha256:31f1a379c9dcbb3f09cf6be1b7e83b67c0e9faabed0471556d9438a4a4e14202 \ + --hash=sha256:32b026a6ab64245b584acf4931fe21842374da82372d5c039cba6bf99ef722f3 \ + --hash=sha256:342622287b5604ddf0ed2d085f3a589099c9ae8b7331df3ae9845571586c4f3d \ + --hash=sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590 \ + --hash=sha256:3f68d8e9d5a321163ddacebe97091000955a1b74cd43724e346056030b0bacee \ + --hash=sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12 \ + --hash=sha256:47eb32ef8c729dbc4f4273baece89398a4d4b5d21a1493efea77a17059f4df8a \ + --hash=sha256:4810ea2ae622add560f4aa50c92fef975e475f7ac4900ce5ff5547b2434642d8 \ + --hash=sha256:4e997802d78cdb02623b5941830ab06f8860038faf344f0d288d325cc9c5d2ff \ + --hash=sha256:4ebbeca9360c830766b9f0df3640b791be569d988f4be6c06d6fae41f187f105 \ + --hash=sha256:4f8c4998506241dedf59613082d1c18b836e26ef2a4caecad0ec41e2a15e4226 \ + --hash=sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af \ + --hash=sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9 \ + --hash=sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a \ + --hash=sha256:5e0227b8ed9074c6172cf55d85b5670199c99ab11fd27d2c473aa30aec67ee42 \ + --hash=sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407 \ + --hash=sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205 \ + --hash=sha256:740d103cd01458f22462dedeb5a3382b7f2c57d07ff033fbc9465919e5e1d0f3 \ + --hash=sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff \ + --hash=sha256:7b75fee5a16826cf5c46fe1c63116e4a156924d668c38b013e6276f2582230f0 \ + --hash=sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d \ + --hash=sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9 \ + --hash=sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733 \ + --hash=sha256:8b1f135238e75d075359cf506b27bf3f4ca12029c47d3e769d8593a2024ce161 \ + --hash=sha256:8d0d0630930f5cd5af929040e0778cf676a46775753e442a3f60511f2409f48f \ + --hash=sha256:90192cdc15ab7254caa7765a98132a5a41471cf739513cc9bcf7d2ffcc0ec7b2 \ + --hash=sha256:95b42cac65beae3a362629950c444077d1b44f1790ea2772beaea95451c086bb \ + --hash=sha256:9745a4210b59e218ce64c91deb599ae8775c8a9da4e95fb2ee6fe745fc87d01a \ + --hash=sha256:9d1ef56b56ed7e8f312c934436dea93bfa3e7368adfcf3df4c0da6d4de959a1e \ + --hash=sha256:9eea33ad8c418847dd296e61eb683cae1c63329b6d854aefcd412e12d94ee235 \ + --hash=sha256:9f25d0ba0fe2b6d2c921cf587b2bf4c451860086534f40c384329fb96e2044d1 \ + --hash=sha256:9fe37a2de80aa785d340f2980276b17ef697ab8db6019b07ee4fd28a8359d2f3 \ + --hash=sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b \ + --hash=sha256:a462490e75e466edbb9fc4cd679b62187153b3ba804868452ef0577ec958f5ff \ + --hash=sha256:a5ae5706058b27c74bac987d615105da17724172d5aaacc6c362a40599b6de43 \ + --hash=sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60 \ + --hash=sha256:ab0311bb2ffcd9f74b6c9de2dda1612c13c84b996d032cd74799adb656af4e8b \ + --hash=sha256:ab594e75644421ae0a2484554832ca5895f8cab5ab62de30a1a57db460ce06c6 \ + --hash=sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80 \ + --hash=sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94 \ + --hash=sha256:b77d5622ac5cc91d21ae9c2b284b5d5c51085a0bdb7b518dba263d0af006132c \ + --hash=sha256:ba5bb3073d9db37c64520681dd2650f8bd40902d991e7b4cfaeece3e32561d08 \ + --hash=sha256:bdef5a1be32d0b07dcea3318a0be95d42c98ece24177820226b56276e06b63b0 \ + --hash=sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c \ + --hash=sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e \ + --hash=sha256:cc27a65069bcabac4552f34fd2dce923ce3fcde0721a16e4fb1b466d63ec831f \ + --hash=sha256:cdbd912a61543a36aef85e34f212e5d2486e7c53ebfdb70d1e0b060cc50dd0bf \ + --hash=sha256:cdcc92daeae268de1acf5b7befcd6cfffd9a047098199056c72e4623f531de18 \ + --hash=sha256:d3452c1ec703aa1c61e15dfe9d482543e4145e7c45a6b8566978fbb044265a21 \ + --hash=sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc \ + --hash=sha256:d8d3d9203705b5797f0af7e7e5baa17c8588030aaadb7f6a86107b7247303817 \ + --hash=sha256:e0611d244ce94d83f5b9aff441ad196c6e21b55f77f3c47608dcf651efe54c4a \ + --hash=sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902 \ + --hash=sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d \ + --hash=sha256:f9ce064e81fe79faa925ff03b9f4c1a98b0bbb4a1b8c1b015afa93030cb21a49 \ + --hash=sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844 \ + --hash=sha256:fc2eb5d14a8e0d5df7b36288979176fbb39672d45184fc4b1c004d7c3ce29317 # via uvicorn wcwidth==0.2.13 \ --hash=sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859 \ diff --git a/requirements/tox.txt b/requirements/tox.txt index 5658fc2..e2c5853 100644 --- a/requirements/tox.txt +++ b/requirements/tox.txt @@ -220,24 +220,24 @@ urllib3==2.3.0 \ # -c requirements/dev.txt # docker # requests -uv==0.5.14 \ - --hash=sha256:02fa1c9223ccb90e686a02c5c37a3357cac00165263b41f5f53119f815335157 \ - --hash=sha256:03c0fe3f1257fb73650230e13e1fd928422b708276539e816242953681561bd7 \ - --hash=sha256:12f041458a9d00c84a29eab79d3b5bc53dfd50947787b7c0f628eda7678621a7 \ - --hash=sha256:2a6ecf961d96896f32fd6c198baa25d99f20ed12d6aeb5e514aa5874ae5000c3 \ - --hash=sha256:2dd71f94881d2d402896ab4e94f47dd5e6f4f097c6ae4115345030e263910911 \ - --hash=sha256:42d1e3849d3594b2d3a93f335a776597db8a1d3dcee43dbd4ec49f1ad1f63093 \ - --hash=sha256:484e7921268057011264352696877e1e0c4fd1e0dab5057da73623c0a9d21067 \ - --hash=sha256:54672772289c01811cf1efca4425fa2ae70bfaff020ec197288b7f6c969357e3 \ - --hash=sha256:5fe0748a1c045dc1e95e77a8181ce9bfb848ebbeb175e277ad256a7befbbb61d \ - --hash=sha256:786e16620d679cfa98cea1dd126405a82b63ee43e63994bc208abd0ab68cc529 \ - --hash=sha256:90731cdc9ecb5fa7366696e07b81adf211d54417757f240ddd600f8b7b0107c7 \ - --hash=sha256:93a187ed9da2df5788b755016c77559486c29cf3c87b9ca46822946e3c0734a4 \ - --hash=sha256:a436e948f5ea149b9cd0eb30f923eca190b2d05bb48d9543d4373e1132da1b91 \ - --hash=sha256:b4aa2131c1fc6db62311b26e768987aa582fe6c9256951b5fc9f6f56fb3e2cb9 \ - --hash=sha256:d78a1289d51a9a605703540712963703e58c43380f9645738f6fea0ab4657788 \ - --hash=sha256:f83af784fc842535c382aa58870a8c61aba0dfaa4bf4f2fdf5df03ec934060e2 \ - --hash=sha256:ff362cb0ed592af69433680d9597f34e62c22fe7658a894212e80e24f7c506b6 +uv==0.5.16 \ + --hash=sha256:2cf976b5d29f67bd8e5aa0c9b9c481c7df1580839ac107cbf58811c894a7e6a6 \ + --hash=sha256:2e6f4fbd1cef0227e15374ab56a59ffcbd10c66f263b4e22ac43ca37061539d1 \ + --hash=sha256:3419888b178b82511faebd8d1ca9cb9f5920a7142406898d76878adaffe8dfb1 \ + --hash=sha256:598a0fa168ffe2786e11bfe0103fe22c06e90fe193ced172b843daf9abb42212 \ + --hash=sha256:8fb0b26b9885d57609724fd4efb65785e6896b9bd4ecb865480962999d4247d4 \ + --hash=sha256:9522aad257223c9470bb93b78f08dc473acf73a4a1403b999b2f1871a06343a6 \ + --hash=sha256:9e73ae821678e7395635be0fec810f242e42f47b5f9cb82a7fc3d279bc37edb5 \ + --hash=sha256:a7508b271a461603a38095eaca27b1766bd71f0a02232868a8faae0d8e8a1ae3 \ + --hash=sha256:ba5f364809a46e590368a3f85638b71118952f30c79125f87144d9f00be0c9cb \ + --hash=sha256:c609bb564f4d5822acd25d8881d76123c2e9aa26b634a6444b56142daff851b8 \ + --hash=sha256:c6d3b45d66adedebe5c56602970b2c0fdf6d1977f53464691d428f1a7daa911b \ + --hash=sha256:c7ee83877f4e324df4a61e7c79a476d26725e415894a755ce45de87709fc9581 \ + --hash=sha256:c8310f40b8834812ddfb195fd62aafaed44b2993c71de8fa75ca0960c739d04e \ + --hash=sha256:d540bbc1831d4187a29a9e8f7219e2093c491a45bcbcf2a6196f81c6d2162691 \ + --hash=sha256:d54777d6c87a12213a59d2109289e4e35374cc79f88e29617ff764189b8b9cad \ + --hash=sha256:e49b2c242cbc201665670dcc3ffa509fa6e131ebcf0423c59df91f2f21eca9d7 \ + --hash=sha256:e764721c425ca6cde184ae69517748e47c4ea7f0d9c7dafa78721feeabc58e01 # via tox-uv virtualenv==20.28.1 \ --hash=sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb \ From ff34d77a977d3977b63210bcf2e4cf43aa085544 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Fri, 10 Jan 2025 14:33:38 -0500 Subject: [PATCH 03/14] Add Alembic dependency --- requirements/main.in | 1 + requirements/main.txt | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/requirements/main.in b/requirements/main.in index 2d1bac8..0966492 100644 --- a/requirements/main.in +++ b/requirements/main.in @@ -14,6 +14,7 @@ uvicorn[standard] # Other dependencies. # git+https://github.com/lsst-sqre/safir.git@main#egg=safir[db,arq,redis] safir[db,arq,redis] +alembic[tz] pydantic[email] pydantic_settings click diff --git a/requirements/main.txt b/requirements/main.txt index ecc636d..cb5051e 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -40,7 +40,9 @@ aiokafka==0.12.0 \ alembic==1.14.0 \ --hash=sha256:99bd884ca390466db5e27ffccff1d179ec5c05c965cfefc0607e69f9e411cb25 \ --hash=sha256:b00892b53b3642d0b8dbedba234dbf1924b69be83a9a769d5a624b01094e304b - # via safir + # via + # -r requirements/main.in + # safir annotated-types==0.7.0 \ --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 From 69f4b842b4dda49cf81deab2a7206cb4f0866358 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Fri, 10 Jan 2025 15:18:45 -0500 Subject: [PATCH 04/14] Add Alembic integration This Alembic integration is described in https://safir.lsst.io/user-guide/database/schema.html#set-up-alembic - Includes Alembic configuration files, and adds those Alembic files to the Dockerfile to run online migrations. - Modifies the init CLI command to ensure the database is stamped with the current schema - Includes addition CLI commands for applying the alembic migration and for verifying that hte DB schema is current - Updates the start up of the FastAPI service and the Arq workers to check that the database is current --- Dockerfile | 7 ++++ alembic.ini | 15 +++++++++ alembic/README | 1 + alembic/env.py | 22 +++++++++++++ alembic/script.py.mako | 26 +++++++++++++++ pyproject.toml | 3 ++ src/timessquare/cli.py | 58 ++++++++++++++++++++++++++++++++-- src/timessquare/main.py | 8 +++++ src/timessquare/worker/main.py | 9 ++++++ tests/conftest.py | 8 ++++- tox.ini | 38 ++++++++++++++++++++++ 11 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 alembic.ini create mode 100644 alembic/README create mode 100644 alembic/env.py create mode 100644 alembic/script.py.mako diff --git a/Dockerfile b/Dockerfile index 3eb8f8d..0f8e4a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -57,6 +57,13 @@ COPY --from=install-image /opt/venv /opt/venv COPY scripts/start-api.sh /start-api.sh +# Copy the Alembic configuration and migrations, and set that path as the +# working directory so that Alembic can be run with a simple entry command +# and no extra configuration. +COPY --from=install-image /workdir/alembic.ini /app/alembic.ini +COPY --from=install-image /workdir/alembic /app/alembic +WORKDIR /app + # Make sure we use the virtualenv ENV PATH="/opt/venv/bin:$PATH" diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..86357dd --- /dev/null +++ b/alembic.ini @@ -0,0 +1,15 @@ +[alembic] +script_location = %(here)s/alembic +file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d_%%(rev)s_%%(slug)s +prepend_sys_path = . +timezone = UTC +version_path_separator = os + +[post_write_hooks] +hooks = ruff ruff_format +ruff.type = exec +ruff.executable = ruff +ruff.options = check --fix REVISION_SCRIPT_FILENAME +ruff_format.type = exec +ruff_format.executable = ruff +ruff_format.options = format REVISION_SCRIPT_FILENAME diff --git a/alembic/README b/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/alembic/env.py b/alembic/env.py new file mode 100644 index 0000000..6fbd927 --- /dev/null +++ b/alembic/env.py @@ -0,0 +1,22 @@ +"""Alembic migration environment.""" + +from alembic import context +from safir.database import run_migrations_offline, run_migrations_online +from safir.logging import configure_alembic_logging, configure_logging + +from timessquare.config import config +from timessquare.dbschema import Base + +# Configure structlog. +configure_logging(name="timessquare", log_level=config.log_level) +configure_alembic_logging() + +# Run the migrations. +if context.is_offline_mode(): + run_migrations_offline(Base.metadata, config.database_url) +else: + run_migrations_online( + Base.metadata, + config.database_url, + config.database_password, + ) diff --git a/alembic/script.py.mako b/alembic/script.py.mako new file mode 100644 index 0000000..fbc4b07 --- /dev/null +++ b/alembic/script.py.mako @@ -0,0 +1,26 @@ +"""${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 ""} + +# 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"} diff --git a/pyproject.toml b/pyproject.toml index e8e86dd..6521d5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,6 +110,9 @@ extend-exclude = [ ] [tool.ruff.lint.per-file-ignores] +"alembic/env.py" = [ + "INP001", # Alembic config isn't a package +] "src/timessquare/config.py" = [ "FBT001", # Pydantic validators take boolean arguments ] diff --git a/src/timessquare/cli.py b/src/timessquare/cli.py index cc84291..f838881 100644 --- a/src/timessquare/cli.py +++ b/src/timessquare/cli.py @@ -2,12 +2,20 @@ from __future__ import annotations +import subprocess +from pathlib import Path + import click import structlog import uvicorn from redis.asyncio import Redis from safir.asyncio import run_with_asyncio -from safir.database import create_database_engine, initialize_database +from safir.database import ( + create_database_engine, + initialize_database, + is_database_current, + stamp_database, +) from .config import config from .dbschema import Base @@ -53,12 +61,19 @@ def develop(port: int) -> None: @main.command() +@click.option( + "--alembic-config-path", + envvar="TS_ALEMBIC_CONFIG_PATH", + type=click.Path(path_type=Path), + default=Path("/app/alembic.ini"), + help="Alembic configuration file.", +) @click.option( "--reset", is_flag=True, help="Delete all existing database data." ) @run_with_asyncio -async def init(*, reset: bool) -> None: - """Initialize the database storage.""" +async def init(*, alembic_config_path: Path, reset: bool) -> None: + """Initialize the SQL database storage.""" logger = structlog.get_logger(config.logger_name) engine = create_database_engine( str(config.database_url), config.database_password.get_secret_value() @@ -67,6 +82,43 @@ async def init(*, reset: bool) -> None: engine, logger, schema=Base.metadata, reset=reset ) await engine.dispose() + stamp_database(alembic_config_path) + + +@main.command() +@click.option( + "--alembic-config-path", + envvar="TS_ALEMBIC_CONFIG_PATH", + type=click.Path(path_type=Path), + default=Path("/app/alembic.ini"), + help="Alembic configuration file.", +) +def update_db_schema(*, alembic_config_path: Path) -> None: + """Update the SQL database schema.""" + subprocess.run( + ["alembic", "upgrade", "head"], + check=True, + cwd=str(alembic_config_path.parent), + ) + + +@main.command() +@click.option( + "--alembic-config-path", + envvar="TS_ALEMBIC_CONFIG_PATH", + type=click.Path(path_type=Path), + default=Path("/app/alembic.ini"), + help="Alembic configuration file.", +) +@run_with_asyncio +async def validate_db_schema(*, alembic_config_path: Path) -> None: + """Validate that the SQL database schema is current.""" + engine = create_database_engine( + config.database_url, config.database_password + ) + logger = structlog.get_logger("timessquare") + if not await is_database_current(engine, logger, alembic_config_path): + raise click.ClickException("Database schema is not current") @main.command("reset-html") diff --git a/src/timessquare/main.py b/src/timessquare/main.py index b2dc99b..07a2974 100644 --- a/src/timessquare/main.py +++ b/src/timessquare/main.py @@ -17,6 +17,7 @@ from fastapi import FastAPI from fastapi.openapi.utils import get_openapi +from safir.database import create_database_engine, is_database_current from safir.dependencies.arq import arq_dependency from safir.dependencies.db_session import db_session_dependency from safir.dependencies.http_client import http_client_dependency @@ -41,6 +42,13 @@ async def lifespan(app: FastAPI) -> AsyncIterator: logger = get_logger(__name__) logger.debug("Times Square is starting up.") + engine = create_database_engine( + config.database_url, config.database_password + ) + if not await is_database_current(engine, logger): + raise RuntimeError("Database schema out of date") + await engine.dispose() + await db_session_dependency.initialize( str(config.database_url), config.database_password.get_secret_value() ) diff --git a/src/timessquare/worker/main.py b/src/timessquare/worker/main.py index eeba171..49a91a7 100644 --- a/src/timessquare/worker/main.py +++ b/src/timessquare/worker/main.py @@ -9,6 +9,7 @@ import arq import httpx import structlog +from safir.database import create_database_engine, is_database_current from safir.dependencies.db_session import db_session_dependency from safir.logging import configure_logging from safir.slack.blockkit import SlackMessage, SlackTextField @@ -58,6 +59,14 @@ async def startup(ctx: dict[Any, Any]) -> None: ctx["logger"] = logger + # Check if the database schema is up to date + engine = create_database_engine( + config.database_url, config.database_password + ) + if not await is_database_current(engine, logger): + raise RuntimeError("Database schema out of date") + await engine.dispose() + # Set up FastAPI dependencies; we can use them "manually" with # arq to provide resources similarly to FastAPI endpoints await db_session_dependency.initialize( diff --git a/tests/conftest.py b/tests/conftest.py index cfa2b40..6d0614a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,11 @@ from fastapi import FastAPI from gidgethub.httpx import GitHubAPI from httpx import ASGITransport, AsyncClient -from safir.database import create_database_engine, initialize_database +from safir.database import ( + create_database_engine, + initialize_database, + stamp_database_async, +) from timessquare import main from timessquare.config import config @@ -25,10 +29,12 @@ async def app() -> AsyncIterator[FastAPI]: events are sent during test execution. """ logger = structlog.get_logger(config.logger_name) + engine = create_database_engine( config.database_url, config.database_password.get_secret_value() ) await initialize_database(engine, logger, schema=Base.metadata, reset=True) + await stamp_database_async(engine) await engine.dispose() async with LifespanManager(main.app): yield main.app diff --git a/tox.ini b/tox.ini index fe682e0..420692d 100644 --- a/tox.ini +++ b/tox.ini @@ -45,6 +45,7 @@ setenv = # environment variables; pytest can override these for individual test runs SAFIR_LOG_LEVEL = DEBUG SAFIR_PROFILE = development + TS_ALEMBIC_CONFIG_PATH = {toxinidir}/alembic.ini TS_ENVIRONMENT_URL = https://test.example.com TS_DATABASE_URL = postgresql://timessquare@localhost:5433/timessquare TS_DATABASE_PASSWORD = INSECURE-PASSWORD @@ -81,6 +82,42 @@ deps = pre-commit commands = pre-commit run --all-files +[testenv:alembic] +description = Run Alembic against a test database +setenv = + TS_ENVIRONMENT_URL = https://test.example.com + TS_PATH_PREFIX = /times-square/api + TS_ALEMBIC_CONFIG_PATH = {toxinidir}/alembic.ini + TS_DATABASE_URL = postgresql://timessquare@127.0.0.1:5432/timessquare + TS_DATABASE_PASSWORD = INSECURE-PASSWORD + TS_REDIS_URL = redis://localhost:6379/0 + TS_REDIS_QUEUE_URL = redis://localhost:6379/1 + TS_GAFAELFAWR_TOKEN = gt-eOfLolxU8FJ1xr08U7RTbg.Jr-KHSeISXwR5GXHiLemhw + TS_GITHUB_APP_PRIVATE_KEY = foo + TS_GITHUB_WEBHOOK_SECRET = foo + TS_ENABLE_GITHUB_APP = false +deps = + ruff # For Alembic revision formatting +commands = + alembic {posargs} + +[testenv:cli] +description = Run command-line tool against a test database +commands = + timessquare {posargs} +setenv = + TS_ENVIRONMENT_URL = https://test.example.com + TS_PATH_PREFIX = /times-square/api + TS_ALEMBIC_CONFIG_PATH = {toxinidir}/alembic.ini + TS_DATABASE_URL = postgresql://timessquare@127.0.0.1:5432/timessquare + TS_DATABASE_PASSWORD = INSECURE-PASSWORD + TS_REDIS_URL = redis://localhost:6379/0 + TS_REDIS_QUEUE_URL = redis://localhost:6379/1 + TS_GAFAELFAWR_TOKEN = gt-eOfLolxU8FJ1xr08U7RTbg.Jr-KHSeISXwR5GXHiLemhw + TS_GITHUB_APP_PRIVATE_KEY = foo + TS_GITHUB_WEBHOOK_SECRET = foo + TS_ENABLE_GITHUB_APP = false + [testenv:run] description = Run the development server with auto-reload for code changes. usedevelop = true @@ -90,6 +127,7 @@ setenv = SAFIR_PROFILE = development TS_ENVIRONMENT_URL = https://test.example.com TS_PATH_PREFIX = /times-square/api + TS_ALEMBIC_CONFIG_PATH = {toxinidir}/alembic.ini TS_DATABASE_URL = postgresql://timessquare@127.0.0.1:5432/timessquare TS_DATABASE_PASSWORD = INSECURE-PASSWORD TS_REDIS_URL = redis://localhost:6379/0 From 509e5c6cc3a500cc89a92393a9e25b2ccb4db2a6 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Fri, 10 Jan 2025 16:53:14 -0500 Subject: [PATCH 05/14] Create initial migration This initial Alembic migration is created via: docker-compose -f docker-compose.yaml up tox run -e alembic -- revision --autogenerate -m "Initial schema." While doing this we discovered some tweaks to ruff formatting: - Adjusted template - Added ruff configurations to pyproject.toml --- alembic/script.py.mako | 12 ++-- ...250110_2121_487195933fee_initial_schema.py | 69 +++++++++++++++++++ pyproject.toml | 5 ++ 3 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 alembic/versions/20250110_2121_487195933fee_initial_schema.py diff --git a/alembic/script.py.mako b/alembic/script.py.mako index fbc4b07..6fcfd30 100644 --- a/alembic/script.py.mako +++ b/alembic/script.py.mako @@ -3,19 +3,19 @@ Revision ID: ${up_revision} Revises: ${down_revision | comma,n} Create Date: ${create_date} - """ -from typing import Sequence, Union -from alembic import op +from collections.abc import Sequence + import sqlalchemy as sa +from alembic import op ${imports if imports else ""} # 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)} +down_revision: str | None = ${repr(down_revision)} +branch_labels: str | Sequence[str] | None = ${repr(branch_labels)} +depends_on: str | Sequence[str] | None = ${repr(depends_on)} def upgrade() -> None: diff --git a/alembic/versions/20250110_2121_487195933fee_initial_schema.py b/alembic/versions/20250110_2121_487195933fee_initial_schema.py new file mode 100644 index 0000000..beb28e6 --- /dev/null +++ b/alembic/versions/20250110_2121_487195933fee_initial_schema.py @@ -0,0 +1,69 @@ +"""Initial schema. + +Revision ID: 487195933fee +Revises: +Create Date: 2025-01-10 21:21:26.706763+00:00 +""" + +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "487195933fee" +down_revision: str | None = None +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + op.create_table( + "pages", + sa.Column("name", sa.Unicode(length=32), nullable=False), + sa.Column("ipynb", sa.UnicodeText(), nullable=False), + sa.Column("parameters", sa.JSON(), nullable=False), + sa.Column("title", sa.UnicodeText(), nullable=False), + sa.Column("date_added", sa.DateTime(), nullable=False), + sa.Column("authors", sa.JSON(), nullable=False), + sa.Column("tags", sa.JSON(), nullable=False), + sa.Column("uploader_username", sa.Unicode(length=64), nullable=True), + sa.Column("date_deleted", sa.DateTime(), nullable=True), + sa.Column("description", sa.UnicodeText(), nullable=True), + sa.Column("cache_ttl", sa.Integer(), nullable=True), + sa.Column("github_owner", sa.Unicode(length=255), nullable=True), + sa.Column("github_repo", sa.Unicode(length=255), nullable=True), + sa.Column("github_commit", sa.Unicode(length=40), nullable=True), + sa.Column( + "repository_path_prefix", sa.Unicode(length=2048), nullable=True + ), + sa.Column( + "repository_display_path_prefix", + sa.Unicode(length=2048), + nullable=True, + ), + sa.Column( + "repository_path_stem", sa.Unicode(length=255), nullable=True + ), + sa.Column( + "repository_source_extension", + sa.Unicode(length=255), + nullable=True, + ), + sa.Column( + "repository_sidecar_extension", + sa.Unicode(length=255), + nullable=True, + ), + sa.Column( + "repository_source_sha", sa.Unicode(length=40), nullable=True + ), + sa.Column( + "repository_sidecar_sha", sa.Unicode(length=40), nullable=True + ), + sa.PrimaryKeyConstraint("name"), + ) + + +def downgrade() -> None: + op.drop_table("pages") diff --git a/pyproject.toml b/pyproject.toml index 6521d5b..b5b3e9d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,6 +113,10 @@ extend-exclude = [ "alembic/env.py" = [ "INP001", # Alembic config isn't a package ] +"alembic/versions/**" = [ + "INP001", # Alembic config isn't a package + "D103", # Alembic migrations don't need docstrings +] "src/timessquare/config.py" = [ "FBT001", # Pydantic validators take boolean arguments ] @@ -130,4 +134,5 @@ extend-exclude = [ [tool.ruff.lint.isort] known-first-party = ["timessquare", "tests"] +known-third-party = ["alembic"] split-on-trailing-comma = false From b48432dfe0f95790ea15a947fcbb033977ff5c5f Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Fri, 10 Jan 2025 17:09:11 -0500 Subject: [PATCH 06/14] Add change log entry for alembic --- changelog.d/20250110_170830_jsick_DM_48385.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 changelog.d/20250110_170830_jsick_DM_48385.md diff --git a/changelog.d/20250110_170830_jsick_DM_48385.md b/changelog.d/20250110_170830_jsick_DM_48385.md new file mode 100644 index 0000000..b73c730 --- /dev/null +++ b/changelog.d/20250110_170830_jsick_DM_48385.md @@ -0,0 +1,17 @@ + + +### Backwards-incompatible changes + +- + +### New features + +- + +### Bug fixes + +- + +### Other changes + +- Times Square now uses Alembic to manage database schema versioning and migrations. From d9fb9bfbf6ad2c4ac9ae70ae76ab61a431607938 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Mon, 13 Jan 2025 11:54:14 -0500 Subject: [PATCH 07/14] Set TS_ALEMBIC_CONFIG_PATH in dockerfile Since the dockerfile "knows" where the alembic configuration is, we'll add the env var configuration there rather than putting it as a command line default. --- Dockerfile | 4 ++++ src/timessquare/cli.py | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0f8e4a5..8af0981 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,6 +67,10 @@ WORKDIR /app # Make sure we use the virtualenv ENV PATH="/opt/venv/bin:$PATH" +# Set environment variable for Alembic config; other variables are set +# via Kubernetes. +ENV TS_ALEMBIC_CONFIG_PATH="/app/alembic.ini" + # Switch to the non-root user. USER appuser diff --git a/src/timessquare/cli.py b/src/timessquare/cli.py index f838881..bcf4b15 100644 --- a/src/timessquare/cli.py +++ b/src/timessquare/cli.py @@ -65,7 +65,6 @@ def develop(port: int) -> None: "--alembic-config-path", envvar="TS_ALEMBIC_CONFIG_PATH", type=click.Path(path_type=Path), - default=Path("/app/alembic.ini"), help="Alembic configuration file.", ) @click.option( @@ -90,7 +89,6 @@ async def init(*, alembic_config_path: Path, reset: bool) -> None: "--alembic-config-path", envvar="TS_ALEMBIC_CONFIG_PATH", type=click.Path(path_type=Path), - default=Path("/app/alembic.ini"), help="Alembic configuration file.", ) def update_db_schema(*, alembic_config_path: Path) -> None: @@ -107,7 +105,6 @@ def update_db_schema(*, alembic_config_path: Path) -> None: "--alembic-config-path", envvar="TS_ALEMBIC_CONFIG_PATH", type=click.Path(path_type=Path), - default=Path("/app/alembic.ini"), help="Alembic configuration file.", ) @run_with_asyncio From b2046c62af67fa15042a455f500364560f421499 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Mon, 13 Jan 2025 11:56:02 -0500 Subject: [PATCH 08/14] Make capitalization of AS consistent --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8af0981..2c5d27e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ # - Runs a non-root user. # - Sets up the entrypoint and port. -FROM python:3.12.6-slim-bookworm as base-image +FROM python:3.12.6-slim-bookworm AS base-image # Update system packages COPY scripts/install-base-packages.sh . From 477904ccbbf7a33f7041204c5b73a9dc73c5c349 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Mon, 13 Jan 2025 13:01:53 -0500 Subject: [PATCH 09/14] Make the init command sync for stamp_database It turns out stamp_database needs to run from a sync context because it'll run its own asyncio even loop to call `run_async_migrations`. Therefore we're following Gafaelfawr's pattern of making init sync but then running an extracted database initialization function via asyncio.run. --- src/timessquare/cli.py | 19 +++++--------- src/timessquare/database.py | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 src/timessquare/database.py diff --git a/src/timessquare/cli.py b/src/timessquare/cli.py index bcf4b15..d16ae6a 100644 --- a/src/timessquare/cli.py +++ b/src/timessquare/cli.py @@ -2,6 +2,7 @@ from __future__ import annotations +import asyncio import subprocess from pathlib import Path @@ -12,13 +13,12 @@ from safir.asyncio import run_with_asyncio from safir.database import ( create_database_engine, - initialize_database, is_database_current, stamp_database, ) from .config import config -from .dbschema import Base +from .database import init_database from .storage.nbhtmlcache import NbHtmlCacheStore @@ -70,18 +70,13 @@ def develop(port: int) -> None: @click.option( "--reset", is_flag=True, help="Delete all existing database data." ) -@run_with_asyncio -async def init(*, alembic_config_path: Path, reset: bool) -> None: +def init(*, alembic_config_path: Path, reset: bool) -> None: """Initialize the SQL database storage.""" - logger = structlog.get_logger(config.logger_name) - engine = create_database_engine( - str(config.database_url), config.database_password.get_secret_value() - ) - await initialize_database( - engine, logger, schema=Base.metadata, reset=reset - ) - await engine.dispose() + logger = structlog.get_logger("timessquare") + logger.debug("Initializing database") + asyncio.run(init_database(config, logger, reset=reset)) stamp_database(alembic_config_path) + logger.debug("Finished initializing data stores") @main.command() diff --git a/src/timessquare/database.py b/src/timessquare/database.py new file mode 100644 index 0000000..6b0a92b --- /dev/null +++ b/src/timessquare/database.py @@ -0,0 +1,51 @@ +"""Database management functions.""" + +from __future__ import annotations + +from safir.database import create_database_engine, initialize_database +from sqlalchemy.ext.asyncio import AsyncEngine +from structlog.stdlib import BoundLogger + +from .config import Config +from .dbschema import Base + +__all__ = ["init_database"] + + +async def init_database( + config: Config, + logger: BoundLogger, + engine: AsyncEngine | None = None, + *, + reset: bool = False, +) -> None: + """Initialize the database. + + This is the internal async implementation details of the ``init`` command, + except for the Alembic parts. Alembic has to run outside of a running + asyncio loop, hence this separation. Always stamp the database with + Alembic after calling this function. + + Parameters + ---------- + config + Application configuration. + logger + Logger to use for status reporting. + engine + If given, database engine to use, which avoids the need to create + another one. + reset + Whether to reset the database. + """ + engine_created = False + if not engine: + engine = create_database_engine( + config.database_url, config.database_password + ) + engine_created = True + await initialize_database( + engine, logger, schema=Base.metadata, reset=reset + ) + if engine_created: + await engine.dispose() From 0b40fbb2be95b66e1cb2a8eb6b1e4c11aa899cf1 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Mon, 13 Jan 2025 15:21:20 -0500 Subject: [PATCH 10/14] Add test to ensure schema is current This test from https://safir.lsst.io/user-guide/database/schema.html#testing-database-migrations ensures that the SQLAlchemy models are consistent with the current revision of the Alembic migrations. --- pyproject.toml | 1 + tests/schema_test.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/schema_test.py diff --git a/pyproject.toml b/pyproject.toml index b5b3e9d..c0d27ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,6 +130,7 @@ extend-exclude = [ "S106", # tests are allowed to hard-code dummy passwords "SLF001", # tests are allowed to access private members "T201", # tests are allowed to use print + "ASYNC221", # async tests are allowed to use sync functions ] [tool.ruff.lint.isort] diff --git a/tests/schema_test.py b/tests/schema_test.py new file mode 100644 index 0000000..1d2e060 --- /dev/null +++ b/tests/schema_test.py @@ -0,0 +1,23 @@ +"""Test the schema of the database.""" + +import subprocess + +import pytest +from safir.database import create_database_engine, drop_database + +from timessquare.config import config +from timessquare.dbschema import Base + + +@pytest.mark.asyncio +async def test_schema() -> None: + """Ensure that the SQLAlchemy model is consistent with the current + Alembic revision. + """ + engine = create_database_engine( + config.database_url, config.database_password + ) + await drop_database(engine, Base.metadata) + await engine.dispose() + subprocess.run(["alembic", "upgrade", "head"], check=True) + subprocess.run(["alembic", "check"], check=True) From 9626629159f6e0de48da1e8c31efdf5d01734a42 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Mon, 13 Jan 2025 16:54:17 -0500 Subject: [PATCH 11/14] Add brief note on database migrations --- docs/_rst_epilog.rst | 1 + docs/dev/development.rst | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/docs/_rst_epilog.rst b/docs/_rst_epilog.rst index c841474..0130bff 100644 --- a/docs/_rst_epilog.rst +++ b/docs/_rst_epilog.rst @@ -4,4 +4,5 @@ .. _scriv: https://scriv.readthedocs.io/en/latest/ .. _semver: https://semver.org .. _tox: https://tox.readthedocs.io/en/latest/ +.. _Alembic: https://alembic.sqlalchemy.org/en/latest/ diff --git a/docs/dev/development.rst b/docs/dev/development.rst index 9cf2ef7..d47dd24 100644 --- a/docs/dev/development.rst +++ b/docs/dev/development.rst @@ -68,6 +68,14 @@ To see a listing of specific tox sessions, run: Times Square requires Docker to run its tests. +Database migrations +=================== + +Times Square uses Alembic_ for database migrations. +If your work involves changing the database schema (in :file:`/src/timessquare/dbschema`) you will need to prepare an Alembic migration in the same PR. +This process is outlined in the `Safir documentation `__. +Note that in Times Square the :file:`docker-compose.yaml` is hosted in the root of the repository rather than in the :file:`alembic` directory. + Building documentation ====================== From 734f947cd1be972f6566c07689dd97fb13d9b6a7 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Mon, 13 Jan 2025 16:54:38 -0500 Subject: [PATCH 12/14] Make small edits to development docs --- docs/dev/development.rst | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/dev/development.rst b/docs/dev/development.rst index d47dd24..f86e294 100644 --- a/docs/dev/development.rst +++ b/docs/dev/development.rst @@ -24,12 +24,14 @@ Setting up a local development environment Times Square is a Python project that should be developed within a virtual environment. If you already have a Python virtual environment set up in your shell, you can use the :command:`make init` command to install Times Square and its development dependencies into it. +This complete procedure shows how to use uv to create a virtual environment and install Times Square into it: .. code-block:: sh git clone https://github.com/lsst-sqre/times-square.git cd times-square - pip install tox + uv venv + source .venv/bin/activate make init .. _pre-commit-hooks: @@ -39,12 +41,7 @@ Pre-commit The pre-commit hooks, which are automatically installed by running the :command:`make init` command on :ref:`set up `, ensure that files are valid and properly formatted. Some pre-commit hooks automatically reformat code: - -``ruff`` - Sorts Python imports and automatically fixes some common Python issues. - -``black`` - Automatically formats Python code. +The ``ruff`` hook automatically fixes some common Python issues and sorts Python imports. When these hooks fail, your Git commit will be aborted. To proceed, stage the new modifications and proceed with your Git commit. @@ -106,7 +103,7 @@ When preparing a pull request, run .. code-block:: sh - scrive create + scriv create This will create a change log fragment in :file:`changelog.d`. Edit that fragment, removing the sections that do not apply and adding entries for your pull request. From 7a79029e9cee955464b08be483ee7d7a99cef60b Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Mon, 13 Jan 2025 17:04:56 -0500 Subject: [PATCH 13/14] Update change log for 0.14.0 release --- CHANGELOG.md | 16 +++++++++++++++ changelog.d/20250106_164329_jsick_DM_48307.md | 20 ------------------- changelog.d/20250110_170830_jsick_DM_48385.md | 17 ---------------- 3 files changed, 16 insertions(+), 37 deletions(-) delete mode 100644 changelog.d/20250106_164329_jsick_DM_48307.md delete mode 100644 changelog.d/20250110_170830_jsick_DM_48385.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fccc2a..cd09926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,22 @@ Collect fragments into this file with: scriv collect --version X.Y.Z + + +## 0.14.0 (2025-01-13) + +### Other changes + +- Times Square now uses Alembic to manage database schema versioning and migrations. + +- Begin SQLAlchemy 2 adoption with the new `DeclarativeBase`, `mapped_column`, and the `Mapped` type. + +- Update the `make update` command in the Makefile to use the `--universal` flag with `uv pip compile`. + +- Fix type checking issues for Pydantic fields to use empty lists as the default, rather than using a default factory. + +- Explicitly set `asyncio_default_fixture_loop_scope` to `function`. An explicit setting is now required by pytest-asyncio. + ## 0.13.0 (2024-09-12) diff --git a/changelog.d/20250106_164329_jsick_DM_48307.md b/changelog.d/20250106_164329_jsick_DM_48307.md deleted file mode 100644 index e92f287..0000000 --- a/changelog.d/20250106_164329_jsick_DM_48307.md +++ /dev/null @@ -1,20 +0,0 @@ - - -### Backwards-incompatible changes - -- - -### New features - -- - -### Bug fixes - -- - -### Other changes - -- Update the `make update` command in the Makefile to use the `--universal` flag with `uv pip compile`. -- Begin SQLAlchemy 2 migration by adopting the new `DeclarativeBase`, `mapped_column`, and the `Mapped` type. -- Fix type checking issues for Pydantic fields to use empty lists as the default, rather than using a default factory. -- Explicitly set `asyncio_default_fixture_loop_scope` to function. An explicit setting is now required by pytest-asyncio. diff --git a/changelog.d/20250110_170830_jsick_DM_48385.md b/changelog.d/20250110_170830_jsick_DM_48385.md deleted file mode 100644 index b73c730..0000000 --- a/changelog.d/20250110_170830_jsick_DM_48385.md +++ /dev/null @@ -1,17 +0,0 @@ - - -### Backwards-incompatible changes - -- - -### New features - -- - -### Bug fixes - -- - -### Other changes - -- Times Square now uses Alembic to manage database schema versioning and migrations. From fee26e608b68892aeecb31512965bd11222d9d00 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Mon, 13 Jan 2025 17:17:40 -0500 Subject: [PATCH 14/14] Improve the README - Link to deployments - Link to documentation sites --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 633937a..0cadaf6 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,20 @@ Excellent applications for Times Square include: - Quick-look data previewing - Reports that incorporate live data sources +## Deployments of Times Square + +| Link | User guide | Purpose | +| --------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------- | +| [USDF RSP (dev)](https://usdf-rsp-dev.slac.stanford.edu/times-square) | [Documentation](https://rsp.lsst.io/v/usdfdev/guides/times-square/index.html) | For Rubin Observatory staff | +| [IDF (dev)](https://data-dev.lsst.cloud/times-square) | [Documentation](https://rsp.lsst.io/v/idfdev/guides/times-square/index.html) | For SQuaRE developers | + +## Design and development + The design and architecture of Times Square is described in [SQR-062: The Times Square service for publishing parameterized Jupyter Notebooks in the Rubin Science platform](https://sqr-062.lsst.io). Times Square uses [Noteburst](https://noteburst.lsst.io) ([GitHub](https://github.com/lsst-sqre/noteburst), [SQR-065](https://sqr-065.lsst.io)) to execute Jupyter Notebooks in Nublado (JupyterLab) instances, thereby mechanizing the RSP's notebook aspect. This Times Square API service is developed at https://github.com/lsst-sqre/times-square. -It's user interface is part of [Squareone](https://github.com/lsst-sqre/squareone). +Its user interface is part of [Squareone](https://github.com/lsst-sqre/squareone). Times Square is deployed with [Phalanx](https://phalanx.lsst.io/applications/times-square/index.html). + +REST API and developer documentation is at [times-square.lsst.io](https://times-square.lsst.io) and deployment documentation is [available at phalanx.lsst.io](https://phalanx.lsst.io/applications/times-square/index.html).