From d5a7286aed89a5da7a99a2ddc8fde91fcfb9c48f Mon Sep 17 00:00:00 2001 From: pgoslatara Date: Sat, 28 Sep 2024 19:38:52 +0200 Subject: [PATCH 1/8] More granular parsing structure --- poetry.lock | 715 +++++++++++++++++- pyproject.toml | 5 +- .../dbt_cloud/README.md | 0 .../dbt_cloud/manifest_latest.py | 4 +- .../artifact_parsers/parsers_catalog.py | 80 ++ .../artifact_parsers/parsers_common.py | 191 +++++ .../artifact_parsers/parsers_manifest.py | 298 ++++++++ .../artifact_parsers/parsers_run_results.py | 113 +++ .../checks/catalog/check_catalog_sources.py | 2 +- .../checks/catalog/check_columns.py | 5 +- .../checks/manifest/check_exposures.py | 5 +- .../checks/manifest/check_lineage.py | 2 +- .../checks/manifest/check_metadata.py | 2 +- .../checks/manifest/check_models.py | 4 +- .../checks/manifest/check_semantic_models.py | 2 +- .../checks/manifest/check_sources.py | 4 +- .../checks/manifest/check_unit_tests.py | 4 +- .../checks/run_results/check_run_results.py | 2 +- src/dbt_bouncer/config_file_validator.py | 14 +- src/dbt_bouncer/main.py | 2 +- src/dbt_bouncer/parsers.py | 581 -------------- src/dbt_bouncer/runner.py | 4 +- tests/conftest.py | 5 +- .../checks/catalog/test_catalog_sources.py | 4 +- tests/unit/checks/catalog/test_columns.py | 10 +- tests/unit/checks/manifest/test_exposures.py | 10 +- tests/unit/checks/manifest/test_lineage.py | 5 +- tests/unit/checks/manifest/test_metadata.py | 4 +- tests/unit/checks/manifest/test_models.py | 2 +- .../checks/manifest/test_semantic_models.py | 8 +- tests/unit/checks/manifest/test_sources.py | 12 +- tests/unit/checks/manifest/test_unit_tests.py | 8 +- .../checks/run_results/test_run_results.py | 2 +- tests/unit/test_runner.py | 36 +- 34 files changed, 1491 insertions(+), 654 deletions(-) rename src/dbt_bouncer/{ => artifact_parsers}/dbt_cloud/README.md (100%) rename src/dbt_bouncer/{ => artifact_parsers}/dbt_cloud/manifest_latest.py (99%) create mode 100644 src/dbt_bouncer/artifact_parsers/parsers_catalog.py create mode 100644 src/dbt_bouncer/artifact_parsers/parsers_common.py create mode 100644 src/dbt_bouncer/artifact_parsers/parsers_manifest.py create mode 100644 src/dbt_bouncer/artifact_parsers/parsers_run_results.py delete mode 100644 src/dbt_bouncer/parsers.py diff --git a/poetry.lock b/poetry.lock index 7eef6552..c1ed644a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -37,6 +37,35 @@ files = [ [package.dependencies] typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} +[[package]] +name = "appnope" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = ">=3.6" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] + [[package]] name = "astunparse" version = "1.6.3" @@ -103,6 +132,17 @@ pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +optional = false +python-versions = "*" +files = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] + [[package]] name = "black" version = "24.8.0" @@ -160,6 +200,85 @@ files = [ {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + [[package]] name = "cfgv" version = "3.4.0" @@ -295,6 +414,23 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "comm" +version = "0.2.2" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +files = [ + {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, + {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +test = ["pytest"] + [[package]] name = "coverage" version = "7.6.1" @@ -560,6 +696,48 @@ python-dateutil = ">=2.0,<3" pyyaml = ">=6.0,<7" typing-extensions = ">=4.4,<5" +[[package]] +name = "debugpy" +version = "1.8.6" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:30f467c5345d9dfdcc0afdb10e018e47f092e383447500f125b4e013236bf14b"}, + {file = "debugpy-1.8.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d73d8c52614432f4215d0fe79a7e595d0dd162b5c15233762565be2f014803b"}, + {file = "debugpy-1.8.6-cp310-cp310-win32.whl", hash = "sha256:e3e182cd98eac20ee23a00653503315085b29ab44ed66269482349d307b08df9"}, + {file = "debugpy-1.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:e3a82da039cfe717b6fb1886cbbe5c4a3f15d7df4765af857f4307585121c2dd"}, + {file = "debugpy-1.8.6-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67479a94cf5fd2c2d88f9615e087fcb4fec169ec780464a3f2ba4a9a2bb79955"}, + {file = "debugpy-1.8.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb8653f6cbf1dd0a305ac1aa66ec246002145074ea57933978346ea5afdf70b"}, + {file = "debugpy-1.8.6-cp311-cp311-win32.whl", hash = "sha256:cdaf0b9691879da2d13fa39b61c01887c34558d1ff6e5c30e2eb698f5384cd43"}, + {file = "debugpy-1.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:43996632bee7435583952155c06881074b9a742a86cee74e701d87ca532fe833"}, + {file = "debugpy-1.8.6-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:db891b141fc6ee4b5fc6d1cc8035ec329cabc64bdd2ae672b4550c87d4ecb128"}, + {file = "debugpy-1.8.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:567419081ff67da766c898ccf21e79f1adad0e321381b0dfc7a9c8f7a9347972"}, + {file = "debugpy-1.8.6-cp312-cp312-win32.whl", hash = "sha256:c9834dfd701a1f6bf0f7f0b8b1573970ae99ebbeee68314116e0ccc5c78eea3c"}, + {file = "debugpy-1.8.6-cp312-cp312-win_amd64.whl", hash = "sha256:e4ce0570aa4aca87137890d23b86faeadf184924ad892d20c54237bcaab75d8f"}, + {file = "debugpy-1.8.6-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:df5dc9eb4ca050273b8e374a4cd967c43be1327eeb42bfe2f58b3cdfe7c68dcb"}, + {file = "debugpy-1.8.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a85707c6a84b0c5b3db92a2df685b5230dd8fb8c108298ba4f11dba157a615a"}, + {file = "debugpy-1.8.6-cp38-cp38-win32.whl", hash = "sha256:538c6cdcdcdad310bbefd96d7850be1cd46e703079cc9e67d42a9ca776cdc8a8"}, + {file = "debugpy-1.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:22140bc02c66cda6053b6eb56dfe01bbe22a4447846581ba1dd6df2c9f97982d"}, + {file = "debugpy-1.8.6-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:c1cef65cffbc96e7b392d9178dbfd524ab0750da6c0023c027ddcac968fd1caa"}, + {file = "debugpy-1.8.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e60bd06bb3cc5c0e957df748d1fab501e01416c43a7bdc756d2a992ea1b881"}, + {file = "debugpy-1.8.6-cp39-cp39-win32.whl", hash = "sha256:f7158252803d0752ed5398d291dee4c553bb12d14547c0e1843ab74ee9c31123"}, + {file = "debugpy-1.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:3358aa619a073b620cd0d51d8a6176590af24abcc3fe2e479929a154bf591b51"}, + {file = "debugpy-1.8.6-py2.py3-none-any.whl", hash = "sha256:b48892df4d810eff21d3ef37274f4c60d32cdcafc462ad5647239036b0f0649f"}, + {file = "debugpy-1.8.6.zip", hash = "sha256:c931a9371a86784cee25dec8d65bc2dc7a21f3f1552e3833d9ef8f919d22280a"}, +] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + [[package]] name = "deepdiff" version = "7.0.1" @@ -678,6 +856,20 @@ files = [ [package.extras] testing = ["hatch", "pre-commit", "pytest", "tox"] +[[package]] +name = "executing" +version = "2.1.0" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.8" +files = [ + {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, + {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + [[package]] name = "filelock" version = "3.16.1" @@ -838,6 +1030,78 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "ipykernel" +version = "6.29.5" +description = "IPython Kernel for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5"}, + {file = "ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=24" +tornado = ">=6.1" +traitlets = ">=5.4.0" + +[package.extras] +cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "8.12.3" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"}, + {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" +typing-extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] + [[package]] name = "isodate" version = "0.6.1" @@ -866,6 +1130,25 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + [[package]] name = "jinja2" version = "3.1.4" @@ -935,6 +1218,49 @@ files = [ importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} referencing = ">=0.31.0" +[[package]] +name = "jupyter-client" +version = "8.6.3" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"}, + {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = ">=5.3" + +[package.extras] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] + [[package]] name = "leather" version = "0.4.0" @@ -1188,6 +1514,20 @@ orjson = ["orjson"] toml = ["tomli (>=1.1.0)", "tomli-w (>=1.0)"] yaml = ["pyyaml (>=3.13)"] +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, + {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, +] + +[package.dependencies] +traitlets = "*" + [[package]] name = "mergedeep" version = "1.3.4" @@ -1566,6 +1906,17 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + [[package]] name = "networkx" version = "3.1" @@ -1646,6 +1997,21 @@ files = [ {file = "parsedatetime-2.6.tar.gz", hash = "sha256:4cb368fbb18a0b7231f4d76119165451c8d2e35951455dfee97c62a87b04d455"}, ] +[[package]] +name = "parso" +version = "0.8.4" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, +] + +[package.extras] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] + [[package]] name = "pathspec" version = "0.12.1" @@ -1671,6 +2037,31 @@ files = [ [package.extras] subprocess = ["subprocess32 (>=3.2.7)"] +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +optional = false +python-versions = "*" +files = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] + [[package]] name = "pkgutil-resolve-name" version = "1.3.10" @@ -1741,6 +2132,20 @@ files = [ {file = "progress-1.6.tar.gz", hash = "sha256:c9c86e98b5c03fa1fe11e3b67c1feda4788b8d0fe7336c2ff7d5644ccfba34cd"}, ] +[[package]] +name = "prompt-toolkit" +version = "3.0.48" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, + {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, +] + +[package.dependencies] +wcwidth = "*" + [[package]] name = "protobuf" version = "4.25.5" @@ -1761,6 +2166,71 @@ files = [ {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"}, ] +[[package]] +name = "psutil" +version = "6.0.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, + {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, + {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, + {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, + {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, + {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, + {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, + {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, + {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, + {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, + {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, +] + +[package.extras] +tests = ["pytest"] + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + [[package]] name = "pydantic" version = "2.9.2" @@ -1909,13 +2379,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.10.2" +version = "10.11" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "pymdown_extensions-10.10.2-py3-none-any.whl", hash = "sha256:513a9e9432b197cf0539356c8f1fc376e0d10b70ad150cadeb649a5628aacd45"}, - {file = "pymdown_extensions-10.10.2.tar.gz", hash = "sha256:65d82324ef2497931bc858c8320540c6264ab0d9a292707edb61f4fe0cd56633"}, + {file = "pymdown_extensions-10.11-py3-none-any.whl", hash = "sha256:e68080eac44634406b31f4aec58fbad17b0ec5fca6b086e29008616d54c3906b"}, + {file = "pymdown_extensions-10.11.tar.gz", hash = "sha256:2653fb658bca5f278029f8c67a67f0f08b7bd3c657e2630d261ad542e97c4192"}, ] [package.dependencies] @@ -2052,6 +2522,29 @@ files = [ {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, ] +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -2128,6 +2621,127 @@ files = [ [package.dependencies] pyyaml = "*" +[[package]] +name = "pyzmq" +version = "26.2.0" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, + {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, + {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, + {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, + {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, + {file = "pyzmq-26.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win32.whl", hash = "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd"}, + {file = "pyzmq-26.2.0-cp38-cp38-win32.whl", hash = "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988"}, + {file = "pyzmq-26.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, + {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, + {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + [[package]] name = "rapidfuzz" version = "3.9.7" @@ -2607,6 +3221,25 @@ files = [ dev = ["build", "hatch"] doc = ["sphinx"] +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + [[package]] name = "tabulate" version = "0.9.0" @@ -2654,6 +3287,26 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "tornado" +version = "6.4.1" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">=3.8" +files = [ + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, + {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, + {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, + {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, +] + [[package]] name = "tqdm" version = "4.66.5" @@ -2674,6 +3327,32 @@ notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] +[[package]] +name = "traitlets" +version = "5.14.3" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, + {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20240917" +description = "Typing stubs for PyYAML" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, + {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, +] + [[package]] name = "types-tabulate" version = "0.9.0.20240106" @@ -2685,6 +3364,17 @@ files = [ {file = "types_tabulate-0.9.0.20240106-py3-none-any.whl", hash = "sha256:0378b7b6fe0ccb4986299496d027a6d4c218298ecad67199bbd0e2d7e9d335a1"}, ] +[[package]] +name = "types-toml" +version = "0.10.8.20240310" +description = "Typing stubs for toml" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331"}, + {file = "types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d"}, +] + [[package]] name = "typing-extensions" version = "4.12.2" @@ -2740,13 +3430,13 @@ test = ["coverage", "flake8 (>=3.7)", "mypy", "pretend", "pytest"] [[package]] name = "virtualenv" -version = "20.26.5" +version = "20.26.6" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, - {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, + {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, + {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, ] [package.dependencies] @@ -2805,6 +3495,17 @@ files = [ [package.extras] watchmedo = ["PyYAML (>=3.10)"] +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + [[package]] name = "wheel" version = "0.44.0" @@ -2841,4 +3542,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<3.13" -content-hash = "2c9a632320bd6c6802c8c7d8be332cf1c3b161174f46dcde68b37b8b8bfa7e6c" +content-hash = "9fe301c21f932da2d48a256eb64882a1a01e5f96b7b179e65b4944e978ade233" diff --git a/pyproject.toml b/pyproject.toml index d683957d..a1897b8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,7 +110,10 @@ pytest-xdist = "^3" ruff = "^0" sh = "^2" "shandy-sqlfmt" = {version = "^0", extras=["shandy-sqlfmt[jinjafmt]"]} +types-PyYaml = "^6" types-tabulate = "^0" +types-toml = "^0" +ipykernel = "^6.29.5" [tool.poetry.group.docs.dependencies] mike = "^2" @@ -154,7 +157,7 @@ exclude = [ "dist", "node_modules", "site-packages", - "src/dbt_bouncer/dbt_cloud", + "src/dbt_bouncer/artifact_parsers/dbt_cloud", "venv", ] line-length = 88 diff --git a/src/dbt_bouncer/dbt_cloud/README.md b/src/dbt_bouncer/artifact_parsers/dbt_cloud/README.md similarity index 100% rename from src/dbt_bouncer/dbt_cloud/README.md rename to src/dbt_bouncer/artifact_parsers/dbt_cloud/README.md diff --git a/src/dbt_bouncer/dbt_cloud/manifest_latest.py b/src/dbt_bouncer/artifact_parsers/dbt_cloud/manifest_latest.py similarity index 99% rename from src/dbt_bouncer/dbt_cloud/manifest_latest.py rename to src/dbt_bouncer/artifact_parsers/dbt_cloud/manifest_latest.py index 676006c0..14add729 100644 --- a/src/dbt_bouncer/dbt_cloud/manifest_latest.py +++ b/src/dbt_bouncer/artifact_parsers/dbt_cloud/manifest_latest.py @@ -15,7 +15,7 @@ class CustomGranularity(BaseParserModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) name: str column_name: Optional[str] = None @@ -23,7 +23,7 @@ class CustomGranularity(BaseParserModel): class TimeSpine(BaseParserModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) standard_granularity_column: str custom_granularities: Optional[List[CustomGranularity]] = None diff --git a/src/dbt_bouncer/artifact_parsers/parsers_catalog.py b/src/dbt_bouncer/artifact_parsers/parsers_catalog.py new file mode 100644 index 00000000..9a2a1827 --- /dev/null +++ b/src/dbt_bouncer/artifact_parsers/parsers_catalog.py @@ -0,0 +1,80 @@ +# TODO Remove after this program no longer support Python 3.8.* +from __future__ import annotations + +import logging +import warnings +from typing import TYPE_CHECKING, List + +from pydantic import BaseModel + +from dbt_bouncer.utils import clean_path_str + +with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=UserWarning) + from dbt_artifacts_parser.parsers.catalog.catalog_v1 import ( + CatalogTable, # noqa: TCH002 + CatalogV1, # noqa: TCH002 + ) +if TYPE_CHECKING: + from pathlib import Path + + from dbt_bouncer.artifact_parsers.parsers_manifest import DbtBouncerManifest + + +from dbt_bouncer.artifact_parsers.parsers_common import load_dbt_artifact + + +class DbtBouncerCatalogNode(BaseModel): + """Model for all nodes in `catalog.json`.""" + + catalog_node: CatalogTable + original_file_path: str + unique_id: str + + +def parse_catalog( + artifact_dir: Path, + manifest_obj: "DbtBouncerManifest", +) -> tuple[List[DbtBouncerCatalogNode], List[DbtBouncerCatalogNode]]: + """Parse the catalog.json artifact. + + Returns: + List[DbtBouncerCatalogNode]: List of catalog nodes for the project. + List[DbtBouncerCatalogNode]: List of catalog nodes for the project sources. + + """ + catalog_obj: CatalogV1 = load_dbt_artifact( + artifact_name="catalog.json", + dbt_artifacts_dir=artifact_dir, + ) + project_catalog_nodes = [ + DbtBouncerCatalogNode( + **{ + "catalog_node": v, + "original_file_path": clean_path_str( + manifest_obj.manifest.nodes[k].original_file_path + ), + "unique_id": k, + }, + ) + for k, v in catalog_obj.nodes.items() + if k.split(".")[-2] == manifest_obj.manifest.metadata.project_name + ] + project_catalog_sources = [ + DbtBouncerCatalogNode( + **{ + "catalog_node": v, + "original_file_path": clean_path_str( + manifest_obj.manifest.sources[k].original_file_path + ), + "unique_id": k, + }, + ) + for k, v in catalog_obj.sources.items() + if k.split(".")[1] == manifest_obj.manifest.metadata.project_name + ] + logging.info( + f"Parsed `catalog.json`: {len(project_catalog_nodes)} nodes, {len(project_catalog_sources)} sources.", + ) + + return project_catalog_nodes, project_catalog_sources diff --git a/src/dbt_bouncer/artifact_parsers/parsers_common.py b/src/dbt_bouncer/artifact_parsers/parsers_common.py new file mode 100644 index 00000000..04942200 --- /dev/null +++ b/src/dbt_bouncer/artifact_parsers/parsers_common.py @@ -0,0 +1,191 @@ +# TODO Remove after this program no longer support Python 3.8.* +from __future__ import annotations + +import json +import logging +import warnings +from pathlib import Path +from typing import TYPE_CHECKING, List, Literal, Union + +import semver + +if TYPE_CHECKING: + from dbt_bouncer.artifact_parsers.parsers_manifest import DbtBouncerManifest +if TYPE_CHECKING: + from dbt_bouncer.artifact_parsers.parsers_catalog import DbtBouncerCatalogNode + from dbt_bouncer.artifact_parsers.parsers_manifest import ( + DbtBouncerModel, + DbtBouncerSemanticModel, + DbtBouncerSource, + DbtBouncerTest, + UnitTests, + ) + from dbt_bouncer.artifact_parsers.parsers_run_results import ( + DbtBouncerRunResult, + DbtBouncerRunResultBase, + ) + from dbt_bouncer.config_file_validator import DbtBouncerConf + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=UserWarning) + from dbt_artifacts_parser.parsers.catalog.catalog_v1 import CatalogV1 + from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( + Exposures, + Macros, + UnitTests, + ) + + +def load_dbt_artifact( + artifact_name: Literal["catalog.json", "manifest.json", "run_results.json"], + dbt_artifacts_dir: Path, +) -> Union["CatalogV1", "DbtBouncerManifest", "DbtBouncerRunResultBase"]: + """Load a dbt artifact from a JSON file to a Pydantic object. + + Returns: + Union[CatalogV1, DbtBouncerManifest, RunResultsV4, RunResultsV5, RunResultsV6]: + The dbt artifact loaded as a Pydantic object. + + Raises: + FileNotFoundError: + If the artifact file does not exist. + + """ + logging.debug(f"{artifact_name=}") + logging.debug(f"{dbt_artifacts_dir=}") + + artifact_path = dbt_artifacts_dir / Path(artifact_name) + logging.debug(f"Loading {artifact_name} from {artifact_path}...") + if not Path(artifact_path).exists(): + raise FileNotFoundError( + f"No {artifact_name} found at {artifact_path}.", + ) + + if artifact_name == "catalog.json": + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=UserWarning) + from dbt_artifacts_parser.parsers.catalog.catalog_v1 import CatalogV1 + with Path.open(Path(artifact_path), "r") as fp: + catalog_obj = CatalogV1(**json.load(fp)) + + return catalog_obj + + elif artifact_name == "manifest.json": + # First assess dbt version is sufficient + with Path.open(Path(artifact_path), "r") as fp: + manifest_json = json.load(fp) + + assert ( + semver.Version.parse(manifest_json["metadata"]["dbt_version"]) >= "1.6.0" + ), f"The supplied `manifest.json` was generated with dbt version {manifest_json['metadata']['dbt_version']}, this is below the minimum supported version of 1.6.0." + + from dbt_bouncer.artifact_parsers.parsers_manifest import ( + DbtBouncerManifest, + parse_manifest, + ) + + manifest_obj = parse_manifest(manifest_json) + return DbtBouncerManifest(**{"manifest": manifest_obj}) + + elif artifact_name == "run_results.json": + from dbt_bouncer.artifact_parsers.parsers_run_results import parse_run_results + + with Path.open(Path(artifact_path), "r") as fp: + run_results_obj = parse_run_results(run_results=json.load(fp)) + + return run_results_obj + + +def parse_dbt_artifacts( + bouncer_config: "DbtBouncerConf", dbt_artifacts_dir: Path +) -> tuple[ + "DbtBouncerManifest", + List["Exposures"], + List["Macros"], + List["DbtBouncerModel"], + List["DbtBouncerSemanticModel"], + List["DbtBouncerSource"], + List["DbtBouncerTest"], + List["UnitTests"], + List["DbtBouncerCatalogNode"], + List["DbtBouncerCatalogNode"], + List["DbtBouncerRunResult"], +]: + """Parse all required dbt artifacts. + + Args: + bouncer_config (DbtBouncerConf): All checks to be run. + dbt_artifacts_dir (Path): Path to directory where artifacts are located. + + Returns: + DbtBouncerManifest: The manifest object. + List[DbtBouncerExposure]: List of exposures in the project. + List[DbtBouncerMacro]: List of macros in the project. + List[DbtBouncerModel]: List of models in the project. + List[DbtBouncerSemanticModel]: List of semantic models in the project. + List[DbtBouncerSource]: List of sources in the project. + List[DbtBouncerTest]: List of tests in the project. + List[DbtBouncerUnitTest]: List of unit tests in the project. + List[DbtBouncerCatalogNode]: List of catalog nodes for the project. + List[DbtBouncerCatalogNode]: List of catalog nodes for the project sources. + List[DbtBouncerRunResult]: A list of DbtBouncerRunResult objects. + + """ + from dbt_bouncer.artifact_parsers.parsers_common import load_dbt_artifact + from dbt_bouncer.artifact_parsers.parsers_manifest import parse_manifest_artifact + + # Manifest, will always be parsed + manifest_obj = load_dbt_artifact( + artifact_name="manifest.json", + dbt_artifacts_dir=dbt_artifacts_dir, + ) + ( + project_exposures, + project_macros, + project_models, + project_semantic_models, + project_sources, + project_tests, + project_unit_tests, + ) = parse_manifest_artifact( + manifest_obj=manifest_obj, + ) + + # Catalog, must come after manifest is parsed + from dbt_bouncer.artifact_parsers.parsers_catalog import parse_catalog + + if bouncer_config.catalog_checks != []: + project_catalog_nodes, project_catalog_sources = parse_catalog( + artifact_dir=dbt_artifacts_dir, + manifest_obj=manifest_obj, + ) + else: + project_catalog_nodes = [] + project_catalog_sources = [] + + # Run results, must come after manifest is parsed + from dbt_bouncer.artifact_parsers.parsers_run_results import ( + parse_run_results_artifact, + ) + + if bouncer_config.run_results_checks != []: + project_run_results = parse_run_results_artifact( + artifact_dir=dbt_artifacts_dir, + manifest_obj=manifest_obj, + ) + else: + project_run_results = [] + + return ( + manifest_obj, + project_exposures, + project_macros, + project_models, + project_semantic_models, + project_sources, + project_tests, + project_unit_tests, + project_catalog_nodes, + project_catalog_sources, + project_run_results, + ) diff --git a/src/dbt_bouncer/artifact_parsers/parsers_manifest.py b/src/dbt_bouncer/artifact_parsers/parsers_manifest.py new file mode 100644 index 00000000..bdf15287 --- /dev/null +++ b/src/dbt_bouncer/artifact_parsers/parsers_manifest.py @@ -0,0 +1,298 @@ +# TODO Remove after this program no longer support Python 3.8.* +from __future__ import annotations + +import logging +import warnings +from enum import Enum +from typing import Any, Dict, List, Union + +import semver +from pydantic import BaseModel + +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ManifestLatest +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + Nodes2 as Nodes2Latest, +) +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + Nodes4 as Nodes4Latest, +) +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + Nodes6 as Nodes6Latest, +) +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + SemanticModels as SemanticModelsLatest, +) +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + Sources as SourcesLatest, +) +from dbt_bouncer.utils import clean_path_str + +with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=UserWarning) + from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( + Exposure as Exposure_v10, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( + GenericTestNode as GenericTestNode_v10, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ManifestV10 + from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( + ModelNode as ModelNode_v10, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( + SemanticModel as SemanticModel_v10, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( + SingularTestNode as SingularTestNode_v10, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( + SourceDefinition as SourceDefinition_v10, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( + Exposure as Exposure_v11, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( + GenericTestNode as GenericTestNode_v11, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ManifestV11 + from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( + ModelNode as ModelNode_v11, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( + SemanticModel as SemanticModel_v11, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( + SingularTestNode as SingularTestNode_v11, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( + SourceDefinition as SourceDefinition_v11, + ) + from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( + Exposures, + Macros, + ManifestV12, + Nodes1, + Nodes2, + Nodes4, + Nodes6, + SemanticModels, + Sources, + UnitTests, + ) + + +class DbtBouncerManifest(BaseModel): + """Model for all manifest objects.""" + + manifest: Union[ManifestV10, ManifestV11, ManifestV12, ManifestLatest] + + +DbtBouncerExposureBase = Union[Exposure_v10, Exposure_v11, Exposures] + + +class DbtBouncerExposure(BaseModel): + """Model for all exposure nodes in `manifest.json`.""" + + model: DbtBouncerExposureBase + original_file_path: str + unique_id: str + + +DbtBouncerModelBase = Union[ModelNode_v10, ModelNode_v11, Nodes4, Nodes4Latest] + + +class DbtBouncerModel(BaseModel): + """Model for all model nodes in `manifest.json`.""" + + model: DbtBouncerModelBase + original_file_path: str + unique_id: str + + +DbtBouncerSemanticModelBase = Union[ + SemanticModel_v10, SemanticModel_v11, SemanticModels, SemanticModelsLatest +] + + +class DbtBouncerSemanticModel(BaseModel): + """Model for all semantic model nodes in `manifest.json`.""" + + original_file_path: str + semantic_model: DbtBouncerSemanticModelBase + unique_id: str + + +DbtBouncerSourceBase = Union[ + SourceDefinition_v10, SourceDefinition_v11, Sources, SourcesLatest +] + + +class DbtBouncerSource(BaseModel): + """Model for all source nodes in `manifest.json`.""" + + original_file_path: str + source: DbtBouncerSourceBase + unique_id: str + + +DbtBouncerTestBase = Union[ + GenericTestNode_v10, + SingularTestNode_v10, + GenericTestNode_v11, + SingularTestNode_v11, + Nodes1, + Nodes2, + Nodes6, + Nodes2Latest, + Nodes6Latest, +] + + +class DbtBouncerTest(BaseModel): + """Model for all test nodes in `manifest.json`.""" + + original_file_path: str + test: DbtBouncerTestBase + unique_id: str + + +def parse_manifest( + manifest: Dict[str, Any], +) -> DbtBouncerManifest: + """Parse manifest.json. + + Args: + manifest: A dict of manifest.json + + Raises: + ValueError: If the manifest.json is not a valid manifest.json + + Returns: + DbtBouncerManifest + + """ + dbt_schema_version = manifest["metadata"]["dbt_schema_version"] + if dbt_schema_version == "https://schemas.getdbt.com/dbt/manifest/v10.json": + return ManifestV10(**manifest) + elif dbt_schema_version == "https://schemas.getdbt.com/dbt/manifest/v11.json": + return ManifestV11(**manifest) + elif dbt_schema_version == "https://schemas.getdbt.com/dbt/manifest/v12.json": + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + ManifestLatest, + ) + + return ManifestLatest(**manifest) + + raise ValueError("Not a manifest.json") + + +def parse_manifest_artifact( + manifest_obj: DbtBouncerManifest, +) -> tuple[ + List[Exposures], + List[Macros], + List[DbtBouncerModel], + List[DbtBouncerSemanticModel], + List[DbtBouncerSource], + List[DbtBouncerTest], + List[UnitTests], +]: + """Parse the manifest.json artifact. + + Returns: + List[DbtBouncerExposure]: List of exposures in the project. + List[DbtBouncerMacro]: List of macros in the project. + List[DbtBouncerModel]: List of models in the project. + List[DbtBouncerSemanticModel]: List of semantic models in the project. + List[DbtBouncerSource]: List of sources in the project. + List[DbtBouncerTest]: List of tests in the project. + List[DbtBouncerUnitTest]: List of unit tests in the project. + + """ + project_exposures = [ + v + for _, v in manifest_obj.manifest.exposures.items() + if v.package_name == manifest_obj.manifest.metadata.project_name + ] + project_macros = [ + v + for _, v in manifest_obj.manifest.macros.items() + if v.package_name == manifest_obj.manifest.metadata.project_name + ] + project_models = [] + project_tests = [] + for k, v in manifest_obj.manifest.nodes.items(): + if ( + isinstance(v.resource_type, Enum) and v.resource_type.value == "model" + ) or v.resource_type == "model": + if ( + v.package_name == manifest_obj.manifest.metadata.project_name + ): # dbt 1.6 # dbt 1.7+ + project_models.append( + DbtBouncerModel( + **{ + "model": v, + "original_file_path": clean_path_str(v.original_file_path), + "unique_id": k, + }, + ), + ) + elif ( + (isinstance(v.resource_type, Enum) and v.resource_type.value == "test") + or v.resource_type == "test" + ) and v.package_name == manifest_obj.manifest.metadata.project_name: + project_tests.append( + DbtBouncerTest( + **{ + "original_file_path": clean_path_str(v.original_file_path), + "test": v, + "unique_id": k, + }, + ), + ) + + if semver.Version.parse(manifest_obj.manifest.metadata.dbt_version) >= "1.8.0": + project_unit_tests = [ + v + for _, v in manifest_obj.manifest.unit_tests.items() + if v.package_name == manifest_obj.manifest.metadata.project_name + ] + else: + project_unit_tests = [] + + project_semantic_models = [ + DbtBouncerSemanticModel( + **{ + "original_file_path": clean_path_str(v.original_file_path), + "semantic_model": v, + "unique_id": k, + }, + ) + for _, v in manifest_obj.manifest.semantic_models.items() + if v.package_name == manifest_obj.manifest.metadata.project_name + ] + + project_sources = [ + DbtBouncerSource( + **{ + "original_file_path": clean_path_str(v.original_file_path), + "source": v, + "unique_id": k, + }, + ) + for _, v in manifest_obj.manifest.sources.items() + if v.package_name == manifest_obj.manifest.metadata.project_name + ] + + logging.info( + f"Parsed `manifest.json`, found `{manifest_obj.manifest.metadata.project_name}` project: {len(project_exposures)} exposures, {len(project_macros)} macros, {len(project_models)} nodes, {len(project_semantic_models)} semantic models, {len(project_sources)} sources, {len(project_tests)} tests, {len(project_unit_tests)} unit tests.", + ) + return ( + project_exposures, + project_macros, + project_models, + project_semantic_models, + project_sources, + project_tests, + project_unit_tests, + ) diff --git a/src/dbt_bouncer/artifact_parsers/parsers_run_results.py b/src/dbt_bouncer/artifact_parsers/parsers_run_results.py new file mode 100644 index 00000000..42b9f4a5 --- /dev/null +++ b/src/dbt_bouncer/artifact_parsers/parsers_run_results.py @@ -0,0 +1,113 @@ +# TODO Remove after this program no longer support Python 3.8.* +from __future__ import annotations + +import logging +import warnings +from typing import TYPE_CHECKING, Any, Dict, List, Union + +from pydantic import BaseModel + +from dbt_bouncer.utils import clean_path_str + +with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=UserWarning) + from dbt_artifacts_parser.parsers.run_results.run_results_v4 import ( + RunResultOutput as RunResultOutput_v4, + ) + from dbt_artifacts_parser.parsers.run_results.run_results_v4 import RunResultsV4 + from dbt_artifacts_parser.parsers.run_results.run_results_v5 import ( + RunResultOutput as RunResultOutput_v5, + ) + from dbt_artifacts_parser.parsers.run_results.run_results_v5 import RunResultsV5 + from dbt_artifacts_parser.parsers.run_results.run_results_v6 import ( + Result as RunResultOutput_v6, + ) + from dbt_artifacts_parser.parsers.run_results.run_results_v6 import ( + RunResultsV6, + ) + +if TYPE_CHECKING: + from pathlib import Path + + from dbt_bouncer.artifact_parsers.parsers_manifest import DbtBouncerManifest + +from dbt_bouncer.artifact_parsers.parsers_common import load_dbt_artifact + +DbtBouncerRunResultBase = Union[ + RunResultOutput_v4, RunResultOutput_v5, RunResultOutput_v6 +] + + +class DbtBouncerRunResult(BaseModel): + """Model for all results in `run_results.json`.""" + + original_file_path: str + run_result: DbtBouncerRunResultBase + unique_id: str + + +def parse_run_results( + run_results: Dict[str, Any], +) -> Union[RunResultsV4, RunResultsV5, RunResultsV6]: + """Parse run-results.json. + + Args: + run_results: A dict of run-results.json + + Raises: + ValueError: If the schema version is not supported. + + Returns: + Union[RunResultsV4, RunResultsV5, RunResultsV6]: + + """ + dbt_schema_version = run_results["metadata"]["dbt_schema_version"] + if dbt_schema_version == "https://schemas.getdbt.com/dbt/run-results/v4.json": + return RunResultsV4(**run_results) + elif dbt_schema_version == "https://schemas.getdbt.com/dbt/run-results/v5.json": + return RunResultsV5(**run_results) + elif dbt_schema_version == "https://schemas.getdbt.com/dbt/run-results/v6.json": + return RunResultsV6(**run_results) + raise ValueError("Not a manifest.json") + + +def parse_run_results_artifact( + artifact_dir: "Path", + manifest_obj: "DbtBouncerManifest", +) -> List[DbtBouncerRunResult]: + """Parse the run_results.json artifact. + + Returns: + List[DbtBouncerRunResult]: A list of DbtBouncerRunResult objects. + + """ + run_results_obj: Union[RunResultsV4, RunResultsV5, RunResultsV6] = ( + load_dbt_artifact( + artifact_name="run_results.json", + dbt_artifacts_dir=artifact_dir, + ) + ) + + project_run_results = [ + DbtBouncerRunResult( + **{ + "original_file_path": ( + clean_path_str( + manifest_obj.manifest.nodes[r.unique_id].original_file_path + ) + if r.unique_id in manifest_obj.manifest.nodes + else clean_path_str( + manifest_obj.manifest.unit_tests[r.unique_id].original_file_path + ) + ), + "run_result": r, + "unique_id": r.unique_id, + }, + ) + for r in run_results_obj.results + if r.unique_id.split(".")[1] == manifest_obj.manifest.metadata.project_name + ] + logging.info( + f"Parsed `run_results.json`: {len(project_run_results)} results.", + ) + return project_run_results diff --git a/src/dbt_bouncer/checks/catalog/check_catalog_sources.py b/src/dbt_bouncer/checks/catalog/check_catalog_sources.py index 331c699c..6ceb54da 100644 --- a/src/dbt_bouncer/checks/catalog/check_catalog_sources.py +++ b/src/dbt_bouncer/checks/catalog/check_catalog_sources.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: import warnings - from dbt_bouncer.parsers import ( + from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerSourceBase, ) diff --git a/src/dbt_bouncer/checks/catalog/check_columns.py b/src/dbt_bouncer/checks/catalog/check_columns.py index b804ebb7..a894bc09 100644 --- a/src/dbt_bouncer/checks/catalog/check_columns.py +++ b/src/dbt_bouncer/checks/catalog/check_columns.py @@ -9,7 +9,10 @@ with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.catalog.catalog_v1 import CatalogTable - from dbt_bouncer.parsers import DbtBouncerModelBase, DbtBouncerTestBase + from dbt_bouncer.artifact_parsers.parsers_common import ( + DbtBouncerModelBase, + DbtBouncerTestBase, + ) from pydantic import Field diff --git a/src/dbt_bouncer/checks/manifest/check_exposures.py b/src/dbt_bouncer/checks/manifest/check_exposures.py index f20abee7..db8da176 100644 --- a/src/dbt_bouncer/checks/manifest/check_exposures.py +++ b/src/dbt_bouncer/checks/manifest/check_exposures.py @@ -12,7 +12,10 @@ with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) - from dbt_bouncer.parsers import DbtBouncerExposureBase, DbtBouncerModelBase + from dbt_bouncer.artifact_parsers.parsers_common import ( + DbtBouncerExposureBase, + DbtBouncerModelBase, + ) class CheckExposureOnNonPublicModels(BaseCheck): diff --git a/src/dbt_bouncer/checks/manifest/check_lineage.py b/src/dbt_bouncer/checks/manifest/check_lineage.py index cdb68445..c760a6b7 100644 --- a/src/dbt_bouncer/checks/manifest/check_lineage.py +++ b/src/dbt_bouncer/checks/manifest/check_lineage.py @@ -6,7 +6,7 @@ from dbt_bouncer.check_base import BaseCheck if TYPE_CHECKING: - from dbt_bouncer.parsers import ( + from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerManifest, DbtBouncerModelBase, ) diff --git a/src/dbt_bouncer/checks/manifest/check_metadata.py b/src/dbt_bouncer/checks/manifest/check_metadata.py index 4b5e3c18..e3cbad13 100644 --- a/src/dbt_bouncer/checks/manifest/check_metadata.py +++ b/src/dbt_bouncer/checks/manifest/check_metadata.py @@ -6,7 +6,7 @@ from pydantic import BaseModel, ConfigDict, Field if TYPE_CHECKING: - from dbt_bouncer.parsers import DbtBouncerManifest + from dbt_bouncer.artifact_parsers.parsers_common import DbtBouncerManifest class CheckProjectName(BaseModel): diff --git a/src/dbt_bouncer/checks/manifest/check_models.py b/src/dbt_bouncer/checks/manifest/check_models.py index 0b9a6cf7..5074ee4f 100644 --- a/src/dbt_bouncer/checks/manifest/check_models.py +++ b/src/dbt_bouncer/checks/manifest/check_models.py @@ -20,13 +20,13 @@ from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( UnitTests, ) - from dbt_bouncer.parsers import ( + from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerManifest, DbtBouncerModelBase, DbtBouncerTestBase, ) if TYPE_CHECKING: - from dbt_bouncer.parsers import ( + from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerManifest, DbtBouncerModelBase, ) diff --git a/src/dbt_bouncer/checks/manifest/check_semantic_models.py b/src/dbt_bouncer/checks/manifest/check_semantic_models.py index 4640684b..33776f72 100644 --- a/src/dbt_bouncer/checks/manifest/check_semantic_models.py +++ b/src/dbt_bouncer/checks/manifest/check_semantic_models.py @@ -8,7 +8,7 @@ if TYPE_CHECKING: import warnings - from dbt_bouncer.parsers import ( + from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerModelBase, DbtBouncerSemanticModelBase, ) diff --git a/src/dbt_bouncer/checks/manifest/check_sources.py b/src/dbt_bouncer/checks/manifest/check_sources.py index 9d5ee530..d6b14134 100644 --- a/src/dbt_bouncer/checks/manifest/check_sources.py +++ b/src/dbt_bouncer/checks/manifest/check_sources.py @@ -4,11 +4,11 @@ from typing import TYPE_CHECKING, List, Literal if TYPE_CHECKING: - from dbt_bouncer.checks.common import NestedDict - from dbt_bouncer.parsers import ( + from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerModelBase, DbtBouncerSourceBase, ) + from dbt_bouncer.checks.common import NestedDict from pydantic import Field diff --git a/src/dbt_bouncer/checks/manifest/check_unit_tests.py b/src/dbt_bouncer/checks/manifest/check_unit_tests.py index 27e133ee..4d76bb50 100644 --- a/src/dbt_bouncer/checks/manifest/check_unit_tests.py +++ b/src/dbt_bouncer/checks/manifest/check_unit_tests.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: import warnings - from dbt_bouncer.parsers import DbtBouncerManifest + from dbt_bouncer.artifact_parsers.parsers_common import DbtBouncerManifest with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) @@ -22,7 +22,7 @@ from dbt_bouncer.utils import object_in_path if TYPE_CHECKING: - from dbt_bouncer.parsers import ( + from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerManifest, DbtBouncerModelBase, ) diff --git a/src/dbt_bouncer/checks/run_results/check_run_results.py b/src/dbt_bouncer/checks/run_results/check_run_results.py index 63fafd3c..e5c755a1 100644 --- a/src/dbt_bouncer/checks/run_results/check_run_results.py +++ b/src/dbt_bouncer/checks/run_results/check_run_results.py @@ -7,7 +7,7 @@ from dbt_bouncer.check_base import BaseCheck if TYPE_CHECKING: - from dbt_bouncer.parsers import DbtBouncerRunResultBase + from dbt_bouncer.artifact_parsers.parsers_common import DbtBouncerRunResultBase import warnings diff --git a/src/dbt_bouncer/config_file_validator.py b/src/dbt_bouncer/config_file_validator.py index 0bf0a987..252a69d8 100644 --- a/src/dbt_bouncer/config_file_validator.py +++ b/src/dbt_bouncer/config_file_validator.py @@ -122,16 +122,14 @@ def validate_conf(config_file_contents: Dict[str, Any]) -> "DbtBouncerConf": ) import dbt_bouncer.checks # noqa: F401 - from dbt_bouncer.checks.common import NestedDict # noqa: F401 - from dbt_bouncer.config_file_parser import DbtBouncerConf - from dbt_bouncer.parsers import ( # noqa: F401 + from dbt_bouncer.artifact_parsers.parsers_catalog import ( # noqa: F401 DbtBouncerCatalogNode, + ) + from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerManifest, DbtBouncerModel, DbtBouncerModelBase, - DbtBouncerRunResult, - DbtBouncerRunResultBase, DbtBouncerSemanticModel, DbtBouncerSemanticModelBase, DbtBouncerSource, @@ -139,6 +137,12 @@ def validate_conf(config_file_contents: Dict[str, Any]) -> "DbtBouncerConf": DbtBouncerTest, DbtBouncerTestBase, ) + from dbt_bouncer.artifact_parsers.parsers_run_results import ( # noqa: F401 + DbtBouncerRunResult, + DbtBouncerRunResultBase, + ) + from dbt_bouncer.checks.common import NestedDict # noqa: F401 + from dbt_bouncer.config_file_parser import DbtBouncerConf DbtBouncerConf.model_rebuild() diff --git a/src/dbt_bouncer/main.py b/src/dbt_bouncer/main.py index a0386dc2..8d81725b 100644 --- a/src/dbt_bouncer/main.py +++ b/src/dbt_bouncer/main.py @@ -113,7 +113,7 @@ def cli( dbt_artifacts_dir = config_file.parent / bouncer_config.dbt_artifacts_dir - from dbt_bouncer.parsers import parse_dbt_artifacts + from dbt_bouncer.artifact_parsers.parsers_common import parse_dbt_artifacts ( manifest_obj, diff --git a/src/dbt_bouncer/parsers.py b/src/dbt_bouncer/parsers.py deleted file mode 100644 index fa2418b1..00000000 --- a/src/dbt_bouncer/parsers.py +++ /dev/null @@ -1,581 +0,0 @@ -# TODO Remove after this program no longer support Python 3.8.* -from __future__ import annotations - -import json -import logging -import warnings -from enum import Enum -from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, List, Literal, Union - -import semver -from pydantic import BaseModel - -from dbt_bouncer.dbt_cloud.manifest_latest import ManifestLatest -from dbt_bouncer.dbt_cloud.manifest_latest import Nodes2 as Nodes2Latest -from dbt_bouncer.dbt_cloud.manifest_latest import Nodes4 as Nodes4Latest -from dbt_bouncer.dbt_cloud.manifest_latest import Nodes6 as Nodes6Latest -from dbt_bouncer.dbt_cloud.manifest_latest import SemanticModels as SemanticModelsLatest -from dbt_bouncer.dbt_cloud.manifest_latest import Sources as SourcesLatest -from dbt_bouncer.utils import clean_path_str - -if TYPE_CHECKING: - from dbt_bouncer.config_file_validator import DbtBouncerConf - -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.catalog.catalog_v1 import CatalogTable, CatalogV1 - from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( - Exposure as Exposure_v10, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( - GenericTestNode as GenericTestNode_v10, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ManifestV10 - from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( - ModelNode as ModelNode_v10, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( - SemanticModel as SemanticModel_v10, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( - SingularTestNode as SingularTestNode_v10, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v10 import ( - SourceDefinition as SourceDefinition_v10, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( - Exposure as Exposure_v11, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( - GenericTestNode as GenericTestNode_v11, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ManifestV11 - from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( - ModelNode as ModelNode_v11, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( - SemanticModel as SemanticModel_v11, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( - SingularTestNode as SingularTestNode_v11, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( - SourceDefinition as SourceDefinition_v11, - ) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( - Exposures, - Macros, - ManifestV12, - Nodes1, - Nodes2, - Nodes4, - Nodes6, - SemanticModels, - Sources, - UnitTests, - ) - from dbt_artifacts_parser.parsers.run_results.run_results_v4 import ( - RunResultOutput as RunResultOutput_v4, - ) - from dbt_artifacts_parser.parsers.run_results.run_results_v4 import RunResultsV4 - from dbt_artifacts_parser.parsers.run_results.run_results_v5 import ( - RunResultOutput as RunResultOutput_v5, - ) - from dbt_artifacts_parser.parsers.run_results.run_results_v5 import RunResultsV5 - from dbt_artifacts_parser.parsers.run_results.run_results_v6 import ( - Result, - RunResultsV6, - ) - - -class DbtBouncerCatalogNode(BaseModel): - """Model for all nodes in `catalog.json`.""" - - catalog_node: CatalogTable - original_file_path: str - unique_id: str - - -class DbtBouncerManifest(BaseModel): - """Model for all manifest objects.""" - - manifest: Union[ManifestV10, ManifestV11, ManifestV12, ManifestLatest] - - -DbtBouncerExposureBase = Union[Exposure_v10, Exposure_v11, Exposures] - - -class DbtBouncerExposure(BaseModel): - """Model for all exposure nodes in `manifest.json`.""" - - model: DbtBouncerExposureBase - original_file_path: str - unique_id: str - - -DbtBouncerModelBase = Union[ModelNode_v10, ModelNode_v11, Nodes4, Nodes4Latest] - - -class DbtBouncerModel(BaseModel): - """Model for all model nodes in `manifest.json`.""" - - model: DbtBouncerModelBase - original_file_path: str - unique_id: str - - -DbtBouncerRunResultBase = Union[RunResultOutput_v4, RunResultOutput_v5, Result] - - -class DbtBouncerRunResult(BaseModel): - """Model for all results in `run_results.json`.""" - - original_file_path: str - run_result: DbtBouncerRunResultBase - unique_id: str - - -DbtBouncerSemanticModelBase = Union[ - SemanticModel_v10, SemanticModel_v11, SemanticModels, SemanticModelsLatest -] - - -class DbtBouncerSemanticModel(BaseModel): - """Model for all semantic model nodes in `manifest.json`.""" - - original_file_path: str - semantic_model: DbtBouncerSemanticModelBase - unique_id: str - - -DbtBouncerSourceBase = Union[ - SourceDefinition_v10, SourceDefinition_v11, Sources, SourcesLatest -] - - -class DbtBouncerSource(BaseModel): - """Model for all source nodes in `manifest.json`.""" - - original_file_path: str - source: DbtBouncerSourceBase - unique_id: str - - -DbtBouncerTestBase = Union[ - GenericTestNode_v10, - SingularTestNode_v10, - GenericTestNode_v11, - SingularTestNode_v11, - Nodes1, - Nodes2, - Nodes6, - Nodes2Latest, - Nodes6Latest, -] - - -class DbtBouncerTest(BaseModel): - """Model for all test nodes in `manifest.json`.""" - - original_file_path: str - test: DbtBouncerTestBase - unique_id: str - - -def load_dbt_artifact( - artifact_name: Literal["catalog.json", "manifest.json", "run_results.json"], - dbt_artifacts_dir: Path, -) -> Union[CatalogV1, DbtBouncerManifest, RunResultsV4, RunResultsV5, RunResultsV6]: - """Load a dbt artifact from a JSON file to a Pydantic object. - - Returns: - Union[CatalogV1, DbtBouncerManifest, RunResultsV4, RunResultsV5, RunResultsV6]: - The dbt artifact loaded as a Pydantic object. - - Raises: - FileNotFoundError: - If the artifact file does not exist. - - """ - logging.debug(f"{artifact_name=}") - logging.debug(f"{dbt_artifacts_dir=}") - - artifact_path = dbt_artifacts_dir / Path(artifact_name) - logging.debug(f"Loading {artifact_name} from {artifact_path}...") - if not Path(artifact_path).exists(): - raise FileNotFoundError( - f"No {artifact_name} found at {artifact_path}.", - ) - - if artifact_name == "catalog.json": - with Path.open(Path(artifact_path), "r") as fp: - catalog_obj = CatalogV1(**json.load(fp)) - - return catalog_obj - - elif artifact_name == "manifest.json": - # First assess dbt version is sufficient - with Path.open(Path(artifact_path), "r") as fp: - manifest_json = json.load(fp) - - assert ( - semver.Version.parse(manifest_json["metadata"]["dbt_version"]) >= "1.6.0" - ), f"The supplied `manifest.json` was generated with dbt version {manifest_json['metadata']['dbt_version']}, this is below the minimum supported version of 1.6.0." - - manifest_obj = parse_manifest(manifest_json) - - return DbtBouncerManifest(**{"manifest": manifest_obj}) - - elif artifact_name == "run_results.json": - with Path.open(Path(artifact_path), "r") as fp: - run_results_obj = parse_run_results(run_results=json.load(fp)) - - return run_results_obj - - -def parse_catalog_artifact( - artifact_dir: Path, - manifest_obj: DbtBouncerManifest, -) -> tuple[List[DbtBouncerCatalogNode], List[DbtBouncerCatalogNode]]: - """Parse the catalog.json artifact. - - Returns: - List[DbtBouncerCatalogNode]: List of catalog nodes for the project. - List[DbtBouncerCatalogNode]: List of catalog nodes for the project sources. - - """ - catalog_obj: CatalogV1 = load_dbt_artifact( - artifact_name="catalog.json", - dbt_artifacts_dir=artifact_dir, - ) - project_catalog_nodes = [ - DbtBouncerCatalogNode( - **{ - "catalog_node": v, - "original_file_path": clean_path_str( - manifest_obj.manifest.nodes[k].original_file_path - ), - "unique_id": k, - }, - ) - for k, v in catalog_obj.nodes.items() - if k.split(".")[-2] == manifest_obj.manifest.metadata.project_name - ] - project_catalog_sources = [ - DbtBouncerCatalogNode( - **{ - "catalog_node": v, - "original_file_path": clean_path_str( - manifest_obj.manifest.sources[k].original_file_path - ), - "unique_id": k, - }, - ) - for k, v in catalog_obj.sources.items() - if k.split(".")[1] == manifest_obj.manifest.metadata.project_name - ] - logging.info( - f"Parsed `catalog.json`: {len(project_catalog_nodes)} nodes, {len(project_catalog_sources)} sources.", - ) - - return project_catalog_nodes, project_catalog_sources - - -def parse_dbt_artifacts( - bouncer_config: DbtBouncerConf, dbt_artifacts_dir: Path -) -> tuple[ - DbtBouncerManifest, - List[Exposures], - List[Macros], - List[DbtBouncerModel], - List[DbtBouncerSemanticModel], - List[DbtBouncerSource], - List[DbtBouncerTest], - List[UnitTests], - List[DbtBouncerCatalogNode], - List[DbtBouncerCatalogNode], - List[DbtBouncerRunResult], -]: - """Parse all required dbt artifacts. - - Args: - bouncer_config (DbtBouncerConf): All checks to be run. - dbt_artifacts_dir (Path): Path to directory where artifacts are located. - - Returns: - DbtBouncerManifest: The manifest object. - List[DbtBouncerExposure]: List of exposures in the project. - List[DbtBouncerMacro]: List of macros in the project. - List[DbtBouncerModel]: List of models in the project. - List[DbtBouncerSemanticModel]: List of semantic models in the project. - List[DbtBouncerSource]: List of sources in the project. - List[DbtBouncerTest]: List of tests in the project. - List[DbtBouncerUnitTest]: List of unit tests in the project. - List[DbtBouncerCatalogNode]: List of catalog nodes for the project. - List[DbtBouncerCatalogNode]: List of catalog nodes for the project sources. - List[DbtBouncerRunResult]: A list of DbtBouncerRunResult objects. - - """ - from dbt_bouncer.parsers import load_dbt_artifact, parse_manifest_artifact - - # Manifest, will always be parsed - manifest_obj = load_dbt_artifact( - artifact_name="manifest.json", - dbt_artifacts_dir=dbt_artifacts_dir, - ) - ( - project_exposures, - project_macros, - project_models, - project_semantic_models, - project_sources, - project_tests, - project_unit_tests, - ) = parse_manifest_artifact( - manifest_obj=manifest_obj, - ) - - # Catalog, must come after manifest is parsed - from dbt_bouncer.parsers import parse_catalog_artifact - - if bouncer_config.catalog_checks != []: - project_catalog_nodes, project_catalog_sources = parse_catalog_artifact( - artifact_dir=dbt_artifacts_dir, - manifest_obj=manifest_obj, - ) - else: - project_catalog_nodes = [] - project_catalog_sources = [] - - # Run results, must come after manifest is parsed - from dbt_bouncer.parsers import parse_run_results_artifact - - if bouncer_config.run_results_checks != []: - project_run_results = parse_run_results_artifact( - artifact_dir=dbt_artifacts_dir, - manifest_obj=manifest_obj, - ) - else: - project_run_results = [] - - return ( - manifest_obj, - project_exposures, - project_macros, - project_models, - project_semantic_models, - project_sources, - project_tests, - project_unit_tests, - project_catalog_nodes, - project_catalog_sources, - project_run_results, - ) - - -def parse_manifest( - manifest: Dict[str, Any], -) -> DbtBouncerManifest: - """Parse manifest.json. - - Args: - manifest: A dict of manifest.json - - Raises: - ValueError: If the manifest.json is not a valid manifest.json - - Returns: - DbtBouncerManifest - - """ - dbt_schema_version = manifest["metadata"]["dbt_schema_version"] - if dbt_schema_version == "https://schemas.getdbt.com/dbt/manifest/v10.json": - return ManifestV10(**manifest) - elif dbt_schema_version == "https://schemas.getdbt.com/dbt/manifest/v11.json": - return ManifestV11(**manifest) - elif dbt_schema_version == "https://schemas.getdbt.com/dbt/manifest/v12.json": - from dbt_bouncer.dbt_cloud.manifest_latest import ManifestLatest - - return ManifestLatest(**manifest) - - raise ValueError("Not a manifest.json") - - -def parse_manifest_artifact( - manifest_obj: DbtBouncerManifest, -) -> tuple[ - List[Exposures], - List[Macros], - List[DbtBouncerModel], - List[DbtBouncerSemanticModel], - List[DbtBouncerSource], - List[DbtBouncerTest], - List[UnitTests], -]: - """Parse the manifest.json artifact. - - Returns: - List[DbtBouncerExposure]: List of exposures in the project. - List[DbtBouncerMacro]: List of macros in the project. - List[DbtBouncerModel]: List of models in the project. - List[DbtBouncerSemanticModel]: List of semantic models in the project. - List[DbtBouncerSource]: List of sources in the project. - List[DbtBouncerTest]: List of tests in the project. - List[DbtBouncerUnitTest]: List of unit tests in the project. - - """ - project_exposures = [ - v - for _, v in manifest_obj.manifest.exposures.items() - if v.package_name == manifest_obj.manifest.metadata.project_name - ] - project_macros = [ - v - for _, v in manifest_obj.manifest.macros.items() - if v.package_name == manifest_obj.manifest.metadata.project_name - ] - project_models = [] - project_tests = [] - for k, v in manifest_obj.manifest.nodes.items(): - if ( - isinstance(v.resource_type, Enum) and v.resource_type.value == "model" - ) or v.resource_type == "model": - if ( - v.package_name == manifest_obj.manifest.metadata.project_name - ): # dbt 1.6 # dbt 1.7+ - project_models.append( - DbtBouncerModel( - **{ - "model": v, - "original_file_path": clean_path_str(v.original_file_path), - "unique_id": k, - }, - ), - ) - elif ( - (isinstance(v.resource_type, Enum) and v.resource_type.value == "test") - or v.resource_type == "test" - ) and v.package_name == manifest_obj.manifest.metadata.project_name: - project_tests.append( - DbtBouncerTest( - **{ - "original_file_path": clean_path_str(v.original_file_path), - "test": v, - "unique_id": k, - }, - ), - ) - - if semver.Version.parse(manifest_obj.manifest.metadata.dbt_version) >= "1.8.0": - project_unit_tests = [ - v - for _, v in manifest_obj.manifest.unit_tests.items() - if v.package_name == manifest_obj.manifest.metadata.project_name - ] - else: - project_unit_tests = [] - - project_semantic_models = [ - DbtBouncerSemanticModel( - **{ - "original_file_path": clean_path_str(v.original_file_path), - "semantic_model": v, - "unique_id": k, - }, - ) - for _, v in manifest_obj.manifest.semantic_models.items() - if v.package_name == manifest_obj.manifest.metadata.project_name - ] - - project_sources = [ - DbtBouncerSource( - **{ - "original_file_path": clean_path_str(v.original_file_path), - "source": v, - "unique_id": k, - }, - ) - for _, v in manifest_obj.manifest.sources.items() - if v.package_name == manifest_obj.manifest.metadata.project_name - ] - - logging.info( - f"Parsed `manifest.json`, found `{manifest_obj.manifest.metadata.project_name}` project: {len(project_exposures)} exposures, {len(project_macros)} macros, {len(project_models)} nodes, {len(project_semantic_models)} semantic models, {len(project_sources)} sources, {len(project_tests)} tests, {len(project_unit_tests)} unit tests.", - ) - return ( - project_exposures, - project_macros, - project_models, - project_semantic_models, - project_sources, - project_tests, - project_unit_tests, - ) - - -def parse_run_results( - run_results: Dict[str, Any], -) -> Union[RunResultsV4, RunResultsV5, RunResultsV6]: - """Parse run-results.json. - - Args: - run_results: A dict of run-results.json - - Raises: - ValueError: If the schema version is not supported. - - Returns: - Union[RunResultsV4, RunResultsV5, RunResultsV6]: - - """ - dbt_schema_version = run_results["metadata"]["dbt_schema_version"] - if dbt_schema_version == "https://schemas.getdbt.com/dbt/run-results/v4.json": - return RunResultsV4(**run_results) - elif dbt_schema_version == "https://schemas.getdbt.com/dbt/run-results/v5.json": - return RunResultsV5(**run_results) - elif dbt_schema_version == "https://schemas.getdbt.com/dbt/run-results/v6.json": - return RunResultsV6(**run_results) - raise ValueError("Not a manifest.json") - - -def parse_run_results_artifact( - artifact_dir: Path, - manifest_obj: DbtBouncerManifest, -) -> List[DbtBouncerRunResult]: - """Parse the run_results.json artifact. - - Returns: - List[DbtBouncerRunResult]: A list of DbtBouncerRunResult objects. - - """ - run_results_obj: Union[RunResultsV4, RunResultsV5, RunResultsV6] = ( - load_dbt_artifact( - artifact_name="run_results.json", - dbt_artifacts_dir=artifact_dir, - ) - ) - - project_run_results = [ - DbtBouncerRunResult( - **{ - "original_file_path": ( - clean_path_str( - manifest_obj.manifest.nodes[r.unique_id].original_file_path - ) - if r.unique_id in manifest_obj.manifest.nodes - else clean_path_str( - manifest_obj.manifest.unit_tests[r.unique_id].original_file_path - ) - ), - "run_result": r, - "unique_id": r.unique_id, - }, - ) - for r in run_results_obj.results - if r.unique_id.split(".")[1] == manifest_obj.manifest.metadata.project_name - ] - logging.info( - f"Parsed `run_results.json`: {len(project_run_results)} results.", - ) - return project_run_results diff --git a/src/dbt_bouncer/runner.py b/src/dbt_bouncer/runner.py index e9c27d89..cf4b9cb7 100644 --- a/src/dbt_bouncer/runner.py +++ b/src/dbt_bouncer/runner.py @@ -31,8 +31,7 @@ UnitTests, ) - from dbt_bouncer.config_file_validator import DbtBouncerConf - from dbt_bouncer.parsers import ( + from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerCatalogNode, DbtBouncerManifest, DbtBouncerModel, @@ -41,6 +40,7 @@ DbtBouncerSource, DbtBouncerTest, ) + from dbt_bouncer.config_file_validator import DbtBouncerConf def runner( diff --git a/tests/conftest.py b/tests/conftest.py index 5416f5c4..0d998300 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,7 +12,10 @@ def pytest_configure(config): @pytest.fixture(scope="session") def manifest_obj(): - from dbt_bouncer.parsers import DbtBouncerManifest, parse_manifest + from dbt_bouncer.artifact_parsers.parsers_manifest import ( + DbtBouncerManifest, + parse_manifest, + ) manifest_json_path = Path("dbt_project") / "target/manifest.json" with Path.open(manifest_json_path, "r") as fp: diff --git a/tests/unit/checks/catalog/test_catalog_sources.py b/tests/unit/checks/catalog/test_catalog_sources.py index 6899bcf3..cabed864 100644 --- a/tests/unit/checks/catalog/test_catalog_sources.py +++ b/tests/unit/checks/catalog/test_catalog_sources.py @@ -8,10 +8,12 @@ from dbt_artifacts_parser.parsers.catalog.catalog_v1 import CatalogTable from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Sources +from dbt_bouncer.artifact_parsers.parsers_manifest import ( + DbtBouncerSourceBase, # noqa: F401 +) from dbt_bouncer.checks.catalog.check_catalog_sources import ( CheckSourceColumnsAreAllDocumented, ) -from dbt_bouncer.parsers import DbtBouncerSourceBase # noqa: F401 CheckSourceColumnsAreAllDocumented.model_rebuild() diff --git a/tests/unit/checks/catalog/test_columns.py b/tests/unit/checks/catalog/test_columns.py index db71df47..46cfde21 100644 --- a/tests/unit/checks/catalog/test_columns.py +++ b/tests/unit/checks/catalog/test_columns.py @@ -7,6 +7,11 @@ warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.catalog.catalog_v1 import CatalogTable from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Nodes4, Nodes6 +from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 + DbtBouncerModelBase, + DbtBouncerTest, + DbtBouncerTestBase, +) from dbt_bouncer.checks.catalog.check_columns import ( CheckColumnDescriptionPopulated, CheckColumnHasSpecifiedTest, @@ -14,11 +19,6 @@ CheckColumnsAreAllDocumented, CheckColumnsAreDocumentedInPublicModels, ) -from dbt_bouncer.parsers import ( # noqa: F401 - DbtBouncerModelBase, - DbtBouncerTest, - DbtBouncerTestBase, -) CheckColumnDescriptionPopulated.model_rebuild() CheckColumnNameCompliesToColumnType.model_rebuild() diff --git a/tests/unit/checks/manifest/test_exposures.py b/tests/unit/checks/manifest/test_exposures.py index 3007f7a6..9f94adfa 100644 --- a/tests/unit/checks/manifest/test_exposures.py +++ b/tests/unit/checks/manifest/test_exposures.py @@ -7,15 +7,15 @@ warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Exposures, Nodes4 -from dbt_bouncer.checks.manifest.check_exposures import ( - CheckExposureOnNonPublicModels, - CheckExposureOnView, -) -from dbt_bouncer.parsers import ( # noqa: F401 +from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerModel, DbtBouncerModelBase, ) +from dbt_bouncer.checks.manifest.check_exposures import ( + CheckExposureOnNonPublicModels, + CheckExposureOnView, +) CheckExposureOnNonPublicModels.model_rebuild() CheckExposureOnView.model_rebuild() diff --git a/tests/unit/checks/manifest/test_lineage.py b/tests/unit/checks/manifest/test_lineage.py index 92062efe..f946dc69 100644 --- a/tests/unit/checks/manifest/test_lineage.py +++ b/tests/unit/checks/manifest/test_lineage.py @@ -7,12 +7,15 @@ warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Nodes4 +from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 + DbtBouncerManifest, + DbtBouncerModelBase, +) from dbt_bouncer.checks.manifest.check_lineage import ( CheckLineagePermittedUpstreamModels, CheckLineageSeedCannotBeUsed, CheckLineageSourceCannotBeUsed, ) -from dbt_bouncer.parsers import DbtBouncerManifest, DbtBouncerModelBase # noqa: F401 CheckLineagePermittedUpstreamModels.model_rebuild() CheckLineageSeedCannotBeUsed.model_rebuild() diff --git a/tests/unit/checks/manifest/test_metadata.py b/tests/unit/checks/manifest/test_metadata.py index 93160cc3..4a74f2b6 100644 --- a/tests/unit/checks/manifest/test_metadata.py +++ b/tests/unit/checks/manifest/test_metadata.py @@ -2,8 +2,10 @@ import pytest +from dbt_bouncer.artifact_parsers.parsers_manifest import ( + DbtBouncerManifest, # noqa: F401 +) from dbt_bouncer.checks.manifest.check_metadata import CheckProjectName -from dbt_bouncer.parsers import DbtBouncerManifest # noqa: F401 CheckProjectName.model_rebuild() diff --git a/tests/unit/checks/manifest/test_models.py b/tests/unit/checks/manifest/test_models.py index 90ccde3f..0535eaca 100644 --- a/tests/unit/checks/manifest/test_models.py +++ b/tests/unit/checks/manifest/test_models.py @@ -11,7 +11,7 @@ UnitTests, ) - from dbt_bouncer.parsers import ( # noqa: F401 + from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerManifest, DbtBouncerModel, DbtBouncerModelBase, diff --git a/tests/unit/checks/manifest/test_semantic_models.py b/tests/unit/checks/manifest/test_semantic_models.py index 6b77ce31..120692e5 100644 --- a/tests/unit/checks/manifest/test_semantic_models.py +++ b/tests/unit/checks/manifest/test_semantic_models.py @@ -10,13 +10,13 @@ SemanticModels, ) -from dbt_bouncer.checks.manifest.check_semantic_models import ( - CheckSemanticModelOnNonPublicModels, -) -from dbt_bouncer.parsers import ( # noqa: F401 +from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerModelBase, DbtBouncerSemanticModelBase, ) +from dbt_bouncer.checks.manifest.check_semantic_models import ( + CheckSemanticModelOnNonPublicModels, +) CheckSemanticModelOnNonPublicModels.model_rebuild() diff --git a/tests/unit/checks/manifest/test_sources.py b/tests/unit/checks/manifest/test_sources.py index 929bb45e..18e4273e 100644 --- a/tests/unit/checks/manifest/test_sources.py +++ b/tests/unit/checks/manifest/test_sources.py @@ -7,6 +7,12 @@ warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Nodes4, Sources +from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 + DbtBouncerModel, + DbtBouncerModelBase, + DbtBouncerSource, + DbtBouncerSourceBase, +) from dbt_bouncer.checks.common import NestedDict # noqa: F401 from dbt_bouncer.checks.manifest.check_sources import ( CheckSourceDescriptionPopulated, @@ -20,12 +26,6 @@ CheckSourceUsedByModelsInSameDirectory, CheckSourceUsedByOnlyOneModel, ) -from dbt_bouncer.parsers import ( # noqa: F401 - DbtBouncerModel, - DbtBouncerModelBase, - DbtBouncerSource, - DbtBouncerSourceBase, -) CheckSourceDescriptionPopulated.model_rebuild() CheckSourceFreshnessPopulated.model_rebuild() diff --git a/tests/unit/checks/manifest/test_unit_tests.py b/tests/unit/checks/manifest/test_unit_tests.py index 7a93123b..77d00e31 100644 --- a/tests/unit/checks/manifest/test_unit_tests.py +++ b/tests/unit/checks/manifest/test_unit_tests.py @@ -7,16 +7,20 @@ warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.manifest.manifest_v12 import UnitTests - from dbt_bouncer.parsers import DbtBouncerModelBase # noqa: F401 + from dbt_bouncer.artifact_parsers.parsers_manifest import ( + DbtBouncerModelBase, # noqa: F401 + ) import warnings +from dbt_bouncer.artifact_parsers.parsers_manifest import ( + DbtBouncerManifest, # noqa: F401 +) from dbt_bouncer.checks.manifest.check_unit_tests import ( CheckUnitTestCoverage, CheckUnitTestExpectFormats, CheckUnitTestGivenFormats, ) -from dbt_bouncer.parsers import DbtBouncerManifest # noqa: F401 with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) diff --git a/tests/unit/checks/run_results/test_run_results.py b/tests/unit/checks/run_results/test_run_results.py index ea33e577..f0fdef6c 100644 --- a/tests/unit/checks/run_results/test_run_results.py +++ b/tests/unit/checks/run_results/test_run_results.py @@ -8,11 +8,11 @@ from pydantic import TypeAdapter +from dbt_bouncer.artifact_parsers.parsers_run_results import DbtBouncerRunResultBase from dbt_bouncer.checks.run_results.check_run_results import ( CheckRunResultsMaxExecutionTime, CheckRunResultsMaxGigabytesBilled, ) -from dbt_bouncer.parsers import DbtBouncerRunResultBase CheckRunResultsMaxGigabytesBilled.model_rebuild() CheckRunResultsMaxExecutionTime.model_rebuild() diff --git a/tests/unit/test_runner.py b/tests/unit/test_runner.py index e36c17ad..92fb3957 100644 --- a/tests/unit/test_runner.py +++ b/tests/unit/test_runner.py @@ -20,19 +20,21 @@ import click from click.globals import push_context -from dbt_bouncer.checks.common import NestedDict # noqa: F401 -from dbt_bouncer.logger import configure_console_logging -from dbt_bouncer.main import cli -from dbt_bouncer.parsers import ( # noqa: F401 +from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerManifest, DbtBouncerModel, DbtBouncerModelBase, - DbtBouncerRunResultBase, DbtBouncerSemanticModelBase, DbtBouncerSourceBase, DbtBouncerTestBase, ) +from dbt_bouncer.artifact_parsers.parsers_run_results import ( # noqa: F401 + DbtBouncerRunResultBase, +) +from dbt_bouncer.checks.common import NestedDict # noqa: F401 +from dbt_bouncer.logger import configure_console_logging +from dbt_bouncer.main import cli from dbt_bouncer.runner import runner @@ -62,17 +64,19 @@ def test_runner_coverage(caplog, tmp_path): UnitTests, ) - from dbt_bouncer.checks.common import NestedDict # noqa: F401 - from dbt_bouncer.parsers import ( # noqa: F401 + from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerManifest, DbtBouncerModel, DbtBouncerModelBase, - DbtBouncerRunResultBase, DbtBouncerSemanticModelBase, DbtBouncerSourceBase, DbtBouncerTestBase, ) + from dbt_bouncer.artifact_parsers.parsers_run_results import ( # noqa: F401 + DbtBouncerRunResultBase, + ) + from dbt_bouncer.checks.common import NestedDict # noqa: F401 DbtBouncerConf.model_rebuild() @@ -193,17 +197,19 @@ def test_runner_failure(): UnitTests, ) - from dbt_bouncer.checks.common import NestedDict # noqa: F401 - from dbt_bouncer.parsers import ( # noqa: F401 + from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerManifest, DbtBouncerModel, DbtBouncerModelBase, - DbtBouncerRunResultBase, DbtBouncerSemanticModelBase, DbtBouncerSourceBase, DbtBouncerTestBase, ) + from dbt_bouncer.artifact_parsers.parsers_run_results import ( # noqa: F401 + DbtBouncerRunResultBase, + ) + from dbt_bouncer.checks.common import NestedDict # noqa: F401 DbtBouncerConf.model_rebuild() @@ -316,17 +322,19 @@ def test_runner_success(): UnitTests, ) - from dbt_bouncer.checks.common import NestedDict # noqa: F401 - from dbt_bouncer.parsers import ( # noqa: F401 + from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerManifest, DbtBouncerModel, DbtBouncerModelBase, - DbtBouncerRunResultBase, DbtBouncerSemanticModelBase, DbtBouncerSourceBase, DbtBouncerTestBase, ) + from dbt_bouncer.artifact_parsers.parsers_run_results import ( # noqa: F401 + DbtBouncerRunResultBase, + ) + from dbt_bouncer.checks.common import NestedDict # noqa: F401 DbtBouncerConf.model_rebuild() From 6d1621a27d67bc2236e54f63ccdbb02e9503b292 Mon Sep 17 00:00:00 2001 From: pgoslatara Date: Sat, 28 Sep 2024 20:23:04 +0200 Subject: [PATCH 2/8] Cleaner imports --- .../artifact_parsers/parsers_common.py | 3 +-- .../checks/manifest/check_exposures.py | 4 ---- .../checks/manifest/check_semantic_models.py | 5 ---- .../checks/run_results/check_run_results.py | 7 +----- src/dbt_bouncer/config_file_validator.py | 24 ++++++++++++------- src/dbt_bouncer/main.py | 14 +++++------ 6 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/dbt_bouncer/artifact_parsers/parsers_common.py b/src/dbt_bouncer/artifact_parsers/parsers_common.py index 04942200..1babb23d 100644 --- a/src/dbt_bouncer/artifact_parsers/parsers_common.py +++ b/src/dbt_bouncer/artifact_parsers/parsers_common.py @@ -9,11 +9,10 @@ import semver -if TYPE_CHECKING: - from dbt_bouncer.artifact_parsers.parsers_manifest import DbtBouncerManifest if TYPE_CHECKING: from dbt_bouncer.artifact_parsers.parsers_catalog import DbtBouncerCatalogNode from dbt_bouncer.artifact_parsers.parsers_manifest import ( + DbtBouncerManifest, DbtBouncerModel, DbtBouncerSemanticModel, DbtBouncerSource, diff --git a/src/dbt_bouncer/checks/manifest/check_exposures.py b/src/dbt_bouncer/checks/manifest/check_exposures.py index db8da176..bb56d321 100644 --- a/src/dbt_bouncer/checks/manifest/check_exposures.py +++ b/src/dbt_bouncer/checks/manifest/check_exposures.py @@ -8,10 +8,6 @@ from dbt_bouncer.check_base import BaseCheck if TYPE_CHECKING: - import warnings - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerExposureBase, DbtBouncerModelBase, diff --git a/src/dbt_bouncer/checks/manifest/check_semantic_models.py b/src/dbt_bouncer/checks/manifest/check_semantic_models.py index 33776f72..c8d77894 100644 --- a/src/dbt_bouncer/checks/manifest/check_semantic_models.py +++ b/src/dbt_bouncer/checks/manifest/check_semantic_models.py @@ -6,16 +6,11 @@ from dbt_bouncer.check_base import BaseCheck if TYPE_CHECKING: - import warnings - from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerModelBase, DbtBouncerSemanticModelBase, ) - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from pydantic import Field diff --git a/src/dbt_bouncer/checks/run_results/check_run_results.py b/src/dbt_bouncer/checks/run_results/check_run_results.py index e5c755a1..19b69f93 100644 --- a/src/dbt_bouncer/checks/run_results/check_run_results.py +++ b/src/dbt_bouncer/checks/run_results/check_run_results.py @@ -7,12 +7,7 @@ from dbt_bouncer.check_base import BaseCheck if TYPE_CHECKING: - from dbt_bouncer.artifact_parsers.parsers_common import DbtBouncerRunResultBase - -import warnings - -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) + from dbt_bouncer.artifact_parsers.parsers_run_results import DbtBouncerRunResultBase class CheckRunResultsMaxExecutionTime(BaseCheck): diff --git a/src/dbt_bouncer/config_file_validator.py b/src/dbt_bouncer/config_file_validator.py index 252a69d8..28a35b23 100644 --- a/src/dbt_bouncer/config_file_validator.py +++ b/src/dbt_bouncer/config_file_validator.py @@ -95,7 +95,9 @@ def load_config_file_contents(config_file_path: PurePath) -> Mapping[str, Any]: ) -def validate_conf(config_file_contents: Dict[str, Any]) -> "DbtBouncerConf": +def validate_conf( + config_file_contents: Dict[str, Any], +) -> "DbtBouncerConf": """Validate the configuration and return the Pydantic model. Raises: @@ -110,18 +112,22 @@ def validate_conf(config_file_contents: Dict[str, Any]) -> "DbtBouncerConf": # Rebuild the model to ensure all fields are present import warnings + # catalog_checks with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.catalog.catalog_v1 import ( CatalogTable, # noqa: F401 ) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( - Exposures, # noqa: F401 - Macros, # noqa: F401 - UnitTests, # noqa: F401 - ) + # manifest_checks + from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( + Exposures, # noqa: F401 + Macros, # noqa: F401 + UnitTests, # noqa: F401 + ) - import dbt_bouncer.checks # noqa: F401 + import dbt_bouncer.checks.catalog + import dbt_bouncer.checks.manifest + import dbt_bouncer.checks.run_results # noqa: F401 from dbt_bouncer.artifact_parsers.parsers_catalog import ( # noqa: F401 DbtBouncerCatalogNode, ) @@ -137,6 +143,8 @@ def validate_conf(config_file_contents: Dict[str, Any]) -> "DbtBouncerConf": DbtBouncerTest, DbtBouncerTestBase, ) + + # run_results_checks from dbt_bouncer.artifact_parsers.parsers_run_results import ( # noqa: F401 DbtBouncerRunResult, DbtBouncerRunResultBase, @@ -144,7 +152,7 @@ def validate_conf(config_file_contents: Dict[str, Any]) -> "DbtBouncerConf": from dbt_bouncer.checks.common import NestedDict # noqa: F401 from dbt_bouncer.config_file_parser import DbtBouncerConf - DbtBouncerConf.model_rebuild() + DbtBouncerConf().model_rebuild() try: return DbtBouncerConf(**config_file_contents) diff --git a/src/dbt_bouncer/main.py b/src/dbt_bouncer/main.py index 8d81725b..d03184d7 100644 --- a/src/dbt_bouncer/main.py +++ b/src/dbt_bouncer/main.py @@ -80,6 +80,13 @@ def cli( logging.debug(f"{config_file_contents=}") + check_categories = [ + i + for i in config_file_contents + if i.endswith("_checks") and config_file_contents.get(i) != [] + ] + logging.debug(f"{check_categories=}") + # Set click context object for dbt_bouncer.utils.get_check_objects() ctx.obj = { "config_file_path": config_file_path, @@ -92,13 +99,6 @@ def cli( del config_file_contents logging.debug(f"{bouncer_config=}") - check_categories = [ - i - for i in bouncer_config.model_dump() - if i.endswith("_checks") and getattr(bouncer_config, i) != [] - ] - logging.debug(f"{check_categories=}") - for category in check_categories: for idx, check in enumerate(getattr(bouncer_config, category)): # Add indices to uniquely identify checks From f8c9b40cc3cf996c306730e6f4f076dd434c6742 Mon Sep 17 00:00:00 2001 From: pgoslatara Date: Sat, 28 Sep 2024 20:50:12 +0200 Subject: [PATCH 3/8] Replace Manifest_v12 with manifest_latest --- .../artifact_parsers/parsers_common.py | 10 ++-- .../artifact_parsers/parsers_manifest.py | 26 ++++---- .../checks/manifest/check_macros.py | 6 +- .../checks/manifest/check_models.py | 17 ++---- .../checks/manifest/check_unit_tests.py | 11 +--- src/dbt_bouncer/config_file_validator.py | 9 ++- src/dbt_bouncer/runner.py | 15 ++--- .../checks/catalog/test_catalog_sources.py | 2 +- tests/unit/checks/catalog/test_columns.py | 4 +- tests/unit/checks/manifest/test_exposures.py | 6 +- tests/unit/checks/manifest/test_lineage.py | 6 +- tests/unit/checks/manifest/test_macros.py | 6 +- tests/unit/checks/manifest/test_models.py | 11 ++-- .../checks/manifest/test_semantic_models.py | 12 ++-- tests/unit/checks/manifest/test_sources.py | 6 +- tests/unit/checks/manifest/test_unit_tests.py | 8 +-- tests/unit/test_runner.py | 59 ++++++++----------- 17 files changed, 80 insertions(+), 134 deletions(-) diff --git a/src/dbt_bouncer/artifact_parsers/parsers_common.py b/src/dbt_bouncer/artifact_parsers/parsers_common.py index 1babb23d..b41671c2 100644 --- a/src/dbt_bouncer/artifact_parsers/parsers_common.py +++ b/src/dbt_bouncer/artifact_parsers/parsers_common.py @@ -28,11 +28,11 @@ with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.catalog.catalog_v1 import CatalogV1 - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( - Exposures, - Macros, - UnitTests, - ) + + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + Exposures, + Macros, + ) def load_dbt_artifact( diff --git a/src/dbt_bouncer/artifact_parsers/parsers_manifest.py b/src/dbt_bouncer/artifact_parsers/parsers_manifest.py index bdf15287..f2590691 100644 --- a/src/dbt_bouncer/artifact_parsers/parsers_manifest.py +++ b/src/dbt_bouncer/artifact_parsers/parsers_manifest.py @@ -67,24 +67,24 @@ from dbt_artifacts_parser.parsers.manifest.manifest_v11 import ( SourceDefinition as SourceDefinition_v11, ) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( - Exposures, - Macros, - ManifestV12, - Nodes1, - Nodes2, - Nodes4, - Nodes6, - SemanticModels, - Sources, - UnitTests, - ) + +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + Exposures, + Macros, + Nodes1, + Nodes2, + Nodes4, + Nodes6, + SemanticModels, + Sources, + UnitTests, +) class DbtBouncerManifest(BaseModel): """Model for all manifest objects.""" - manifest: Union[ManifestV10, ManifestV11, ManifestV12, ManifestLatest] + manifest: Union[ManifestV10, ManifestV11, ManifestLatest] DbtBouncerExposureBase = Union[Exposure_v10, Exposure_v11, Exposures] diff --git a/src/dbt_bouncer/checks/manifest/check_macros.py b/src/dbt_bouncer/checks/manifest/check_macros.py index 410d513a..8d0be657 100644 --- a/src/dbt_bouncer/checks/manifest/check_macros.py +++ b/src/dbt_bouncer/checks/manifest/check_macros.py @@ -8,11 +8,7 @@ from dbt_bouncer.utils import clean_path_str if TYPE_CHECKING: - import warnings - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Macros + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import Macros import jinja2 diff --git a/src/dbt_bouncer/checks/manifest/check_models.py b/src/dbt_bouncer/checks/manifest/check_models.py index 5074ee4f..acd671b9 100644 --- a/src/dbt_bouncer/checks/manifest/check_models.py +++ b/src/dbt_bouncer/checks/manifest/check_models.py @@ -2,7 +2,6 @@ import logging import re -import warnings from typing import TYPE_CHECKING, List, Literal, Optional import semver @@ -13,23 +12,15 @@ from dbt_bouncer.utils import find_missing_meta_keys if TYPE_CHECKING: - import warnings - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( - UnitTests, - ) - from dbt_bouncer.artifact_parsers.parsers_common import ( - DbtBouncerManifest, - DbtBouncerModelBase, - DbtBouncerTestBase, + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + UnitTests, ) -if TYPE_CHECKING: from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerManifest, DbtBouncerModelBase, + DbtBouncerTestBase, ) + from dbt_bouncer.utils import clean_path_str diff --git a/src/dbt_bouncer/checks/manifest/check_unit_tests.py b/src/dbt_bouncer/checks/manifest/check_unit_tests.py index 4d76bb50..23a5d38c 100644 --- a/src/dbt_bouncer/checks/manifest/check_unit_tests.py +++ b/src/dbt_bouncer/checks/manifest/check_unit_tests.py @@ -9,16 +9,11 @@ from dbt_bouncer.check_base import BaseCheck if TYPE_CHECKING: - import warnings - + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + UnitTests, + ) from dbt_bouncer.artifact_parsers.parsers_common import DbtBouncerManifest - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( - UnitTests, - ) - from dbt_bouncer.utils import object_in_path if TYPE_CHECKING: diff --git a/src/dbt_bouncer/config_file_validator.py b/src/dbt_bouncer/config_file_validator.py index 28a35b23..ac0c52f3 100644 --- a/src/dbt_bouncer/config_file_validator.py +++ b/src/dbt_bouncer/config_file_validator.py @@ -119,15 +119,14 @@ def validate_conf( CatalogTable, # noqa: F401 ) # manifest_checks - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( + import dbt_bouncer.checks.catalog + import dbt_bouncer.checks.manifest + import dbt_bouncer.checks.run_results # noqa: F401 + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( Exposures, # noqa: F401 Macros, # noqa: F401 UnitTests, # noqa: F401 ) - - import dbt_bouncer.checks.catalog - import dbt_bouncer.checks.manifest - import dbt_bouncer.checks.run_results # noqa: F401 from dbt_bouncer.artifact_parsers.parsers_catalog import ( # noqa: F401 DbtBouncerCatalogNode, ) diff --git a/src/dbt_bouncer/runner.py b/src/dbt_bouncer/runner.py index cf4b9cb7..056740a9 100644 --- a/src/dbt_bouncer/runner.py +++ b/src/dbt_bouncer/runner.py @@ -21,16 +21,11 @@ ) if TYPE_CHECKING: - import warnings - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( - Exposures, - Macros, - UnitTests, - ) - + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + Exposures, + Macros, + UnitTests, + ) from dbt_bouncer.artifact_parsers.parsers_common import ( DbtBouncerCatalogNode, DbtBouncerManifest, diff --git a/tests/unit/checks/catalog/test_catalog_sources.py b/tests/unit/checks/catalog/test_catalog_sources.py index cabed864..132301f5 100644 --- a/tests/unit/checks/catalog/test_catalog_sources.py +++ b/tests/unit/checks/catalog/test_catalog_sources.py @@ -6,8 +6,8 @@ with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.catalog.catalog_v1 import CatalogTable - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Sources +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import Sources from dbt_bouncer.artifact_parsers.parsers_manifest import ( DbtBouncerSourceBase, # noqa: F401 ) diff --git a/tests/unit/checks/catalog/test_columns.py b/tests/unit/checks/catalog/test_columns.py index 46cfde21..1853c56e 100644 --- a/tests/unit/checks/catalog/test_columns.py +++ b/tests/unit/checks/catalog/test_columns.py @@ -6,7 +6,9 @@ with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.catalog.catalog_v1 import CatalogTable - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Nodes4, Nodes6 + + +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import Nodes4, Nodes6 from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerModelBase, DbtBouncerTest, diff --git a/tests/unit/checks/manifest/test_exposures.py b/tests/unit/checks/manifest/test_exposures.py index 9f94adfa..d5e69454 100644 --- a/tests/unit/checks/manifest/test_exposures.py +++ b/tests/unit/checks/manifest/test_exposures.py @@ -1,12 +1,8 @@ -import warnings from contextlib import nullcontext as does_not_raise import pytest -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Exposures, Nodes4 - +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import Exposures, Nodes4 from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerModel, diff --git a/tests/unit/checks/manifest/test_lineage.py b/tests/unit/checks/manifest/test_lineage.py index f946dc69..25fccbe8 100644 --- a/tests/unit/checks/manifest/test_lineage.py +++ b/tests/unit/checks/manifest/test_lineage.py @@ -1,12 +1,8 @@ -import warnings from contextlib import nullcontext as does_not_raise import pytest -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Nodes4 - +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import Nodes4 from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerManifest, DbtBouncerModelBase, diff --git a/tests/unit/checks/manifest/test_macros.py b/tests/unit/checks/manifest/test_macros.py index ddba286f..8da9bfaf 100644 --- a/tests/unit/checks/manifest/test_macros.py +++ b/tests/unit/checks/manifest/test_macros.py @@ -1,12 +1,8 @@ -import warnings from contextlib import nullcontext as does_not_raise import pytest -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Macros - +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import Macros from dbt_bouncer.checks.manifest.check_macros import ( CheckMacroArgumentsDescriptionPopulated, CheckMacroCodeDoesNotContainRegexpPattern, diff --git a/tests/unit/checks/manifest/test_models.py b/tests/unit/checks/manifest/test_models.py index 0535eaca..184c88e9 100644 --- a/tests/unit/checks/manifest/test_models.py +++ b/tests/unit/checks/manifest/test_models.py @@ -3,13 +3,14 @@ import pytest +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + Nodes4, + Nodes6, + UnitTests, +) + with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( - Nodes4, - Nodes6, - UnitTests, - ) from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerManifest, diff --git a/tests/unit/checks/manifest/test_semantic_models.py b/tests/unit/checks/manifest/test_semantic_models.py index 120692e5..1988c4a5 100644 --- a/tests/unit/checks/manifest/test_semantic_models.py +++ b/tests/unit/checks/manifest/test_semantic_models.py @@ -1,15 +1,11 @@ -import warnings from contextlib import nullcontext as does_not_raise import pytest -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( - Nodes4, - SemanticModels, - ) - +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + Nodes4, + SemanticModels, +) from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerModelBase, DbtBouncerSemanticModelBase, diff --git a/tests/unit/checks/manifest/test_sources.py b/tests/unit/checks/manifest/test_sources.py index 18e4273e..0f96a1e9 100644 --- a/tests/unit/checks/manifest/test_sources.py +++ b/tests/unit/checks/manifest/test_sources.py @@ -1,12 +1,8 @@ -import warnings from contextlib import nullcontext as does_not_raise import pytest -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Nodes4, Sources - +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import Nodes4, Sources from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerModel, DbtBouncerModelBase, diff --git a/tests/unit/checks/manifest/test_unit_tests.py b/tests/unit/checks/manifest/test_unit_tests.py index 77d00e31..c1355ce0 100644 --- a/tests/unit/checks/manifest/test_unit_tests.py +++ b/tests/unit/checks/manifest/test_unit_tests.py @@ -3,9 +3,10 @@ import pytest +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import UnitTests + with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import UnitTests from dbt_bouncer.artifact_parsers.parsers_manifest import ( DbtBouncerModelBase, # noqa: F401 @@ -13,6 +14,7 @@ import warnings +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import Nodes4 from dbt_bouncer.artifact_parsers.parsers_manifest import ( DbtBouncerManifest, # noqa: F401 ) @@ -22,10 +24,6 @@ CheckUnitTestGivenFormats, ) -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import Nodes4 - CheckUnitTestCoverage.model_rebuild() CheckUnitTestExpectFormats.model_rebuild() CheckUnitTestGivenFormats.model_rebuild() diff --git a/tests/unit/test_runner.py b/tests/unit/test_runner.py index 92fb3957..c10b610a 100644 --- a/tests/unit/test_runner.py +++ b/tests/unit/test_runner.py @@ -7,19 +7,17 @@ from dbt_artifacts_parser.parsers.catalog.catalog_v1 import ( CatalogTable, # noqa: F401 ) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( # noqa: F401 - Exposures, - Macros, - Nodes4, - UnitTests, - ) - - from unittest.mock import MagicMock import click from click.globals import push_context +from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( # noqa: F401 + Exposures, + Macros, + Nodes4, + UnitTests, +) from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerManifest, @@ -57,13 +55,12 @@ def test_runner_coverage(caplog, tmp_path): from dbt_artifacts_parser.parsers.catalog.catalog_v1 import ( CatalogTable, # noqa: F401 ) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( # noqa: F401 - Exposures, - Macros, - Nodes4, - UnitTests, - ) - + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( # noqa: F401 + Exposures, + Macros, + Nodes4, + UnitTests, + ) from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerManifest, @@ -190,13 +187,12 @@ def test_runner_failure(): from dbt_artifacts_parser.parsers.catalog.catalog_v1 import ( CatalogTable, # noqa: F401 ) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( # noqa: F401 - Exposures, - Macros, - Nodes4, - UnitTests, - ) - + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( # noqa: F401 + Exposures, + Macros, + Nodes4, + UnitTests, + ) from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerManifest, @@ -315,13 +311,12 @@ def test_runner_success(): from dbt_artifacts_parser.parsers.catalog.catalog_v1 import ( CatalogTable, # noqa: F401 ) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( # noqa: F401 - Exposures, - Macros, - Nodes4, - UnitTests, - ) - + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( # noqa: F401 + Exposures, + Macros, + Nodes4, + UnitTests, + ) from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 DbtBouncerExposureBase, DbtBouncerManifest, @@ -435,12 +430,6 @@ def test_runner_windows(caplog, tmp_path): from dbt_artifacts_parser.parsers.catalog.catalog_v1 import ( CatalogTable, # noqa: F401 ) - from dbt_artifacts_parser.parsers.manifest.manifest_v12 import ( # noqa: F401 - Exposures, - Macros, - Nodes4, - UnitTests, - ) DbtBouncerConf.model_rebuild() results = runner( From 358f962fc10b2136f62343ca893772c06f2baf4a Mon Sep 17 00:00:00 2001 From: pgoslatara Date: Sat, 28 Sep 2024 21:06:06 +0200 Subject: [PATCH 4/8] Small cleanup --- src/dbt_bouncer/artifact_parsers/parsers_common.py | 2 +- src/dbt_bouncer/config_file_validator.py | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/dbt_bouncer/artifact_parsers/parsers_common.py b/src/dbt_bouncer/artifact_parsers/parsers_common.py index b41671c2..8fca12df 100644 --- a/src/dbt_bouncer/artifact_parsers/parsers_common.py +++ b/src/dbt_bouncer/artifact_parsers/parsers_common.py @@ -42,7 +42,7 @@ def load_dbt_artifact( """Load a dbt artifact from a JSON file to a Pydantic object. Returns: - Union[CatalogV1, DbtBouncerManifest, RunResultsV4, RunResultsV5, RunResultsV6]: + Union[CatalogV1, DbtBouncerManifest, DbtBouncerRunResultBase]: The dbt artifact loaded as a Pydantic object. Raises: diff --git a/src/dbt_bouncer/config_file_validator.py b/src/dbt_bouncer/config_file_validator.py index ac0c52f3..c19d900d 100644 --- a/src/dbt_bouncer/config_file_validator.py +++ b/src/dbt_bouncer/config_file_validator.py @@ -112,13 +112,11 @@ def validate_conf( # Rebuild the model to ensure all fields are present import warnings - # catalog_checks with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) from dbt_artifacts_parser.parsers.catalog.catalog_v1 import ( CatalogTable, # noqa: F401 ) - # manifest_checks import dbt_bouncer.checks.catalog import dbt_bouncer.checks.manifest import dbt_bouncer.checks.run_results # noqa: F401 @@ -142,8 +140,6 @@ def validate_conf( DbtBouncerTest, DbtBouncerTestBase, ) - - # run_results_checks from dbt_bouncer.artifact_parsers.parsers_run_results import ( # noqa: F401 DbtBouncerRunResult, DbtBouncerRunResultBase, From 1a557b59ede066dbab45e6b466c0bb51775d9073 Mon Sep 17 00:00:00 2001 From: pgoslatara Date: Sat, 28 Sep 2024 22:51:39 +0200 Subject: [PATCH 5/8] Moving LevenShtein and Jinja2 imports --- src/dbt_bouncer/checks/manifest/check_macros.py | 10 +++++----- src/dbt_bouncer/config_file_validator.py | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/dbt_bouncer/checks/manifest/check_macros.py b/src/dbt_bouncer/checks/manifest/check_macros.py index 8d0be657..d20dff93 100644 --- a/src/dbt_bouncer/checks/manifest/check_macros.py +++ b/src/dbt_bouncer/checks/manifest/check_macros.py @@ -11,7 +11,7 @@ from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import Macros -import jinja2 +from jinja2 import Environment, nodes from jinja2_simple_tags import StandaloneTag from dbt_bouncer.check_base import BaseCheck @@ -51,7 +51,7 @@ class CheckMacroArgumentsDescriptionPopulated(BaseCheck): def execute(self) -> None: """Execute the check.""" - environment = jinja2.Environment(autoescape=True, extensions=[TagExtension]) + environment = Environment(autoescape=True, extensions=[TagExtension]) ast = environment.parse(self.macro.macro_sql) if hasattr(ast.body[0], "args"): @@ -61,7 +61,7 @@ def execute(self) -> None: if "materialization" in [ x.value.value for x in ast.body[0].nodes[0].kwargs # type: ignore[attr-defined] - if isinstance(x.value, jinja2.nodes.Const) + if isinstance(x.value, nodes.Const) ]: # Materializations don't have arguments macro_arguments = [] @@ -70,12 +70,12 @@ def execute(self) -> None: test_macro = next( x for x in ast.body - if not isinstance(x.nodes[0], jinja2.nodes.Call) # type: ignore[attr-defined] + if not isinstance(x.nodes[0], nodes.Call) # type: ignore[attr-defined] ) macro_arguments = [ x.name for x in test_macro.nodes # type: ignore[attr-defined] - if isinstance(x, jinja2.nodes.Name) + if isinstance(x, nodes.Name) ] # macro_arguments: List of args parsed from macro SQL diff --git a/src/dbt_bouncer/config_file_validator.py b/src/dbt_bouncer/config_file_validator.py index c19d900d..646ad2bb 100644 --- a/src/dbt_bouncer/config_file_validator.py +++ b/src/dbt_bouncer/config_file_validator.py @@ -12,7 +12,6 @@ import re -from Levenshtein import distance from pydantic import ValidationError @@ -152,6 +151,8 @@ def validate_conf( try: return DbtBouncerConf(**config_file_contents) except ValidationError as e: + from Levenshtein import distance + error_message: List[str] = [] for error in e.errors(): if ( From ded614c9de3f2160b4485118e9f17fc5b191a91f Mon Sep 17 00:00:00 2001 From: pgoslatara Date: Mon, 30 Sep 2024 16:24:39 +0200 Subject: [PATCH 6/8] Refactor config classes --- .../artifact_parsers/parsers_common.py | 22 ++- src/dbt_bouncer/config_file_parser.py | 146 ++++++++++++------ src/dbt_bouncer/config_file_validator.py | 134 +++++++++++----- src/dbt_bouncer/main.py | 4 +- src/dbt_bouncer/runner.py | 6 +- tests/unit/test_config_file_validator.py | 20 ++- tests/unit/test_runner.py | 16 +- 7 files changed, 241 insertions(+), 107 deletions(-) diff --git a/src/dbt_bouncer/artifact_parsers/parsers_common.py b/src/dbt_bouncer/artifact_parsers/parsers_common.py index 8fca12df..44c3d6d9 100644 --- a/src/dbt_bouncer/artifact_parsers/parsers_common.py +++ b/src/dbt_bouncer/artifact_parsers/parsers_common.py @@ -23,7 +23,6 @@ DbtBouncerRunResult, DbtBouncerRunResultBase, ) - from dbt_bouncer.config_file_validator import DbtBouncerConf with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) @@ -33,6 +32,9 @@ Exposures, Macros, ) + from dbt_bouncer.config_file_parser import ( + DbtBouncerConfAllCategories as DbtBouncerConf, + ) def load_dbt_artifact( @@ -151,9 +153,12 @@ def parse_dbt_artifacts( ) # Catalog, must come after manifest is parsed - from dbt_bouncer.artifact_parsers.parsers_catalog import parse_catalog + if ( + hasattr(bouncer_config, "catalog_checks") + and bouncer_config.catalog_checks != [] + ): + from dbt_bouncer.artifact_parsers.parsers_catalog import parse_catalog - if bouncer_config.catalog_checks != []: project_catalog_nodes, project_catalog_sources = parse_catalog( artifact_dir=dbt_artifacts_dir, manifest_obj=manifest_obj, @@ -163,11 +168,14 @@ def parse_dbt_artifacts( project_catalog_sources = [] # Run results, must come after manifest is parsed - from dbt_bouncer.artifact_parsers.parsers_run_results import ( - parse_run_results_artifact, - ) + if ( + hasattr(bouncer_config, "run_results_checks") + and bouncer_config.run_results_checks != [] + ): + from dbt_bouncer.artifact_parsers.parsers_run_results import ( + parse_run_results_artifact, + ) - if bouncer_config.run_results_checks != []: project_run_results = parse_run_results_artifact( artifact_dir=dbt_artifacts_dir, manifest_obj=manifest_obj, diff --git a/src/dbt_bouncer/config_file_parser.py b/src/dbt_bouncer/config_file_parser.py index dbb1b9b7..d6bb4ae5 100644 --- a/src/dbt_bouncer/config_file_parser.py +++ b/src/dbt_bouncer/config_file_parser.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Any, ClassVar, Dict, List, Literal, Optional, Union +from typing import Any, Dict, List, Literal, Optional, Union from pydantic import BaseModel, ConfigDict, Field from typing_extensions import Annotated @@ -7,11 +7,20 @@ from dbt_bouncer.utils import clean_path_str -class DbtBouncerConf(BaseModel): - """Base model for the config file contents.""" +def get_check_types( + check_type: List[ + Literal["catalog_checks", "manifest_checks", "run_results_checks"] + ], +) -> List[Any]: + """Get the check types from the check categories. - model_config = ConfigDict(extra="forbid") + Args: + check_type: List[Literal["catalog_checks", "manifest_checks", "run_results_checks"]] + + Returns: + List[str]: The check types. + """ from dbt_bouncer.utils import get_check_objects check_classes: List[Dict[str, Union[Any, Path]]] = [ @@ -21,56 +30,27 @@ class DbtBouncerConf(BaseModel): } for x in get_check_objects() ] - - # Catalog checks - catalog_check_classes: ClassVar = [ - x["class"] for x in check_classes if x["source_file"].parts[-2] == "catalog" - ] - - CatalogCheckConfigs: ClassVar = Annotated[ - Union[tuple(catalog_check_classes)], - Field(discriminator="name"), - ] - - # Manifest checks - manifest_check_classes: ClassVar = [ - x["class"] for x in check_classes if x["source_file"].parts[-2] == "manifest" - ] - - ManifestCheckConfigs: ClassVar = Annotated[ - Union[tuple(manifest_check_classes)], - Field(discriminator="name"), - ] - - # Run result checks - run_results_check_classes: ClassVar = [ - x["class"] for x in check_classes if x["source_file"].parts[-2] == "run_results" - ] - - RunResultsCheckConfigs: ClassVar = Annotated[ - Union[tuple(run_results_check_classes)], - Field(discriminator="name"), - ] - - catalog_checks: List[ + return List[ # type: ignore[misc, return-value] Annotated[ - CatalogCheckConfigs, + Annotated[ + Union[ + tuple( + x["class"] + for x in check_classes + if x["source_file"].parts[-2] == check_type + ) + ], + Field(discriminator="name"), + ], Field(discriminator="name"), ] - ] = Field(default=[]) + ] - manifest_checks: List[ - Annotated[ - ManifestCheckConfigs, - Field(discriminator="name"), - ] - ] = Field(default=[]) - run_results_checks: List[ - Annotated[ - RunResultsCheckConfigs, - Field(discriminator="name"), - ] - ] = Field(default=[]) + +class DbtBouncerConfBase(BaseModel): + """Base model for the config file contents.""" + + model_config = ConfigDict(extra="forbid") custom_checks_dir: Optional[str] = Field( default=None, @@ -89,3 +69,69 @@ class DbtBouncerConf(BaseModel): default=None, description="Severity of the check, one of 'error' or 'warn'.", ) + + +class DbtBouncerConfAllCategories(DbtBouncerConfBase): + """Base model for the config file contents.""" + + model_config = ConfigDict(extra="forbid") + + catalog_checks: get_check_types(check_type="catalog") = Field(default=[]) # type: ignore[valid-type] + manifest_checks: get_check_types(check_type="manifest") = Field(default=[]) # type: ignore[valid-type] + run_results_checks: get_check_types(check_type="run_results") = Field(default=[]) # type: ignore[valid-type] + + custom_checks_dir: Optional[str] = Field( + default=None, + description="Path to a directory containing custom checks.", + ) + + +class DbtBouncerConfCatalogManifest(DbtBouncerConfBase): + """Base model for the config file contents.""" + + model_config = ConfigDict(extra="forbid") + + catalog_checks: get_check_types(check_type="catalog") = Field(default=[]) # type: ignore[valid-type] + manifest_checks: get_check_types(check_type="manifest") = Field(default=[]) # type: ignore[valid-type] + + +class DbtBouncerConfCatalogOnly(DbtBouncerConfBase): + """Base model for the config file contents.""" + + model_config = ConfigDict(extra="forbid") + + catalog_checks: get_check_types(check_type="catalog") = Field(default=[]) # type: ignore[valid-type] + + +class DbtBouncerConfCatalogRunResults(DbtBouncerConfBase): + """Base model for the config file contents.""" + + model_config = ConfigDict(extra="forbid") + + catalog_checks: get_check_types(check_type="catalog") = Field(default=[]) # type: ignore[valid-type] + run_results_checks: get_check_types(check_type="run_results") = Field(default=[]) # type: ignore[valid-type] + + +class DbtBouncerConfManifestOnly(DbtBouncerConfBase): + """Base model for the config file contents.""" + + model_config = ConfigDict(extra="forbid") + + manifest_checks: get_check_types(check_type="manifest") = Field(default=[]) # type: ignore[valid-type] + + +class DbtBouncerConfManifestRunResults(DbtBouncerConfBase): + """Base model for the config file contents.""" + + model_config = ConfigDict(extra="forbid") + + manifest_checks: get_check_types(check_type="manifest") = Field(default=[]) # type: ignore[valid-type] + run_results_checks: get_check_types(check_type="run_results") = Field(default=[]) # type: ignore[valid-type] + + +class DbtBouncerConftRunResultsOnly(DbtBouncerConfBase): + """Base model for the config file contents.""" + + model_config = ConfigDict(extra="forbid") + + run_results_checks: get_check_types(check_type="run_results") = Field(default=[]) # type: ignore[valid-type] diff --git a/src/dbt_bouncer/config_file_validator.py b/src/dbt_bouncer/config_file_validator.py index 646ad2bb..30de5ad9 100644 --- a/src/dbt_bouncer/config_file_validator.py +++ b/src/dbt_bouncer/config_file_validator.py @@ -1,18 +1,70 @@ import logging +import re from pathlib import Path, PurePath -from typing import TYPE_CHECKING, Any, Dict, List, Mapping +from typing import TYPE_CHECKING, Any, Dict, List, Literal, Mapping import toml +from pydantic import ValidationError from dbt_bouncer.utils import load_config_from_yaml if TYPE_CHECKING: - from dbt_bouncer.config_file_parser import DbtBouncerConf + from dbt_bouncer.config_file_parser import ( + DbtBouncerConfAllCategories as DbtBouncerConf, + ) -import re +def conf_cls_factory( + check_categories: List[ + Literal["catalog_checks", "manifest_checks", "run_results_checks"] + ], +): + """Return the appropriate configuration class based on the check categories. -from pydantic import ValidationError + Args: + check_categories: List[Literal["catalog_checks", "manifest_checks", "run_results_checks"]] + + Raises: + ValueError: If the check categories are not valid. + + Returns: + DbtBouncerConf: The configuration class. + + """ + if check_categories == ["catalog_checks"]: + from dbt_bouncer.config_file_parser import DbtBouncerConfCatalogOnly + + return DbtBouncerConfCatalogOnly + elif check_categories == ["manifest_checks"]: + from dbt_bouncer.config_file_parser import DbtBouncerConfManifestOnly + + return DbtBouncerConfManifestOnly + elif check_categories == ["run_results_checks"]: + from dbt_bouncer.config_file_parser import DbtBouncerConftRunResultsOnly + + return DbtBouncerConftRunResultsOnly + elif check_categories == ["catalog_checks", "manifest_checks"]: + from dbt_bouncer.config_file_parser import DbtBouncerConfCatalogManifest + + return DbtBouncerConfCatalogManifest + elif check_categories == ["catalog_checks", "run_results_checks"]: + from dbt_bouncer.config_file_parser import DbtBouncerConfCatalogRunResults + + return DbtBouncerConfCatalogRunResults + elif check_categories == ["manifest_checks", "run_results_checks"]: + from dbt_bouncer.config_file_parser import DbtBouncerConfManifestRunResults + + return DbtBouncerConfManifestRunResults + elif check_categories == [ + "catalog_checks", + "manifest_checks", + "run_results_checks", + ]: + from dbt_bouncer.config_file_parser import DbtBouncerConfAllCategories + + return DbtBouncerConfAllCategories + else: + raise ValueError(f"Invalid check_categories: {check_categories}") def get_config_file_path( @@ -95,6 +147,7 @@ def load_config_file_contents(config_file_path: PurePath) -> Mapping[str, Any]: def validate_conf( + check_categories, #: List[Literal["catalog_checks"], Literal["manifest_checks"], Literal["run_results_checks"]], config_file_contents: Dict[str, Any], ) -> "DbtBouncerConf": """Validate the configuration and return the Pydantic model. @@ -109,43 +162,48 @@ def validate_conf( logging.info("Validating conf...") # Rebuild the model to ensure all fields are present - import warnings + from dbt_bouncer.checks.common import NestedDict # noqa: F401 + + if "catalog_checks" in check_categories: + import warnings - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=UserWarning) - from dbt_artifacts_parser.parsers.catalog.catalog_v1 import ( - CatalogTable, # noqa: F401 + import dbt_bouncer.checks.catalog + from dbt_bouncer.artifact_parsers.parsers_catalog import ( # noqa: F401 + DbtBouncerCatalogNode, + ) + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=UserWarning) + from dbt_artifacts_parser.parsers.catalog.catalog_v1 import ( + CatalogTable, # noqa: F401 + ) + if "manifest_checks" in check_categories: + import dbt_bouncer.checks.manifest + from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( + Exposures, # noqa: F401 + Macros, # noqa: F401 + UnitTests, # noqa: F401 + ) + from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 + DbtBouncerExposureBase, + DbtBouncerManifest, + DbtBouncerModel, + DbtBouncerModelBase, + DbtBouncerSemanticModel, + DbtBouncerSemanticModelBase, + DbtBouncerSource, + DbtBouncerSourceBase, + DbtBouncerTest, + DbtBouncerTestBase, + ) + if "run_results_checks" in check_categories: + import dbt_bouncer.checks.run_results # noqa: F401 + from dbt_bouncer.artifact_parsers.parsers_run_results import ( # noqa: F401 + DbtBouncerRunResult, + DbtBouncerRunResultBase, ) - import dbt_bouncer.checks.catalog - import dbt_bouncer.checks.manifest - import dbt_bouncer.checks.run_results # noqa: F401 - from dbt_bouncer.artifact_parsers.dbt_cloud.manifest_latest import ( - Exposures, # noqa: F401 - Macros, # noqa: F401 - UnitTests, # noqa: F401 - ) - from dbt_bouncer.artifact_parsers.parsers_catalog import ( # noqa: F401 - DbtBouncerCatalogNode, - ) - from dbt_bouncer.artifact_parsers.parsers_manifest import ( # noqa: F401 - DbtBouncerExposureBase, - DbtBouncerManifest, - DbtBouncerModel, - DbtBouncerModelBase, - DbtBouncerSemanticModel, - DbtBouncerSemanticModelBase, - DbtBouncerSource, - DbtBouncerSourceBase, - DbtBouncerTest, - DbtBouncerTestBase, - ) - from dbt_bouncer.artifact_parsers.parsers_run_results import ( # noqa: F401 - DbtBouncerRunResult, - DbtBouncerRunResultBase, - ) - from dbt_bouncer.checks.common import NestedDict # noqa: F401 - from dbt_bouncer.config_file_parser import DbtBouncerConf + DbtBouncerConf = conf_cls_factory(check_categories=check_categories) # noqa: N806 DbtBouncerConf().model_rebuild() try: diff --git a/src/dbt_bouncer/main.py b/src/dbt_bouncer/main.py index d03184d7..af1f7a31 100644 --- a/src/dbt_bouncer/main.py +++ b/src/dbt_bouncer/main.py @@ -95,7 +95,9 @@ def cli( from dbt_bouncer.config_file_validator import validate_conf - bouncer_config = validate_conf(config_file_contents=config_file_contents) + bouncer_config = validate_conf( + check_categories=check_categories, config_file_contents=config_file_contents + ) del config_file_contents logging.debug(f"{bouncer_config=}") diff --git a/src/dbt_bouncer/runner.py b/src/dbt_bouncer/runner.py index 056740a9..4709724d 100644 --- a/src/dbt_bouncer/runner.py +++ b/src/dbt_bouncer/runner.py @@ -35,11 +35,13 @@ DbtBouncerSource, DbtBouncerTest, ) - from dbt_bouncer.config_file_validator import DbtBouncerConf + from dbt_bouncer.config_file_parser import ( + DbtBouncerConfAllCategories as DbtBouncerConf, + ) def runner( - bouncer_config: DbtBouncerConf, + bouncer_config: "DbtBouncerConf", catalog_nodes: List[DbtBouncerCatalogNode], catalog_sources: List[DbtBouncerCatalogNode], check_categories: List[str], diff --git a/tests/unit/test_config_file_validator.py b/tests/unit/test_config_file_validator.py index 8b4480f1..e8ca3d84 100644 --- a/tests/unit/test_config_file_validator.py +++ b/tests/unit/test_config_file_validator.py @@ -5,6 +5,7 @@ import pytest import toml import yaml +from pydantic import PydanticUserError from dbt_bouncer.config_file_validator import ( get_config_file_path, @@ -110,7 +111,7 @@ def test_load_config_file_contents_pyproject_toml_no_bouncer_section( invalid_confs = [ ( f, - pytest.raises(RuntimeError), + pytest.raises(Exception), # noqa: PT011 ) for f in Path("./tests/unit/config_files/invalid").glob("*.yml") ] @@ -134,7 +135,11 @@ def test_validate_conf_invalid(f, expectation): ) with ctx as _, expectation as _: - validate_conf(config_file_contents=conf) + result = validate_conf( + check_categories=[x for x in conf if x.endswith("_checks")], + config_file_contents=conf, + ) + assert isinstance(result.exception, (RuntimeError, PydanticUserError)) def test_validate_conf_incorrect_name(): @@ -148,9 +153,10 @@ def test_validate_conf_incorrect_name(): with ctx, pytest.raises(Exception) as excinfo: # noqa: PT011 validate_conf( + check_categories=["manifest_checks"], config_file_contents={ "manifest_checks": [{"name": "check_model_has_unique_tst"}] - } + }, ) assert ( @@ -170,12 +176,13 @@ def test_validate_conf_incorrect_names(): with ctx, pytest.raises(Exception) as excinfo: # noqa: PT011 validate_conf( + check_categories=["manifest_checks"], config_file_contents={ "manifest_checks": [ {"name": "check_model_has_unique_tst"}, {"name": "check_exposure_based_on_viw"}, ] - } + }, ) assert ( @@ -212,4 +219,7 @@ def test_validate_conf_valid(f, expectation): ) with ctx, expectation: - validate_conf(config_file_contents=conf) + validate_conf( + check_categories=[x for x in conf if x.endswith("_checks")], + config_file_contents=conf, + ) diff --git a/tests/unit/test_runner.py b/tests/unit/test_runner.py index c10b610a..96741d6f 100644 --- a/tests/unit/test_runner.py +++ b/tests/unit/test_runner.py @@ -47,7 +47,9 @@ def test_runner_coverage(caplog, tmp_path): ) with ctx: - from dbt_bouncer.config_file_parser import DbtBouncerConf + from dbt_bouncer.config_file_parser import ( + DbtBouncerConfAllCategories as DbtBouncerConf, + ) with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) @@ -179,7 +181,9 @@ def test_runner_failure(): ) with ctx: - from dbt_bouncer.config_file_parser import DbtBouncerConf + from dbt_bouncer.config_file_parser import ( + DbtBouncerConfAllCategories as DbtBouncerConf, + ) with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) @@ -303,7 +307,9 @@ def test_runner_success(): ) with ctx: - from dbt_bouncer.config_file_parser import DbtBouncerConf + from dbt_bouncer.config_file_parser import ( + DbtBouncerConfAllCategories as DbtBouncerConf, + ) with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) @@ -422,7 +428,9 @@ def test_runner_windows(caplog, tmp_path): ctx = MagicMock(obj={"verbosity": 3}) push_context(ctx) - from dbt_bouncer.config_file_parser import DbtBouncerConf + from dbt_bouncer.config_file_parser import ( + DbtBouncerConfAllCategories as DbtBouncerConf, + ) with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning) From 6d4babf29b9664ac0c51887732992771b13efe6f Mon Sep 17 00:00:00 2001 From: pgoslatara Date: Mon, 30 Sep 2024 16:29:42 +0200 Subject: [PATCH 7/8] Updating doc strings --- src/dbt_bouncer/config_file_parser.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/dbt_bouncer/config_file_parser.py b/src/dbt_bouncer/config_file_parser.py index d6bb4ae5..4e5bfa22 100644 --- a/src/dbt_bouncer/config_file_parser.py +++ b/src/dbt_bouncer/config_file_parser.py @@ -72,7 +72,7 @@ class DbtBouncerConfBase(BaseModel): class DbtBouncerConfAllCategories(DbtBouncerConfBase): - """Base model for the config file contents.""" + """Config file contents when all categories are used.""" model_config = ConfigDict(extra="forbid") @@ -87,7 +87,7 @@ class DbtBouncerConfAllCategories(DbtBouncerConfBase): class DbtBouncerConfCatalogManifest(DbtBouncerConfBase): - """Base model for the config file contents.""" + """Config file contents when catalog and manifest are used.""" model_config = ConfigDict(extra="forbid") @@ -96,7 +96,7 @@ class DbtBouncerConfCatalogManifest(DbtBouncerConfBase): class DbtBouncerConfCatalogOnly(DbtBouncerConfBase): - """Base model for the config file contents.""" + """Config file contents when catalog only is used.""" model_config = ConfigDict(extra="forbid") @@ -104,7 +104,7 @@ class DbtBouncerConfCatalogOnly(DbtBouncerConfBase): class DbtBouncerConfCatalogRunResults(DbtBouncerConfBase): - """Base model for the config file contents.""" + """Config file contents when catalog and run_results are used.""" model_config = ConfigDict(extra="forbid") @@ -113,7 +113,7 @@ class DbtBouncerConfCatalogRunResults(DbtBouncerConfBase): class DbtBouncerConfManifestOnly(DbtBouncerConfBase): - """Base model for the config file contents.""" + """Config file contents when manifest only is used.""" model_config = ConfigDict(extra="forbid") @@ -121,7 +121,7 @@ class DbtBouncerConfManifestOnly(DbtBouncerConfBase): class DbtBouncerConfManifestRunResults(DbtBouncerConfBase): - """Base model for the config file contents.""" + """Config file contents when manifest and run_results are used.""" model_config = ConfigDict(extra="forbid") @@ -129,8 +129,8 @@ class DbtBouncerConfManifestRunResults(DbtBouncerConfBase): run_results_checks: get_check_types(check_type="run_results") = Field(default=[]) # type: ignore[valid-type] -class DbtBouncerConftRunResultsOnly(DbtBouncerConfBase): - """Base model for the config file contents.""" +class DbtBouncerConfRunResultsOnly(DbtBouncerConfBase): + """Config file contents when run_results only is used.""" model_config = ConfigDict(extra="forbid") From 5ed8908658c65d38ac213f93f4bc4bd8d5a48d84 Mon Sep 17 00:00:00 2001 From: pgoslatara Date: Mon, 30 Sep 2024 16:31:48 +0200 Subject: [PATCH 8/8] Updating lock file --- poetry.lock | 693 +------------------------------------------------ pyproject.toml | 1 - 2 files changed, 7 insertions(+), 687 deletions(-) diff --git a/poetry.lock b/poetry.lock index c1ed644a..96096091 100644 --- a/poetry.lock +++ b/poetry.lock @@ -37,35 +37,6 @@ files = [ [package.dependencies] typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} -[[package]] -name = "appnope" -version = "0.1.4" -description = "Disable App Nap on macOS >= 10.9" -optional = false -python-versions = ">=3.6" -files = [ - {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, - {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, -] - -[[package]] -name = "asttokens" -version = "2.4.1" -description = "Annotate AST trees with source code positions" -optional = false -python-versions = "*" -files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, -] - -[package.dependencies] -six = ">=1.12.0" - -[package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] - [[package]] name = "astunparse" version = "1.6.3" @@ -132,17 +103,6 @@ pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] -[[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" -optional = false -python-versions = "*" -files = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, -] - [[package]] name = "black" version = "24.8.0" @@ -200,85 +160,6 @@ files = [ {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] -[[package]] -name = "cffi" -version = "1.17.1" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, -] - -[package.dependencies] -pycparser = "*" - [[package]] name = "cfgv" version = "3.4.0" @@ -414,23 +295,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "comm" -version = "0.2.2" -description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." -optional = false -python-versions = ">=3.8" -files = [ - {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, - {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, -] - -[package.dependencies] -traitlets = ">=4" - -[package.extras] -test = ["pytest"] - [[package]] name = "coverage" version = "7.6.1" @@ -696,48 +560,6 @@ python-dateutil = ">=2.0,<3" pyyaml = ">=6.0,<7" typing-extensions = ">=4.4,<5" -[[package]] -name = "debugpy" -version = "1.8.6" -description = "An implementation of the Debug Adapter Protocol for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "debugpy-1.8.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:30f467c5345d9dfdcc0afdb10e018e47f092e383447500f125b4e013236bf14b"}, - {file = "debugpy-1.8.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d73d8c52614432f4215d0fe79a7e595d0dd162b5c15233762565be2f014803b"}, - {file = "debugpy-1.8.6-cp310-cp310-win32.whl", hash = "sha256:e3e182cd98eac20ee23a00653503315085b29ab44ed66269482349d307b08df9"}, - {file = "debugpy-1.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:e3a82da039cfe717b6fb1886cbbe5c4a3f15d7df4765af857f4307585121c2dd"}, - {file = "debugpy-1.8.6-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67479a94cf5fd2c2d88f9615e087fcb4fec169ec780464a3f2ba4a9a2bb79955"}, - {file = "debugpy-1.8.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb8653f6cbf1dd0a305ac1aa66ec246002145074ea57933978346ea5afdf70b"}, - {file = "debugpy-1.8.6-cp311-cp311-win32.whl", hash = "sha256:cdaf0b9691879da2d13fa39b61c01887c34558d1ff6e5c30e2eb698f5384cd43"}, - {file = "debugpy-1.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:43996632bee7435583952155c06881074b9a742a86cee74e701d87ca532fe833"}, - {file = "debugpy-1.8.6-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:db891b141fc6ee4b5fc6d1cc8035ec329cabc64bdd2ae672b4550c87d4ecb128"}, - {file = "debugpy-1.8.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:567419081ff67da766c898ccf21e79f1adad0e321381b0dfc7a9c8f7a9347972"}, - {file = "debugpy-1.8.6-cp312-cp312-win32.whl", hash = "sha256:c9834dfd701a1f6bf0f7f0b8b1573970ae99ebbeee68314116e0ccc5c78eea3c"}, - {file = "debugpy-1.8.6-cp312-cp312-win_amd64.whl", hash = "sha256:e4ce0570aa4aca87137890d23b86faeadf184924ad892d20c54237bcaab75d8f"}, - {file = "debugpy-1.8.6-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:df5dc9eb4ca050273b8e374a4cd967c43be1327eeb42bfe2f58b3cdfe7c68dcb"}, - {file = "debugpy-1.8.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a85707c6a84b0c5b3db92a2df685b5230dd8fb8c108298ba4f11dba157a615a"}, - {file = "debugpy-1.8.6-cp38-cp38-win32.whl", hash = "sha256:538c6cdcdcdad310bbefd96d7850be1cd46e703079cc9e67d42a9ca776cdc8a8"}, - {file = "debugpy-1.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:22140bc02c66cda6053b6eb56dfe01bbe22a4447846581ba1dd6df2c9f97982d"}, - {file = "debugpy-1.8.6-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:c1cef65cffbc96e7b392d9178dbfd524ab0750da6c0023c027ddcac968fd1caa"}, - {file = "debugpy-1.8.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e60bd06bb3cc5c0e957df748d1fab501e01416c43a7bdc756d2a992ea1b881"}, - {file = "debugpy-1.8.6-cp39-cp39-win32.whl", hash = "sha256:f7158252803d0752ed5398d291dee4c553bb12d14547c0e1843ab74ee9c31123"}, - {file = "debugpy-1.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:3358aa619a073b620cd0d51d8a6176590af24abcc3fe2e479929a154bf591b51"}, - {file = "debugpy-1.8.6-py2.py3-none-any.whl", hash = "sha256:b48892df4d810eff21d3ef37274f4c60d32cdcafc462ad5647239036b0f0649f"}, - {file = "debugpy-1.8.6.zip", hash = "sha256:c931a9371a86784cee25dec8d65bc2dc7a21f3f1552e3833d9ef8f919d22280a"}, -] - -[[package]] -name = "decorator" -version = "5.1.1" -description = "Decorators for Humans" -optional = false -python-versions = ">=3.5" -files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, -] - [[package]] name = "deepdiff" version = "7.0.1" @@ -856,20 +678,6 @@ files = [ [package.extras] testing = ["hatch", "pre-commit", "pytest", "tox"] -[[package]] -name = "executing" -version = "2.1.0" -description = "Get the currently executing AST node of a frame, and other information" -optional = false -python-versions = ">=3.8" -files = [ - {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, - {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, -] - -[package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] - [[package]] name = "filelock" version = "3.16.1" @@ -1030,78 +838,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "ipykernel" -version = "6.29.5" -description = "IPython Kernel for Jupyter" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5"}, - {file = "ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215"}, -] - -[package.dependencies] -appnope = {version = "*", markers = "platform_system == \"Darwin\""} -comm = ">=0.1.1" -debugpy = ">=1.6.5" -ipython = ">=7.23.1" -jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -matplotlib-inline = ">=0.1" -nest-asyncio = "*" -packaging = "*" -psutil = "*" -pyzmq = ">=24" -tornado = ">=6.1" -traitlets = ">=5.4.0" - -[package.extras] -cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] -pyqt5 = ["pyqt5"] -pyside6 = ["pyside6"] -test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "ipython" -version = "8.12.3" -description = "IPython: Productive Interactive Computing" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"}, - {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"}, -] - -[package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" -pygments = ">=2.4.0" -stack-data = "*" -traitlets = ">=5" -typing-extensions = {version = "*", markers = "python_version < \"3.10\""} - -[package.extras] -all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] -black = ["black"] -doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] - [[package]] name = "isodate" version = "0.6.1" @@ -1130,25 +866,6 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] -[[package]] -name = "jedi" -version = "0.19.1" -description = "An autocompletion tool for Python that can be used for text editors." -optional = false -python-versions = ">=3.6" -files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, -] - -[package.dependencies] -parso = ">=0.8.3,<0.9.0" - -[package.extras] -docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] - [[package]] name = "jinja2" version = "3.1.4" @@ -1218,49 +935,6 @@ files = [ importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} referencing = ">=0.31.0" -[[package]] -name = "jupyter-client" -version = "8.6.3" -description = "Jupyter protocol implementation and client libraries" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"}, - {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -python-dateutil = ">=2.8.2" -pyzmq = ">=23.0" -tornado = ">=6.2" -traitlets = ">=5.3" - -[package.extras] -docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] - -[[package]] -name = "jupyter-core" -version = "5.7.2" -description = "Jupyter core package. A base package on which Jupyter projects rely." -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, - {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, -] - -[package.dependencies] -platformdirs = ">=2.5" -pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} -traitlets = ">=5.3" - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] -test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] - [[package]] name = "leather" version = "0.4.0" @@ -1514,20 +1188,6 @@ orjson = ["orjson"] toml = ["tomli (>=1.1.0)", "tomli-w (>=1.0)"] yaml = ["pyyaml (>=3.13)"] -[[package]] -name = "matplotlib-inline" -version = "0.1.7" -description = "Inline Matplotlib backend for Jupyter" -optional = false -python-versions = ">=3.8" -files = [ - {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, - {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, -] - -[package.dependencies] -traitlets = "*" - [[package]] name = "mergedeep" version = "1.3.4" @@ -1681,13 +1341,13 @@ dev = ["click", "codecov", "mkdocs-gen-files", "mkdocs-git-authors-plugin", "mkd [[package]] name = "mkdocs-material" -version = "9.5.38" +version = "9.5.39" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.38-py3-none-any.whl", hash = "sha256:d4779051d52ba9f1e7e344b34de95449c7c366c212b388e4a2db9a3db043c228"}, - {file = "mkdocs_material-9.5.38.tar.gz", hash = "sha256:1843c5171ad6b489550aeaf7358e5b7128cc03ddcf0fb4d91d19aa1e691a63b8"}, + {file = "mkdocs_material-9.5.39-py3-none-any.whl", hash = "sha256:0f2f68c8db89523cb4a59705cd01b4acd62b2f71218ccb67e1e004e560410d2b"}, + {file = "mkdocs_material-9.5.39.tar.gz", hash = "sha256:25faa06142afa38549d2b781d475a86fb61de93189f532b88e69bf11e5e5c3be"}, ] [package.dependencies] @@ -1906,17 +1566,6 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] -[[package]] -name = "nest-asyncio" -version = "1.6.0" -description = "Patch asyncio to allow nested event loops" -optional = false -python-versions = ">=3.5" -files = [ - {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, - {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, -] - [[package]] name = "networkx" version = "3.1" @@ -1997,21 +1646,6 @@ files = [ {file = "parsedatetime-2.6.tar.gz", hash = "sha256:4cb368fbb18a0b7231f4d76119165451c8d2e35951455dfee97c62a87b04d455"}, ] -[[package]] -name = "parso" -version = "0.8.4" -description = "A Python Parser" -optional = false -python-versions = ">=3.6" -files = [ - {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, - {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, -] - -[package.extras] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["docopt", "pytest"] - [[package]] name = "pathspec" version = "0.12.1" @@ -2037,31 +1671,6 @@ files = [ [package.extras] subprocess = ["subprocess32 (>=3.2.7)"] -[[package]] -name = "pexpect" -version = "4.9.0" -description = "Pexpect allows easy control of interactive console applications." -optional = false -python-versions = "*" -files = [ - {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, - {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, -] - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -optional = false -python-versions = "*" -files = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, -] - [[package]] name = "pkgutil-resolve-name" version = "1.3.10" @@ -2132,20 +1741,6 @@ files = [ {file = "progress-1.6.tar.gz", hash = "sha256:c9c86e98b5c03fa1fe11e3b67c1feda4788b8d0fe7336c2ff7d5644ccfba34cd"}, ] -[[package]] -name = "prompt-toolkit" -version = "3.0.48" -description = "Library for building powerful interactive command lines in Python" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, - {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, -] - -[package.dependencies] -wcwidth = "*" - [[package]] name = "protobuf" version = "4.25.5" @@ -2166,71 +1761,6 @@ files = [ {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"}, ] -[[package]] -name = "psutil" -version = "6.0.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, - {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, - {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, - {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, - {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, - {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, - {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, - {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, - {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, - {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -optional = false -python-versions = "*" -files = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] - -[[package]] -name = "pure-eval" -version = "0.2.3" -description = "Safely evaluate AST nodes without side effects" -optional = false -python-versions = "*" -files = [ - {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, - {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, -] - -[package.extras] -tests = ["pytest"] - -[[package]] -name = "pycparser" -version = "2.22" -description = "C parser in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, -] - [[package]] name = "pydantic" version = "2.9.2" @@ -2379,13 +1909,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.11" +version = "10.11.1" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "pymdown_extensions-10.11-py3-none-any.whl", hash = "sha256:e68080eac44634406b31f4aec58fbad17b0ec5fca6b086e29008616d54c3906b"}, - {file = "pymdown_extensions-10.11.tar.gz", hash = "sha256:2653fb658bca5f278029f8c67a67f0f08b7bd3c657e2630d261ad542e97c4192"}, + {file = "pymdown_extensions-10.11.1-py3-none-any.whl", hash = "sha256:a2b28f5786e041f19cb5bb30a1c2c853668a7099da8e3dd822a5ad05f2e855e3"}, + {file = "pymdown_extensions-10.11.1.tar.gz", hash = "sha256:a8836e955851542fa2625d04d59fdf97125ca001377478ed5618e04f9183a59a"}, ] [package.dependencies] @@ -2522,29 +2052,6 @@ files = [ {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, ] -[[package]] -name = "pywin32" -version = "306" -description = "Python for Window Extensions" -optional = false -python-versions = "*" -files = [ - {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, - {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, - {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, - {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, - {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, - {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, - {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, - {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, - {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, - {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, - {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, - {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, - {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, - {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, -] - [[package]] name = "pyyaml" version = "6.0.2" @@ -2621,127 +2128,6 @@ files = [ [package.dependencies] pyyaml = "*" -[[package]] -name = "pyzmq" -version = "26.2.0" -description = "Python bindings for 0MQ" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, - {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, - {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, - {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, - {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, - {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, - {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, - {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, - {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, - {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, - {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, - {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, - {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, - {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, - {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, - {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, - {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, - {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, - {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, - {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, - {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, - {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, - {file = "pyzmq-26.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8"}, - {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8"}, - {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1"}, - {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9"}, - {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27"}, - {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097"}, - {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93"}, - {file = "pyzmq-26.2.0-cp37-cp37m-win32.whl", hash = "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951"}, - {file = "pyzmq-26.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231"}, - {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f"}, - {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2"}, - {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6"}, - {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289"}, - {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732"}, - {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780"}, - {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640"}, - {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd"}, - {file = "pyzmq-26.2.0-cp38-cp38-win32.whl", hash = "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988"}, - {file = "pyzmq-26.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f"}, - {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, - {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, - {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, - {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, - {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, - {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, -] - -[package.dependencies] -cffi = {version = "*", markers = "implementation_name == \"pypy\""} - [[package]] name = "rapidfuzz" version = "3.9.7" @@ -3221,25 +2607,6 @@ files = [ dev = ["build", "hatch"] doc = ["sphinx"] -[[package]] -name = "stack-data" -version = "0.6.3" -description = "Extract data from python stack frames and tracebacks for informative displays" -optional = false -python-versions = "*" -files = [ - {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, - {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, -] - -[package.dependencies] -asttokens = ">=2.1.0" -executing = ">=1.2.0" -pure-eval = "*" - -[package.extras] -tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] - [[package]] name = "tabulate" version = "0.9.0" @@ -3287,26 +2654,6 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "tornado" -version = "6.4.1" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -optional = false -python-versions = ">=3.8" -files = [ - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, - {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, - {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, - {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, -] - [[package]] name = "tqdm" version = "4.66.5" @@ -3327,21 +2674,6 @@ notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] -[[package]] -name = "traitlets" -version = "5.14.3" -description = "Traitlets Python configuration system" -optional = false -python-versions = ">=3.8" -files = [ - {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, - {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, -] - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] - [[package]] name = "types-pyyaml" version = "6.0.12.20240917" @@ -3495,17 +2827,6 @@ files = [ [package.extras] watchmedo = ["PyYAML (>=3.10)"] -[[package]] -name = "wcwidth" -version = "0.2.13" -description = "Measures the displayed width of unicode strings in a terminal" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, -] - [[package]] name = "wheel" version = "0.44.0" @@ -3542,4 +2863,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<3.13" -content-hash = "9fe301c21f932da2d48a256eb64882a1a01e5f96b7b179e65b4944e978ade233" +content-hash = "bbacf157c102eea01a24e7616631747ccdddb87dd77a00485e20d994314545a0" diff --git a/pyproject.toml b/pyproject.toml index a1897b8e..8d14f47a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,7 +113,6 @@ sh = "^2" types-PyYaml = "^6" types-tabulate = "^0" types-toml = "^0" -ipykernel = "^6.29.5" [tool.poetry.group.docs.dependencies] mike = "^2"