From b156a952c07a4ce74bd9c74c426e0d3c07ce2f78 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 25 Jul 2023 15:01:19 +0100 Subject: [PATCH 1/5] Bump pydantic to v2 and install pydantic-settings --- poetry.lock | 234 ++++++++++++++++++++++++++++++++++--------------- pyproject.toml | 3 +- 2 files changed, 166 insertions(+), 71 deletions(-) diff --git a/poetry.lock b/poetry.lock index e45697329e..b467300fe7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -136,6 +136,17 @@ files = [ [package.dependencies] frozenlist = ">=1.1.0" +[[package]] +name = "annotated-types" +version = "0.5.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.7" +files = [ + {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"}, + {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"}, +] + [[package]] name = "anyio" version = "3.7.1" @@ -190,13 +201,13 @@ fakeredis = ["fakeredis[lua] (>=1.7.1)"] [[package]] name = "async-timeout" -version = "4.0.2" +version = "4.0.3" description = "Timeout context manager for asyncio programs" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, - {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] [[package]] @@ -931,13 +942,10 @@ files = [ {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, - {file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"}, - {file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"}, {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, @@ -946,7 +954,6 @@ files = [ {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, @@ -966,7 +973,6 @@ files = [ {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, @@ -976,7 +982,6 @@ files = [ {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, @@ -986,7 +991,6 @@ files = [ {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, @@ -996,7 +1000,6 @@ files = [ {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, @@ -1007,16 +1010,13 @@ files = [ {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, @@ -1206,18 +1206,18 @@ test = ["docutils", "mypy", "pytest-cov", "pytest-pycodestyle", "pytest-runner"] [[package]] name = "platformdirs" -version = "3.9.1" +version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.9.1-py3-none-any.whl", hash = "sha256:ad8291ae0ae5072f66c16945166cb11c63394c7a3ad1b1bc9828ca3162da8c2f"}, - {file = "platformdirs-3.9.1.tar.gz", hash = "sha256:1b42b450ad933e981d56e59f1b97495428c9bd60698baab9f3eb3d00d5822421"}, + {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, + {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, ] [package.extras] -docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" @@ -1375,56 +1375,150 @@ files = [ [[package]] name = "pydantic" -version = "1.10.9" -description = "Data validation and settings management using python type hints" +version = "2.0.3" +description = "Data validation using Python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e692dec4a40bfb40ca530e07805b1208c1de071a18d26af4a2a0d79015b352ca"}, - {file = "pydantic-1.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c52eb595db83e189419bf337b59154bdcca642ee4b2a09e5d7797e41ace783f"}, - {file = "pydantic-1.10.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:939328fd539b8d0edf244327398a667b6b140afd3bf7e347cf9813c736211896"}, - {file = "pydantic-1.10.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b48d3d634bca23b172f47f2335c617d3fcb4b3ba18481c96b7943a4c634f5c8d"}, - {file = "pydantic-1.10.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f0b7628fb8efe60fe66fd4adadd7ad2304014770cdc1f4934db41fe46cc8825f"}, - {file = "pydantic-1.10.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e1aa5c2410769ca28aa9a7841b80d9d9a1c5f223928ca8bec7e7c9a34d26b1d4"}, - {file = "pydantic-1.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:eec39224b2b2e861259d6f3c8b6290d4e0fbdce147adb797484a42278a1a486f"}, - {file = "pydantic-1.10.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d111a21bbbfd85c17248130deac02bbd9b5e20b303338e0dbe0faa78330e37e0"}, - {file = "pydantic-1.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e9aec8627a1a6823fc62fb96480abe3eb10168fd0d859ee3d3b395105ae19a7"}, - {file = "pydantic-1.10.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07293ab08e7b4d3c9d7de4949a0ea571f11e4557d19ea24dd3ae0c524c0c334d"}, - {file = "pydantic-1.10.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ee829b86ce984261d99ff2fd6e88f2230068d96c2a582f29583ed602ef3fc2c"}, - {file = "pydantic-1.10.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b466a23009ff5cdd7076eb56aca537c745ca491293cc38e72bf1e0e00de5b91"}, - {file = "pydantic-1.10.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7847ca62e581e6088d9000f3c497267868ca2fa89432714e21a4fb33a04d52e8"}, - {file = "pydantic-1.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:7845b31959468bc5b78d7b95ec52fe5be32b55d0d09983a877cca6aedc51068f"}, - {file = "pydantic-1.10.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:517a681919bf880ce1dac7e5bc0c3af1e58ba118fd774da2ffcd93c5f96eaece"}, - {file = "pydantic-1.10.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67195274fd27780f15c4c372f4ba9a5c02dad6d50647b917b6a92bf00b3d301a"}, - {file = "pydantic-1.10.9-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2196c06484da2b3fded1ab6dbe182bdabeb09f6318b7fdc412609ee2b564c49a"}, - {file = "pydantic-1.10.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6257bb45ad78abacda13f15bde5886efd6bf549dd71085e64b8dcf9919c38b60"}, - {file = "pydantic-1.10.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3283b574b01e8dbc982080d8287c968489d25329a463b29a90d4157de4f2baaf"}, - {file = "pydantic-1.10.9-cp37-cp37m-win_amd64.whl", hash = "sha256:5f8bbaf4013b9a50e8100333cc4e3fa2f81214033e05ac5aa44fa24a98670a29"}, - {file = "pydantic-1.10.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9cd67fb763248cbe38f0593cd8611bfe4b8ad82acb3bdf2b0898c23415a1f82"}, - {file = "pydantic-1.10.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f50e1764ce9353be67267e7fd0da08349397c7db17a562ad036aa7c8f4adfdb6"}, - {file = "pydantic-1.10.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73ef93e5e1d3c8e83f1ff2e7fdd026d9e063c7e089394869a6e2985696693766"}, - {file = "pydantic-1.10.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:128d9453d92e6e81e881dd7e2484e08d8b164da5507f62d06ceecf84bf2e21d3"}, - {file = "pydantic-1.10.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ad428e92ab68798d9326bb3e5515bc927444a3d71a93b4a2ca02a8a5d795c572"}, - {file = "pydantic-1.10.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fab81a92f42d6d525dd47ced310b0c3e10c416bbfae5d59523e63ea22f82b31e"}, - {file = "pydantic-1.10.9-cp38-cp38-win_amd64.whl", hash = "sha256:963671eda0b6ba6926d8fc759e3e10335e1dc1b71ff2a43ed2efd6996634dafb"}, - {file = "pydantic-1.10.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:970b1bdc6243ef663ba5c7e36ac9ab1f2bfecb8ad297c9824b542d41a750b298"}, - {file = "pydantic-1.10.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7e1d5290044f620f80cf1c969c542a5468f3656de47b41aa78100c5baa2b8276"}, - {file = "pydantic-1.10.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83fcff3c7df7adff880622a98022626f4f6dbce6639a88a15a3ce0f96466cb60"}, - {file = "pydantic-1.10.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0da48717dc9495d3a8f215e0d012599db6b8092db02acac5e0d58a65248ec5bc"}, - {file = "pydantic-1.10.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0a2aabdc73c2a5960e87c3ffebca6ccde88665616d1fd6d3db3178ef427b267a"}, - {file = "pydantic-1.10.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9863b9420d99dfa9c064042304868e8ba08e89081428a1c471858aa2af6f57c4"}, - {file = "pydantic-1.10.9-cp39-cp39-win_amd64.whl", hash = "sha256:e7c9900b43ac14110efa977be3da28931ffc74c27e96ee89fbcaaf0b0fe338e1"}, - {file = "pydantic-1.10.9-py3-none-any.whl", hash = "sha256:6cafde02f6699ce4ff643417d1a9223716ec25e228ddc3b436fe7e2d25a1f305"}, - {file = "pydantic-1.10.9.tar.gz", hash = "sha256:95c70da2cd3b6ddf3b9645ecaa8d98f3d80c606624b6d245558d202cd23ea3be"}, + {file = "pydantic-2.0.3-py3-none-any.whl", hash = "sha256:614eb3321eb600c81899a88fa9858b008e3c79e0d4f1b49ab1f516b4b0c27cfb"}, + {file = "pydantic-2.0.3.tar.gz", hash = "sha256:94f13e0dcf139a5125e88283fc999788d894e14ed90cf478bcc2ee50bd4fc630"}, ] [package.dependencies] -python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""} -typing-extensions = ">=4.2.0" +annotated-types = ">=0.4.0" +pydantic-core = "2.3.0" +typing-extensions = ">=4.6.1" [package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.3.0" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.3.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:4542c98b8364b976593703a2dda97377433b102f380b61bc3a2cbc2fbdae1d1f"}, + {file = "pydantic_core-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9342de50824b40f55d2600f66c6f9a91a3a24851eca39145a749a3dc804ee599"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:539432f911686cb80284c30b33eaf9f4fd9a11e1111fe0dc98fdbdce69b49821"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38a0e7ee65c8999394d92d9c724434cb629279d19844f2b69d9bbc46dc8b8b61"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_24_armv7l.whl", hash = "sha256:e3ed6834cc005798187a56c248a2240207cb8ffdda1c89e9afda4c3d526c2ea0"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:e72ac299a6bf732a60852d052acf3999d234686755a02ba111e85e7ebf8155b1"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_24_s390x.whl", hash = "sha256:616b3451b05ca63b8f433c627f68046b39543faeaa4e50d8c6699a2a1e4b85a5"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:adcb9c8848e15c613e483e0b99767ae325af27fe0dbd866df01fe5849d06e6e1"}, + {file = "pydantic_core-2.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:464bf799b422be662e5e562e62beeffc9eaa907d381a9d63a2556615bbda286d"}, + {file = "pydantic_core-2.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4638ebc17de08c2f3acba557efeb6f195c88b7299d8c55c0bb4e20638bbd4d03"}, + {file = "pydantic_core-2.3.0-cp310-none-win32.whl", hash = "sha256:9ff322c7e1030543d35d83bb521b69114d3d150750528d7757544f639def9ad6"}, + {file = "pydantic_core-2.3.0-cp310-none-win_amd64.whl", hash = "sha256:4824eb018f0a4680b1e434697a9bf3f41c7799b80076d06530cbbd212e040ccc"}, + {file = "pydantic_core-2.3.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:0aa429578e23885b3984c49d687cd05ab06f0b908ea1711a8bf7e503b7f97160"}, + {file = "pydantic_core-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20d710c1f79af930b8891bcebd84096798e4387ab64023ef41521d58f21277d3"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:309f45d4d7481d6f09cb9e35c72caa0e50add4a30bb08c04c5fe5956a0158633"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bcfb7be905aa849bd882262e1df3f75b564e2f708b4b4c7ad2d3deaf5410562"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_24_armv7l.whl", hash = "sha256:85cd9c0af34e371390e3cb2f3a470b0b40cc07568c1e966c638c49062be6352d"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_24_ppc64le.whl", hash = "sha256:37c5028cebdf731298724070838fb3a71ef1fbd201d193d311ac2cbdbca25a23"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_24_s390x.whl", hash = "sha256:e4208f23f12d0ad206a07a489ef4cb15722c10b62774c4460ee4123250be938e"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c24465dd11b65c8510f251b095fc788c7c91481c81840112fe3f76c30793a455"}, + {file = "pydantic_core-2.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3cd7ee8bbfab277ab56e272221886fd33a1b5943fbf45ae9195aa6a48715a8a0"}, + {file = "pydantic_core-2.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0fc7e0b056b66cc536e97ef60f48b3b289f6b3b62ac225afd4b22a42434617bf"}, + {file = "pydantic_core-2.3.0-cp311-none-win32.whl", hash = "sha256:4788135db4bd83a5edc3522b11544b013be7d25b74b155e08dd3b20cd6663bbb"}, + {file = "pydantic_core-2.3.0-cp311-none-win_amd64.whl", hash = "sha256:f93c867e5e85584a28c6a6feb6f2086d717266eb5d1210d096dd717b7f4dec04"}, + {file = "pydantic_core-2.3.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:73f62bb7fd862d9bcd886e10612bade6fe042eda8b47e8c129892bcfb7b45e84"}, + {file = "pydantic_core-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d889d498fce64bfcd8adf1a78579a7f626f825cbeb2956a24a29b35f9a1df32"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d55e38a89ec2ae17b2fa7ffeda6b70f63afab1888bd0d57aaa7b7879760acb4"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1aefebb506bc1fe355d91d25f12bcdea7f4d7c2d9f0f6716dd025543777c99a5"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_24_armv7l.whl", hash = "sha256:6441a29f42585f085db0c04cd0557d4cbbb46fa68a0972409b1cfe9f430280c1"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_24_ppc64le.whl", hash = "sha256:47e8f034be31390a8f525431eb5e803a78ce7e2e11b32abf5361a972e14e6b61"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_24_s390x.whl", hash = "sha256:ad814864aba263be9c83ada44a95f72d10caabbf91589321f95c29c902bdcff0"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9eff3837d447fccf2ac38c259b14ab9cbde700df355a45a1f3ff244d5e78f8b6"}, + {file = "pydantic_core-2.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:534f3f63c000f08050c6f7f4378bf2b52d7ba9214e9d35e3f60f7ad24a4d6425"}, + {file = "pydantic_core-2.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ef6a222d54f742c24f6b143aab088702db3a827b224e75b9dd28b38597c595fe"}, + {file = "pydantic_core-2.3.0-cp312-none-win32.whl", hash = "sha256:4e26944e64ecc1d7b19db954c0f7b471f3b141ec8e1a9f57cfe27671525cd248"}, + {file = "pydantic_core-2.3.0-cp312-none-win_amd64.whl", hash = "sha256:019c5c41941438570dfc7d3f0ae389b2425add1775a357ce1e83ed1434f943d6"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:27c1bbfb9d84a75cf33b7f19b53c29eb7ead99b235fce52aced5507174ab8f98"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:7cb496e934b71f1ade844ab91d6ccac78a3520e5df02fdb2357f85a71e541e69"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5af2d43b1978958d91351afbcc9b4d0cfe144c46c61740e82aaac8bb39ab1a4d"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d3097c39d7d4e8dba2ef86de171dcccad876c36d8379415ba18a5a4d0533510"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_24_armv7l.whl", hash = "sha256:dd3b023f3317dbbbc775e43651ce1a31a9cea46216ad0b5be37afc18a2007699"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:27babb9879bf2c45ed655d02639f4c30e2b9ef1b71ce59c2305bbf7287910a18"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_24_s390x.whl", hash = "sha256:2183a9e18cdc0de53bdaa1675f237259162abeb62d6ac9e527c359c1074dc55d"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c089d8e7f1b4db08b2f8e4107304eec338df046275dad432635a9be9531e2fc8"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f10aa5452b865818dd0137f568d443f5e93b60a27080a01aa4b7512c7ba13a3"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f642313d559f9d9a00c4de6820124059cc3342a0d0127b18301de2c680d5ea40"}, + {file = "pydantic_core-2.3.0-cp37-none-win32.whl", hash = "sha256:45327fc57afbe3f2c3d7f54a335d5cecee8a9fdb3906a2fbed8af4092f4926df"}, + {file = "pydantic_core-2.3.0-cp37-none-win_amd64.whl", hash = "sha256:e427b66596a6441a5607dfc0085b47d36073f88da7ac48afd284263b9b99e6ce"}, + {file = "pydantic_core-2.3.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:0b3d781c71b8bfb621ef23b9c874933e2cd33237c1a65cc20eeb37437f8e7e18"}, + {file = "pydantic_core-2.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad46027dbd5c1db87dc0b49becbe23093b143a20302028d387dae37ee5ef95f5"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39aa09ed7ce2a648c904f79032d16dda29e6913112af8465a7bf710eef23c7ca"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05b4bf8c58409586a7a04c858a86ab10f28c6c1a7c33da65e0326c59d5b0ab16"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_24_armv7l.whl", hash = "sha256:ba2b807d2b62c446120906b8580cddae1d76d3de4efbb95ccc87f5e35c75b4b2"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:ea955e4ed21f4bbb9b83fea09fc6af0bed82e69ecf6b35ec89237a0a49633033"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_24_s390x.whl", hash = "sha256:06884c07956526ac9ebfef40fe21a11605569b8fc0e2054a375fb39c978bf48f"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f868e731a18b403b88aa434d960489ceeed0ddeb44ebc02389540731a67705e0"}, + {file = "pydantic_core-2.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cb08fab0fc1db15c277b72e33ac74ad9c0c789413da8984a3eacb22a94b42ef4"}, + {file = "pydantic_core-2.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6ca34c29fbd6592de5fd39e80c1993634d704c4e7e14ba54c87b2c7c53da68fe"}, + {file = "pydantic_core-2.3.0-cp38-none-win32.whl", hash = "sha256:cd782807d35c8a41aaa7d30b5107784420eefd9fdc1c760d86007d43ae00b15d"}, + {file = "pydantic_core-2.3.0-cp38-none-win_amd64.whl", hash = "sha256:01f56d5ee70b1d39c0fd08372cc5142274070ab7181d17c86035f130eebc05b8"}, + {file = "pydantic_core-2.3.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:78b1ac0151271ce62bc2b33755f1043eda6a310373143a2f27e2bcd3d5fc8633"}, + {file = "pydantic_core-2.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:64bfd2c35a2c350f73ac52dc134d8775f93359c4c969280a6fe5301b5b6e7431"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:937c0fe9538f1212b62df6a68f8d78df3572fe3682d9a0dd8851eac8a4e46063"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d965c7c4b40d1cedec9188782e98bd576f9a04868835604200c3a6e817b824f"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_24_armv7l.whl", hash = "sha256:ad442b8585ed4a3c2d22e4bf7b465d9b7d281e055b09719a8aeb5b576422dc9b"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:4bf20c9722821fce766e685718e739deeccc60d6bc7be5029281db41f999ee0c"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_24_s390x.whl", hash = "sha256:f3dd5333049b5b3faa739e0f40b77cc8b7a1aded2f2da0e28794c81586d7b08a"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dc5f516b24d24bc9e8dd9305460899f38302b3c4f9752663b396ef9848557bf"}, + {file = "pydantic_core-2.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:055f7ea6b1fbb37880d66d70eefd22dd319b09c79d2cb99b1dbfeb34b653b0b2"}, + {file = "pydantic_core-2.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:af693a89db6d6ac97dd84dd7769b3f2bd9007b578127d0e7dda03053f4d3b34b"}, + {file = "pydantic_core-2.3.0-cp39-none-win32.whl", hash = "sha256:f60e31e3e15e8c294bf70c60f8ae4d0c3caf3af8f26466e9aa8ea4c01302749b"}, + {file = "pydantic_core-2.3.0-cp39-none-win_amd64.whl", hash = "sha256:2b79f3681481f4424d7845cc7a261d5a4baa810d656b631fa844dc9967b36a7b"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:a666134b41712e30a71afaa26deeb4da374179f769fa49784cdf0e7698880fab"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c119e9227487ad3d7c3c737d896afe548a6be554091f9745da1f4b489c40561"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73929a2fb600a2333fce2efd92596cff5e6bf8946e20e93c067b220760064862"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:41bbc2678a5b6a19371b2cb51f30ccea71f0c14b26477d2d884fed761cea42c7"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dcbff997f47d45bf028bda4c3036bb3101e89a3df271281d392b6175f71c71d1"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:afa8808159169368b66e4fbeafac6c6fd8f26246dc4d0dcc2caf94bd9cf1b828"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:12be3b5f54f8111ca38e6b7277f26c23ba5cb3344fae06f879a0a93dfc8b479e"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ed5babdcd3d052ba5cf8832561f18df20778c7ccf12587b2d82f7bf3bf259a0e"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d642e5c029e2acfacf6aa0a7a3e822086b3b777c70d364742561f9ca64c1ffc"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba3073eb38a1294e8c7902989fb80a7a147a69db2396818722bd078476586a0"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5146a6749b1905e04e62e0ad4622f079e5582f8b3abef5fb64516c623127908"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:deeb64335f489c3c11949cbd1d1668b3f1fb2d1c6a5bf40e126ef7bf95f9fa40"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:31acc37288b8e69e4849f618c3d5cf13b58077c1a1ff9ade0b3065ba974cd385"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e09d9f6d722de9d4c1c5f122ea9bc6b25a05f975457805af4dcab7b0128aacbf"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ba6a8cf089222a171b8f84e6ec2d10f7a9d14f26be3a347b14775a8741810676"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1fd1b24e9bcddcb168437686677104e205c8e25b066e73ffdf331d3bb8792b"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eda1a89c4526826c0a87d33596a4cd15b8f58e9250f503e39af1699ba9c878e8"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3e9a18401a28db4358da2e191508702dbf065f2664c710708cdf9552b9fa50c"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a439fd0d45d51245bbde799726adda5bd18aed3fa2b01ab2e6a64d6d13776fa3"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:bf6a1d2c920cc9528e884850a4b2ee7629e3d362d5c44c66526d4097bbb07a1a"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e33fcbea3b63a339dd94de0fc442fefacfe681cc7027ce63f67af9f7ceec7422"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:bf3ed993bdf4754909f175ff348cf8f78d4451215b8aa338633f149ca3b1f37a"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7584171eb3115acd4aba699bc836634783f5bd5aab131e88d8eeb8a3328a4a72"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1624baa76d1740711b2048f302ae9a6d73d277c55a8c3e88b53b773ebf73a971"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:06f33f695527f5a86e090f208978f9fd252c9cfc7e869d3b679bd71f7cb2c1fa"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7ecf0a67b212900e92f328181fed02840d74ed39553cdb38d27314e2b9c89dfa"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:45fa1e8ad6f4367ad73674ca560da8e827cc890eaf371f3ee063d6d7366a207b"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8d0dbcc57839831ae79fd24b1b83d42bc9448d79feaf3ed3fb5cbf94ffbf3eb7"}, + {file = "pydantic_core-2.3.0.tar.gz", hash = "sha256:5cfb5ac4e82c47d5dc25b209dd4c3989e284b80109f9e08b33c895080c424b4f"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-settings" +version = "2.0.2" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_settings-2.0.2-py3-none-any.whl", hash = "sha256:6183a2abeab465d5a3ab69758e9a22d38b0cc2ba193f0b85f6971a252ea630f6"}, + {file = "pydantic_settings-2.0.2.tar.gz", hash = "sha256:342337fff50b23585e807a86dec85037900972364435c55c2fc00d16ff080539"}, +] + +[package.dependencies] +pydantic = ">=2.0.1" +python-dotenv = ">=0.21.0" [[package]] name = "pydis-core" @@ -2104,23 +2198,23 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.24.1" +version = "20.24.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.1-py3-none-any.whl", hash = "sha256:01aacf8decd346cf9a865ae85c0cdc7f64c8caa07ff0d8b1dfc1733d10677442"}, - {file = "virtualenv-20.24.1.tar.gz", hash = "sha256:2ef6a237c31629da6442b0bcaa3999748108c7166318d1f55cc9f8d7294e97bd"}, + {file = "virtualenv-20.24.2-py3-none-any.whl", hash = "sha256:43a3052be36080548bdee0b42919c88072037d50d56c28bd3f853cbe92b953ff"}, + {file = "virtualenv-20.24.2.tar.gz", hash = "sha256:fd8a78f46f6b99a67b7ec5cf73f92357891a7b3a40fd97637c27f854aae3b9e0"}, ] [package.dependencies] -distlib = ">=0.3.6,<1" -filelock = ">=3.12,<4" -platformdirs = ">=3.5.1,<4" +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<4" [package.extras] docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezer (>=0.4.6)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.8)", "time-machine (>=2.9)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "wcwidth" @@ -2223,4 +2317,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "3.11.*" -content-hash = "60353f299efeb38128df337aabef6ec53c5018c9a1fe14254737b5079f094f41" +content-hash = "e7e58f19c35b45ba8b1ee279e852fbe5111ad70b1bb9f5f5de0941dd966bb6ed" diff --git a/pyproject.toml b/pyproject.toml index 68aa2685f7..af17a1ed9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,8 @@ rapidfuzz = "3.2.0" regex = "2023.8.8" sentry-sdk = "1.29.2" tldextract = "3.4.4" -pydantic = { version = "1.10.9", extras = ["dotenv"]} +pydantic = "2.0.3" +pydantic-settings = "2.0.2" [tool.poetry.dev-dependencies] coverage = "7.2.7" From 38ad2c47e9203966f6506e114e489d36c991c3a2 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 25 Jul 2023 15:09:53 +0100 Subject: [PATCH 2/5] Use new Pydantic v2 logic for constant loading --- bot/constants.py | 521 ++++++++++++++++++++++------------------------- 1 file changed, 246 insertions(+), 275 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index fa72de19f5..922f41abdb 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -8,23 +8,23 @@ import os from enum import Enum -from pydantic import BaseModel, BaseSettings, root_validator +from pydantic import BaseModel, root_validator +from pydantic_settings import BaseSettings -class EnvConfig(BaseSettings): +class EnvConfig( + BaseSettings, + env_file=(".env.server", ".env"), + env_file_encoding = "utf-8", + env_nested_delimiter = "__", + extra="ignore", +): """Our default configuration for models that should load from .env files.""" - class Config: - """Specify what .env files to load, and how to load them.""" - - env_file = ".env.server", ".env", - env_file_encoding = "utf-8" - env_nested_delimiter = "__" - class _Miscellaneous(EnvConfig): - debug = True - file_logs = False + debug: bool = True + file_logs: bool = False Miscellaneous = _Miscellaneous() @@ -34,192 +34,186 @@ class _Miscellaneous(EnvConfig): DEBUG_MODE = Miscellaneous.debug -class _Bot(EnvConfig): - EnvConfig.Config.env_prefix = "bot_" +class _Bot(EnvConfig, env_prefix="bot_"): - prefix = "!" - sentry_dsn = "" - token = "" - trace_loggers = "*" + prefix: str = "!" + sentry_dsn: str = "" + token: str + trace_loggers: str = "*" Bot = _Bot() -class _Channels(EnvConfig): - EnvConfig.Config.env_prefix = "channels_" +class _Channels(EnvConfig, env_prefix="channels_"): - announcements = 354619224620138496 - changelog = 748238795236704388 - mailing_lists = 704372456592506880 - python_events = 729674110270963822 - python_news = 704372456592506880 - reddit = 458224812528238616 + announcements: int = 354619224620138496 + changelog: int = 748238795236704388 + mailing_lists: int = 704372456592506880 + python_events: int = 729674110270963822 + python_news: int = 704372456592506880 + reddit: int = 458224812528238616 - dev_contrib = 635950537262759947 - dev_core = 411200599653351425 - dev_log = 622895325144940554 + dev_contrib: int = 635950537262759947 + dev_core: int = 411200599653351425 + dev_log: int = 622895325144940554 - meta = 429409067623251969 - python_general = 267624335836053506 + meta: int = 429409067623251969 + python_general: int = 267624335836053506 - python_help = 1035199133436354600 + python_help: int = 1035199133436354600 - attachment_log = 649243850006855680 - filter_log = 1014943924185473094 - message_log = 467752170159079424 - mod_log = 282638479504965634 - nomination_voting_archive = 833371042046148738 - user_log = 528976905546760203 - voice_log = 640292421988646961 + attachment_log: int = 649243850006855680 + filter_log: int = 1014943924185473094 + message_log: int = 467752170159079424 + mod_log: int = 282638479504965634 + nomination_voting_archive: int = 833371042046148738 + user_log: int = 528976905546760203 + voice_log: int = 640292421988646961 - off_topic_0 = 291284109232308226 - off_topic_1 = 463035241142026251 - off_topic_2 = 463035268514185226 + off_topic_0: int = 291284109232308226 + off_topic_1: int = 463035241142026251 + off_topic_2: int = 463035268514185226 - bot_commands = 267659945086812160 - discord_bots = 343944376055103488 - esoteric = 470884583684964352 - voice_gate = 764802555427029012 - code_jam_planning = 490217981872177157 + bot_commands: int = 267659945086812160 + discord_bots: int = 343944376055103488 + esoteric: int = 470884583684964352 + voice_gate: int = 764802555427029012 + code_jam_planning: int = 490217981872177157 # Staff - admins = 365960823622991872 - admin_spam = 563594791770914816 - defcon = 464469101889454091 - helpers = 385474242440986624 - incidents = 714214212200562749 - incidents_archive = 720668923636351037 - mod_alerts = 473092532147060736 - mod_meta = 775412552795947058 - mods = 305126844661760000 - nominations = 822920136150745168 - nomination_discussion = 798959130634747914 - nomination_voting = 822853512709931008 - organisation = 551789653284356126 + admins: int = 365960823622991872 + admin_spam: int = 563594791770914816 + defcon: int = 464469101889454091 + helpers: int = 385474242440986624 + incidents: int = 714214212200562749 + incidents_archive: int = 720668923636351037 + mod_alerts: int = 473092532147060736 + mod_meta: int = 775412552795947058 + mods: int = 305126844661760000 + nominations: int = 822920136150745168 + nomination_discussion: int = 798959130634747914 + nomination_voting: int = 822853512709931008 + organisation: int = 551789653284356126 # Staff announcement channels - admin_announcements = 749736155569848370 - mod_announcements = 372115205867700225 - staff_announcements = 464033278631084042 - staff_info = 396684402404622347 - staff_lounge = 464905259261755392 + admin_announcements: int = 749736155569848370 + mod_announcements: int = 372115205867700225 + staff_announcements: int = 464033278631084042 + staff_info: int = 396684402404622347 + staff_lounge: int = 464905259261755392 # Voice Channels - admins_voice = 500734494840717332 - code_help_voice_0 = 751592231726481530 - code_help_voice_1 = 764232549840846858 - general_voice_0 = 751591688538947646 - general_voice_1 = 799641437645701151 - staff_voice = 412375055910043655 + admins_voice: int = 500734494840717332 + code_help_voice_0: int = 751592231726481530 + code_help_voice_1: int = 764232549840846858 + general_voice_0: int = 751591688538947646 + general_voice_1: int = 799641437645701151 + staff_voice: int = 412375055910043655 - black_formatter = 846434317021741086 + black_formatter: int = 846434317021741086 # Voice Chat - code_help_chat_0 = 755154969761677312 - code_help_chat_1 = 766330079135268884 - staff_voice_chat = 541638762007101470 - voice_chat_0 = 412357430186344448 - voice_chat_1 = 799647045886541885 + code_help_chat_0: int = 755154969761677312 + code_help_chat_1: int = 766330079135268884 + staff_voice_chat: int = 541638762007101470 + voice_chat_0: int = 412357430186344448 + voice_chat_1: int = 799647045886541885 - big_brother = 468507907357409333 - duck_pond = 637820308341915648 - roles = 851270062434156586 + big_brother: int = 468507907357409333 + duck_pond: int = 637820308341915648 + roles: int = 851270062434156586 - rules = 693837295685730335 + rules: int = 693837295685730335 Channels = _Channels() -class _Roles(EnvConfig): - - EnvConfig.Config.env_prefix = "roles_" +class _Roles(EnvConfig, env_prefix="roles_"): # Self-assignable roles, see the Subscribe cog - advent_of_code = 518565788744024082 - announcements = 463658397560995840 - lovefest = 542431903886606399 - pyweek_announcements = 897568414044938310 - revival_of_code = 988801794668908655 - legacy_help_channels_access = 1074780483776417964 - - contributors = 295488872404484098 - help_cooldown = 699189276025421825 - partners = 323426753857191936 - python_community = 458226413825294336 - voice_verified = 764802720779337729 + advent_of_code: int = 518565788744024082 + announcements: int = 463658397560995840 + lovefest: int = 542431903886606399 + pyweek_announcements: int = 897568414044938310 + revival_of_code: int = 988801794668908655 + legacy_help_channels_access: int = 1074780483776417964 + + contributors: int = 295488872404484098 + help_cooldown: int = 699189276025421825 + partners: int = 323426753857191936 + python_community: int = 458226413825294336 + voice_verified: int = 764802720779337729 # Streaming - video = 764245844798079016 + video: int = 764245844798079016 # Staff - admins = 267628507062992896 - core_developers = 587606783669829632 - code_jam_event_team = 787816728474288181 - devops = 409416496733880320 - domain_leads = 807415650778742785 - events_lead = 778361735739998228 - helpers = 267630620367257601 - moderators = 831776746206265384 - mod_team = 267629731250176001 - owners = 267627879762755584 - project_leads = 815701647526330398 + admins: int = 267628507062992896 + core_developers: int = 587606783669829632 + code_jam_event_team: int = 787816728474288181 + devops: int = 409416496733880320 + domain_leads: int = 807415650778742785 + events_lead: int = 778361735739998228 + helpers: int = 267630620367257601 + moderators: int = 831776746206265384 + mod_team: int = 267629731250176001 + owners: int = 267627879762755584 + project_leads: int = 815701647526330398 # Code Jam - jammers = 737249140966162473 + jammers: int = 737249140966162473 # Patreon - patreon_tier_1 = 505040943800516611 - patreon_tier_2 = 743399725914390631 - patreon_tier_3 = 743400204367036520 + patreon_tier_1: int = 505040943800516611 + patreon_tier_2: int = 743399725914390631 + patreon_tier_3: int = 743400204367036520 Roles = _Roles() -class _Categories(EnvConfig): - EnvConfig.Config.env_prefix = "categories_" +class _Categories(EnvConfig, env_prefix="categories_"): - logs = 468520609152892958 - moderators = 749736277464842262 - modmail = 714494672835444826 - appeals = 890331800025563216 - appeals_2 = 895417395261341766 - voice = 356013253765234688 + logs: int = 468520609152892958 + moderators: int = 749736277464842262 + modmail: int = 714494672835444826 + appeals: int = 890331800025563216 + appeals_2: int = 895417395261341766 + voice: int = 356013253765234688 # 2021 Summer Code Jam - summer_code_jam = 861692638540857384 - python_help_system = 691405807388196926 + summer_code_jam: int = 861692638540857384 + python_help_system: int = 691405807388196926 Categories = _Categories() -class _Guild(EnvConfig): - EnvConfig.Config.env_prefix = "guild_" +class _Guild(EnvConfig, env_prefix="guild_"): - id = 267624335836053506 - invite = "https://discord.gg/python" + id: int = 267624335836053506 + invite: str = "https://discord.gg/python" - moderation_categories = [ + moderation_categories: tuple[int, ...] = ( Categories.moderators, Categories.modmail, Categories.logs, Categories.appeals, Categories.appeals_2 - ] - moderation_channels = [Channels.admins, Channels.admin_spam, Channels.mods] - modlog_blacklist = [ + ) + moderation_channels: tuple[int, ...] = (Channels.admins, Channels.admin_spam, Channels.mods) + modlog_blacklist: tuple[int, ...] = ( Channels.attachment_log, Channels.message_log, Channels.mod_log, Channels.staff_voice, Channels.filter_log - ] - reminder_whitelist = [Channels.bot_commands, Channels.dev_contrib, Channels.black_formatter] - moderation_roles = [Roles.admins, Roles.mod_team, Roles.moderators, Roles.owners] - staff_roles = [Roles.admins, Roles.helpers, Roles.mod_team, Roles.owners] + ) + reminder_whitelist: tuple[int, ...] = (Channels.bot_commands, Channels.dev_contrib, Channels.black_formatter) + moderation_roles: tuple[int, ...] = (Roles.admins, Roles.mod_team, Roles.moderators, Roles.owners) + staff_roles: tuple[int, ...] = (Roles.admins, Roles.helpers, Roles.mod_team, Roles.owners) Guild = _Guild() @@ -268,8 +262,7 @@ class Webhook(BaseModel): channel: int -class _Webhooks(EnvConfig): - EnvConfig.Config.env_prefix = "webhooks_" +class _Webhooks(EnvConfig, env_prefix="webhooks_"): big_brother: Webhook = Webhook(id=569133704568373283, channel=Channels.big_brother) dev_log: Webhook = Webhook(id=680501655111729222, channel=Channels.dev_log) @@ -282,44 +275,41 @@ class _Webhooks(EnvConfig): Webhooks = _Webhooks() -class _BigBrother(EnvConfig): - EnvConfig.Config.env_prefix = "big_brother_" +class _BigBrother(EnvConfig, env_prefix="big_brother_"): - header_message_limit = 15 - log_delay = 15 + header_message_limit: int = 15 + log_delay: int = 15 BigBrother = _BigBrother() -class _CodeBlock(EnvConfig): - EnvConfig.Config.env_prefix = "code_block_" +class _CodeBlock(EnvConfig, env_prefix="code_block_"): # The channels in which code blocks will be detected. They are not subject to a cooldown. - channel_whitelist: list[int] = [Channels.bot_commands] + channel_whitelist: tuple[int, ...] = (Channels.bot_commands,) # The channels which will be affected by a cooldown. These channels are also whitelisted. - cooldown_channels: list[int] = [Channels.python_general] + cooldown_channels: tuple[int, ...] = (Channels.python_general,) - cooldown_seconds = 300 - minimum_lines = 4 + cooldown_seconds: int = 300 + minimum_lines: int = 4 CodeBlock = _CodeBlock() -class _Colours(EnvConfig): - EnvConfig.Config.env_prefix = "colours_" +class _Colours(EnvConfig, env_prefix="colours_"): - blue = 0x3775a8 - bright_green = 0x01d277 - orange = 0xe67e22 - pink = 0xcf84e0 - purple = 0xb734eb - soft_green = 0x68c290 - soft_orange = 0xf9cb54 - soft_red = 0xcd6d6d - white = 0xfffffe - yellow = 0xffd241 + blue: int = 0x3775a8 + bright_green: int = 0x01d277 + orange: int = 0xe67e22 + pink: int = 0xcf84e0 + purple: int = 0xb734eb + soft_green: int = 0x68c290 + soft_orange: int = 0xf9cb54 + soft_red: int = 0xcd6d6d + white: int = 0xfffffe + yellow: int = 0xffd241 @root_validator(pre=True) def parse_hex_values(cls, values: dict) -> dict: # noqa: N805 @@ -332,35 +322,32 @@ def parse_hex_values(cls, values: dict) -> dict: # noqa: N805 Colours = _Colours() -class _HelpChannels(EnvConfig): - EnvConfig.Config.env_prefix = "help_channels_" +class _HelpChannels(EnvConfig, env_prefix="help_channels_"): - enable = True - idle_minutes = 60 - deleted_idle_minutes = 5 + enable: bool = True + idle_minutes: int = 60 + deleted_idle_minutes: int = 5 # Roles which are allowed to use the command which makes channels dormant - cmd_whitelist = Guild.moderation_roles + cmd_whitelist: tuple[int, ...] = Guild.moderation_roles HelpChannels = _HelpChannels() -class _RedirectOutput(EnvConfig): - EnvConfig.Config.env_prefix = "redirect_output_" +class _RedirectOutput(EnvConfig, env_prefix="redirect_output_"): - delete_delay = 15 - delete_invocation = True + delete_delay: int = 15 + delete_invocation: bool = True RedirectOutput = _RedirectOutput() -class _DuckPond(EnvConfig): - EnvConfig.Config.env_prefix = "duck_pond_" +class _DuckPond(EnvConfig, env_prefix="duck_pond_"): - threshold = 7 + threshold: int = 7 - channel_blacklist: list[int] = [ + channel_blacklist: tuple[int, ...] = ( Channels.announcements, Channels.python_news, Channels.python_events, @@ -371,126 +358,114 @@ class _DuckPond(EnvConfig): Channels.staff_announcements, Channels.mod_announcements, Channels.admin_announcements, - Channels.staff_info - ] + Channels.staff_info, + ) DuckPond = _DuckPond() -class _PythonNews(EnvConfig): - EnvConfig.Config.env_prefix = "python_news_" +class _PythonNews(EnvConfig, env_prefix="python_news_"): channel: int = Webhooks.python_news.channel webhook: int = Webhooks.python_news.id - mail_lists = ["python-ideas", "python-announce-list", "pypi-announce", "python-dev"] + mail_lists: tuple[str, ...] = ("python-ideas", "python-announce-list", "pypi-announce", "python-dev") PythonNews = _PythonNews() -class _VoiceGate(EnvConfig): - EnvConfig.Config.env_prefix = "voice_gate_" +class _VoiceGate(EnvConfig, env_prefix="voice_gate_"): - bot_message_delete_delay = 10 - minimum_activity_blocks = 3 - minimum_days_member = 3 - minimum_messages = 50 - voice_ping_delete_delay = 60 + bot_message_delete_delay: int = 10 + minimum_activity_blocks: int = 3 + minimum_days_member: int = 3 + minimum_messages: int = 50 + voice_ping_delete_delay: int = 60 VoiceGate = _VoiceGate() -class _Branding(EnvConfig): - EnvConfig.Config.env_prefix = "branding_" +class _Branding(EnvConfig, env_prefix="branding_"): - cycle_frequency = 3 + cycle_frequency: int = 3 Branding = _Branding() -class _VideoPermission(EnvConfig): - EnvConfig.Config.env_prefix = "video_permission_" +class _VideoPermission(EnvConfig, env_prefix="video_permission_"): - default_permission_duration = 5 + default_permission_duration: int = 5 VideoPermission = _VideoPermission() -class _Redis(EnvConfig): - EnvConfig.Config.env_prefix = "redis_" +class _Redis(EnvConfig, env_prefix="redis_"): - host = "redis.default.svc.cluster.local" - password = "" - port = 6379 - use_fakeredis = False # If this is True, Bot will use fakeredis.aioredis + host: str = "redis.default.svc.cluster.local" + password: str = "" + port: int = 6379 + use_fakeredis: bool = False # If this is True, Bot will use fakeredis.aioredis Redis = _Redis() -class _CleanMessages(EnvConfig): - EnvConfig.Config.env_prefix = "clean_" +class _CleanMessages(EnvConfig, env_prefix="clean_"): - message_limit = 10_000 + message_limit: int = 10_000 CleanMessages = _CleanMessages() -class _Stats(EnvConfig): - EnvConfig.Config.env_prefix = "stats_" +class _Stats(EnvConfig, env_prefix="stats_"): - presence_update_timeout = 30 - statsd_host = "graphite.default.svc.cluster.local" + presence_update_timeout: int = 30 + statsd_host: str = "graphite.default.svc.cluster.local" Stats = _Stats() -class _Cooldowns(EnvConfig): - EnvConfig.Config.env_prefix = "cooldowns_" +class _Cooldowns(EnvConfig, env_prefix="cooldowns_"): - tags = 60 + tags: int = 60 Cooldowns = _Cooldowns() -class _Metabase(EnvConfig): - EnvConfig.Config.env_prefix = "metabase_" +class _Metabase(EnvConfig, env_prefix="metabase_"): - username = "" - password = "" - base_url = "http://metabase.default.svc.cluster.local" - public_url = "https://metabase.pythondiscord.com" - max_session_age = 20_160 + username: str = "" + password: str = "" + base_url: str = "http://metabase.default.svc.cluster.local" + public_url: str = "https://metabase.pythondiscord.com" + max_session_age: int = 20_160 Metabase = _Metabase() -class _BaseURLs(EnvConfig): - EnvConfig.Config.env_prefix = "urls_" +class _BaseURLs(EnvConfig, env_prefix="urls_"): # Snekbox endpoints - snekbox_eval_api = "http://snekbox.default.svc.cluster.local/eval" + snekbox_eval_api: str = "http://snekbox.default.svc.cluster.local/eval" # Discord API - discord_api = "https://discordapp.com/api/v7/" + discord_api: str = "https://discordapp.com/api/v7/" # Misc endpoints - bot_avatar = "https://raw.githubusercontent.com/python-discord/branding/main/logos/logo_circle/logo_circle.png" - - github_bot_repo = "https://github.com/python-discord/bot" + bot_avatar: str = "https://raw.githubusercontent.com/python-discord/branding/main/logos/logo_circle/logo_circle.png" + github_bot_repo: str = "https://github.com/python-discord/bot" # Site - site_api = "http://site.default.svc.cluster.local/api" - - paste_url = "https://paste.pythondiscord.com" + site_api: str = "http://site.default.svc.cluster.local/api" + paste_url: str = "https://paste.pythondiscord.com" BaseURLs = _BaseURLs() @@ -502,8 +477,8 @@ class _URLs(_BaseURLs): discord_invite_api: str = "".join([BaseURLs.discord_api, "invites"]) # Base site vars - connect_max_retries = 3 - connect_cooldown = 5 + connect_max_retries: int = 3 + connect_cooldown: int = 5 site_logs_view: str = "https://pythondiscord.com/staff/bot/logs" @@ -511,59 +486,57 @@ class _URLs(_BaseURLs): URLs = _URLs() -class _Emojis(EnvConfig): - EnvConfig.Config.env_prefix = "emojis_" +class _Emojis(EnvConfig, env_prefix="emojis_"): - badge_bug_hunter = "<:bug_hunter_lvl1:743882896372269137>" - badge_bug_hunter_level_2 = "<:bug_hunter_lvl2:743882896611344505>" - badge_early_supporter = "<:early_supporter:743882896909140058>" - badge_hypesquad = "<:hypesquad_events:743882896892362873>" - badge_hypesquad_balance = "<:hypesquad_balance:743882896460480625>" - badge_hypesquad_bravery = "<:hypesquad_bravery:743882896745693335>" - badge_hypesquad_brilliance = "<:hypesquad_brilliance:743882896938631248>" - badge_partner = "<:partner:748666453242413136>" - badge_staff = "<:discord_staff:743882896498098226>" - badge_verified_bot_developer = "<:verified_bot_dev:743882897299210310>" - badge_discord_certified_moderator = "<:discord_certified_moderator:1114130029547364434>" - badge_bot_http_interactions = "<:bot_http_interactions:1114130379754975283>" - badge_active_developer = "<:active_developer:1114130031036338176>" - verified_bot = "<:verified_bot:811645219220750347>" - bot = "<:bot:812712599464443914>" + badge_bug_hunter: str = "<:bug_hunter_lvl1:743882896372269137>" + badge_bug_hunter_level_2: str = "<:bug_hunter_lvl2:743882896611344505>" + badge_early_supporter: str = "<:early_supporter:743882896909140058>" + badge_hypesquad: str = "<:hypesquad_events:743882896892362873>" + badge_hypesquad_balance: str = "<:hypesquad_balance:743882896460480625>" + badge_hypesquad_bravery: str = "<:hypesquad_bravery:743882896745693335>" + badge_hypesquad_brilliance: str = "<:hypesquad_brilliance:743882896938631248>" + badge_partner: str = "<:partner:748666453242413136>" + badge_staff: str = "<:discord_staff:743882896498098226>" + badge_verified_bot_developer: str = "<:verified_bot_dev:743882897299210310>" + badge_discord_certified_moderator: str = "<:discord_certified_moderator:1114130029547364434>" + badge_bot_http_interactions: str = "<:bot_http_interactions:1114130379754975283>" + badge_active_developer: str = "<:active_developer:1114130031036338176>" + verified_bot: str = "<:verified_bot:811645219220750347>" + bot: str = "<:bot:812712599464443914>" - defcon_shutdown = "<:defcondisabled:470326273952972810>" - defcon_unshutdown = "<:defconenabled:470326274213150730>" - defcon_update = "<:defconsettingsupdated:470326274082996224>" + defcon_shutdown: str = "<:defcondisabled:470326273952972810>" + defcon_unshutdown: str = "<:defconenabled:470326274213150730>" + defcon_update: str = "<:defconsettingsupdated:470326274082996224>" - failmail = "<:failmail:633660039931887616>" - failed_file = "<:failed_file:1073298441968562226>" + failmail: str = "<:failmail:633660039931887616>" + failed_file: str = "<:failed_file:1073298441968562226>" - incident_actioned = "<:incident_actioned:714221559279255583>" - incident_investigating = "<:incident_investigating:714224190928191551>" - incident_unactioned = "<:incident_unactioned:714223099645526026>" + incident_actioned: str = "<:incident_actioned:714221559279255583>" + incident_investigating: str = "<:incident_investigating:714224190928191551>" + incident_unactioned: str = "<:incident_unactioned:714223099645526026>" - status_dnd = "<:status_dnd:470326272082313216>" - status_idle = "<:status_idle:470326266625785866>" - status_offline = "<:status_offline:470326266537705472>" - status_online = "<:status_online:470326272351010816>" + status_dnd: str = "<:status_dnd:470326272082313216>" + status_idle: str = "<:status_idle:470326266625785866>" + status_offline: str = "<:status_offline:470326266537705472>" + status_online: str = "<:status_online:470326272351010816>" - ducky_dave = "<:ducky_dave:742058418692423772>" + ducky_dave: str = "<:ducky_dave:742058418692423772>" - trashcan = "<:trashcan:637136429717389331>" + trashcan: str = "<:trashcan:637136429717389331>" - bullet = "\u2022" - check_mark = "\u2705" - cross_mark = "\u274C" - new = "\U0001F195" - pencil = "\u270F" + bullet: str = "\u2022" + check_mark: str = "\u2705" + cross_mark: str = "\u274C" + new: str = "\U0001F195" + pencil: str = "\u270F" - ok_hand = ":ok_hand:" + ok_hand: str = ":ok_hand:" Emojis = _Emojis() -class _Icons(EnvConfig): - EnvConfig.Config.env_prefix = "icons_" +class _Icons(EnvConfig, env_prefix="icons_"): crown_blurple = "https://cdn.discordapp.com/emojis/469964153289965568.png" crown_green = "https://cdn.discordapp.com/emojis/469964154719961088.png" @@ -620,12 +593,10 @@ class _Icons(EnvConfig): Icons = _Icons() -class _Keys(EnvConfig): - - EnvConfig.Config.env_prefix = "api_keys_" +class _Keys(EnvConfig, env_prefix="api_keys_"): - github = "" - site_api = "" + github: str = "" + site_api: str = "" Keys = _Keys() @@ -637,7 +608,7 @@ class _Keys(EnvConfig): # Default role combinations MODERATION_ROLES = Guild.moderation_roles STAFF_ROLES = Guild.staff_roles -STAFF_PARTNERS_COMMUNITY_ROLES = STAFF_ROLES + [Roles.partners, Roles.python_community] +STAFF_PARTNERS_COMMUNITY_ROLES = STAFF_ROLES + (Roles.partners, Roles.python_community) # Channel combinations MODERATION_CHANNELS = Guild.moderation_channels @@ -650,7 +621,7 @@ class _Keys(EnvConfig): # Bot replies -NEGATIVE_REPLIES = [ +NEGATIVE_REPLIES = ( "Noooooo!!", "Nope.", "I'm sorry Dave, I'm afraid I can't do that.", @@ -668,9 +639,9 @@ class _Keys(EnvConfig): "NEGATORY.", "Nuh-uh.", "Not in my house!", -] +) -POSITIVE_REPLIES = [ +POSITIVE_REPLIES = ( "Yep.", "Absolutely!", "Can do!", @@ -688,9 +659,9 @@ class _Keys(EnvConfig): "Of course!", "Aye aye, cap'n!", "I'll allow it.", -] +) -ERROR_REPLIES = [ +ERROR_REPLIES = ( "Please don't do that.", "You have to stop.", "Do you mind?", @@ -701,4 +672,4 @@ class _Keys(EnvConfig): "Are you trying to kill me?", "Noooooo!!", "I can't believe you've done this", -] +) From 292fba7c58b24df30f7631290bfecdbbd74da141 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 25 Jul 2023 15:21:02 +0100 Subject: [PATCH 3/5] Replace deprecated functions with new pydantic v2 functions --- bot/constants.py | 4 ++-- bot/exts/filtering/_filter_lists/antispam.py | 2 +- bot/exts/filtering/_filters/filter.py | 4 ++-- bot/exts/filtering/_settings.py | 2 +- .../actions/infraction_and_notification.py | 16 ++++++++-------- .../filtering/_settings_types/actions/ping.py | 4 ++-- .../filtering/_settings_types/settings_entry.py | 2 +- .../_settings_types/validations/channel_scope.py | 4 ++-- bot/exts/filtering/_ui/filter.py | 6 +++--- bot/exts/filtering/_ui/filter_list.py | 2 +- bot/exts/filtering/_utils.py | 13 +++++++++---- bot/exts/filtering/filtering.py | 14 ++++++++++---- bot/exts/recruitment/talentpool/_api.py | 12 ++++++------ botstrap.py | 6 +++--- .../bot/exts/filtering/test_settings_entries.py | 4 ++-- 15 files changed, 53 insertions(+), 42 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 922f41abdb..fce3c09ecd 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -8,7 +8,7 @@ import os from enum import Enum -from pydantic import BaseModel, root_validator +from pydantic import BaseModel, model_validator from pydantic_settings import BaseSettings @@ -311,7 +311,7 @@ class _Colours(EnvConfig, env_prefix="colours_"): white: int = 0xfffffe yellow: int = 0xffd241 - @root_validator(pre=True) + @model_validator(mode="before") def parse_hex_values(cls, values: dict) -> dict: # noqa: N805 """Convert hex strings to ints.""" for key, value in values.items(): diff --git a/bot/exts/filtering/_filter_lists/antispam.py b/bot/exts/filtering/_filter_lists/antispam.py index 22f35f40ea..f27412e1a4 100644 --- a/bot/exts/filtering/_filter_lists/antispam.py +++ b/bot/exts/filtering/_filter_lists/antispam.py @@ -93,7 +93,7 @@ async def actions_for( current_actions.pop("ping", None) current_actions.pop("send_alert", None) - new_infraction = current_actions[InfractionAndNotification.name].copy() + new_infraction = current_actions[InfractionAndNotification.name].model_copy() # Smaller infraction value => higher in hierarchy. if not current_infraction or new_infraction.infraction_type.value < current_infraction.value: # Pick the first triggered filter for the reason, there's no good way to decide between them. diff --git a/bot/exts/filtering/_filters/filter.py b/bot/exts/filtering/_filters/filter.py index 6745e4f66d..3f201cfde4 100644 --- a/bot/exts/filtering/_filters/filter.py +++ b/bot/exts/filtering/_filters/filter.py @@ -31,7 +31,7 @@ def __init__(self, filter_data: dict, defaults: Defaults | None = None): self.updated_at = arrow.get(filter_data["updated_at"]) self.actions, self.validations = create_settings(filter_data["settings"], defaults=defaults) if self.extra_fields_type: - self.extra_fields = self.extra_fields_type.parse_obj(filter_data["additional_settings"]) + self.extra_fields = self.extra_fields_type.model_validate(filter_data["additional_settings"]) else: self.extra_fields = None @@ -46,7 +46,7 @@ def overrides(self) -> tuple[dict[str, Any], dict[str, Any]]: filter_settings = {} if self.extra_fields: - filter_settings = self.extra_fields.dict(exclude_unset=True) + filter_settings = self.extra_fields.model_dump(exclude_unset=True) return settings, filter_settings diff --git a/bot/exts/filtering/_settings.py b/bot/exts/filtering/_settings.py index 766a5ea10e..7005dd2d1b 100644 --- a/bot/exts/filtering/_settings.py +++ b/bot/exts/filtering/_settings.py @@ -227,5 +227,5 @@ def dict(self) -> dict[str, Any]: """Return a dict representation of the stored fields across all entries.""" dict_ = {} for settings in self: - dict_ = reduce(operator.or_, (entry.dict() for entry in settings.values()), dict_) + dict_ = reduce(operator.or_, (entry.model_dump() for entry in settings.values()), dict_) return dict_ diff --git a/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py b/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py index f125382941..359aa7bc34 100644 --- a/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py +++ b/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py @@ -6,7 +6,7 @@ from dateutil.relativedelta import relativedelta from discord import Colour, Embed, Member, User from discord.errors import Forbidden -from pydantic import validator +from pydantic import field_validator from pydis_core.utils.logging import get_logger from pydis_core.utils.members import get_or_fetch_member @@ -151,7 +151,7 @@ class InfractionAndNotification(ActionEntry): infraction_duration: InfractionDuration infraction_channel: int - @validator("infraction_type", pre=True) + @field_validator("infraction_type", mode="before") @classmethod def convert_infraction_name(cls, infr_type: str | Infraction) -> Infraction: """Convert the string to an Infraction by name.""" @@ -221,14 +221,14 @@ def union(self, other: Self) -> Self: """ # Lower number -> higher in the hierarchy if self.infraction_type is None: - return other.copy() + return other.model_copy() if other.infraction_type is None: - return self.copy() + return self.model_copy() if self.infraction_type.value < other.infraction_type.value: - result = self.copy() + result = self.model_copy() elif self.infraction_type.value > other.infraction_type.value: - result = other.copy() + result = other.model_copy() other = self else: now = arrow.utcnow().datetime @@ -236,9 +236,9 @@ def union(self, other: Self) -> Self: other.infraction_duration is not None and now + self.infraction_duration.value > now + other.infraction_duration.value ): - result = self.copy() + result = self.model_copy() else: - result = other.copy() + result = other.model_copy() other = self # If the winner has no message but the loser does, copy the message to the winner. diff --git a/bot/exts/filtering/_settings_types/actions/ping.py b/bot/exts/filtering/_settings_types/actions/ping.py index fa3e2224fa..4b38a19f34 100644 --- a/bot/exts/filtering/_settings_types/actions/ping.py +++ b/bot/exts/filtering/_settings_types/actions/ping.py @@ -1,6 +1,6 @@ from typing import ClassVar, Self -from pydantic import validator +from pydantic import field_validator from bot.exts.filtering._filter_context import FilterContext from bot.exts.filtering._settings_types.settings_entry import ActionEntry @@ -25,7 +25,7 @@ class Ping(ActionEntry): guild_pings: set[str] dm_pings: set[str] - @validator("*", pre=True) + @field_validator("*", mode="before") @classmethod def init_sequence_if_none(cls, pings: list[str] | None) -> list[str]: """Initialize an empty sequence if the value is None.""" diff --git a/bot/exts/filtering/_settings_types/settings_entry.py b/bot/exts/filtering/_settings_types/settings_entry.py index a3a2cbe1a5..1cb9d9f2a9 100644 --- a/bot/exts/filtering/_settings_types/settings_entry.py +++ b/bot/exts/filtering/_settings_types/settings_entry.py @@ -28,7 +28,7 @@ class SettingsEntry(BaseModel, FieldRequiring): def __init__(self, defaults: SettingsEntry | None = None, /, **data): overrides = set() if defaults: - defaults_dict = defaults.dict() + defaults_dict = defaults.model_dump() for field_name, field_value in list(data.items()): if field_value is None: data[field_name] = defaults_dict[field_name] diff --git a/bot/exts/filtering/_settings_types/validations/channel_scope.py b/bot/exts/filtering/_settings_types/validations/channel_scope.py index 1880ae27ed..69de4199c3 100644 --- a/bot/exts/filtering/_settings_types/validations/channel_scope.py +++ b/bot/exts/filtering/_settings_types/validations/channel_scope.py @@ -1,6 +1,6 @@ from typing import ClassVar, Union -from pydantic import validator +from pydantic import field_validator from bot.exts.filtering._filter_context import FilterContext from bot.exts.filtering._settings_types.settings_entry import ValidationEntry @@ -36,7 +36,7 @@ class ChannelScope(ValidationEntry): enabled_channels: set[Union[int, str]] # noqa: UP007 enabled_categories: set[Union[int, str]] # noqa: UP007 - @validator("*", pre=True) + @field_validator("*", mode="before") @classmethod def init_if_sequence_none(cls, sequence: list[str] | None) -> list[str]: """Initialize an empty sequence if the value is None.""" diff --git a/bot/exts/filtering/_ui/filter.py b/bot/exts/filtering/_ui/filter.py index 4300de19ce..6927a5d419 100644 --- a/bot/exts/filtering/_ui/filter.py +++ b/bot/exts/filtering/_ui/filter.py @@ -34,7 +34,7 @@ def build_filter_repr_dict( default_setting_values = {} for settings_group in filter_list[list_type].defaults: for _, setting in settings_group.items(): - default_setting_values.update(to_serializable(setting.dict(), ui_repr=True)) + default_setting_values.update(to_serializable(setting.model_dump(), ui_repr=True)) # Add overrides. It's done in this way to preserve field order, since the filter won't have all settings. total_values = {} @@ -47,7 +47,7 @@ def build_filter_repr_dict( # Add the filter-specific settings. if filter_type.extra_fields_type: # This iterates over the default values of the extra fields model. - for name, value in filter_type.extra_fields_type().dict().items(): + for name, value in filter_type.extra_fields_type().model_dump().items(): if name not in extra_fields_overrides or repr_equals(extra_fields_overrides[name], value): total_values[f"{filter_type.name}/{name}"] = value else: @@ -287,7 +287,7 @@ async def update_embed( if "/" in setting_name: filter_name, setting_name = setting_name.split("/", maxsplit=1) dict_to_edit = self.filter_settings_overrides - default_value = self.filter_type.extra_fields_type().dict()[setting_name] + default_value = self.filter_type.extra_fields_type().model_dump()[setting_name] else: dict_to_edit = self.settings_overrides default_value = self.filter_list[self.list_type].default(setting_name) diff --git a/bot/exts/filtering/_ui/filter_list.py b/bot/exts/filtering/_ui/filter_list.py index 062975ad75..2858817e39 100644 --- a/bot/exts/filtering/_ui/filter_list.py +++ b/bot/exts/filtering/_ui/filter_list.py @@ -50,7 +50,7 @@ def build_filterlist_repr_dict(filter_list: FilterList, list_type: ListType, new default_setting_values = {} for settings_group in filter_list[list_type].defaults: for _, setting in settings_group.items(): - default_setting_values.update(to_serializable(setting.dict(), ui_repr=True)) + default_setting_values.update(to_serializable(setting.model_dump(), ui_repr=True)) # Add new values. It's done in this way to preserve field order, since the new_values won't have all settings. total_values = {} diff --git a/bot/exts/filtering/_utils.py b/bot/exts/filtering/_utils.py index e109a47ee1..944cf38370 100644 --- a/bot/exts/filtering/_utils.py +++ b/bot/exts/filtering/_utils.py @@ -7,7 +7,7 @@ import types from abc import ABC, abstractmethod from collections import defaultdict -from collections.abc import Iterable +from collections.abc import Callable, Iterable from dataclasses import dataclass from functools import cache from typing import Any, Self, TypeVar, Union, get_args, get_origin @@ -15,6 +15,7 @@ import discord import regex from discord.ext.commands import Command +from pydantic_core import core_schema import bot from bot.bot import Bot @@ -252,12 +253,16 @@ def __init__(self, value: Any): self.value = self.process_value(value) @classmethod - def __get_validators__(cls): + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: Callable[[Any], core_schema.CoreSchema], + ) -> core_schema.CoreSchema: """Boilerplate for Pydantic.""" - yield cls.validate + return core_schema.general_plain_validator_function(cls.validate) @classmethod - def validate(cls, v: Any) -> Self: + def validate(cls, v: Any, _info: core_schema.ValidationInfo) -> Self: """Takes the given value and returns a class instance with that value.""" if isinstance(v, CustomIOField): return cls(v.value) diff --git a/bot/exts/filtering/filtering.py b/bot/exts/filtering/filtering.py index 2b7ad2ff31..bedd9958f9 100644 --- a/bot/exts/filtering/filtering.py +++ b/bot/exts/filtering/filtering.py @@ -181,7 +181,7 @@ def collect_loaded_types(self, example_list: AtomicList) -> None: extra_fields_type, type_hints[field_name] ) - for field_name in extra_fields_type.__fields__ + for field_name in extra_fields_type.model_fields } async def schedule_offending_messages_deletion(self) -> None: @@ -754,7 +754,7 @@ async def fl_describe( setting_values = {} for settings_group in filter_list[list_type].defaults: for _, setting in settings_group.items(): - setting_values.update(to_serializable(setting.dict(), ui_repr=True)) + setting_values.update(to_serializable(setting.model_dump(), ui_repr=True)) embed = Embed(colour=Colour.blue()) populate_embed_from_dict(embed, setting_values) @@ -1239,7 +1239,13 @@ async def _patch_filter( for current_settings in (filter_.actions, filter_.validations): if current_settings: for setting_entry in current_settings.values(): - settings.update({setting: None for setting in setting_entry.dict() if setting not in settings}) + settings.update( + { + setting: None + for setting in setting_entry.model_dump() + if setting not in settings + } + ) # Even though the list ID remains unchanged, it still needs to be provided for correct serializer validation. list_id = filter_list[list_type].id @@ -1295,7 +1301,7 @@ def _filter_match_query( if not (differ_by_default <= override_matches): # The overrides didn't cover for the default mismatches. return False - filter_settings = filter_.extra_fields.dict() if filter_.extra_fields else {} + filter_settings = filter_.extra_fields.model_dump() if filter_.extra_fields else {} # If the dict changes then some fields were not the same. return (filter_settings | filter_settings_query) == filter_settings diff --git a/bot/exts/recruitment/talentpool/_api.py b/bot/exts/recruitment/talentpool/_api.py index e12111de5f..f7b2432092 100644 --- a/bot/exts/recruitment/talentpool/_api.py +++ b/bot/exts/recruitment/talentpool/_api.py @@ -1,6 +1,6 @@ from datetime import datetime -from pydantic import BaseModel, Field, parse_obj_as +from pydantic import BaseModel, Field, TypeAdapter from pydis_core.site_api import APIClient @@ -50,13 +50,13 @@ async def get_nominations( params["user__id"] = str(user_id) data = await self.site_api.get("bot/nominations", params=params) - nominations = parse_obj_as(list[Nomination], data) + nominations = TypeAdapter(list[Nomination]).validate_python(data) return nominations async def get_nomination(self, nomination_id: int) -> Nomination: """Fetch a nomination by ID.""" data = await self.site_api.get(f"bot/nominations/{nomination_id}") - nomination = Nomination.parse_obj(data) + nomination = Nomination.model_validate(data) return nomination async def edit_nomination( @@ -84,7 +84,7 @@ async def edit_nomination( data["thread_id"] = thread_id result = await self.site_api.patch(f"bot/nominations/{nomination_id}", json=data) - return Nomination.parse_obj(result) + return Nomination.model_validate(result) async def edit_nomination_entry( self, @@ -96,7 +96,7 @@ async def edit_nomination_entry( """Edit a nomination entry.""" data = {"actor": actor_id, "reason": reason} result = await self.site_api.patch(f"bot/nominations/{nomination_id}", json=data) - return Nomination.parse_obj(result) + return Nomination.model_validate(result) async def post_nomination( self, @@ -111,7 +111,7 @@ async def post_nomination( "user": user_id, } result = await self.site_api.post("bot/nominations", json=data) - return Nomination.parse_obj(result) + return Nomination.model_validate(result) async def get_activity( self, diff --git a/botstrap.py b/botstrap.py index c57a254a34..7a9d94d8b4 100644 --- a/botstrap.py +++ b/botstrap.py @@ -176,7 +176,7 @@ def create_webhook(self, name: str, channel_id_: int) -> str: all_roles = discord_client.get_all_roles() - for role_name in _Roles.__fields__: + for role_name in _Roles.model_fields: role_id = all_roles.get(role_name, None) if not role_id: @@ -209,7 +209,7 @@ def create_webhook(self, name: str, channel_id_: int) -> str: python_help_channel_id = discord_client.create_forum_channel(python_help_channel_name, python_help_category_id) all_channels[PYTHON_HELP_CHANNEL_NAME] = python_help_channel_id - for channel_name in _Channels.__fields__: + for channel_name in _Channels.model_fields: channel_id = all_channels.get(channel_name, None) if not channel_id: log.warning( @@ -222,7 +222,7 @@ def create_webhook(self, name: str, channel_id_: int) -> str: config_str += "\n#Categories\n" - for category_name in _Categories.__fields__: + for category_name in _Categories.model_fields: category_id = all_categories.get(category_name, None) if not category_id: log.warning( diff --git a/tests/bot/exts/filtering/test_settings_entries.py b/tests/bot/exts/filtering/test_settings_entries.py index 3ae0b5ab56..988435022e 100644 --- a/tests/bot/exts/filtering/test_settings_entries.py +++ b/tests/bot/exts/filtering/test_settings_entries.py @@ -173,7 +173,7 @@ def test_infraction_merge_of_same_infraction_type(self): result = infraction1.union(infraction2) self.assertDictEqual( - result.dict(), + result.model_dump(), { "infraction_type": Infraction.TIMEOUT, "infraction_reason": "there", @@ -206,7 +206,7 @@ def test_infraction_merge_of_different_infraction_types(self): result = infraction1.union(infraction2) self.assertDictEqual( - result.dict(), + result.model_dump(), { "infraction_type": Infraction.BAN, "infraction_reason": "", From 505c377a5a333104e6ffff95d4a2bf007097abd4 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 31 Jul 2023 17:45:33 +0100 Subject: [PATCH 4/5] Use a custom type coercion function as pydantic removed their's https://docs.pydantic.dev/latest/migration/#changes-to-handling-of-standard-types > While union types will still attempt validation of each choice from left to right, they now preserve the type of the input whenever possible, even if the correct type is not the first choice for which the input would pass validation --- .../validations/bypass_roles.py | 25 ++++++++++++++-- .../validations/channel_scope.py | 30 ++++++++++++------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/bot/exts/filtering/_settings_types/validations/bypass_roles.py b/bot/exts/filtering/_settings_types/validations/bypass_roles.py index 50fb1e650a..0ddb2fdb5b 100644 --- a/bot/exts/filtering/_settings_types/validations/bypass_roles.py +++ b/bot/exts/filtering/_settings_types/validations/bypass_roles.py @@ -1,6 +1,8 @@ -from typing import ClassVar, Union +from collections.abc import Sequence +from typing import ClassVar from discord import Member +from pydantic import field_validator from bot.exts.filtering._filter_context import FilterContext from bot.exts.filtering._settings_types.settings_entry import ValidationEntry @@ -12,7 +14,26 @@ class RoleBypass(ValidationEntry): name: ClassVar[str] = "bypass_roles" description: ClassVar[str] = "A list of role IDs or role names. Users with these roles will not trigger the filter." - bypass_roles: set[Union[int, str]] # noqa: UP007 + bypass_roles: set[int | str] + + @field_validator("bypass_roles", mode="before") + @classmethod + def init_if_bypass_roles_none(cls, bypass_roles: Sequence[int | str] | None) -> Sequence[int | str]: + """ + Initialize an empty sequence if the value is None. + + This also coerces each element of bypass_roles to an int, if possible. + """ + if bypass_roles is None: + return [] + + def _coerce_to_int(input: int | str) -> int | str: + try: + return int(input) + except ValueError: + return input + + return map(_coerce_to_int, bypass_roles) def triggers_on(self, ctx: FilterContext) -> bool: """Return whether the filter should be triggered on this user given their roles.""" diff --git a/bot/exts/filtering/_settings_types/validations/channel_scope.py b/bot/exts/filtering/_settings_types/validations/channel_scope.py index 69de4199c3..3d31131af4 100644 --- a/bot/exts/filtering/_settings_types/validations/channel_scope.py +++ b/bot/exts/filtering/_settings_types/validations/channel_scope.py @@ -1,4 +1,5 @@ -from typing import ClassVar, Union +from collections.abc import Sequence +from typing import ClassVar from pydantic import field_validator @@ -29,20 +30,29 @@ class ChannelScope(ValidationEntry): ) } - # NOTE: Don't change this to use the new 3.10 union syntax unless you ensure Pydantic type validation and coercion - # work properly. At the time of writing this code there's a difference. - disabled_channels: set[Union[int, str]] # noqa: UP007 - disabled_categories: set[Union[int, str]] # noqa: UP007 - enabled_channels: set[Union[int, str]] # noqa: UP007 - enabled_categories: set[Union[int, str]] # noqa: UP007 + disabled_channels: set[int | str] + disabled_categories: set[int | str] + enabled_channels: set[int | str] + enabled_categories: set[int | str] @field_validator("*", mode="before") @classmethod - def init_if_sequence_none(cls, sequence: list[str] | None) -> list[str]: - """Initialize an empty sequence if the value is None.""" + def init_if_sequence_none(cls, sequence: Sequence[int | str] | None) -> Sequence[int | str]: + """ + Initialize an empty sequence if the value is None. + + This also coerces each element of sequence to an int, if possible. + """ if sequence is None: return [] - return sequence + + def _coerce_to_int(input: int | str) -> int | str: + try: + return int(input) + except ValueError: + return input + + return map(_coerce_to_int, sequence) def triggers_on(self, ctx: FilterContext) -> bool: """ From 97fa61614e727afab4335cf15bc09a4c1aa0d731 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 25 Jul 2023 15:24:45 +0100 Subject: [PATCH 5/5] Don't use a BaseSettings instance for constants that don't need to be overwritten --- bot/constants.py | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index fce3c09ecd..4e73fbe746 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -8,7 +8,7 @@ import os from enum import Enum -from pydantic import BaseModel, model_validator +from pydantic import BaseModel from pydantic_settings import BaseSettings @@ -298,30 +298,6 @@ class _CodeBlock(EnvConfig, env_prefix="code_block_"): CodeBlock = _CodeBlock() -class _Colours(EnvConfig, env_prefix="colours_"): - - blue: int = 0x3775a8 - bright_green: int = 0x01d277 - orange: int = 0xe67e22 - pink: int = 0xcf84e0 - purple: int = 0xb734eb - soft_green: int = 0x68c290 - soft_orange: int = 0xf9cb54 - soft_red: int = 0xcd6d6d - white: int = 0xfffffe - yellow: int = 0xffd241 - - @model_validator(mode="before") - def parse_hex_values(cls, values: dict) -> dict: # noqa: N805 - """Convert hex strings to ints.""" - for key, value in values.items(): - values[key] = int(value, 16) - return values - - -Colours = _Colours() - - class _HelpChannels(EnvConfig, env_prefix="help_channels_"): enable: bool = True @@ -536,7 +512,8 @@ class _Emojis(EnvConfig, env_prefix="emojis_"): Emojis = _Emojis() -class _Icons(EnvConfig, env_prefix="icons_"): +class Icons: + """URLs to commonly used icons.""" crown_blurple = "https://cdn.discordapp.com/emojis/469964153289965568.png" crown_green = "https://cdn.discordapp.com/emojis/469964154719961088.png" @@ -590,7 +567,19 @@ class _Icons(EnvConfig, env_prefix="icons_"): voice_state_red = "https://cdn.discordapp.com/emojis/656899769905709076.png" -Icons = _Icons() +class Colours: + """Colour codes, mostly used to set discord.Embed colours.""" + + blue: int = 0x3775a8 + bright_green: int = 0x01d277 + orange: int = 0xe67e22 + pink: int = 0xcf84e0 + purple: int = 0xb734eb + soft_green: int = 0x68c290 + soft_orange: int = 0xf9cb54 + soft_red: int = 0xcd6d6d + white: int = 0xfffffe + yellow: int = 0xffd241 class _Keys(EnvConfig, env_prefix="api_keys_"):