diff --git a/.github/workflows/test-ci.yaml b/.github/workflows/test-ci.yaml index 422168e5..b77387d3 100644 --- a/.github/workflows/test-ci.yaml +++ b/.github/workflows/test-ci.yaml @@ -9,12 +9,15 @@ jobs: name: Run test on ${{ matrix.os }} with ${{ matrix.py_ver }} strategy: matrix: - os: + os: - macOS-latest - - macos-11 + - macos-14 + - macos-13 - ubuntu-latest - - ubuntu-20.04 + - ubuntu-24.04 + - ubuntu-22.04 - windows-latest + - windows-2022 - windows-2019 runs-on: ${{ matrix.os }} diff --git a/Pipfile b/Pipfile index 3bcbe159..3d925e25 100644 --- a/Pipfile +++ b/Pipfile @@ -10,8 +10,9 @@ requests = "*" cerberus = "*" defusedxml = "*" xmltodict = "*" -jinja2 = "*" python-dotenv = "*" +pydantic = "*" +loguru = "*" [dev-packages] pytest = "*" @@ -19,11 +20,11 @@ mypy = "*" pylint = "*" flake8 = "*" black = {extras = ["d"], version = "*"} -var-dump = "*" types-pyyaml = "*" types-requests = "*" pytest-cov = "*" coveralls = "*" +icecream = "*" [requires] python_version = "3.11" diff --git a/Pipfile.lock b/Pipfile.lock index 3948920e..5a7ec08c 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "87e664a0e25e7b0a590fdf2ef6c7be3266ff1e1081d362423814e68f053cb8d8" + "sha256": "d6c63b4f8cfa223f5dbf696af5fcbc0a612208db2934fa6b07a3632829c20f4a" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,14 @@ ] }, "default": { + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "cerberus": { "hashes": [ "sha256:7649a5815024d18eb7c6aa5e7a95355c649a53aacfc9b050e9d0bf6bfa2af372", @@ -26,107 +34,122 @@ }, "certifi": { "hashes": [ - "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", - "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90" + "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" ], "markers": "python_version >= '3.6'", - "version": "==2024.7.4" + "version": "==2024.8.30" }, "charset-normalizer": { "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", + "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", + "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", + "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", + "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", + "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", + "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", + "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", + "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", + "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", + "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", + "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", + "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", + "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", + "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", + "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", + "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", + "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", + "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", + "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", + "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", + "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", + "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", + "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", + "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", + "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", + "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", + "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", + "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", + "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", + "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", + "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", + "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", + "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", + "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", + "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", + "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", + "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", + "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", + "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", + "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", + "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", + "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", + "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", + "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", + "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", + "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", + "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", + "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", + "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", + "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", + "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", + "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", + "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", + "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", + "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", + "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", + "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", + "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", + "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", + "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", + "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", + "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", + "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", + "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", + "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", + "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", + "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", + "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", + "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", + "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", + "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", + "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", + "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", + "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", + "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", + "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", + "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", + "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", + "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", + "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", + "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", + "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", + "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", + "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", + "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", + "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", + "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", + "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", + "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", + "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", + "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", + "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", + "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", + "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", + "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", + "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", + "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", + "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", + "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", + "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", + "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", + "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", + "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", + "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" + "version": "==3.4.0" }, "click": { "hashes": [ @@ -148,86 +171,124 @@ }, "idna": { "hashes": [ - "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", - "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" + ], + "markers": "python_version >= '3.6'", + "version": "==3.10" + }, + "loguru": { + "hashes": [ + "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb", + "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac" ], + "index": "pypi", "markers": "python_version >= '3.5'", - "version": "==3.7" + "version": "==0.7.2" }, - "jinja2": { + "pydantic": { "hashes": [ - "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", - "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d" + "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f", + "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==3.1.4" + "markers": "python_version >= '3.8'", + "version": "==2.9.2" }, - "markupsafe": { + "pydantic-core": { "hashes": [ - "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", - "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", - "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", - "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", - "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", - "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", - "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", - "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df", - "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", - "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", - "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", - "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", - "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", - "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371", - "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2", - "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", - "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52", - "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", - "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", - "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", - "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", - "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", - "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", - "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", - "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", - "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", - "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", - "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", - "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", - "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9", - "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", - "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", - "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", - "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", - "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", - "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", - "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a", - "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", - "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", - "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", - "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", - "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", - "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", - "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", - "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", - "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f", - "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50", - "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", - "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", - "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", - "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", - "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", - "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", - "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", - "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf", - "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", - "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", - "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", - "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", - "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68" + "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36", + "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05", + "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071", + "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327", + "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c", + "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36", + "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29", + "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744", + "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d", + "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec", + "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e", + "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e", + "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577", + "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232", + "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", + "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6", + "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368", + "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480", + "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2", + "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2", + "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6", + "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", + "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d", + "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2", + "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84", + "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166", + "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271", + "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", + "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb", + "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13", + "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323", + "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556", + "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665", + "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef", + "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb", + "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119", + "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126", + "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510", + "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b", + "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87", + "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f", + "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc", + "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8", + "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21", + "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f", + "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6", + "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658", + "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b", + "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3", + "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb", + "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59", + "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24", + "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9", + "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3", + "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd", + "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753", + "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55", + "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad", + "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a", + "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605", + "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e", + "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b", + "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433", + "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8", + "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07", + "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728", + "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0", + "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327", + "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555", + "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64", + "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6", + "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea", + "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b", + "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df", + "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e", + "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd", + "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068", + "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3", + "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040", + "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12", + "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916", + "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", + "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f", + "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801", + "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231", + "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5", + "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8", + "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee", + "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607" ], - "markers": "python_version >= '3.7'", - "version": "==2.1.5" + "markers": "python_version >= '3.8'", + "version": "==2.23.4" }, "python-dotenv": { "hashes": [ @@ -307,128 +368,136 @@ "markers": "python_version >= '3.8'", "version": "==2.32.3" }, + "typing-extensions": { + "hashes": [ + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" + ], + "markers": "python_version < '3.13'", + "version": "==4.12.2" + }, "urllib3": { "hashes": [ - "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", - "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168" + "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", + "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" ], "markers": "python_version >= '3.8'", - "version": "==2.2.2" + "version": "==2.2.3" }, "xmltodict": { "hashes": [ - "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56", - "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852" + "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", + "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac" ], "index": "pypi", - "markers": "python_version >= '3.4'", - "version": "==0.13.0" + "markers": "python_version >= '3.6'", + "version": "==0.14.2" } }, "develop": { "aiohappyeyeballs": { "hashes": [ - "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2", - "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd" + "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586", + "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572" ], "markers": "python_version >= '3.8'", - "version": "==2.4.0" + "version": "==2.4.3" }, "aiohttp": { "hashes": [ - "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277", - "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1", - "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe", - "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb", - "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca", - "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91", - "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972", - "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a", - "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3", - "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa", - "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77", - "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b", - "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8", - "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599", - "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc", - "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf", - "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511", - "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699", - "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487", - "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987", - "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff", - "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db", - "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022", - "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce", - "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a", - "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5", - "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7", - "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820", - "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf", - "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e", - "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf", - "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5", - "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6", - "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6", - "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91", - "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3", - "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a", - "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d", - "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088", - "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc", - "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f", - "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75", - "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471", - "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e", - "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697", - "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092", - "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69", - "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3", - "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32", - "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589", - "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178", - "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92", - "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2", - "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e", - "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058", - "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857", - "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1", - "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6", - "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22", - "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0", - "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b", - "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57", - "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f", - "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e", - "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16", - "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1", - "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f", - "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6", - "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04", - "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae", - "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d", - "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b", - "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f", - "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862", - "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689", - "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c", - "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683", - "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef", - "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f", - "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12", - "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73", - "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061", - "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072", - "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11", - "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691", - "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77", - "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385", - "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172", - "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569", - "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f", - "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5" + "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138", + "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c", + "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24", + "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480", + "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2", + "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5", + "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a", + "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8", + "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf", + "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871", + "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486", + "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9", + "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d", + "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb", + "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68", + "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1", + "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d", + "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd", + "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1", + "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8", + "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7", + "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959", + "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7", + "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42", + "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79", + "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38", + "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a", + "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8", + "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8", + "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151", + "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6", + "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e", + "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7", + "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce", + "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b", + "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8", + "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628", + "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f", + "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a", + "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7", + "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc", + "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab", + "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b", + "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911", + "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9", + "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572", + "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554", + "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d", + "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257", + "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c", + "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b", + "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742", + "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090", + "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6", + "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc", + "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142", + "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16", + "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a", + "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28", + "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e", + "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94", + "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026", + "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb", + "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28", + "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9", + "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3", + "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f", + "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983", + "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205", + "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f", + "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa", + "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c", + "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2", + "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb", + "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67", + "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762", + "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a", + "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8", + "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a", + "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a", + "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc", + "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91", + "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23", + "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527", + "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6", + "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c", + "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7", + "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f", + "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a", + "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092", + "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414" ], - "version": "==3.10.5" + "version": "==3.10.10" }, "aiosignal": { "hashes": [ @@ -440,11 +509,18 @@ }, "astroid": { "hashes": [ - "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a", - "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25" + "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d", + "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8" ], - "markers": "python_full_version >= '3.8.0'", - "version": "==3.2.4" + "markers": "python_full_version >= '3.9.0'", + "version": "==3.3.5" + }, + "asttokens": { + "hashes": [ + "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", + "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0" + ], + "version": "==2.4.1" }, "attrs": { "hashes": [ @@ -459,135 +535,150 @@ "d" ], "hashes": [ - "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6", - "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e", - "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f", - "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018", - "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e", - "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd", - "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4", - "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed", - "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2", - "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42", - "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af", - "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb", - "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368", - "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb", - "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af", - "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed", - "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47", - "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2", - "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a", - "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c", - "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920", - "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1" + "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", + "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd", + "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea", + "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", + "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", + "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7", + "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", + "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175", + "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", + "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392", + "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad", + "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f", + "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f", + "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", + "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", + "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", + "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800", + "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", + "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", + "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812", + "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50", + "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e" ], - "markers": "python_version >= '3.8'", - "version": "==24.8.0" + "markers": "python_version >= '3.9'", + "version": "==24.10.0" }, "certifi": { "hashes": [ - "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", - "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90" + "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" ], "markers": "python_version >= '3.6'", - "version": "==2024.7.4" + "version": "==2024.8.30" }, "charset-normalizer": { "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", + "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", + "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", + "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", + "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", + "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", + "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", + "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", + "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", + "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", + "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", + "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", + "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", + "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", + "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", + "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", + "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", + "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", + "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", + "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", + "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", + "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", + "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", + "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", + "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", + "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", + "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", + "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", + "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", + "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", + "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", + "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", + "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", + "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", + "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", + "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", + "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", + "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", + "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", + "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", + "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", + "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", + "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", + "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", + "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", + "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", + "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", + "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", + "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", + "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", + "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", + "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", + "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", + "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", + "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", + "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", + "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", + "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", + "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", + "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", + "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", + "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", + "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", + "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", + "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", + "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", + "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", + "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", + "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", + "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", + "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", + "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", + "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", + "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", + "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", + "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", + "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", + "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", + "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", + "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", + "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", + "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", + "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", + "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", + "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", + "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", + "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", + "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", + "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", + "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", + "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", + "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", + "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", + "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", + "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", + "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", + "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", + "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", + "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", + "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", + "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", + "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", + "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", + "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", + "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" + "version": "==3.4.0" }, "click": { "hashes": [ @@ -598,86 +689,84 @@ "markers": "python_version >= '3.7'", "version": "==8.1.7" }, + "colorama": { + "hashes": [ + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==0.4.6" + }, "coverage": { "extras": [ "toml" ], "hashes": [ - "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca", - "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", - "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", - "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989", - "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c", - "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", - "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", - "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", - "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", - "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", - "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", - "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb", - "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", - "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0", - "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", - "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8", - "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", - "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", - "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", - "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8", - "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", - "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc", - "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2", - "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", - "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", - "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0", - "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c", - "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", - "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004", - "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", - "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232", - "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", - "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", - "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", - "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de", - "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6", - "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23", - "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569", - "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", - "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", - "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", - "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36", - "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a", - "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6", - "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34", - "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", - "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", - "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", - "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", - "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b", - "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255", - "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16", - "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3", - "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", - "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", - "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", - "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", - "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", - "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36", - "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", - "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", - "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", - "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7", - "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", - "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02", - "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c", - "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df", - "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", - "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a", - "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959", - "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", - "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc" + "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376", + "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", + "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111", + "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", + "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", + "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", + "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", + "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", + "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", + "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c", + "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", + "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", + "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", + "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0", + "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", + "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", + "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", + "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", + "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", + "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", + "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", + "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", + "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", + "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", + "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", + "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", + "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", + "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", + "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", + "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901", + "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", + "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", + "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", + "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", + "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", + "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", + "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", + "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", + "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3", + "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", + "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076", + "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", + "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", + "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", + "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", + "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", + "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", + "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09", + "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", + "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", + "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f", + "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", + "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", + "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", + "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", + "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", + "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", + "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", + "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", + "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", + "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", + "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858" ], - "markers": "python_version >= '3.8'", - "version": "==7.6.1" + "markers": "python_version >= '3.9'", + "version": "==7.6.4" }, "coveralls": { "hashes": [ @@ -690,11 +779,11 @@ }, "dill": { "hashes": [ - "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", - "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7" + "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", + "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c" ], "markers": "python_version >= '3.11'", - "version": "==0.3.8" + "version": "==0.3.9" }, "docopt": { "hashes": [ @@ -702,6 +791,14 @@ ], "version": "==0.6.2" }, + "executing": { + "hashes": [ + "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf", + "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab" + ], + "markers": "python_version >= '3.8'", + "version": "==2.1.0" + }, "flake8": { "hashes": [ "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38", @@ -713,94 +810,117 @@ }, "frozenlist": { "hashes": [ - "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", - "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98", - "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad", - "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5", - "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae", - "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e", - "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a", - "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701", - "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d", - "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6", - "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6", - "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106", - "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75", - "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868", - "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a", - "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0", - "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1", - "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826", - "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec", - "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6", - "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950", - "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19", - "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0", - "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8", - "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a", - "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09", - "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86", - "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c", - "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5", - "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b", - "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b", - "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d", - "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0", - "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea", - "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776", - "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a", - "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897", - "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7", - "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09", - "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9", - "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe", - "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd", - "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742", - "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09", - "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", - "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932", - "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1", - "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a", - "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", - "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d", - "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7", - "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480", - "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89", - "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e", - "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", - "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82", - "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb", - "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068", - "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8", - "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b", - "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb", - "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2", - "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11", - "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b", - "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc", - "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0", - "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497", - "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17", - "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0", - "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2", - "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439", - "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5", - "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac", - "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825", - "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887", - "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", - "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74" + "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", + "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf", + "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6", + "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a", + "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d", + "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f", + "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28", + "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b", + "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9", + "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2", + "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec", + "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2", + "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c", + "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336", + "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4", + "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d", + "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b", + "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c", + "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10", + "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08", + "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942", + "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", + "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f", + "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10", + "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5", + "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", + "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", + "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c", + "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d", + "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923", + "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608", + "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de", + "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17", + "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0", + "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", + "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641", + "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c", + "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a", + "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0", + "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", + "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab", + "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f", + "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3", + "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", + "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", + "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604", + "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", + "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5", + "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03", + "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", + "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953", + "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", + "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d", + "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", + "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3", + "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", + "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", + "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9", + "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf", + "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76", + "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba", + "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171", + "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb", + "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439", + "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", + "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972", + "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d", + "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869", + "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9", + "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411", + "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723", + "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2", + "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b", + "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99", + "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e", + "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840", + "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3", + "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb", + "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", + "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0", + "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca", + "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45", + "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e", + "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f", + "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5", + "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307", + "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e", + "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2", + "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778", + "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a", + "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30", + "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a" ], "markers": "python_version >= '3.8'", - "version": "==1.4.1" + "version": "==1.5.0" + }, + "icecream": { + "hashes": [ + "sha256:0aa4a7c3374ec36153a1d08f81e3080e83d8ac1eefd97d2f4fe9544e8f9b49de", + "sha256:757aec31ad4488b949bc4f499d18e6e5973c40cc4d4fc607229e78cfaec94c34" + ], + "index": "pypi", + "version": "==2.1.3" }, "idna": { "hashes": [ - "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", - "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" ], - "markers": "python_version >= '3.5'", - "version": "==3.7" + "markers": "python_version >= '3.6'", + "version": "==3.10" }, "iniconfig": { "hashes": [ @@ -828,133 +948,140 @@ }, "multidict": { "hashes": [ - "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556", - "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c", - "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29", - "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b", - "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8", - "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7", - "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd", - "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40", - "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6", - "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3", - "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c", - "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9", - "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5", - "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae", - "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442", - "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9", - "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc", - "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c", - "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea", - "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5", - "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50", - "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182", - "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453", - "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e", - "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600", - "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733", - "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda", - "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241", - "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461", - "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e", - "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e", - "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b", - "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e", - "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7", - "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386", - "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd", - "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9", - "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf", - "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee", - "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5", - "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a", - "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271", - "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54", - "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4", - "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496", - "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb", - "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319", - "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3", - "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f", - "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527", - "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed", - "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604", - "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef", - "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8", - "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5", - "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5", - "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626", - "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c", - "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d", - "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c", - "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc", - "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc", - "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b", - "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38", - "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450", - "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1", - "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f", - "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3", - "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755", - "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226", - "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a", - "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046", - "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf", - "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479", - "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e", - "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1", - "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a", - "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83", - "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929", - "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93", - "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a", - "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c", - "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44", - "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89", - "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba", - "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e", - "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da", - "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24", - "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423", - "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef" + "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", + "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056", + "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", + "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", + "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b", + "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", + "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748", + "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", + "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f", + "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", + "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6", + "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada", + "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", + "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2", + "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d", + "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", + "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef", + "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", + "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb", + "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60", + "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6", + "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", + "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478", + "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", + "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7", + "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56", + "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", + "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6", + "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30", + "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb", + "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", + "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0", + "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", + "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c", + "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", + "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", + "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", + "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", + "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", + "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2", + "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", + "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3", + "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", + "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", + "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657", + "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581", + "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492", + "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43", + "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", + "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", + "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926", + "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057", + "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc", + "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80", + "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255", + "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1", + "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972", + "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53", + "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1", + "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423", + "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a", + "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160", + "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c", + "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd", + "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", + "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5", + "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", + "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", + "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", + "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", + "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4", + "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156", + "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753", + "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28", + "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d", + "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a", + "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304", + "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", + "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429", + "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72", + "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399", + "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3", + "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392", + "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167", + "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c", + "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", + "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351", + "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76", + "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875", + "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd", + "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", + "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db" ], - "markers": "python_version >= '3.7'", - "version": "==6.0.5" + "markers": "python_version >= '3.8'", + "version": "==6.1.0" }, "mypy": { "hashes": [ - "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54", - "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a", - "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72", - "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69", - "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b", - "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe", - "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4", - "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd", - "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0", - "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525", - "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2", - "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c", - "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5", - "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de", - "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74", - "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c", - "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e", - "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58", - "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b", - "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417", - "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411", - "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb", - "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03", - "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca", - "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8", - "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08", - "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809" + "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc", + "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", + "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f", + "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74", + "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a", + "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", + "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", + "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73", + "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", + "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", + "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d", + "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6", + "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", + "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", + "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", + "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", + "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a", + "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc", + "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7", + "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb", + "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", + "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732", + "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80", + "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", + "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", + "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", + "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", + "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24", + "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", + "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b", + "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372", + "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.11.1" + "version": "==1.13.0" }, "mypy-extensions": { "hashes": [ @@ -982,11 +1109,11 @@ }, "platformdirs": { "hashes": [ - "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", - "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3" + "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", + "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" ], "markers": "python_version >= '3.8'", - "version": "==4.2.2" + "version": "==4.3.6" }, "pluggy": { "hashes": [ @@ -996,6 +1123,110 @@ "markers": "python_version >= '3.8'", "version": "==1.5.0" }, + "propcache": { + "hashes": [ + "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9", + "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763", + "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325", + "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb", + "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b", + "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09", + "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957", + "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68", + "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f", + "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798", + "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418", + "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6", + "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162", + "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f", + "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036", + "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8", + "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2", + "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110", + "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23", + "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8", + "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638", + "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a", + "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44", + "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2", + "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2", + "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850", + "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136", + "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b", + "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887", + "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89", + "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87", + "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348", + "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4", + "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861", + "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e", + "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c", + "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b", + "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb", + "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1", + "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de", + "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354", + "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563", + "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5", + "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf", + "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9", + "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12", + "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4", + "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5", + "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71", + "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9", + "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed", + "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336", + "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90", + "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063", + "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad", + "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6", + "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8", + "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e", + "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2", + "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7", + "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d", + "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d", + "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df", + "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b", + "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178", + "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2", + "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630", + "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48", + "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61", + "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89", + "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb", + "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3", + "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6", + "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562", + "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b", + "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58", + "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db", + "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99", + "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37", + "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83", + "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a", + "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d", + "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04", + "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70", + "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544", + "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394", + "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea", + "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7", + "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1", + "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793", + "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577", + "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7", + "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57", + "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d", + "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032", + "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d", + "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016", + "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504" + ], + "markers": "python_version >= '3.8'", + "version": "==0.2.0" + }, "pycodestyle": { "hashes": [ "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3", @@ -1012,23 +1243,31 @@ "markers": "python_version >= '3.8'", "version": "==3.2.0" }, + "pygments": { + "hashes": [ + "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", + "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a" + ], + "markers": "python_version >= '3.8'", + "version": "==2.18.0" + }, "pylint": { "hashes": [ - "sha256:03c8e3baa1d9fb995b12c1dbe00aa6c4bcef210c2a2634374aedeb22fb4a8f8f", - "sha256:a5d01678349454806cff6d886fb072294f56a58c4761278c97fb557d708e1eb3" + "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9", + "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e" ], "index": "pypi", - "markers": "python_full_version >= '3.8.0'", - "version": "==3.2.6" + "markers": "python_full_version >= '3.9.0'", + "version": "==3.3.1" }, "pytest": { "hashes": [ - "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5", - "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce" + "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", + "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==8.3.2" + "version": "==8.3.3" }, "pytest-cov": { "hashes": [ @@ -1048,6 +1287,14 @@ "markers": "python_version >= '3.8'", "version": "==2.32.3" }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, "tomlkit": { "hashes": [ "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", @@ -1058,140 +1305,125 @@ }, "types-pyyaml": { "hashes": [ - "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af", - "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35" + "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570", + "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==6.0.12.20240808" + "version": "==6.0.12.20240917" }, "types-requests": { "hashes": [ - "sha256:90c079ff05e549f6bf50e02e910210b98b8ff1ebdd18e19c873cd237737c1358", - "sha256:f754283e152c752e46e70942fa2a146b5bc70393522257bb85bd1ef7e019dcc3" + "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95", + "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.32.0.20240712" + "version": "==2.32.0.20241016" }, "typing-extensions": { "hashes": [ "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], - "markers": "python_version >= '3.8'", + "markers": "python_version < '3.13'", "version": "==4.12.2" }, "urllib3": { "hashes": [ - "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", - "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168" + "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", + "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" ], "markers": "python_version >= '3.8'", - "version": "==2.2.2" - }, - "var-dump": { - "hashes": [ - "sha256:9031944c174f8ce971b71a047fc56a81dfa1ce05d354fff3a16d781391d020ae" - ], - "index": "pypi", - "version": "==1.2" + "version": "==2.2.3" }, "yarl": { "hashes": [ - "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", - "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", - "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559", - "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", - "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", - "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", - "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4", - "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c", - "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130", - "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136", - "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e", - "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec", - "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7", - "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1", - "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455", - "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", - "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", - "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", - "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", - "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", - "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa", - "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", - "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", - "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", - "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", - "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c", - "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", - "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b", - "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", - "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23", - "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd", - "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27", - "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f", - "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece", - "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434", - "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec", - "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", - "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78", - "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d", - "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863", - "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53", - "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", - "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15", - "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5", - "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b", - "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57", - "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3", - "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1", - "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f", - "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", - "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c", - "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", - "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", - "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b", - "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2", - "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b", - "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", - "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be", - "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e", - "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", - "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", - "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", - "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2", - "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392", - "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91", - "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541", - "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf", - "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", - "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66", - "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575", - "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14", - "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5", - "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", - "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e", - "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551", - "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17", - "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead", - "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", - "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", - "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234", - "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0", - "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7", - "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34", - "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", - "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385", - "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", - "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be", - "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", - "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749", - "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec" + "sha256:019f5d58093402aa8f6661e60fd82a28746ad6d156f6c5336a70a39bd7b162b9", + "sha256:0fd9c227990f609c165f56b46107d0bc34553fe0387818c42c02f77974402c36", + "sha256:1208ca14eed2fda324042adf8d6c0adf4a31522fa95e0929027cd487875f0240", + "sha256:122d8e7986043d0549e9eb23c7fd23be078be4b70c9eb42a20052b3d3149c6f2", + "sha256:147b0fcd0ee33b4b5f6edfea80452d80e419e51b9a3f7a96ce98eaee145c1581", + "sha256:178ccb856e265174a79f59721031060f885aca428983e75c06f78aa24b91d929", + "sha256:1a5cf32539373ff39d97723e39a9283a7277cbf1224f7aef0c56c9598b6486c3", + "sha256:1a5e9d8ce1185723419c487758d81ac2bde693711947032cce600ca7c9cda7d6", + "sha256:1bc22e00edeb068f71967ab99081e9406cd56dbed864fc3a8259442999d71552", + "sha256:1cf936ba67bc6c734f3aa1c01391da74ab7fc046a9f8bbfa230b8393b90cf472", + "sha256:234f3a3032b505b90e65b5bc6652c2329ea7ea8855d8de61e1642b74b4ee65d2", + "sha256:26768342f256e6e3c37533bf9433f5f15f3e59e3c14b2409098291b3efaceacb", + "sha256:27e11db3f1e6a51081a981509f75617b09810529de508a181319193d320bc5c7", + "sha256:2bd6a51010c7284d191b79d3b56e51a87d8e1c03b0902362945f15c3d50ed46b", + "sha256:2f1fe2b2e3ee418862f5ebc0c0083c97f6f6625781382f828f6d4e9b614eba9b", + "sha256:32468f41242d72b87ab793a86d92f885355bcf35b3355aa650bfa846a5c60058", + "sha256:35b4f7842154176523e0a63c9b871168c69b98065d05a4f637fce342a6a2693a", + "sha256:38fec8a2a94c58bd47c9a50a45d321ab2285ad133adefbbadf3012c054b7e656", + "sha256:3a91654adb7643cb21b46f04244c5a315a440dcad63213033826549fa2435f71", + "sha256:3ab3ed42c78275477ea8e917491365e9a9b69bb615cb46169020bd0aa5e2d6d3", + "sha256:3d375a19ba2bfe320b6d873f3fb165313b002cef8b7cc0a368ad8b8a57453837", + "sha256:4199db024b58a8abb2cfcedac7b1292c3ad421684571aeb622a02f242280e8d6", + "sha256:4f32c4cb7386b41936894685f6e093c8dfaf0960124d91fe0ec29fe439e201d0", + "sha256:4ffb7c129707dd76ced0a4a4128ff452cecf0b0e929f2668ea05a371d9e5c104", + "sha256:504e1fe1cc4f170195320eb033d2b0ccf5c6114ce5bf2f617535c01699479bca", + "sha256:542fa8e09a581bcdcbb30607c7224beff3fdfb598c798ccd28a8184ffc18b7eb", + "sha256:5570e6d47bcb03215baf4c9ad7bf7c013e56285d9d35013541f9ac2b372593e7", + "sha256:571f781ae8ac463ce30bacebfaef2c6581543776d5970b2372fbe31d7bf31a07", + "sha256:595ca5e943baed31d56b33b34736461a371c6ea0038d3baec399949dd628560b", + "sha256:5b8e265a0545637492a7e12fd7038370d66c9375a61d88c5567d0e044ded9202", + "sha256:5b9101f528ae0f8f65ac9d64dda2bb0627de8a50344b2f582779f32fda747c1d", + "sha256:5ff96da263740779b0893d02b718293cc03400c3a208fc8d8cd79d9b0993e532", + "sha256:621280719c4c5dad4c1391160a9b88925bb8b0ff6a7d5af3224643024871675f", + "sha256:62c7da0ad93a07da048b500514ca47b759459ec41924143e2ddb5d7e20fd3db5", + "sha256:649bddcedee692ee8a9b7b6e38582cb4062dc4253de9711568e5620d8707c2a3", + "sha256:66ea8311422a7ba1fc79b4c42c2baa10566469fe5a78500d4e7754d6e6db8724", + "sha256:676d96bafc8c2d0039cea0cd3fd44cee7aa88b8185551a2bb93354668e8315c2", + "sha256:707ae579ccb3262dfaef093e202b4c3fb23c3810e8df544b1111bd2401fd7b09", + "sha256:7118bdb5e3ed81acaa2095cba7ec02a0fe74b52a16ab9f9ac8e28e53ee299732", + "sha256:789a3423f28a5fff46fbd04e339863c169ece97c827b44de16e1a7a42bc915d2", + "sha256:7ace71c4b7a0c41f317ae24be62bb61e9d80838d38acb20e70697c625e71f120", + "sha256:7c7c30fb38c300fe8140df30a046a01769105e4cf4282567a29b5cdb635b66c4", + "sha256:7d7aaa8ff95d0840e289423e7dc35696c2b058d635f945bf05b5cd633146b027", + "sha256:7f8713717a09acbfee7c47bfc5777e685539fefdd34fa72faf504c8be2f3df4e", + "sha256:858728086914f3a407aa7979cab743bbda1fe2bdf39ffcd991469a370dd7414d", + "sha256:8791d66d81ee45866a7bb15a517b01a2bcf583a18ebf5d72a84e6064c417e64b", + "sha256:87dd10bc0618991c66cee0cc65fa74a45f4ecb13bceec3c62d78ad2e42b27a16", + "sha256:8994c42f4ca25df5380ddf59f315c518c81df6a68fed5bb0c159c6cb6b92f120", + "sha256:8a0296040e5cddf074c7f5af4a60f3fc42c0237440df7bcf5183be5f6c802ed5", + "sha256:8b37d5ec034e668b22cf0ce1074d6c21fd2a08b90d11b1b73139b750a8b0dd97", + "sha256:8c42998fd1cbeb53cd985bff0e4bc25fbe55fd6eb3a545a724c1012d69d5ec84", + "sha256:8f639e3f5795a6568aa4f7d2ac6057c757dcd187593679f035adbf12b892bb00", + "sha256:921b81b8d78f0e60242fb3db615ea3f368827a76af095d5a69f1c3366db3f596", + "sha256:995d0759004c08abd5d1b81300a91d18c8577c6389300bed1c7c11675105a44d", + "sha256:99a9dcd4b71dd5f5f949737ab3f356cfc058c709b4f49833aeffedc2652dac56", + "sha256:9a91217208306d82357c67daeef5162a41a28c8352dab7e16daa82e3718852a7", + "sha256:a5ace0177520bd4caa99295a9b6fb831d0e9a57d8e0501a22ffaa61b4c024283", + "sha256:a5b6c09b9b4253d6a208b0f4a2f9206e511ec68dce9198e0fbec4f160137aa67", + "sha256:a9394c65ae0ed95679717d391c862dece9afacd8fa311683fc8b4362ce8a410c", + "sha256:aa7943f04f36d6cafc0cf53ea89824ac2c37acbdb4b316a654176ab8ffd0f968", + "sha256:ab2b2ac232110a1fdb0d3ffcd087783edd3d4a6ced432a1bf75caf7b7be70916", + "sha256:ad7a852d1cd0b8d8b37fc9d7f8581152add917a98cfe2ea6e241878795f917ae", + "sha256:b140e532fe0266003c936d017c1ac301e72ee4a3fd51784574c05f53718a55d8", + "sha256:b439cae82034ade094526a8f692b9a2b5ee936452de5e4c5f0f6c48df23f8604", + "sha256:b6f687ced5510a9a2474bbae96a4352e5ace5fa34dc44a217b0537fec1db00b4", + "sha256:b9ca7b9147eb1365c8bab03c003baa1300599575effad765e0b07dd3501ea9af", + "sha256:bdcf667a5dec12a48f669e485d70c54189f0639c2157b538a4cffd24a853624f", + "sha256:cdcffe1dbcb4477d2b4202f63cd972d5baa155ff5a3d9e35801c46a415b7f71a", + "sha256:d1aab176dd55b59f77a63b27cffaca67d29987d91a5b615cbead41331e6b7428", + "sha256:d1b0796168b953bca6600c5f97f5ed407479889a36ad7d17183366260f29a6b9", + "sha256:d3f1cc3d3d4dc574bebc9b387f6875e228ace5748a7c24f49d8f01ac1bc6c31b", + "sha256:d743e3118b2640cef7768ea955378c3536482d95550222f908f392167fe62059", + "sha256:d8643975a0080f361639787415a038bfc32d29208a4bf6b783ab3075a20b1ef3", + "sha256:d9525f03269e64310416dbe6c68d3b23e5d34aaa8f47193a1c45ac568cecbc49", + "sha256:de6c14dd7c7c0badba48157474ea1f03ebee991530ba742d381b28d4f314d6f3", + "sha256:e49e0fd86c295e743fd5be69b8b0712f70a686bc79a16e5268386c2defacaade", + "sha256:e6980a558d8461230c457218bd6c92dfc1d10205548215c2c21d79dc8d0a96f3", + "sha256:e8be3aff14f0120ad049121322b107f8a759be76a6a62138322d4c8a337a9e2c", + "sha256:e9951afe6557c75a71045148890052cb942689ee4c9ec29f5436240e1fcc73b7", + "sha256:ed097b26f18a1f5ff05f661dc36528c5f6735ba4ce8c9645e83b064665131349", + "sha256:f1d1f45e3e8d37c804dca99ab3cf4ab3ed2e7a62cd82542924b14c0a4f46d243", + "sha256:fe8bba2545427418efc1929c5c42852bdb4143eb8d0a46b09de88d1fe99258e7" ], - "markers": "python_version >= '3.7'", - "version": "==1.9.4" + "markers": "python_version >= '3.9'", + "version": "==1.16.0" } } } diff --git a/chk/console/main.py b/chk/console/main.py index c1e8e41a..de778531 100644 --- a/chk/console/main.py +++ b/chk/console/main.py @@ -1,61 +1,25 @@ """Commands""" -import typing -from os import environ - import click -from dotenv import load_dotenv import chk.modules.fetch as fetch_executor import chk.modules.validate as validate_executor +import chk.modules.workflow as workflow_executor +from chk.console.services import ( + after_hook, + combine_initial_variables, + load_variables_as_dict, setup_logger, +) +from chk.infrastructure.file_loader import ExecuteContext, FileContext -from chk.infrastructure.file_loader import ExecuteContext, FileContext, FileLoader -from chk.infrastructure.typing_extras import JsonDecodingError - - -def load_variables_as_dict(json_str: str, **kwargs: typing.Any) -> dict: - """Reads a json string and converts and returns the dict while doing validation""" - - if json_str: - try: - return FileLoader.load_json_from_str(json_str) - except JsonDecodingError as err: - message = kwargs.get("except_msg") or "JSON loading error." - raise click.UsageError(str(message)) from err - - return {} - - -def combine_initial_variables(external_vars: str, **kwargs: typing.Any) -> dict: - """Reads a json string and converts to dict, and combines with env and dotenv - variables""" - - load_dotenv() - - return load_variables_as_dict(external_vars, **kwargs) | {"_ENV": dict(environ)} - - -def after_hook(resp: object) -> None: - """Saves custom data from commands to global context bus - - Args: - resp: object - """ - - if curr_ctx := click.get_current_context(): - if curr_ctx.parent: - if not curr_ctx.parent.obj: - curr_ctx.parent.obj = {} - - curr_ctx.parent.obj |= resp - else: - raise RuntimeError("Default context not found") +VAR_ERROR_MSG = "-V, --variables accept values as JSON object" # root command @click.group() +@click.option("--debug/--no-debug", default=True) @click.pass_context -def chk(ctx: click.Context) -> None: +def chk(ctx: click.Context, debug: bool) -> None: """\b █████████ █████ █████ █████ ████ ███░░░░░███░░███ ░░███ ░░███ ███░ @@ -71,6 +35,9 @@ def chk(ctx: click.Context) -> None: Version 0.5.0, supported version strings: 0.7.2 """ ctx.ensure_object(dict) + ctx.obj["debug"] = debug + + setup_logger(debug) # run fetch sub-command @@ -80,7 +47,8 @@ def chk(ctx: click.Context) -> None: "-nf", "--no-format", is_flag=True, help="No formatting to show the output" ) @click.option("-V", "--variables", type=str, help="Pass variable(s) as JSON object") -def fetch(file: str, no_format: bool, variables: str) -> None: +@click.pass_context +def fetch(cctx: click.Context, file: str, no_format: bool, variables: str) -> None: """\b Command to run Http config files. FILE: Any .chk file, that has any of the following versions: @@ -94,11 +62,12 @@ def fetch(file: str, no_format: bool, variables: str) -> None: { "dump": True, "format": not no_format, + "debug": cctx.obj.get("debug", False), }, { "variables": combine_initial_variables( variables, - except_msg="-V, --variables accept values as JSON object", + except_msg=VAR_ERROR_MSG, ) }, ) @@ -114,9 +83,12 @@ def fetch(file: str, no_format: bool, variables: str) -> None: ) @click.option("-V", "--variables", type=str, help="Pass variable(s) as JSON object") @click.option("-D", "--data", type=str, help="Pass data as JSON") -def validate(file: str, no_format: bool, variables: str, data: str) -> None: +@click.pass_context +def validate( + cctx: click.Context, file: str, no_format: bool, variables: str, data: str +) -> None: """\b - Command to run Http config files. + Command to run Validation specification files. FILE: Any .chk file, that has any of the following versions: \b @@ -128,11 +100,12 @@ def validate(file: str, no_format: bool, variables: str, data: str) -> None: { "dump": True, "format": not no_format, + "debug": cctx.obj.get("debug", False), }, { "variables": combine_initial_variables( variables, - except_msg="-V, --variables accept values as JSON object", + except_msg=VAR_ERROR_MSG, ), "data": load_variables_as_dict( data, @@ -142,3 +115,38 @@ def validate(file: str, no_format: bool, variables: str, data: str) -> None: ) validate_executor.execute(ctx, execution_ctx, after_hook) + + +# run validate sub-command +@chk.command() +@click.argument("file", type=click.Path(exists=True)) +@click.option( + "-nf", "--no-format", is_flag=True, help="No formatting to show the output" +) +@click.option("-V", "--variables", type=str, help="Pass variable(s) as JSON object") +@click.pass_context +def workflow(cctx: click.Context, file: str, no_format: bool, variables: str) -> None: + """\b + Command to run Workflow specification files. + FILE: Any .chk file, that has any of the following versions: + + \b + - default.http.*""" + + ctx: FileContext = FileContext.from_file(file) + + execution_ctx = ExecuteContext( + { + "dump": True, + "format": not no_format, + "debug": cctx.obj.get("debug", False), + }, + { + "variables": combine_initial_variables( + variables, + except_msg=VAR_ERROR_MSG, + ), + }, + ) + + workflow_executor.execute(ctx, execution_ctx, after_hook) diff --git a/chk/console/services.py b/chk/console/services.py new file mode 100644 index 00000000..a6d069ab --- /dev/null +++ b/chk/console/services.py @@ -0,0 +1,45 @@ +""" +Console service module +""" + +from typing import Any + +import click + +from chk.infrastructure.file_loader import FileLoader +from chk.infrastructure.logging import LoggingManager +from chk.infrastructure.typing_extras import JsonDecodingError + + +def load_variables_as_dict(json_str: str, **kwargs: Any) -> dict: + """Reads a json string and converts and returns the dict while doing validation""" + + if json_str: + try: + return FileLoader.load_json_from_str(json_str) + except JsonDecodingError as err: + message = kwargs.get("except_msg") or "JSON loading error." + raise click.UsageError(str(message)) from err + + return {} + + +def combine_initial_variables(external_vars: str, **kwargs: Any) -> dict: + """Reads a json string and converts to dict, and combines with env and dotenv + variables""" + + return load_variables_as_dict(external_vars, **kwargs) + + +def after_hook(*args: list, **kwargs: dict) -> Any: + """Run any function after implementation. Default pass""" + + +def setup_logger(should_log: bool) -> None: + """setup_logger""" + + LoggingManager.remove_loguru() + + if should_log: + log_file = LoggingManager.create_new_log_file() + LoggingManager.setup_loguru(log_file) diff --git a/chk/infrastructure/document.py b/chk/infrastructure/document.py index 672ee153..2f592564 100644 --- a/chk/infrastructure/document.py +++ b/chk/infrastructure/document.py @@ -1,9 +1,11 @@ """ Base document and utility """ + import dataclasses import cerberus +from pydantic import BaseModel, Field from chk.infrastructure.file_loader import FileContext @@ -18,11 +20,22 @@ class VersionedDocument: version: str = dataclasses.field(default_factory=str) +class VersionedDocumentV2(BaseModel): + """ + versioned document entity + """ + + context: tuple = Field(default_factory=tuple) + version: str = Field(default_factory=str) + + class VersionedDocumentSupport: """DocumentVersionSupport""" @staticmethod - def validate_with_schema(schema: dict, doc: VersionedDocument) -> bool: + def validate_with_schema( + schema: dict, doc: VersionedDocument | VersionedDocumentV2 + ) -> bool: """Validate a document with given schema Args: diff --git a/chk/infrastructure/file_loader.py b/chk/infrastructure/file_loader.py index e1189a58..f916e4e8 100644 --- a/chk/infrastructure/file_loader.py +++ b/chk/infrastructure/file_loader.py @@ -1,11 +1,14 @@ """ File loader utility """ + +from __future__ import annotations + import hashlib -from typing import NamedTuple +import json from pathlib import Path +from typing import NamedTuple -import json import yaml from chk.infrastructure.typing_extras import JsonDecodingError @@ -25,7 +28,7 @@ def is_file_ok(file_name: str, allowed_list: list | None = None) -> bool: """ if not allowed_list: - allowed_list = [".chk"] + allowed_list = [".chk", ".yaml", ".yml"] if not Path(file_name).is_file(): raise FileNotFoundError("File not found") @@ -81,7 +84,7 @@ class FileContext(NamedTuple): filepath_hash: str = "" @staticmethod - def from_file(file: str, **kwarg: dict) -> "FileContext": + def from_file(file: str, **kwarg: dict) -> FileContext: FileLoader.is_file_ok(file) absolute_path = str(Path(file).absolute()) fpath_hash = hashlib.sha256(absolute_path.encode("utf-8")).hexdigest() @@ -95,31 +98,48 @@ def from_file(file: str, **kwarg: dict) -> "FileContext": arguments=kwarg["arguments"] if "arguments" in kwarg else {}, ) + @property + def filepath_as_path(self) -> Path: + """Get filepath as Path""" -class PathFrom: - """Utility to expand to full path""" + return Path(self.filepath) - def __init__(self, base: Path): - self.base = base.absolute().parent + @property + def filepath_base_as_path(self) -> Path: + """Get filepath parent or base as Path""" - def absolute(self, target: str) -> str: - """Find absolute in comparison to base URL""" + return Path(self.filepath).absolute().parent - if target.startswith("./") or target.startswith("../"): - if self.base.exists(): - to_path = self.base - target_path_sp = target.split("/") - for part in target_path_sp: - if part == "..": - to_path = to_path.parent - else: - to_path = Path(str(to_path) + "/" + part) +def generate_abs_path(base_: str, target_: str) -> str: + """Generate absolute path in comparison to base path + Args: + base_: str, base path to calculate from + target_: str, file path that need absolute path - return str(to_path) - raise ValueError("Invalid base path.") + Returns: + Absolute path for given filepath + """ + + base = Path(base_) + base_abs = base.absolute().parent if base.is_file() else base.absolute() + + if not base_abs.exists(): + raise ValueError("Invalid base path.") + if not (target_.startswith("./") or target_.startswith("../")): raise ValueError("Invalid target path.") + to_path = base_abs + target_path_sp = target_.split("/") + + for part in target_path_sp: + if part == "..": + to_path = to_path.parent + else: + to_path = to_path / part + + return str(to_path) + class ExecuteContext(NamedTuple): """Information storage for execution context""" diff --git a/chk/infrastructure/helper.py b/chk/infrastructure/helper.py index d2cfd6ec..d2475ad8 100644 --- a/chk/infrastructure/helper.py +++ b/chk/infrastructure/helper.py @@ -1,7 +1,9 @@ """ Helper functions module """ + import ast +import re from collections.abc import Callable from typing import Any @@ -157,6 +159,16 @@ def to_auto(var: str) -> Any: return var + @staticmethod + def try_dict(to_dict: Any, say_exception: bool = False) -> dict | Any: + try: + return dict(to_dict) + except ValueError as ex: + if say_exception: + raise ex + else: + return to_dict + def formatter(message: object, cb: Callable = str, dump: bool = True) -> str: """Format message with given callback @@ -175,3 +187,16 @@ def formatter(message: object, cb: Callable = str, dump: bool = True) -> str: click.echo(printable) return printable + + +def slugify(string: str) -> str: + """Make slug out of string""" + + if not isinstance(string, str): + raise TypeError("slugify: only string value supported.") + + string = string.lower().strip() + string = re.sub(r"[^\w\s-]", "", string) + string = re.sub(r"[\s_-]+", "-", string) + string = re.sub(r"-{2,}", "", string) + return string diff --git a/chk/infrastructure/logging.py b/chk/infrastructure/logging.py new file mode 100644 index 00000000..c70e073e --- /dev/null +++ b/chk/infrastructure/logging.py @@ -0,0 +1,90 @@ +""" +Logging Module +""" + +import os +import time +from pathlib import Path +from uuid import uuid4 + +from loguru import logger + +debug = logger.debug +error = logger.error +critical = logger.critical +success = logger.success + +with_catch_log = logger.catch + + +class LoggingManager: + """LoggingManager""" + + cache_dir = '.chkware_cache/' + loggin_dir = 'logs/' + loggin_ext = '.log' + + + @classmethod + def create_cache_dir(cls) -> Path: + """create_cache_dir""" + + # get cache dir name from env + try: + cache_dir = os.environ["CHK_CACHE_PATH"] + except KeyError: + cache_dir = cls.cache_dir + + # populate cache dir + cache_path = Path.cwd() / cache_dir + if not cache_path.exists(): + cache_path.mkdir() + + return cache_path + + + @classmethod + def create_log_dir(cls, parent: Path) -> Path: + """create_cache_dir""" + + if not parent.exists(): + raise NotADirectoryError("Parent directory do not exists.") + log_dir = parent / cls.loggin_dir + + if not log_dir.exists(): + log_dir.mkdir() + + return log_dir + + + @classmethod + def create_new_log_file(cls) -> Path: + """create new log file""" + + cache_dir =cls.create_cache_dir() + log_dir = cls.create_log_dir(cache_dir) + + log_file = log_dir / create_session_id() + log_file = log_file.with_suffix(cls.loggin_ext) + + log_file.touch() + return log_file + + @classmethod + def setup_loguru(cls, log_path: Path | None) -> None: + """setup_loguru""" + + logger.add(str(log_path), backtrace=True, enqueue=True, catch=True) + + @classmethod + def remove_loguru(cls) -> None: + """setup_loguru""" + + logger.remove() + + + +def create_session_id() -> str: + """create_session_id""" + + return f"{int(time.time())}-{uuid4().hex}" diff --git a/chk/infrastructure/symbol_table.py b/chk/infrastructure/symbol_table.py index 40bd999a..aa783a3b 100644 --- a/chk/infrastructure/symbol_table.py +++ b/chk/infrastructure/symbol_table.py @@ -1,12 +1,19 @@ """ Symbol and variable management """ + +import copy import enum +import os +import typing from collections import UserDict from collections.abc import Callable -from chk.infrastructure.document import VersionedDocument -from chk.infrastructure.file_loader import FileContext, ExecuteContext +from dotenv import load_dotenv +from pydantic import BaseModel, ConfigDict, Field + +from chk.infrastructure.document import VersionedDocument, VersionedDocumentV2 +from chk.infrastructure.file_loader import ExecuteContext, FileContext from chk.infrastructure.helper import data_get from chk.infrastructure.templating import StrTemplate @@ -55,8 +62,27 @@ class Variables(UserDict): """Holds data for a variable""" -class ExposableVariables(UserDict): - """Holds data for a expose data""" +class ExecResponse(BaseModel): + """ExecResponse""" + + model_config = ConfigDict(arbitrary_types_allowed=True) + + file_ctx: FileContext + exec_ctx: ExecuteContext + variables: Variables + variables_exec: Variables + extra: object = Field(default=None) + exception: Exception | None = Field(default=None) + exposed: dict = Field(default_factory=dict) + report: dict = Field(default_factory=dict) + + +@typing.overload +def replace_value(doc: dict, var_s: dict) -> dict: ... + + +@typing.overload +def replace_value(doc: list, var_s: dict) -> list: ... def replace_value(doc: dict | list, var_s: dict) -> dict | list: @@ -79,9 +105,12 @@ def replace_value(doc: dict | list, var_s: dict) -> dict | list: class VariableTableManager: """VariableTableManager""" - @staticmethod + @classmethod def handle( - variable_doc: Variables, document: VersionedDocument, exec_ctx: ExecuteContext + cls, + variable_doc: Variables, + document: VersionedDocument | VersionedDocumentV2, + exec_ctx: ExecuteContext, ) -> None: """Handles variable handling @@ -90,20 +119,22 @@ def handle( document: VersionedDocument of document data exec_ctx: ExecuteContext; passed ExecuteContext """ + # load environment variables + cls.handle_environment(variable_doc) - # make file contexts out od tuple + # make file contexts out of tuple file_ctx = FileContext(*document.context) if variables := data_get(file_ctx.document, VariableConfigNode.VARIABLES): - VariableTableManager.handle_absolute(variable_doc, variables) + cls.handle_absolute(variable_doc, variables) - VariableTableManager.handle_execute_context(variable_doc, exec_ctx) + cls.handle_execute_context(variable_doc, exec_ctx) if variables: - VariableTableManager.handle_composite(variable_doc, variables) + cls.handle_composite(variable_doc, variables) - @staticmethod - def handle_absolute(variable_doc: Variables, document: dict) -> None: + @classmethod + def handle_absolute(cls, variable_doc: Variables, document: dict) -> None: """Handles absolute variables and values from document Args: @@ -117,8 +148,9 @@ def handle_absolute(variable_doc: Variables, document: dict) -> None: variable_doc[key] = val - @staticmethod + @classmethod def handle_composite( + cls, variable_doc: Variables, document: dict, replace_callback: Callable = replace_value, @@ -143,9 +175,9 @@ def handle_composite( for key, val in replaced_values.items(): variable_doc[key] = val - @staticmethod + @classmethod def handle_execute_context( - variable_doc: Variables, exec_ctx: ExecuteContext + cls, variable_doc: Variables, exec_ctx: ExecuteContext ) -> None: """Handle variables passed from external context @@ -159,6 +191,16 @@ def handle_execute_context( for key, val in ext_vars.items(): variable_doc[key] = val + @classmethod + def handle_environment(cls, variable_doc: Variables) -> None: + """Handle variables passed from external context + + Args: + variable_doc: Variables; Variable store + """ + load_dotenv() + variable_doc["_ENV"] = dict(os.environ) + class ExposeManager: """ExposeManager handles all expose related functionality""" @@ -196,8 +238,9 @@ def replace_values( return replace_callback(expose_doc, values) @staticmethod - def get_exposed_replaced_data(document: VersionedDocument, store: dict) -> list: - """Get expose doc from a `VersionedDocument`, and prepare it from the + def get_exposed_replaced_data(document: VersionedDocumentV2, store: dict) -> dict: + """ + Get expose doc from a `VersionedDocument`, and prepare it from the value of `Variables`, and `store`, and return Args: @@ -211,6 +254,14 @@ def get_exposed_replaced_data(document: VersionedDocument, store: dict) -> list: file_ctx = FileContext(*document.context) if expose_doc := ExposeManager.get_expose_doc(file_ctx.document): - return ExposeManager.replace_values(expose_doc, store) - return [] + exposed_doc_t = copy.copy(expose_doc) + exposed_doc_t = [ + str(key).replace("%>", "").replace("<%", "").strip() + for key in exposed_doc_t + ] + + expose_val = ExposeManager.replace_values(expose_doc, store) + return dict(zip(exposed_doc_t, expose_val)) + + return {} diff --git a/chk/infrastructure/third_party/http_fetcher.py b/chk/infrastructure/third_party/http_fetcher.py deleted file mode 100644 index 629d15f5..00000000 --- a/chk/infrastructure/third_party/http_fetcher.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -Driver to handle requests library (https://pypi.org/project/requests/) -""" - -from collections import UserDict - -import requests - - -class ApiResponse(UserDict): - """Represent a response""" - - __slots__ = ("code", "info", "headers", "body") - - code: int - info: str - headers: dict - body: str - - @property - def as_fmt_str(self) -> str: - """String representation of ApiResponse - - Returns: - str: String representation - """ - # set info - presentation = f"{self['info']}\r\n\r\n" - - # set headers - presentation += "\r\n".join(f"{k}: {v}" for k, v in self["headers"].items()) - presentation += "\r\n\r\n" - - # set body - presentation += self["body"] - - return presentation - - @staticmethod - def from_response(response: requests.Response) -> "ApiResponse": - """Create a ApiResponse object from requests.Response object - - Args: - response (requests.Response): _description_ - - Returns: - ApiResponse: _description_ - """ - version = "HTTP/1.0" if response.raw.version == 10 else "HTTP/1.1" - - return ApiResponse( - code=response.status_code, - info=f"{version} {response.status_code} {response.reason}", - headers=dict(response.headers), - body=response.text, - ) - - -class BearerAuthentication(requests.auth.AuthBase): - """Authentication: Bearer ... support""" - - def __init__(self, token: str) -> None: - """Construct BearerAuthentication""" - - self.token = token - - def __call__(self, r: requests.PreparedRequest) -> requests.PreparedRequest: - """Add the actual header on call""" - - r.headers["authorization"] = "Bearer " + self.token - return r - - -def fetch(params: dict) -> ApiResponse: - """Fetch an external resource - - Args: - params (dict): all supported params from requests.request - - Returns: - ApiResponse: a new ApiResponse - """ - - try: - return ApiResponse.from_response(requests.request(**params)) - except requests.ConnectTimeout as err: - raise RuntimeError("Connection time out") from err - except requests.ConnectionError as err: - raise RuntimeError("Connection error") from err - except requests.ReadTimeout as err: - raise RuntimeError("Read time out") from err - except requests.TooManyRedirects as err: - raise RuntimeError("Too many redirects") from err - except requests.RequestException as err: - raise RuntimeError("Request error") from err diff --git a/chk/infrastructure/version.py b/chk/infrastructure/version.py index 449ad70f..c8d52f03 100644 --- a/chk/infrastructure/version.py +++ b/chk/infrastructure/version.py @@ -14,12 +14,18 @@ VERSION_STORE = { "http": [ "0.7.2", + "0.8.0", ], "testcase": [ "0.7.2", + "0.8.0", ], "validation": [ "0.7.2", + "0.8.0", + ], + "workflow": [ + "0.8.0", ], } @@ -108,6 +114,11 @@ def verify_doc_type_ver(self) -> bool: return True + def __str__(self) -> str: + """str dunder""" + + return self.original_version_str + class DocumentVersionMaker: """Create document version object""" diff --git a/chk/infrastructure/view.py b/chk/infrastructure/view.py new file mode 100644 index 00000000..6a59358f --- /dev/null +++ b/chk/infrastructure/view.py @@ -0,0 +1,63 @@ +"""View related utility mod""" + +import abc +import typing + +from pydantic import BaseModel + +from chk.infrastructure.file_loader import ExecuteContext +from chk.infrastructure.helper import formatter +from chk.infrastructure.symbol_table import ExecResponse + + +class PresentationBuilder(BaseModel, abc.ABC): + """Presentable signature""" + + data: typing.Any + + @abc.abstractmethod + def dump_fmt(self) -> str: + """Signature to dump formatted string""" + + raise NotImplementedError("Signature to dump formatted string") + + @abc.abstractmethod + def dump_json(self) -> str: + """Signature to dump json""" + + raise NotImplementedError("Signature to dump json") + + @abc.abstractmethod + def dump_error_fmt(self) -> str: + """Signature to dump formatted error string""" + + raise NotImplementedError("Signature to dump formatted error string") + + @abc.abstractmethod + def dump_error_json(self) -> str: + """Signature to dump error json""" + + raise NotImplementedError("Signature to dump error json") + + +class PresentationService: + """Comment service for presentation""" + + @classmethod + def display( + cls, + ex_resp: ExecResponse, + exec_ctx: ExecuteContext, + presenter: type[PresentationBuilder], + ) -> None: + wfp = presenter(data=ex_resp) + if exec_ctx.options["format"]: + if ex_resp.exception: + formatter(wfp.dump_error_fmt(), dump=exec_ctx.options["dump"]) + else: + formatter(wfp.dump_fmt(), dump=exec_ctx.options["dump"]) + else: + if ex_resp.exception: + formatter(wfp.dump_error_json(), dump=exec_ctx.options["dump"]) + else: + formatter(wfp.dump_json(), dump=exec_ctx.options["dump"]) diff --git a/chk/modules/fetch.py b/chk/modules/fetch.py index a5717c6f..f73214ce 100644 --- a/chk/modules/fetch.py +++ b/chk/modules/fetch.py @@ -1,42 +1,125 @@ """ Fetch module """ -import dataclasses + +from __future__ import annotations + import enum import json import pathlib - from collections import UserDict, abc from urllib.parse import unquote, urlparse -from requests.auth import HTTPBasicAuth - -from defusedxml.minidom import parseString +import requests import xmltodict +from defusedxml.minidom import parseString +from pydantic import BaseModel, Field +from requests.auth import HTTPBasicAuth -from chk.infrastructure.document import VersionedDocument, VersionedDocumentSupport +from chk.infrastructure.document import ( + VersionedDocumentSupport, + VersionedDocumentV2, +) from chk.infrastructure.file_loader import ExecuteContext, FileContext -from chk.infrastructure.helper import data_get, formatter +from chk.infrastructure.helper import data_get +from chk.infrastructure.logging import debug, error, with_catch_log from chk.infrastructure.symbol_table import ( + EXPOSE_SCHEMA as EXP_SCHEMA, + ExecResponse, + ExposeManager, + VARIABLE_SCHEMA as VAR_SCHEMA, VariableTableManager, Variables, replace_value, - VARIABLE_SCHEMA as VAR_SCHEMA, - EXPOSE_SCHEMA as EXP_SCHEMA, - ExposeManager, - ExposableVariables, -) - -from chk.infrastructure.third_party.http_fetcher import ( - ApiResponse, - fetch, - BearerAuthentication, ) from chk.infrastructure.version import DocumentVersionMaker, SCHEMA as VER_SCHEMA +from chk.infrastructure.view import PresentationBuilder, PresentationService VERSION_SCOPE = ["http"] +class ApiResponse(UserDict): + """Represent a response""" + + __slots__ = ("code", "info", "headers", "body") + + code: int + info: str + headers: dict + body: str + + @property + def as_fmt_str(self) -> str: + """String representation of ApiResponse + + Returns: + str: String representation + """ + # set info + presentation = f"{self['info']}\r\n\r\n" + + # set headers + presentation += "\r\n".join(f"{k}: {v}" for k, v in self["headers"].items()) + presentation += "\r\n\r\n" + + # set body + presentation += self["body"] if isinstance(self["body"], str) else json.dumps(self["body"]) + + return presentation + + @staticmethod + def from_response(response: requests.Response) -> ApiResponse: + """Create a ApiResponse object from requests.Response object + + Args: + response (requests.Response): _description_ + + Returns: + ApiResponse: _description_ + """ + version = "HTTP/1.0" if response.raw.version == 10 else "HTTP/1.1" + + return ApiResponse( + code=response.status_code, + info=f"{version} {response.status_code} {response.reason}", + headers=dict(response.headers), + body=response.text, + ) + + def as_dict(self) -> dict: + """as_dict""" + + _data = self.data + + _as_dict = { + "code": _data["code"], + "info": _data["info"], + "headers": _data["headers"], + } + + try: + _as_dict["body"] = json.loads(_data["body"]) + except ValueError: + _as_dict["body"] = _data["body"] + + return _as_dict + + +class BearerAuthentication(requests.auth.AuthBase): + """Authentication: Bearer ... support""" + + def __init__(self, token: str) -> None: + """Construct BearerAuthentication""" + + self.token = token + + def __call__(self, r: requests.PreparedRequest) -> requests.PreparedRequest: + """Add the actual header on call""" + + r.headers["authorization"] = "Bearer " + self.token + return r + + class HttpMethod(enum.StrEnum): """Constants of wellknown http methods""" @@ -201,6 +284,16 @@ def allowed_url(value: str) -> bool: return True +class FetchTask(BaseModel): + """Parsed FetchTask""" + + name: str + uses: str + file: str + variables: dict = Field(default_factory=dict) + arguments: dict = Field(default_factory=dict) + + class HttpRequestArgCompiler: """HttpRequestArgCompiler""" @@ -308,16 +401,15 @@ def add_generic_args(request_data: dict, request_arg: dict) -> None: HttpRequestArgCompiler.add_body(request_data, request_arg) -@dataclasses.dataclass(slots=True) -class HttpDocument(VersionedDocument): +class HttpDocument(VersionedDocumentV2, BaseModel): """ Http document entity """ - request: dict = dataclasses.field(default_factory=dict) + request: dict = Field(default_factory=dict) @staticmethod - def from_file_context(ctx: FileContext) -> "HttpDocument": + def from_file_context(ctx: FileContext) -> HttpDocument: """Create a HttpDocument from FileContext :param ctx: FileContext to create the HttpDocument from """ @@ -328,18 +420,14 @@ def from_file_context(ctx: FileContext) -> "HttpDocument": if not (request_dct := data_get(ctx.document, "request")): raise RuntimeError("`request:` not found.") + # @TODO keep `context`, `version` as object + # @TODO implement __repr__ for WorkflowDocument return HttpDocument( context=tuple(ctx), version=version_str, request=request_dct, ) - @property - def as_dict(self) -> dict: - """Return a dict of the data""" - - return dataclasses.asdict(self) - class ApiResponseDict(UserDict): """Represents a API response with body in dict representation""" @@ -348,7 +436,7 @@ class ApiResponseDict(UserDict): body_as_dict: dict @staticmethod - def from_api_response(resp: ApiResponse) -> "ApiResponseDict": + def from_api_response(resp: ApiResponse) -> ApiResponseDict: """Create JsonApiResponse from ApiResponse Args: @@ -423,7 +511,7 @@ def execute_request(http_doc: HttpDocument) -> ApiResponse: HttpRequestArgCompiler.add_generic_args(http_doc.request, request_args) - return fetch(request_args) + return ApiResponse.from_response(requests.request(**request_args)) @staticmethod def process_request_template(http_doc: HttpDocument, variables: Variables) -> None: @@ -446,83 +534,71 @@ def build_schema() -> dict: return {**VER_SCHEMA, **SCHEMA, **VAR_SCHEMA, **EXP_SCHEMA} - @staticmethod - def display(expose_list: list, exec_ctx: ExecuteContext) -> None: - """Displays the response based on the command response format - Args: - expose_list: list - exec_ctx: ExecuteContext - """ +class FetchPresenter(PresentationBuilder): + """FetchPresenter""" - if not expose_list: - return + def dump_error_json(self) -> str: + return json.dumps( + { + "error": ( + repr(self.data.exception) + if self.data.exception + else "Unspecified error" + ) + } + ) - display_item_list: list[object] = [] + def dump_error_fmt(self) -> str: + """dump fmt error str""" - for expose_item in expose_list: - if isinstance(expose_item, (dict, list)): - if {"code", "info", "headers", "body"}.issubset(expose_item): - resp = ApiResponse(expose_item) + return ( + f"Fetch error\n------\n{repr(self.data.exception)}" + if self.data.exception + else "Fetch error\n------\nUnspecified error" + ) - if exec_ctx.options["format"]: - display_item_list.append(resp.as_fmt_str) - else: - display_item_list.append( - ApiResponseDict.from_api_response(resp).as_dict - ) - else: - if exec_ctx.options["format"]: - display_item_list.append(json.dumps(expose_item)) - else: - display_item_list.append(expose_item) + def dump_json(self) -> str: + """dump json""" + + displayables: list[object] = [] + + for key, expose_item in self.data.exposed.items(): + if key == RequestConfigNode.LOCAL: + resp = ApiResponse(expose_item) + displayables.append(ApiResponseDict.from_api_response(resp).as_dict) else: - if exec_ctx.options["format"]: - display_item_list.append(str(expose_item)) - else: - display_item_list.append(expose_item) - - if exec_ctx.options["format"]: - formatter( - "\n---\n".join( - [ - item if isinstance(item, str) else str(item) - for item in display_item_list - ] - ) - if len(display_item_list) > 1 - else display_item_list.pop(), - dump=exec_ctx.options["dump"], - ) - else: - _to_display = ( - display_item_list - if len(display_item_list) > 1 - else display_item_list.pop() - ) + displayables.append(expose_item) - _to_display = ( - _to_display if isinstance(_to_display, str) else json.dumps(_to_display) - ) + return json.dumps(displayables) - formatter(_to_display, dump=exec_ctx.options["dump"]) + def dump_fmt(self) -> str: + """dump fmt string""" + displayables: list[str] = [] -def execute( - ctx: FileContext, exec_ctx: ExecuteContext, cb: abc.Callable = lambda *args: ... -) -> None: - """Run a http document + for key, expose_item in self.data.exposed.items(): + if key == RequestConfigNode.LOCAL: + resp = ApiResponse(expose_item) + displayables.append(resp.as_fmt_str) + else: + displayables.append(json.dumps(expose_item)) - Args: - ctx: FileContext object to handle - exec_ctx: ExecuteContext - cb: Callable - """ + return "\n======\n".join(displayables) - http_doc = HttpDocument.from_file_context(ctx) + +@with_catch_log +def call(file_ctx: FileContext, exec_ctx: ExecuteContext) -> ExecResponse: + """Call a http document""" + + debug(file_ctx) + debug(exec_ctx) + + http_doc = HttpDocument.from_file_context(file_ctx) + debug(http_doc.model_dump_json()) DocumentVersionMaker.verify_if_allowed( - DocumentVersionMaker.from_dict(http_doc.as_dict), VERSION_SCOPE + DocumentVersionMaker.from_dict(http_doc.model_dump()), VERSION_SCOPE ) VersionedDocumentSupport.validate_with_schema( @@ -531,14 +607,79 @@ def execute( variable_doc = Variables() VariableTableManager.handle(variable_doc, http_doc, exec_ctx) + debug(variable_doc.data) + HttpDocumentSupport.process_request_template(http_doc, variable_doc) + debug(http_doc.model_dump_json()) - response = HttpDocumentSupport.execute_request(http_doc) - output_data = ExposableVariables({"_response": response.data}) + r_exception: Exception | None = None + response = ApiResponse() + + try: + response = HttpDocumentSupport.execute_request(http_doc) + except Exception as ex: + r_exception = ex + error(ex) + + output_data = Variables({"_response": response.as_dict()}) + debug(output_data.data) exposed_data = ExposeManager.get_exposed_replaced_data( - http_doc, {**variable_doc.data, **output_data.data} + http_doc, + {**variable_doc.data, **output_data.data}, ) + debug(exposed_data) + + # TODO: instead if sending specific report items, and making presentable in other + # module, we should prepare and long and short form of presentable that can be + # loaded via other module + + return ExecResponse( + file_ctx=file_ctx, + exec_ctx=exec_ctx, + variables_exec=output_data, + variables=variable_doc, + exception=r_exception, + exposed=exposed_data, + report={ + "is_success": r_exception is None, + "request_method": file_ctx.document["request"]["method"], + "request_url": file_ctx.document["request"]["url"], + }, + ) + + +@with_catch_log +def execute( + ctx: FileContext, exec_ctx: ExecuteContext, cb: abc.Callable = lambda *args: ... +) -> None: + """Call with a http document - cb({ctx.filepath_hash: output_data.data}) - HttpDocumentSupport.display(exposed_data, exec_ctx) + Args: + ctx: FileContext object to handle + exec_ctx: ExecuteContext + cb: Callable + """ + + exr = call(file_ctx=ctx, exec_ctx=exec_ctx) + + cb({ctx.filepath_hash: exr.variables_exec.data}) + PresentationService.display(exr, exec_ctx, FetchPresenter) + + +@with_catch_log +def task_fetch(**kwargs: dict) -> ExecResponse: + """Task impl""" + + if not (doc := kwargs.get("task", {})): + raise ValueError("Wrong task format given.") + + _task = FetchTask(**doc) + + return call( + FileContext.from_file(_task.file), + ExecuteContext( + options={"dump": True, "format": True}, + arguments=_task.arguments | {"variables": _task.variables}, + ), + ) diff --git a/chk/modules/validate/__init__.py b/chk/modules/validate/__init__.py index d63206ed..b7232c56 100644 --- a/chk/modules/validate/__init__.py +++ b/chk/modules/validate/__init__.py @@ -2,38 +2,43 @@ Validate module """ -import dataclasses +from __future__ import annotations + import enum -import json -import operator from collections import abc import cerberus +from pydantic import BaseModel, Field -from chk.infrastructure.document import VersionedDocument, VersionedDocumentSupport -from chk.infrastructure.file_loader import FileContext, ExecuteContext -from chk.infrastructure.helper import data_get, formatter -from chk.infrastructure.version import DocumentVersionMaker, SCHEMA as VER_SCHEMA - +from chk.infrastructure.document import ( + VersionedDocumentSupport, + VersionedDocumentV2, +) +from chk.infrastructure.file_loader import ExecuteContext, FileContext +from chk.infrastructure.helper import data_get +from chk.infrastructure.logging import debug, error, with_catch_log from chk.infrastructure.symbol_table import ( - VARIABLE_SCHEMA as VAR_SCHEMA, EXPOSE_SCHEMA as EXP_SCHEMA, - Variables, + ExecResponse, + ExposeManager, + VARIABLE_SCHEMA as VAR_SCHEMA, VariableTableManager, + Variables, replace_value, - ExposeManager, - ExposableVariables, ) +from chk.infrastructure.version import DocumentVersionMaker, SCHEMA as VER_SCHEMA +from chk.infrastructure.view import PresentationService from chk.modules.validate.assertion_services import ( AssertionEntry, AssertionEntryListRunner, - AllTestRunResult, MAP_TYPE_TO_FN, ) from chk.modules.validate.assertion_validation import ( - get_schema_map, AssertionEntityProperty, + get_schema_map, ) +from chk.modules.validate.entities import RunReport, ValidationTask +from chk.modules.validate.services import ValidatePresenter VERSION_SCOPE = ["validation"] @@ -65,17 +70,16 @@ class ValidationConfigNode(enum.StrEnum): } -@dataclasses.dataclass(slots=True) -class ValidationDocument(VersionedDocument): +class ValidationDocument(VersionedDocumentV2, BaseModel): """ Http document entity """ - data: dict = dataclasses.field(default_factory=dict) - asserts: list = dataclasses.field(default_factory=list) + data: dict = Field(default_factory=dict) + asserts: list = Field(default_factory=list) @staticmethod - def from_file_context(ctx: FileContext) -> "ValidationDocument": + def from_file_context(ctx: FileContext) -> ValidationDocument: """Create a ValidationDocument from FileContext :param ctx: FileContext to create the ValidationDocument from """ @@ -88,6 +92,8 @@ def from_file_context(ctx: FileContext) -> "ValidationDocument": _data = data_get(ctx.document, ValidationConfigNode.DATA, {}) + # @TODO keep `context`, `version` as object + # @TODO implement __repr__ for WorkflowDocument return ValidationDocument( context=tuple(ctx), version=_version, @@ -95,12 +101,6 @@ def from_file_context(ctx: FileContext) -> "ValidationDocument": data=_data, ) - @property - def as_dict(self) -> dict: - """Return a dict of the data""" - - return dataclasses.asdict(self) - class ValidationDocumentSupport: """Service class for ValidationDocument""" @@ -130,19 +130,23 @@ def set_data_template( """sets data or template""" data = data_get(exec_ctx.arguments, "data", {}) - variables[ValidationConfigNode.VAR_NODE] = data if data else validate_doc.data + variables[ValidationConfigNode.VAR_NODE.value] = ( + data if data else validate_doc.data + ) @staticmethod def process_data_template(variables: Variables) -> None: """process data or template before assertion""" - data = variables[ValidationConfigNode.VAR_NODE] + data = variables[ValidationConfigNode.VAR_NODE.value] tmp_variables = { key: val for key, val in variables.data.items() - if key != ValidationConfigNode.VAR_NODE + if key != ValidationConfigNode.VAR_NODE.value } - variables[ValidationConfigNode.VAR_NODE] = replace_value(data, tmp_variables) + variables[ValidationConfigNode.VAR_NODE.value] = replace_value( + data, tmp_variables + ) @staticmethod def make_assertion_entry_list(assert_lst: list[dict]) -> list[AssertionEntry]: @@ -152,7 +156,12 @@ def make_assertion_entry_list(assert_lst: list[dict]) -> list[AssertionEntry]: if not (_assert_type := each_assert.get("type", None)): raise RuntimeError("key: `type` not found in one of the asserts.") - validator = cerberus.Validator(get_schema_map(_assert_type)) + try: + validator = cerberus.Validator(get_schema_map(_assert_type)) + except KeyError as ex: + raise KeyError( + f"`{_assert_type}` key not found. in {repr(each_assert)}" + ) from ex if not validator.validate(each_assert): raise RuntimeError( @@ -181,75 +190,34 @@ def make_assertion_entry_list(assert_lst: list[dict]) -> list[AssertionEntry]: _extra_fld = { key: val for key, val in each_assert.items() if key in only } - - new_assertion_lst.append( - AssertionEntry( - assert_type=_assert_type, - actual=_actual, - expected=_expected, - cast_actual_to=_cast_actual_to, - extra_fields=_extra_fld, - msg_pass=_msg_pass, - msg_fail=_msg_fail, - ) + ae = AssertionEntry( + assert_type=_assert_type, + actual=_actual, + expected=_expected, + cast_actual_to=_cast_actual_to, + extra_fields=_extra_fld, + msg_pass=_msg_pass, + msg_fail=_msg_fail, ) + debug(ae) + + new_assertion_lst.append(ae) return new_assertion_lst - @staticmethod - def display(expose_list: list, exec_ctx: ExecuteContext) -> None: - """Displays the response based on the command response format - Args: - expose_list: list - exec_ctx: ExecuteContext - """ - - if not expose_list: - return - - display_item_list: list[object] = [] - - for expose_item in expose_list: - if isinstance(expose_item, AllTestRunResult): - if exec_ctx.options["format"]: - display_item_list.append(expose_item.as_fmt_str) - else: - display_item_list.append(expose_item.as_dict) - else: - display_item_list.append(expose_item) - - if exec_ctx.options["format"]: - formatter( - "\n---\n".join([str(item) for item in display_item_list]) - if len(display_item_list) > 1 - else display_item_list.pop(), - dump=exec_ctx.options["dump"], - ) - else: - formatter( - json.dumps(display_item_list) - if len(display_item_list) > 1 - else json.dumps(display_item_list.pop()), - dump=exec_ctx.options["dump"], - ) +@with_catch_log +def call(file_ctx: FileContext, exec_ctx: ExecuteContext) -> ExecResponse: + """Call a validation document""" + debug(file_ctx) + debug(exec_ctx) -def execute( - ctx: FileContext, exec_ctx: ExecuteContext, cb: abc.Callable = lambda *args: ... -) -> None: - """Run a validation document - - Args: - ctx: FileContext object to handle - exec_ctx: ExecuteContext - cb: Callable - """ - - validate_doc = ValidationDocument.from_file_context(ctx) + validate_doc = ValidationDocument.from_file_context(file_ctx) + debug(validate_doc.model_dump_json()) DocumentVersionMaker.verify_if_allowed( - DocumentVersionMaker.from_dict(validate_doc.as_dict), VERSION_SCOPE + DocumentVersionMaker.from_dict(validate_doc.model_dump()), VERSION_SCOPE ) VersionedDocumentSupport.validate_with_schema( @@ -258,30 +226,98 @@ def execute( variable_doc = Variables() VariableTableManager.handle(variable_doc, validate_doc, exec_ctx) + debug(variable_doc.data) # handle passed data in asserts - ValidationDocumentSupport.set_data_template(validate_doc, variable_doc, exec_ctx) - ValidationDocumentSupport.process_data_template(variable_doc) + with with_catch_log(): + ValidationDocumentSupport.set_data_template( + validate_doc, variable_doc, exec_ctx + ) + ValidationDocumentSupport.process_data_template(variable_doc) - assert_list = ValidationDocumentSupport.make_assertion_entry_list( - validate_doc.asserts - ) + debug(variable_doc.data) - test_run_result = AssertionEntryListRunner.test_run(assert_list, variable_doc.data) + r_exception: Exception | None = None + run_rpt = RunReport() + + try: + assert_list = ValidationDocumentSupport.make_assertion_entry_list( + validate_doc.asserts + ) + + run_rpt = AssertionEntryListRunner.test_run(assert_list, variable_doc.data) + + if run_rpt.count_fail != 0: + raise SystemError("Validation failed") + except Exception as ex: + r_exception = ex + error(ex) + + output_data = Variables( + { + "_asserts_response": run_rpt, + "_data": variable_doc["_data"], + } + ) + debug(output_data.data) exposed_data = ExposeManager.get_exposed_replaced_data( validate_doc, - {**variable_doc.data, **{"_asserts_response": test_run_result}}, + { + **variable_doc.data, + **{"_asserts_response": run_rpt}, + }, + ) + debug(exposed_data) + + return ExecResponse( + file_ctx=file_ctx, + exec_ctx=exec_ctx, + variables_exec=output_data, + variables=variable_doc, + extra=run_rpt, + exposed=exposed_data, + exception=r_exception, + report={ + "is_success": run_rpt.count_fail == 0 and r_exception is None, + "count_all": len(validate_doc.asserts), + "count_fail": run_rpt.count_fail, + "exceptions": [dtl.message for dtl in run_rpt.details if not dtl.is_pass], + }, ) - ValidationDocumentSupport.display(exposed_data, exec_ctx) - cb( - { - ctx.filepath_hash: ExposableVariables( - { - "_asserts_response": test_run_result.as_dict, - "_data": variable_doc["_data"], - } - ).data - } + +@with_catch_log +def execute( + ctx: FileContext, exec_ctx: ExecuteContext, cb: abc.Callable = lambda *args: ... +) -> None: + """Run a validation document + + Args: + ctx: FileContext object to handle + exec_ctx: ExecuteContext + cb: Callable + """ + + exr = call(file_ctx=ctx, exec_ctx=exec_ctx) + + cb({ctx.filepath_hash: exr.variables_exec.data}) + PresentationService.display(exr, exec_ctx, ValidatePresenter) + + +@with_catch_log +def task_validation(**kwargs: dict) -> ExecResponse: + """Task impl""" + + if not (doc := kwargs.get("task", {})): + raise ValueError("Wrong task format given.") + + _task = ValidationTask(**doc) + + return call( + FileContext.from_file(_task.file), + ExecuteContext( + options={"dump": True, "format": True}, + arguments=_task.arguments | {"variables": _task.variables}, + ), ) diff --git a/chk/modules/validate/assertion_services.py b/chk/modules/validate/assertion_services.py index 89e4ce2f..638a28a8 100644 --- a/chk/modules/validate/assertion_services.py +++ b/chk/modules/validate/assertion_services.py @@ -2,18 +2,16 @@ Assertion services """ -import dataclasses -import typing -import uuid -from collections import UserDict from collections.abc import Callable from datetime import datetime import chk.modules.validate.assertion_function as asrt_f from chk.infrastructure.helper import Cast +from chk.infrastructure.logging import debug from chk.infrastructure.templating import StrTemplate from chk.modules.validate.assertion_message import get_assert_msg_for from chk.modules.validate.assertion_validation import AssertionEntityType +from chk.modules.validate.entities import AssertionEntry, RunDetail, RunReport MAP_TYPE_TO_FN: dict[str, Callable] = { AssertionEntityType.Accepted: asrt_f.accepted, @@ -61,113 +59,6 @@ } -@dataclasses.dataclass -class AssertionEntry: - """AssertionEntry holds one assertion operation""" - - assert_type: str - actual: typing.Any - expected: typing.Any - msg_pass: str = dataclasses.field(default_factory=str) - msg_fail: str = dataclasses.field(default_factory=str) - cast_actual_to: str = dataclasses.field(default_factory=str) - actual_given: typing.Any = dataclasses.field(default=NotImplemented) - actual_b4_cast: typing.Any = dataclasses.field(default=NotImplemented) - extra_fields: dict = dataclasses.field(default_factory=dict) - - @property - def as_dict(self) -> dict: - """Return dict representation""" - - if self.actual_given == NotImplemented: - self.actual_given = "" - if self.actual_b4_cast == NotImplemented: - self.actual_b4_cast = "" - if self.expected == NotImplemented: - self.expected = "" - - return dataclasses.asdict(self) - - -class SingleTestRunResult(UserDict): - """Result of an assertion run - - keys: is_pass, message, assert_used - """ - - @property - def as_dict(self) -> dict: - """Convert SingleTestRunResult to a dict""" - - return { - key: value.as_dict if key == "assert_used" else value - for key, value in self.items() - } - - @property - def as_fmt_str(self) -> str: - """String representation of ApiResponse - - Returns: - str: String representation - """ - - return ( - "\n" - f"{'+' if self['is_pass'] else '-'} {self['assert_used'].assert_type} " - + f"{'PASSED' if self['is_pass'] else 'FAILED'}, {self['message']}" - ) - - -class AllTestRunResult(UserDict): - """Result of a test run - - keys: id, time_start, time_end, count_all, results, count_fail - """ - - @property - def as_dict(self) -> dict: - """Convert AllTestRunResult to a dict""" - _as_dict: dict = { - key: value for key, value in self.items() if not key.startswith("time_") - } - - _as_dict |= { - key: value.timestamp() - for key, value in self.items() - if key.startswith("time_") - } - - if len(self["results"]) > 0: - _as_dict["results"] = [ - test_result.as_dict - for test_result in self["results"] - if isinstance(test_result, SingleTestRunResult) - ] - - return _as_dict - - @property - def as_fmt_str(self) -> str: - """String representation of ApiResponse - - Returns: - str: String representation - """ - - _display = ( - f"Test run id: {self['id']}, time taken {self['time_end'] - self['time_start']}\n" - + f"Total tests: {self['count_all']}, " - + f"Total tests failed: {self['count_fail']}\n" - ) - _display += "\n> Test run result(s):" - - for one_result in self["results"]: - _display += one_result.as_fmt_str - - return _display - - class AssertionEntryListRunner: """AssertionAntiquary is service class that run assertion""" @@ -222,26 +113,15 @@ def _replace_assertion_values( @staticmethod def _prepare_test_run_result( - resp: SingleTestRunResult, assert_item: AssertionEntry, asrt_resp: ValueError | bool, - ) -> None: - def _prepare_message_values() -> dict: - return { - "assert_type": assert_item.assert_type, - "type_actual": assert_item.actual.__class__.__name__, - "type_expected": assert_item.expected.__class__.__name__, - "value_actual": assert_item.actual, - "value_expected": assert_item.expected, - "value_actual_given": assert_item.actual_given, - "value_actual_b4_cast": assert_item.actual_b4_cast, - "extra_fields": assert_item.extra_fields, - } - + ) -> RunDetail: def _prepare_message() -> str: + asrt_fn_name = MAP_TYPE_TO_FN[assert_item.assert_type].__name__ + if isinstance(asrt_resp, ValueError): return get_assert_msg_for(f"{asrt_fn_name}.{str(asrt_resp)}").format( - **_prepare_message_values() + **detail.get_message_values() ) if asrt_resp: @@ -257,16 +137,18 @@ def _prepare_message() -> str: else assert_item.msg_fail ) - return message.format(**_prepare_message_values()) + return message.format(**detail.get_message_values()) - asrt_fn_name = MAP_TYPE_TO_FN[assert_item.assert_type].__name__ + detail = RunDetail(assert_entry=assert_item) if isinstance(asrt_resp, ValueError): - resp["is_pass"] = False - resp["message"] = _prepare_message() + detail.is_pass = False + detail.message = _prepare_message() else: - resp["is_pass"] = asrt_resp - resp["message"] = _prepare_message() + detail.is_pass = asrt_resp + detail.message = _prepare_message() + + return detail @staticmethod def _call_assertion_method( @@ -281,12 +163,10 @@ def _call_assertion_method( """ asrt_fn = MAP_TYPE_TO_FN[assert_item.assert_type] - return asrt_fn(**assert_item.as_dict) + return asrt_fn(**dict(assert_item)) @staticmethod - def test_run( - assert_list: list[AssertionEntry], variables: dict - ) -> AllTestRunResult: + def test_run(assert_list: list[AssertionEntry], variables: dict) -> RunReport: """Run the tests Args: @@ -294,36 +174,25 @@ def test_run( variables: dict Returns: - AllTestRunResult: Test run result + RunReport: Test run report """ - test_run_result = AllTestRunResult( - id=str(uuid.uuid4()), - time_start=datetime.now(), - count_all=len(assert_list), - count_fail=0, - ) - - results: list[SingleTestRunResult] = [] + run_report = RunReport(count_all=len(assert_list)) for assert_item in assert_list: assert_item = AssertionEntryListRunner._replace_assertion_values( assert_item, variables ) + debug(assert_item) - resp = SingleTestRunResult(assert_used=assert_item) - asrt_resp = AssertionEntryListRunner._call_assertion_method(assert_item) - - AssertionEntryListRunner._prepare_test_run_result( - resp, assert_item, asrt_resp + resp: RunDetail = AssertionEntryListRunner._prepare_test_run_result( + assert_item, + AssertionEntryListRunner._call_assertion_method(assert_item), ) + debug(resp) - if resp["is_pass"] is False: - test_run_result["count_fail"] += 1 - - results.append(resp) + run_report.add_run_detail(resp) - test_run_result["time_end"] = datetime.now() - test_run_result["results"] = results + run_report.time_end = datetime.now() - return test_run_result + return run_report diff --git a/chk/modules/validate/entities.py b/chk/modules/validate/entities.py new file mode 100644 index 00000000..bb9bcd08 --- /dev/null +++ b/chk/modules/validate/entities.py @@ -0,0 +1,154 @@ +""" +Validate module entities +""" + +import uuid +from datetime import datetime +from typing import Any + +from pydantic import BaseModel, Field, UUID4 + + +class AssertionEntry(BaseModel): + """ + AssertionEntry holds one assertion operation + """ + + assert_type: str + actual: Any + expected: Any + msg_pass: str = Field(default_factory=str) + msg_fail: str = Field(default_factory=str) + cast_actual_to: str = Field(default_factory=str) + actual_given: Any = Field(default=NotImplemented) + actual_b4_cast: Any = Field(default=NotImplemented) + extra_fields: dict = Field(default_factory=dict) + + def __iter__(self): + yield "assert_type", self.assert_type + yield "actual", self.actual + yield "expected", "" if self.expected == NotImplemented else self.expected + yield "msg_pass", self.msg_pass + yield "msg_fail", self.msg_fail + yield "cast_actual_to", self.cast_actual_to + yield "actual_given", ( + "" if self.actual_given == NotImplemented else self.actual_given + ) + yield "actual_b4_cast", ( + "" if self.actual_b4_cast == NotImplemented else self.actual_b4_cast + ) + yield "extra_fields", self.extra_fields + + +class RunDetail(BaseModel): + """RunDetail stores one single run result""" + + assert_entry: AssertionEntry = Field(default=NotImplemented) + is_pass: bool = Field(default=False) + message: str = Field(default_factory=str) + + def __iter__(self): + """implement __iter__""" + + yield "assert_entry", dict(self.assert_entry) + yield "is_pass", self.is_pass + yield "message", self.message + + def as_fmt_str(self) -> str: + """String representation of ApiResponse + + Returns: + str: String representation + """ + + if self.assert_entry == NotImplemented: + raise NotImplementedError("AssertionEntry not set.") + + return ( + "\n" + f"{'+' if self.is_pass else '-'} {self.assert_entry.assert_type} " + + f"{'PASSED' if self.is_pass else 'FAILED'}, {self.message}" + ) + + def get_message_values(self) -> dict: + """Get tokens for template messages""" + + if self.assert_entry == NotImplemented: + raise NotImplementedError("AssertionEntry not set.") + + return { + "assert_type": self.assert_entry.assert_type, + "type_actual": self.assert_entry.actual.__class__.__name__, + "type_expected": self.assert_entry.expected.__class__.__name__, + "value_actual": self.assert_entry.actual, + "value_expected": self.assert_entry.expected, + "value_actual_given": self.assert_entry.actual_given, + "value_actual_b4_cast": self.assert_entry.actual_b4_cast, + "extra_fields": self.assert_entry.extra_fields, + } + + +class RunReport(BaseModel): + """RunReport stores overall test run report""" + + id: UUID4 = Field(default_factory=uuid.uuid4) + time_start: datetime = Field(default_factory=datetime.now) + time_end: datetime = Field(default_factory=datetime.now) + count_all: int = Field(default=0) + count_fail: int = Field(default=0) + details: list[RunDetail] = Field(default_factory=list) + + def __iter__(self): + """Implement __iter__""" + + yield "id", str(self.id) + yield "time_start", str(self.time_start.timestamp()) + yield "time_end", str(self.time_end.timestamp()) + yield "count_all", self.count_all + yield "count_fail", self.count_fail + yield "details", [dict(detail) for detail in self.details] + + def add_run_detail(self, run_dtl: RunDetail) -> None: + """Append a RunDetail""" + + if not isinstance(run_dtl, RunDetail): + raise TypeError("RunDetail expected.") + + self.details.append(run_dtl) + + if not run_dtl.is_pass: + self.count_fail += 1 + + def as_fmt_str(self, only_incl_errors: bool = False) -> str: + """String representation of ApiResponse + + Returns: + str: String representation + """ + + _display = ( + f"Test run id: {self.id}, time taken {self.time_end - self.time_start}\n" + + f"Total tests: {self.count_all}, " + + f"Total tests failed: {self.count_fail}\n" + ) + _display += "\n> Test run result(s):" + + if only_incl_errors: + for detail in self.details: + if not detail.is_pass: + _display += detail.as_fmt_str() + else: + for detail in self.details: + _display += detail.as_fmt_str() + + return _display + + +class ValidationTask(BaseModel): + """Parsed FetchTask""" + + name: str + uses: str + file: str + variables: dict = Field(default_factory=dict) + arguments: dict = Field(default_factory=dict) diff --git a/chk/modules/validate/services.py b/chk/modules/validate/services.py new file mode 100644 index 00000000..292a1773 --- /dev/null +++ b/chk/modules/validate/services.py @@ -0,0 +1,56 @@ +""" +General validation services +""" + +from json import dumps + +from chk.infrastructure.view import PresentationBuilder +from chk.modules.validate.entities import RunReport + + +class ValidatePresenter(PresentationBuilder): + """ValidatePresenter""" + + def dump_error_json(self) -> str: + return dumps( + { + "error": ( + repr(self.data.exception) + if self.data.exception + else "Unspecified error" + ) + } + ) + + def dump_error_fmt(self) -> str: + """dump fmt error str""" + + # TODO also add tests those failed. + return ( + f"Validate error\n------\n{repr(self.data.exception)}" + if self.data.exception + else "Validate error\n------\nUnspecified error" + ) + + def dump_fmt(self) -> str: + """dump formatted string""" + + display_items: list[str] = [] + + for key, item in self.data.exposed.items(): + if key == "_asserts_response" and isinstance(item, RunReport): + display_items.append(item.as_fmt_str()) + else: + display_items.append(dumps(item)) + + return "\n======\n".join(display_items) + + def dump_json(self) -> str: + """dump json""" + + display_items: list[dict] = [] + + for key, item in self.data.exposed.items(): + display_items.append(dict(item)) + + return dumps(display_items) diff --git a/chk/modules/workflow/__init__.py b/chk/modules/workflow/__init__.py index 9f09a92e..8095ecd3 100644 --- a/chk/modules/workflow/__init__.py +++ b/chk/modules/workflow/__init__.py @@ -1,3 +1,258 @@ """ Workflow module """ + +from __future__ import annotations + +import json +import pathlib +from collections import abc +from collections.abc import Callable + +from pydantic import BaseModel, ConfigDict, Field + +from chk.infrastructure.document import ( + VersionedDocumentV2, +) +from chk.infrastructure.file_loader import ExecuteContext, FileContext +from chk.infrastructure.helper import data_get, slugify +from chk.infrastructure.logging import debug, error, with_catch_log +from chk.infrastructure.symbol_table import ( + ExecResponse, + ExposeManager, + VariableTableManager, + Variables, + replace_value, +) +from chk.infrastructure.version import DocumentVersionMaker +from chk.infrastructure.view import PresentationService +from chk.modules.fetch import task_fetch +from chk.modules.validate import task_validation +from chk.modules.workflow.entities import ( + ChkwareTask, + ChkwareValidateTask, + StepResult, + TaskExecParam, + WorkflowConfigNode, + WorkflowUses, +) +from chk.modules.workflow.services import ChkwareTaskSupport, WorkflowPresenter + +VERSION_SCOPE = ["workflow"] + + +class WorkflowDocument(VersionedDocumentV2, BaseModel): + """WorkflowDocument""" + + model_config = ConfigDict(extra="forbid") + + name: str = Field(default_factory=str) + id: str = Field(default_factory=str) + tasks: list[dict] = Field(default="") + + @staticmethod + def from_file_context(ctx: FileContext) -> WorkflowDocument: + """Create a WorkflowDocument from FileContext""" + + # version + doc_ver = DocumentVersionMaker.from_dict(ctx.document) + DocumentVersionMaker.verify_if_allowed(doc_ver, VERSION_SCOPE) + + # id, name + # @TODO Name and ID processing should have separate func + if name_str := data_get(ctx.document, "name"): + name_str = str(name_str).strip() + + if id_str := data_get(ctx.document, "id"): + id_str = slugify(str(id_str).strip()) + else: + id_str = ( + slugify(name_str) + if name_str and len(name_str) > 0 + else pathlib.Path(ctx.filepath).stem + ) + + if not name_str: + name_str = id_str + + # tasks + if not (tasks_lst := data_get(ctx.document, "tasks")): + raise RuntimeError("`tasks:` not found.") + + if not isinstance(tasks_lst, list): + raise RuntimeError("`tasks:` is not list.") + + # @TODO keep `context`, `version` as object + # @TODO implement __repr__ for WorkflowDocument + return WorkflowDocument( + context=tuple(ctx), + version=str(doc_ver), + name=name_str, + id=id_str, + tasks=tasks_lst, + ) + + +class WorkflowDocumentSupport: + """Workflow document support""" + + @classmethod + def _prepare_validate_task_argument_data_(cls, task: ChkwareTask) -> dict: + """Prepare data for ChkwareValidateTask.arguments.data""" + + if isinstance(task, ChkwareValidateTask) and task.arguments: + return task.arguments.data + + return {} + + @classmethod + def set_step_template(cls, variables: Variables) -> None: + """sets data or template""" + + # @TODO implement data set functionality with validation for variables + variables[WorkflowConfigNode.NODE.value] = [] + + @classmethod + def process_task_template( + cls, document: WorkflowDocument, variables: Variables + ) -> list: + """Process task block of document""" + + base_fpath: str = FileContext(*document.context).filepath + exec_report: list[StepResult] = [] + + is_success = True + for task in document.tasks: + debug(task) + + if not is_success: + debug(f"is_success: {is_success}") + break + + if not isinstance(task, dict): + error("`tasks.*.item` should be map.") + raise RuntimeError("`tasks.*.item` should be map.") + + # replace values in tasks + task_d_: dict = replace_value(task, variables.data) + debug(task_d_) + + task_o_ = ChkwareTaskSupport.make_task( + task_d_, **dict(base_file_path=base_fpath) + ) + + exctx_args = {"variables": json.dumps(task_o_.variables)} + + if isinstance(task_o_, ChkwareValidateTask): + exctx_args["arguments"] = task_o_.arguments.model_dump_json() + + execution_ctx = ExecuteContext({"dump": True, "format": True}, exctx_args) + debug(execution_ctx) + + task_fn = None + + match task_o_.uses: + case WorkflowUses.fetch.value: + task_fn = task_fetch + case WorkflowUses.validate.value: + task_fn = task_validation + + if task_fn: + te_param = TaskExecParam(task=task_o_, exec_ctx=execution_ctx) + debug(execution_ctx) + + task_resp: ExecResponse = cls.execute_task(task_fn, te_param, variables) + debug(execution_ctx) + + is_success = task_resp.report.pop("is_success") + + exec_report.append( + StepResult( + task=task_o_, + is_success=is_success, + others=task_resp.report, + exposed=task_resp.exposed, + exception=task_resp.exception, + ) + ) + + return exec_report + + @classmethod + def execute_task( + cls, task_fn: Callable, task_params: TaskExecParam, variables: Variables + ) -> ExecResponse: + """execute_task""" + + _task_res: ExecResponse = task_fn(**task_params.as_dict()) + variables[WorkflowConfigNode.NODE.value].append(_task_res.exposed) + + return _task_res + + +@with_catch_log +def call(file_ctx: FileContext, exec_ctx: ExecuteContext) -> ExecResponse: + """Call a workflow document""" + + debug(file_ctx) + debug(exec_ctx) + + wflow_doc = WorkflowDocument.from_file_context(file_ctx) + debug(wflow_doc.model_dump_json()) + + variable_doc = Variables() + VariableTableManager.handle(variable_doc, wflow_doc, exec_ctx) + debug(variable_doc.data) + + service = WorkflowDocumentSupport() + # @TODO make sure the document do not call self making it repeating + service.set_step_template(variable_doc) + + r_exception: Exception | None = None + + try: + with with_catch_log(): + exec_report = service.process_task_template(wflow_doc, variable_doc) + except Exception as ex: + r_exception = ex + error(ex) + + output_data = Variables({"_steps": variable_doc[WorkflowConfigNode.NODE.value]}) + debug(output_data.data) + + exposed_data: dict = ExposeManager.get_exposed_replaced_data( + wflow_doc, variable_doc.data + ) + debug(exposed_data) + + # TODO also send failed_details (fail code, message, stacktrace, etc) + return ExecResponse( + file_ctx=file_ctx, + exec_ctx=exec_ctx, + variables=variable_doc, + variables_exec=output_data, + extra=exec_report, + exposed=exposed_data, + exception=r_exception, + report={ + "is_success": r_exception is None, + }, + ) + + +@with_catch_log +def execute( + ctx: FileContext, exec_ctx: ExecuteContext, cb: abc.Callable = lambda *args: ... +) -> None: + """Run a workflow document + + Args: + ctx: FileContext object to handle + exec_ctx: ExecuteContext + cb: Callable + """ + + exr = call(file_ctx=ctx, exec_ctx=exec_ctx) + + cb({ctx.filepath_hash: exr.variables_exec.data}) + PresentationService.display(exr, exec_ctx, WorkflowPresenter) diff --git a/chk/modules/workflow/entities.py b/chk/modules/workflow/entities.py new file mode 100644 index 00000000..07a80e59 --- /dev/null +++ b/chk/modules/workflow/entities.py @@ -0,0 +1,92 @@ +""" +Entities for workflow +""" + +from __future__ import annotations + +import enum +from typing import NamedTuple + +from pydantic import BaseModel, ConfigDict, Field + +from chk.infrastructure.file_loader import ExecuteContext, generate_abs_path + + +class WorkflowConfigNode(enum.StrEnum): + """WorkflowConfigNode""" + + NODE = "_steps" + + +class TaskExecParam(NamedTuple): + """TaskExecParams""" + + task: ChkwareTask + exec_ctx: ExecuteContext + + def as_dict(self) -> dict: + """Convert to dict""" + + return {"task": self.task.model_dump(), "execution_context": self.exec_ctx} + + +class WorkflowUses(enum.StrEnum): + """Types of Workflow Uses""" + + fetch = enum.auto() + validate = enum.auto() + + +class ChkwareTask(BaseModel): + """Chkware task""" + + model_config = ConfigDict(extra="forbid") + + name: str + uses: str + file: str + variables: dict = Field(default_factory=dict) + + def __init__(self, basepath: str, /, **kwargs: dict) -> None: + """Constructor""" + + if ( + "file" in kwargs + and isinstance(kwargs["file"], str) + and len(kwargs["file"]) != 0 + ): + kwargs["file"] = generate_abs_path(basepath, kwargs["file"]) + + super().__init__(**kwargs) + + +class ChkwareValidateTask(ChkwareTask): + """Chkware validation task""" + + class ChkwareTaskDataArgument(BaseModel): + """Chkware task data argument""" + + model_config = ConfigDict(extra="forbid") + + data: dict = Field(default_factory=dict) + + model_config = ConfigDict(extra="forbid") + + arguments: ChkwareTaskDataArgument = Field(default_factory=ChkwareTaskDataArgument) + + def __init__(self, basepath: str, /, **kwargs: dict) -> None: + """Constructor""" + + super().__init__(basepath, **kwargs) + + +class StepResult(BaseModel): + """StepResult""" + + model_config = ConfigDict(arbitrary_types_allowed=True) + + task: ChkwareTask | ChkwareValidateTask + is_success: bool + others: dict = Field(default_factory=dict) + exposed: dict = Field(default_factory=list) + exception: Exception | None = Field(default=None) diff --git a/chk/modules/workflow/services.py b/chk/modules/workflow/services.py new file mode 100644 index 00000000..cfb3fc10 --- /dev/null +++ b/chk/modules/workflow/services.py @@ -0,0 +1,199 @@ +""" +Workflow services module +""" + +from __future__ import annotations + +import json +from collections.abc import Iterable + +from chk.infrastructure.view import PresentationBuilder +from chk.modules.workflow import ( + ChkwareTask, + ChkwareValidateTask, + StepResult, + WorkflowConfigNode, + WorkflowUses, +) + + +class ChkwareTaskSupport: + """ChkwareTaskSupport""" + + @classmethod + def make_task(cls, task_d_: dict, /, **kwargs: dict) -> ChkwareTask: + """validate task data""" + + if "base_file_path" not in kwargs: + raise ValueError("`base_file_path` not passed.") + + if "uses" not in task_d_: + raise RuntimeError("Malformed task item found.") + + if task_d_["uses"] not in ( + WorkflowUses.fetch.value, + WorkflowUses.validate.value, + ): + raise RuntimeError("task.uses unsupported.") + + base_file_path = str(kwargs["base_file_path"]) + + return ( + ChkwareTask(base_file_path, **task_d_) + if task_d_["uses"] == "fetch" + else ChkwareValidateTask(base_file_path, **task_d_) + ) + + +class WorkflowPresenter(PresentationBuilder): + """WorkflowPresenter""" + + def dump_error_json(self) -> str: + return json.dumps( + { + "error": ( + repr(self.data.exception) + if self.data.exception + else "Unspecified error" + ) + } + ) + + def dump_error_fmt(self) -> str: + """dump fmt error str""" + + return ( + f"Workflow error\n------\n{repr(self.data.exception)}" + if self.data.exception + else "Workflow error\n------\nUnspecified error" + ) + + def _prepare_dump_data(self) -> dict: + """prepare dump data""" + + exec_report = self.data.extra + _document = self.data.file_ctx.document + + r_dump = {} + + if _document and "name" in _document: + r_dump["name"] = _document["name"] + + if exec_report: + r_dump["step_count"] = len(_document["tasks"]) + r_dump["step_failed"] = len( + [item for item in exec_report if not item.is_success] + ) + + r_dump["tasks"] = [] + + for item in exec_report: + item: StepResult # type: ignore + + response_task_dump = { + "name": item.task.name, + "uses": item.task.uses, + "is_success": item.is_success, + "exception": repr(item.exception), + } + if item.task.uses == "fetch": + + response_task_dump["fetch_request_method"] = ( + item.others["request_method"] + if "request_method" in item.others + else "" + ) + response_task_dump["fetch_request_url"] = ( + item.others["request_url"] if "request_url" in item.others else "" + ) + + if item.task.uses == "validate": + response_task_dump["validate_asserts_count_all"] = ( + item.others["count_all"] if "count_all" in item.others else "" + ) + response_task_dump["validate_asserts_count_fail"] = ( + item.others["count_fail"] if "count_fail" in item.others else "" + ) + response_task_dump["validate_asserts_err_messages"] = [ + f" >>> {msg}" for msg in item.others["exceptions"] + ] + + r_dump["tasks"].append(response_task_dump) + return r_dump + + def dump_fmt(self) -> str: + """return formatted string representation""" + + exposed_fmt_str = [] + for key, value in self.data.exposed.items(): + node = str(WorkflowConfigNode.NODE) + + if node in key and len(key) == len(node): + to_append = self._prepare_dump_str_for_steps() + else: + # TODO: Need a json.Encoder for specific PresentableExposeTypes + # PresentableExposeTypes for RunReport, ApiResponse, etc + if isinstance(value, dict): + for _k, _v in value.items(): + if isinstance(_v, Iterable): + value[_k] = dict(_v) if isinstance(_v, dict) else _v + + to_append = json.dumps(value) + + exposed_fmt_str.append(to_append) + + return "\n======\n".join(exposed_fmt_str) + + def _prepare_dump_str_for_steps(self) -> str: + """prepare dump str for steps""" + + dump_dct: dict = self._prepare_dump_data() + + _computed_str = f"\n\nWorkflow: {dump_dct.get('name', '')}" + _computed_str += f"\nSteps total: {dump_dct.get('step_count', '')}, " + _computed_str += f"failed: {dump_dct.get('step_failed', '')}" + + tasks = dump_dct.get("tasks", []) + + for one_task in tasks: + _computed_str += "\n------\n" + _computed_str += "+ [PASS] " if one_task["is_success"] else "- [FAIL] " + _computed_str += f"Task: {one_task['name']}\n" + if one_task["uses"] == "fetch": + _computed_str += f">> {one_task['fetch_request_method']} {one_task['fetch_request_url']}" + if not one_task["is_success"]: + _computed_str += f"\n>> With message: {one_task['exception']}" + elif one_task["uses"] == "validate": + _computed_str += ( + f">> Total tests: {one_task['validate_asserts_count_all']}, " + ) + _computed_str += f"Failed: {one_task['validate_asserts_count_fail']}" + if not one_task["is_success"]: + _computed_str += f"\n>> With message: {one_task['exception']}\n" + _computed_str += "\n".join( + one_task["validate_asserts_err_messages"] + ) + + return _computed_str + + def dump_json(self) -> str: + """return json representation""" + exposed_fmt_str = [] + + for key, value in self.data.exposed.items(): + node = str(WorkflowConfigNode.NODE) + _to_append = {} + + if node in key and len(key) == len(node): + _to_append = self._prepare_dump_data() + else: + if isinstance(value, dict): + for _k, _v in value.items(): + if isinstance(_v, Iterable): + value[_k] = dict(_v) + + _to_append = value + + exposed_fmt_str.append(_to_append) + + return json.dumps(exposed_fmt_str) diff --git a/requirements-dev.txt b/requirements-dev.txt index 52118a17..636719a6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,47 +1,55 @@ -i https://pypi.org/simple -aiohappyeyeballs==2.4.0; python_version >= '3.8' -aiohttp==3.10.5 +aiohappyeyeballs==2.4.3; python_version >= '3.8' +aiohttp==3.10.10 aiosignal==1.3.1; python_version >= '3.7' -astroid==3.2.4; python_full_version >= '3.8.0' +astroid==3.3.5; python_full_version >= '3.9.0' +asttokens==2.4.1 attrs==24.2.0; python_version >= '3.7' -black[d]==24.8.0; python_version >= '3.8' -certifi==2024.7.4; python_version >= '3.6' -charset-normalizer==3.3.2; python_full_version >= '3.7.0' +black[d]==24.10.0; python_version >= '3.9' +certifi==2024.8.30; python_version >= '3.6' +charset-normalizer==3.4.0; python_full_version >= '3.7.0' click==8.1.7; python_version >= '3.7' -coverage[toml]==7.6.1; python_version >= '3.8' +colorama==0.4.6; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6' +coverage[toml]==7.6.4; python_version >= '3.9' coveralls==4.0.1; python_version < '3.13' and python_version >= '3.8' -dill==0.3.8; python_version >= '3.11' +dill==0.3.9; python_version >= '3.11' docopt==0.6.2 +executing==2.1.0; python_version >= '3.8' flake8==7.1.1; python_full_version >= '3.8.1' -frozenlist==1.4.1; python_version >= '3.8' -idna==3.7; python_version >= '3.5' +frozenlist==1.5.0; python_version >= '3.8' +icecream==2.1.3 +idna==3.10; python_version >= '3.6' iniconfig==2.0.0; python_version >= '3.7' isort==5.13.2; python_full_version >= '3.8.0' mccabe==0.7.0; python_version >= '3.6' -multidict==6.0.5; python_version >= '3.7' -mypy==1.11.1; python_version >= '3.8' +multidict==6.1.0; python_version >= '3.8' +mypy==1.13.0; python_version >= '3.8' mypy-extensions==1.0.0; python_version >= '3.5' packaging==24.1; python_version >= '3.8' pathspec==0.12.1; python_version >= '3.8' -platformdirs==4.2.2; python_version >= '3.8' +platformdirs==4.3.6; python_version >= '3.8' pluggy==1.5.0; python_version >= '3.8' +propcache==0.2.0; python_version >= '3.8' pycodestyle==2.12.1; python_version >= '3.8' pyflakes==3.2.0; python_version >= '3.8' -pylint==3.2.6; python_full_version >= '3.8.0' -pytest==8.3.2; python_version >= '3.8' +pygments==2.18.0; python_version >= '3.8' +pylint==3.3.1; python_full_version >= '3.9.0' +pytest==8.3.3; python_version >= '3.8' pytest-cov==5.0.0; python_version >= '3.8' requests==2.32.3; python_version >= '3.8' +six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' tomlkit==0.13.2; python_version >= '3.8' -types-pyyaml==6.0.12.20240808; python_version >= '3.8' -types-requests==2.32.0.20240712; python_version >= '3.8' -typing-extensions==4.12.2; python_version >= '3.8' -urllib3==2.2.2; python_version >= '3.8' -var-dump==1.2 -yarl==1.9.4; python_version >= '3.7' +types-pyyaml==6.0.12.20240917; python_version >= '3.8' +types-requests==2.32.0.20241016; python_version >= '3.8' +typing-extensions==4.12.2; python_version < '3.13' +urllib3==2.2.3; python_version >= '3.8' +yarl==1.16.0; python_version >= '3.9' +annotated-types==0.7.0; python_version >= '3.8' cerberus==1.3.5 defusedxml==0.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -jinja2==3.1.4; python_version >= '3.7' -markupsafe==2.1.5; python_version >= '3.7' +loguru==0.7.2; python_version >= '3.5' +pydantic==2.9.2; python_version >= '3.8' +pydantic-core==2.23.4; python_version >= '3.8' python-dotenv==1.0.1; python_version >= '3.8' pyyaml==6.0.2; python_version >= '3.8' -xmltodict==0.13.0; python_version >= '3.4' +xmltodict==0.14.2; python_version >= '3.6' diff --git a/requirements.txt b/requirements.txt index 3b818fb9..34035419 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,16 @@ +annotated-types==0.7.0; python_version >= '3.8' cerberus==1.3.5 -certifi==2024.7.4; python_version >= '3.6' -charset-normalizer==3.3.2; python_full_version >= '3.7.0' +certifi==2024.8.30; python_version >= '3.6' +charset-normalizer==3.4.0; python_full_version >= '3.7.0' click==8.1.7; python_version >= '3.7' defusedxml==0.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -idna==3.7; python_version >= '3.5' -jinja2==3.1.4; python_version >= '3.7' -markupsafe==2.1.5; python_version >= '3.7' +idna==3.10; python_version >= '3.6' +loguru==0.7.2; python_version >= '3.5' +pydantic==2.9.2; python_version >= '3.8' +pydantic-core==2.23.4; python_version >= '3.8' python-dotenv==1.0.1; python_version >= '3.8' pyyaml==6.0.2; python_version >= '3.8' requests==2.32.3; python_version >= '3.8' -urllib3==2.2.2; python_version >= '3.8' -xmltodict==0.13.0; python_version >= '3.4' +typing-extensions==4.12.2; python_version < '3.13' +urllib3==2.2.3; python_version >= '3.8' +xmltodict==0.14.2; python_version >= '3.6' diff --git a/tests/__init__.py b/tests/__init__.py index 22dfd953..8b069528 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,5 +1,42 @@ +# type: ignore """ common config """ +import pytest -RES_DIR = './tests/resources/storage/sample_config/' +from chk.infrastructure.file_loader import ExecuteContext, FileContext, FileLoader + +RES_DIR = "./tests/resources/storage/sample_config/" +SPEC_DIR = "./tests/resources/storage/spec_docs/" + + +@pytest.fixture +def load_chk_file(): + def wrapper(file_path): + if FileLoader.is_file_ok(file_path): + return FileLoader.load_yaml(file_path) + + raise RuntimeError("Issue with file loading.") + + return wrapper + + +@pytest.fixture +def load_file_ctx_for_file(load_chk_file): + def wrapper(filepath): + return FileContext(document=load_chk_file(filepath), filepath=filepath) + + return wrapper + + +@pytest.fixture +def get_exec_ctx(): + def wrapper(format=True): + return ExecuteContext( + options={ + "dump": True, + "format": format, + } + ) + + return wrapper diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..f4727e2f --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,9 @@ +""" ConfTest """ + +import pytest +from loguru import logger + + +@pytest.fixture(scope="session", autouse=True) +def disable_loguru(): + logger.disable("") diff --git a/tests/console/main_test.py b/tests/console/main_test.py index 47c3c344..9605b6d6 100644 --- a/tests/console/main_test.py +++ b/tests/console/main_test.py @@ -34,7 +34,6 @@ def test_pass(): dct = combine_initial_variables(external_vars) assert isinstance(dct, dict) - assert "_ENV" in dct @staticmethod def test_fail_for_invalid_json(): diff --git a/tests/infrastructure/file_loader_test.py b/tests/infrastructure/file_loader_test.py index e8ce8f15..c7974408 100644 --- a/tests/infrastructure/file_loader_test.py +++ b/tests/infrastructure/file_loader_test.py @@ -5,20 +5,55 @@ from pathlib import Path import pytest -import tests +import tests from chk.infrastructure.file_loader import ( FileContext, - PathFrom, FileLoader, + generate_abs_path, ) from chk.infrastructure.typing_extras import JsonDecodingError +@pytest.fixture +def file_n(): + return "bitcoin-usd.chk" + + +@pytest.fixture +def options_n(): + return {"result": False} + + +@pytest.fixture +def arguments_n(): + return {"variables": {"var": 1}} + + +@pytest.fixture +def get_FileContext_v1(file_n): + file_path = tests.RES_DIR + file_n + return FileContext.from_file(file_path), file_path + + +@pytest.fixture +def get_FileContext_v2(file_n, options_n): + file_path = tests.RES_DIR + file_n + return FileContext.from_file(file_path, options=options_n), file_path + + +@pytest.fixture +def get_FileContext_v3(file_n, options_n, arguments_n): + file_path = tests.RES_DIR + file_n + return ( + FileContext.from_file(file_path, options=options_n, arguments=arguments_n), + file_path, + ) + + class TestFileContext: - def test_from_file_pass(self): - file_path = tests.RES_DIR + "bitcoin-usd.chk" - ctx = FileContext.from_file(file_path) + def test_from_file_pass(self, get_FileContext_v1): + (ctx, file_path) = get_FileContext_v1 if sys.platform.startswith("win"): file_path = file_path.replace("/", "\\") @@ -32,9 +67,8 @@ def test_from_file_pass(self): assert len(ctx.arguments) == 0 assert isinstance(ctx.arguments, dict) - def test_from_file_pass_with_opt_set(self): - file_path = tests.RES_DIR + "bitcoin-usd.chk" - ctx = FileContext.from_file(file_path, options={"result": False}) + def test_from_file_pass_with_opt_set(self, get_FileContext_v2): + ctx, file_path = get_FileContext_v2 if sys.platform.startswith("win"): file_path = file_path.replace("/", "\\") @@ -48,11 +82,8 @@ def test_from_file_pass_with_opt_set(self): assert len(ctx.arguments) == 0 assert isinstance(ctx.arguments, dict) - def test_from_file_pass_with_opt_arg_set(self): - file_path = tests.RES_DIR + "bitcoin-usd.chk" - ctx = FileContext.from_file( - file_path, options={"result": False}, arguments={"variables": {"var": 1}} - ) + def test_from_file_pass_with_opt_arg_set(self, get_FileContext_v3): + ctx, file_path = get_FileContext_v3 if sys.platform.startswith("win"): file_path = file_path.replace("/", "\\") @@ -67,11 +98,8 @@ def test_from_file_pass_with_opt_arg_set(self): assert isinstance(ctx.arguments["variables"], dict) assert ctx.arguments["variables"]["var"] == 1 - def test_from_file_pass_with_doc(self): - file_path = tests.RES_DIR + "bitcoin-usd.chk" - ctx = FileContext.from_file( - file_path, options={"result": False}, arguments={"variables": {"var": 1}} - ) + def test_from_file_pass_with_doc(self, get_FileContext_v3): + ctx, file_path = get_FileContext_v3 if sys.platform.startswith("win"): file_path = file_path.replace("/", "\\") @@ -80,14 +108,24 @@ def test_from_file_pass_with_doc(self): assert ctx.document == FileLoader.load_yaml(file_path) + def test_property_filepath_as_path_pass(self, get_FileContext_v3): + ctx, _ = get_FileContext_v3 + + assert isinstance(ctx.filepath_as_path, Path) + + def test_property_filepath_base_as_path_pass(self, get_FileContext_v3): + ctx, _ = get_FileContext_v3 + + assert isinstance(ctx.filepath_base_as_path, Path) -class TestPathFrom: + +class TestGenerateAbsPath: """Test PathResolver""" @staticmethod - def test_absolute_pass(): + def test_generate_abs_path_pass(): ctx = FileContext.from_file(tests.RES_DIR + "bitcoin-usd.chk") - p = PathFrom(Path(ctx.filepath)) + p_base = ctx.filepath path_1 = "tests/resources/storage/sample_config/bitcoin-usd-testcase-data.chk" path_2 = "tests/resources/storage/sample_config/some-folder/bitcoin-usd-testcase-data.chk" @@ -100,13 +138,23 @@ def test_absolute_pass(): path_3 = path_3.replace("/", "\\") path_4 = path_4.replace("/", "\\") - assert path_1 in p.absolute("./bitcoin-usd-testcase-data.chk") - assert path_2 in p.absolute("./some-folder/bitcoin-usd-testcase-data.chk") - assert path_3 in p.absolute("./../bitcoin-usd-testcase-data.chk") - assert path_1 in p.absolute("./some-folder/../bitcoin-usd-testcase-data.chk") - assert path_3 in p.absolute("../some-folder/../bitcoin-usd-testcase-data.chk") - assert path_4 in p.absolute("../some-folder/./bitcoin-usd-testcase-data.chk") - assert path_2 in p.absolute("./some-folder////bitcoin-usd-testcase-data.chk") + assert path_1 in generate_abs_path(p_base, "./bitcoin-usd-testcase-data.chk") + assert path_2 in generate_abs_path( + p_base, "./some-folder/bitcoin-usd-testcase-data.chk" + ) + assert path_3 in generate_abs_path(p_base, "./../bitcoin-usd-testcase-data.chk") + assert path_1 in generate_abs_path( + p_base, "./some-folder/../bitcoin-usd-testcase-data.chk" + ) + assert path_3 in generate_abs_path( + p_base, "../some-folder/../bitcoin-usd-testcase-data.chk" + ) + assert path_4 in generate_abs_path( + p_base, "../some-folder/./bitcoin-usd-testcase-data.chk" + ) + assert path_2 in generate_abs_path( + p_base, "./some-folder////bitcoin-usd-testcase-data.chk" + ) FILE_PATH = "tests/resources/storage/sample_config/" @@ -125,12 +173,6 @@ def test_fail_with_default_allowed_list_file_not_found(self): with pytest.raises(FileNotFoundError): FileLoader.is_file_ok(file_name) - def test_fail_with_default_allowed_list_not_allowed_file(self): - file_name = FILE_PATH + "UserOk.yaml" - - with pytest.raises(LookupError): - FileLoader.is_file_ok(file_name) - def test_pass_with_given_allowed_list(self): file_name = FILE_PATH + "UserOk.yaml" assert FileLoader.is_file_ok(file_name, [".chk", ".yaml", ".yml"]) is True diff --git a/tests/infrastructure/helper_test.py b/tests/infrastructure/helper_test.py index ced73b1c..c9ab8658 100644 --- a/tests/infrastructure/helper_test.py +++ b/tests/infrastructure/helper_test.py @@ -14,6 +14,7 @@ is_scalar, Cast, formatter, + slugify, ) @@ -276,3 +277,30 @@ def fmt(val): # assert assert out == "Hello, I am Some One. I am 43 years old.\n" + + +class TestSlugify: + @staticmethod + def test_slugify_pass(): + a = "Some one goes here" + assert slugify(a) == "some-one-goes-here" + + a = "Some ONE GOES HERE" + assert slugify(a) == "some-one-goes-here" + + a = "So'me ONE GO_ES HERE" + assert slugify(a) == "some-one-go-es-here" + + @staticmethod + def test_slugify_fail(): + a = {} + with pytest.raises(TypeError): + slugify(a) + + a = [] + with pytest.raises(TypeError): + slugify(a) + + a = 1 + with pytest.raises(TypeError): + slugify(a) diff --git a/tests/infrastructure/logging_test.py b/tests/infrastructure/logging_test.py new file mode 100644 index 00000000..a818e486 --- /dev/null +++ b/tests/infrastructure/logging_test.py @@ -0,0 +1,41 @@ +""" +Logging Module Test +""" + +from chk.infrastructure.logging import LoggingManager, create_session_id + + +class TestGetLogFile: + """TestGetLogFile""" + + @staticmethod + def test_create_cache_dir(): + """test_get_log_file""" + + p = LoggingManager.create_cache_dir() + + assert p.exists() and p.is_dir() + + @staticmethod + def test_create_log_dir(): + """test_get_log_file""" + + p = LoggingManager.create_cache_dir() + q = LoggingManager.create_log_dir(p) + + assert q.exists() and q.is_dir() + + @staticmethod + def test_create_new_log_file(): + """test_get_log_file""" + + q = LoggingManager.create_new_log_file() + + assert q.exists() and q.is_file() + q.unlink() + + +def test_create_session_id(): + new_session = create_session_id() + + assert isinstance(new_session, str) diff --git a/tests/infrastructure/symbol_table_test.py b/tests/infrastructure/symbol_table_test.py index 38c8710a..8fcf0746 100644 --- a/tests/infrastructure/symbol_table_test.py +++ b/tests/infrastructure/symbol_table_test.py @@ -5,12 +5,12 @@ import json from chk.console.main import combine_initial_variables -from chk.infrastructure.file_loader import FileContext, ExecuteContext +from chk.infrastructure.file_loader import ExecuteContext, FileContext from chk.infrastructure.symbol_table import ( + ExposeManager, VariableConfigNode, - Variables, VariableTableManager, - ExposeManager, + Variables, replace_value, ) from chk.modules.fetch import HttpDocument @@ -276,9 +276,6 @@ def test_get_exposed_replaced_data_pass_returns_empty_list(): } file_ctx = FileContext(filepath_hash="ab12", document=document) - exec_ctx = ExecuteContext( - arguments={VariableConfigNode.VARIABLES: {"extension": ".org"}} - ) http_doc = HttpDocument.from_file_context(file_ctx) @@ -286,7 +283,7 @@ def test_get_exposed_replaced_data_pass_returns_empty_list(): http_doc, {"a": 1, "b": 2} ) - assert isinstance(exposed_data, list) + assert isinstance(exposed_data, dict) assert len(exposed_data) == 0 @staticmethod @@ -314,7 +311,7 @@ def test_get_exposed_replaced_data_pass_returns_nonempty_list(): http_doc, {**variable_doc.data, **{"_response": {"a": 1, "b": 2}}} ) - assert isinstance(exposed_data, list) + assert isinstance(exposed_data, dict) assert len(exposed_data) == 1 - assert isinstance(exposed_data[0], dict) - assert len(exposed_data[0]) == 2 + assert isinstance(exposed_data["_response"], dict) + assert len(exposed_data["_response"]) == 2 diff --git a/tests/infrastructure/third_party/http_fetcher_test.py b/tests/infrastructure/third_party/http_fetcher_test.py index 0c00011b..b64eaadf 100644 --- a/tests/infrastructure/third_party/http_fetcher_test.py +++ b/tests/infrastructure/third_party/http_fetcher_test.py @@ -5,7 +5,7 @@ import requests from requests.structures import CaseInsensitiveDict -from chk.infrastructure.third_party.http_fetcher import ApiResponse, fetch +from chk.modules.fetch import ApiResponse class TestApiResponse: @@ -33,17 +33,3 @@ class SampleCls: str(api_response.as_fmt_str) == """HTTP/1.0 200 Ok\r\n\r\nAccept: application/json\r\nContent-Type: application/json\r\nApplication: internal\r\n\r\n{"success": "ok"}""" ) - - -class TestFetch: - @staticmethod - def test_fetch_pass(): - resp = fetch( - { - "url": "https://dummyjson.com/products/1", - "method": "GET", - "headers": {"Accept": "application/json", "Application": "pytest"}, - } - ) - - assert isinstance(resp, ApiResponse) diff --git a/tests/modules/fetch/fetch_entities_test.py b/tests/modules/fetch/fetch_entities_test.py index 9343fdc9..abbd8bb8 100644 --- a/tests/modules/fetch/fetch_entities_test.py +++ b/tests/modules/fetch/fetch_entities_test.py @@ -1,18 +1,16 @@ # type: ignore -import dataclasses import json -import sys -import urllib -from io import BytesIO, TextIOWrapper import pytest -import requests -from requests.structures import CaseInsensitiveDict -from chk.infrastructure.file_loader import FileContext, ExecuteContext +from chk.infrastructure.file_loader import FileContext from chk.infrastructure.symbol_table import Variables -from chk.infrastructure.third_party.http_fetcher import ApiResponse -from chk.modules.fetch import ApiResponseDict, HttpDocument, HttpDocumentSupport +from chk.modules.fetch import ( + ApiResponse, + ApiResponseDict, + HttpDocument, + HttpDocumentSupport, +) class TestApiResponseDict: @@ -158,50 +156,3 @@ def test_process_request_template_pass(): def test_build_schema_pass(): x = HttpDocumentSupport.build_schema() assert len(x) == 4 - - @staticmethod - def test_display_pass(): - @dataclasses.dataclass - class SampleCls: - version: int - - cid = CaseInsensitiveDict() - cid["Accept"] = "application/json" - cid["Content-Type"] = "application/json" - cid["Application"] = "internal" - - resp = requests.Response() - resp._content = b'{"success": "ok"}' - resp.status_code = 200 - resp.url = "https://valid.url" - resp.raw = SampleCls(10) - resp.reason = "Ok" - resp.headers = cid - - api_response = ApiResponse.from_response(resp) - - execution_ctx = ExecuteContext( - options={ - "dump": True, - "format": False, - } - ) - - old_stdout = sys.stdout - sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding) - - HttpDocumentSupport.display([api_response.data], execution_ctx) - - # get output - sys.stdout.seek(0) # jump to the start - out = sys.stdout.read() # read output - - # restore stdout - sys.stdout.close() - sys.stdout = old_stdout - - # assert - assert ( - out - == '{"code": 200, "info": "HTTP/1.0 200 Ok", "headers": {"Accept": "application/json", "Content-Type": "application/json", "Application": "internal"}, "body": {"success": "ok"}}\n' - ) diff --git a/tests/modules/fetch/fetch_module_test.py b/tests/modules/fetch/fetch_module_test.py new file mode 100644 index 00000000..3bd16c24 --- /dev/null +++ b/tests/modules/fetch/fetch_module_test.py @@ -0,0 +1,31 @@ +# type: ignore + +""" +Fetch module tests +""" +from chk.infrastructure.file_loader import FileContext, ExecuteContext +from chk.modules.fetch import call + + +class TestFetchCall: + @staticmethod + def test_call_pass(): + file_ctx = FileContext( + document={ + "version": "default:http:0.7.2", + "request": { + "url": "https://jsonplaceholder.typicode.com/albums/1", + "method": "GET", + }, + } + ) + + exec_ctx = ExecuteContext( + options={ + "dump": True, + "format": False, + } + ) + + er = call(file_ctx, exec_ctx) + assert "_response" in er.variables_exec diff --git a/tests/modules/fetch/fetch_request_helper_test.py b/tests/modules/fetch/fetch_request_helper_test.py index 45b7a824..67af3420 100644 --- a/tests/modules/fetch/fetch_request_helper_test.py +++ b/tests/modules/fetch/fetch_request_helper_test.py @@ -2,8 +2,7 @@ import pytest -from chk.infrastructure.third_party.http_fetcher import BearerAuthentication -from chk.modules.fetch import HttpRequestArgCompiler +from chk.modules.fetch import BearerAuthentication, HttpRequestArgCompiler class TestHttpRequestArgCompiler: diff --git a/tests/modules/validate/assertion_services_test.py b/tests/modules/validate/assertion_services_test.py index f938aae7..64e1bb9c 100644 --- a/tests/modules/validate/assertion_services_test.py +++ b/tests/modules/validate/assertion_services_test.py @@ -3,21 +3,18 @@ Test assertion_services """ import copy -import datetime import types -import uuid import pytest -from chk.infrastructure.file_loader import FileContext, ExecuteContext -from chk.infrastructure.symbol_table import Variables, VariableTableManager +from chk.infrastructure.file_loader import ExecuteContext, FileContext +from chk.infrastructure.symbol_table import VariableTableManager, Variables from chk.modules.validate import ValidationDocument, ValidationDocumentSupport from chk.modules.validate.assertion_services import ( - AssertionEntryListRunner, - AllTestRunResult, - SingleTestRunResult, AssertionEntry, + AssertionEntryListRunner, ) +from chk.modules.validate.entities import RunReport @pytest.fixture @@ -121,7 +118,7 @@ def test_test_run_pass(setup_assertion_entry_list_pass_assert): assert_list, variables = setup_assertion_entry_list_pass_assert test_run_result = AssertionEntryListRunner.test_run(assert_list, variables.data) - assert isinstance(test_run_result, AllTestRunResult) + assert isinstance(test_run_result, RunReport) @staticmethod def test__replace_assertion_values_pass(setup_assertion_entry_list_pass_assert): @@ -168,96 +165,22 @@ def test__prepare_test_run_result(setup_assertion_entry_list_pass_assert): assert_resp = AssertionEntryListRunner._call_assertion_method(assert_list[0]) assert_item = assert_list[0] - resp = SingleTestRunResult(assert_used=assert_item) - AssertionEntryListRunner._prepare_test_run_result( - resp, assert_item, assert_resp + resp = AssertionEntryListRunner._prepare_test_run_result( + assert_item, assert_resp ) - assert resp["assert_used"] == assert_item - assert not resp["is_pass"] - assert resp["message"] - - -@pytest.fixture -def setup_new_single_test_run_result(): - s = SingleTestRunResult() - - s["is_pass"] = True - s["message"] = "Ok" - s["assert_used"] = AssertionEntry( - assert_type="Empty", - actual="39", - actual_given="<% _data.roll %>", - expected=39, - ) - - return s - - -class TestSingleTestRunResult: - @staticmethod - def test_create(setup_new_single_test_run_result): - s = SingleTestRunResult() - assert len(s) == 0 - - s = setup_new_single_test_run_result - assert len(s) == 3 - - @staticmethod - def test_as_dict(setup_new_single_test_run_result): - s = setup_new_single_test_run_result - assert isinstance(s.as_dict, dict) - - @staticmethod - def test_as_fmt_str(setup_new_single_test_run_result): - s = setup_new_single_test_run_result - - assert isinstance(s.as_fmt_str, str) - assert s.as_fmt_str == "\n+ Empty PASSED, Ok" - - -@pytest.fixture -def setup_new_all_test_run_result(setup_new_single_test_run_result): - s = AllTestRunResult() - - s["id"] = uuid.uuid4() - s["time_start"] = datetime.datetime.now() - s["time_end"] = datetime.datetime.now() - s["count_all"] = 1 - s["results"] = [setup_new_single_test_run_result] - s["count_fail"] = 0 - - return s - - -class TestAllTestRunResult: - @staticmethod - def test_create(setup_new_all_test_run_result): - s = AllTestRunResult() - assert len(s) == 0 - - s = setup_new_all_test_run_result - assert len(s) == 6 - - @staticmethod - def test_as_dict(setup_new_all_test_run_result): - s = setup_new_all_test_run_result - assert isinstance(s.as_dict, dict) - - @staticmethod - def test_as_fmt_str(setup_new_all_test_run_result): - s = setup_new_all_test_run_result - - assert isinstance(s.as_fmt_str, str) + assert resp.assert_entry == assert_item + assert not resp.is_pass + assert resp.message class TestAssertionEntry: @staticmethod def test_create(): ae = AssertionEntry( - "Empty", - "10", - 10, + assert_type="Empty", + actual="10", + expected=10, ) assert isinstance(ae, AssertionEntry) @@ -267,7 +190,11 @@ def test_create(): @staticmethod def test_copy(): ae = AssertionEntry( - "Empty", "10", 10, cast_actual_to="int", extra_fields={"a": 1} + assert_type="Empty", + actual="10", + expected=10, + cast_actual_to="int", + extra_fields={"a": 1}, ) ar = copy.copy(ae) diff --git a/tests/modules/validate/validate_entity_test.py b/tests/modules/validate/validate_entity_test.py index 021758b4..2e706a22 100644 --- a/tests/modules/validate/validate_entity_test.py +++ b/tests/modules/validate/validate_entity_test.py @@ -117,8 +117,8 @@ def test_as_dict_pass(): doc = ValidationDocument.from_file_context(ctx) - assert isinstance(doc.as_dict, dict) - assert len(doc.as_dict) == 4 + assert isinstance(doc.model_dump(), dict) + assert len(doc.model_dump()) == 4 class TestValidationDocumentSupport: @@ -156,7 +156,7 @@ def test_set_data_template_pass_when_set_from_exec_ctx(self): variables = Variables() ValidationDocumentSupport.set_data_template(doc, variables, exec_ctx) - assert bool(variables[ValidationConfigNode.VAR_NODE]) + assert bool(variables[ValidationConfigNode.VAR_NODE.value]) def test_set_data_template_pass_when_set_from_doc(self): ctx = FileContext( @@ -185,7 +185,7 @@ def test_set_data_template_pass_when_set_from_doc(self): variables = Variables() ValidationDocumentSupport.set_data_template(doc, variables, exec_ctx) - assert bool(variables[ValidationConfigNode.VAR_NODE]) + assert bool(variables[ValidationConfigNode.VAR_NODE.value]) def test_set_data_template_pass_exec_ctx_prioritise(self): ctx = FileContext( @@ -215,8 +215,11 @@ def test_set_data_template_pass_exec_ctx_prioritise(self): variables = Variables() ValidationDocumentSupport.set_data_template(doc, variables, exec_ctx) - assert bool(variables[ValidationConfigNode.VAR_NODE]) - assert variables[ValidationConfigNode.VAR_NODE].get("name") == "Some Name Two" + assert bool(variables[ValidationConfigNode.VAR_NODE.value]) + assert ( + variables[ValidationConfigNode.VAR_NODE.value].get("name") + == "Some Name Two" + ) @staticmethod def test_process_data_template_pass(): @@ -251,8 +254,11 @@ def test_process_data_template_pass(): ValidationDocumentSupport.set_data_template(doc, variables, exec_ctx) ValidationDocumentSupport.process_data_template(variables) - assert bool(variables[ValidationConfigNode.VAR_NODE]) - assert variables[ValidationConfigNode.VAR_NODE].get("greet") == "Hello Somebody" + assert bool(variables[ValidationConfigNode.VAR_NODE.value]) + assert ( + variables[ValidationConfigNode.VAR_NODE.value].get("greet") + == "Hello Somebody" + ) @staticmethod def test_make_assertion_entry_list_pass(): diff --git a/tests/modules/workflow/wf_domain_test.py b/tests/modules/workflow/wf_domain_test.py new file mode 100644 index 00000000..2f20eada --- /dev/null +++ b/tests/modules/workflow/wf_domain_test.py @@ -0,0 +1,62 @@ +# type: ignore +""" +Domain test code +""" +from pydantic import BaseModel, Field + +from chk.infrastructure.symbol_table import Variables +from chk.modules.workflow import WorkflowDocument, WorkflowDocumentSupport +from chk.modules.workflow.entities import ChkwareTask, WorkflowUses +from tests import SPEC_DIR, load_chk_file, load_file_ctx_for_file + + +class TestWorkflowDocument: + @staticmethod + def test_from_file_context_pass(load_chk_file, load_file_ctx_for_file): + + class TaskDict(BaseModel): + file: str = Field(default=str) + name: str = Field(default=str) + uses: WorkflowUses + + filepath = f"{SPEC_DIR}workflow/simple-btc-wf.chk" + file_ctx = load_file_ctx_for_file(filepath) + + wfdoc = WorkflowDocument.from_file_context(file_ctx) + assert wfdoc.id == "simple-btc-workflow-1" + + assert isinstance(wfdoc.tasks, list) + assert all(TaskDict(**i) for i in wfdoc.tasks) + + @staticmethod + def test_from_file_context_pass_noid(load_chk_file, load_file_ctx_for_file): + + class TaskDict(BaseModel): + file: str = Field(default=str) + name: str = Field(default=str) + uses: WorkflowUses + + filepath = f"{SPEC_DIR}workflow/simple-btc-wf.chk" + file_ctx = load_file_ctx_for_file(filepath) + + del file_ctx.document["id"] # remove document id + wfdoc = WorkflowDocument.from_file_context(file_ctx) + + # check if document id defaults to file name + assert wfdoc.id == "simple-btc-workflow" + + assert isinstance(wfdoc.tasks, list) + assert all(TaskDict(**i) for i in wfdoc.tasks) + + +class TestWorkflowDocumentSupport: + @staticmethod + def test_process_task_template_pass(load_chk_file, load_file_ctx_for_file): + # filepath = f"{SPEC_DIR}workflow/simple-btc-wf.chk" + filepath = f"{SPEC_DIR}workflow_cases/simple/coinstats-usd-workflow.chk" + file_ctx = load_file_ctx_for_file(filepath) + + wfdoc = WorkflowDocument.from_file_context(file_ctx) + + rpt = WorkflowDocumentSupport.process_task_template(wfdoc, Variables({"_steps": []})) + assert isinstance(rpt, list) diff --git a/tests/modules/workflow/wf_entities_test.py b/tests/modules/workflow/wf_entities_test.py new file mode 100644 index 00000000..99080add --- /dev/null +++ b/tests/modules/workflow/wf_entities_test.py @@ -0,0 +1,96 @@ +# type: ignore +""" +Testing Entities for workflow +""" +from pathlib import Path + +import pytest +from pydantic import ValidationError + +from chk.modules.workflow.entities import ChkwareTask, ChkwareValidateTask + +fp_p = ( + Path.cwd() + / "tests" + / "resources" + / "storage" + / "spec_docs" + / "workflow_cases" + / "simple" +) + +fp = str(fp_p) + + +class TestChkwareTask: + @staticmethod + def test_from_dict_pass(): + + task = ChkwareTask( + fp, + **{ + "name": "Sample fetch", + "uses": "fetch", + "file": "./coinstats-usd-validate.chk", + }, + ) + + assert task.uses == "fetch" + assert task.file == str(fp_p / "coinstats-usd-validate.chk") + + +class TestChkwareValidateTask: + @staticmethod + def test_from_dict_pass(): + task = ChkwareValidateTask( + fp, + **{ + "name": "Sample validate", + "uses": "validate", + "file": "./coinstats-usd-validate.chk", + }, + ) + + assert task.uses == "validate" + assert task.file == str(fp_p / "coinstats-usd-validate.chk") + assert isinstance(task.arguments, ChkwareValidateTask.ChkwareTaskDataArgument) + + task = ChkwareValidateTask( + fp, + **{ + "name": "Sample validate - 2", + "uses": "validate", + "file": "./coinstats-usd-validate.chk", + "arguments": {"data": {"some": 12, "data": [1, "a"]}}, + }, + ) + + assert task.arguments and "data" in task.arguments.model_dump() + + @staticmethod + def test_from_dict_fail(): + with pytest.raises(ValidationError): + ChkwareValidateTask( + fp, + **{ + "name": "Sample validate - 3", + "uses": "validate", + "file": "./coinstats-usd-validate.chk", + "arguments": { + "data": {"some": 12, "data": [1, "a"]}, + "other_data": 1, + }, + }, + ) + + with pytest.raises(ValidationError): + ChkwareValidateTask( + fp, + **{ + "name": "Sample validate - 4", + "uses": "validate", + "file": "./coinstats-usd-validate.chk", + "other_data": 1, + "arguments": {"data": {"some": 12, "data": [1, "a"]}}, + }, + ) diff --git a/tests/modules/workflow/wf_module_test.py b/tests/modules/workflow/wf_module_test.py new file mode 100644 index 00000000..f36d6e12 --- /dev/null +++ b/tests/modules/workflow/wf_module_test.py @@ -0,0 +1,51 @@ +# type: ignore +""" +Workflow module test +""" + +from chk.infrastructure.symbol_table import ExecResponse +from chk.modules.workflow import call, execute +from tests import SPEC_DIR, get_exec_ctx, load_chk_file, load_file_ctx_for_file + + +class TestWorkflowExecute: + @staticmethod + def test_execute_pass_vars( + load_chk_file, load_file_ctx_for_file, get_exec_ctx, capsys + ): + """test_execute""" + + filepath = f"{SPEC_DIR}workflow_cases/get-req-vars/coinstats-usd-workflow.chk" + file_ctx = load_file_ctx_for_file(filepath) + execution_ctx = get_exec_ctx(True) + + execute(file_ctx, execution_ctx) + + captured = capsys.readouterr() + assert "======" in captured.out + + @staticmethod + def test_execute_pass_args( + load_chk_file, load_file_ctx_for_file, get_exec_ctx, capsys + ): + """test_execute""" + + filepath = f"{SPEC_DIR}workflow_cases/pass_data/coinstats-usd-workflow.chk" + file_ctx = load_file_ctx_for_file(filepath) + execution_ctx = get_exec_ctx(True) + + execute(file_ctx, execution_ctx) + + captured = capsys.readouterr() + assert "======" in captured.out + + @staticmethod + def test_call(load_chk_file, load_file_ctx_for_file, get_exec_ctx): + """test_call""" + + filepath = f"{SPEC_DIR}workflow_cases/get-req-vars/coinstats-usd-workflow.chk" + file_ctx = load_file_ctx_for_file(filepath) + execution_ctx = get_exec_ctx(True) + + er = call(file_ctx, execution_ctx) + assert isinstance(er, ExecResponse) diff --git a/tests/resources/storage/spec_docs/fetch/bitcoin-usd.chk b/tests/resources/storage/spec_docs/fetch/bitcoin-usd.chk index 28f0e9ac..1973269f 100644 --- a/tests/resources/storage/spec_docs/fetch/bitcoin-usd.chk +++ b/tests/resources/storage/spec_docs/fetch/bitcoin-usd.chk @@ -2,8 +2,10 @@ version: default:http:0.7.2 request: - url: https://api.coinstats.app/public/v1/coins/bitcoin?currency=USD + url: https://openapiv1.coinstats.app/coins/bitcoin?currency=USD method: GET + headers: + "X-API-KEY": <% _ENV.COINSTATS_API_KEY %> expose: - <% _response %> diff --git a/tests/resources/storage/spec_docs/validate/btc-usd-validate.chk b/tests/resources/storage/spec_docs/validate/btc-usd-validate.chk index cc651594..6b4f6729 100644 --- a/tests/resources/storage/spec_docs/validate/btc-usd-validate.chk +++ b/tests/resources/storage/spec_docs/validate/btc-usd-validate.chk @@ -3,16 +3,13 @@ version: default:validation:0.7.2 asserts: - { type: Equal, actual: <% _data.code %>, expected: 200, msg_pass: 'Response was successful with code `{type_actual}({value_actual})`' } - - { type: List, actual: <% _data.headers %>, msg_pass: 'Header is a list', msg_fail: 'Header is not a list' } + - { type: Map, actual: <% _data.headers %>, msg_pass: 'Header is a list', msg_fail: 'Header is not a list' } - { type: Str, actual: <% _data.code %>, msg_pass: 'Code `{type_actual}({value_actual})` is a string', msg_fail: 'Code `{type_actual}({value_actual})` is not a string', cast_actual_to: int } - { type: StrHave, actual: <% _data.info %>, other: "200 OK" } - - { type: Map, actual: <% _data.body.coin %> } - - { type: Map, actual: <% _data.body.coin %> } - - { type: List, actual: <% _data.body.coin.exp %> } - - { type: List, actual: <% _data.body.coin.exp %> } - - { type: Float, actual: <% _data.body.coin.price %> } - - { type: Float, actual: <% _data.body.coin.price %> } - - { type: MapHasKeys, actual: <% _data.body.coin %>, expected: [ "price", "priceBtc", "priceChange1d", "priceChange1h", "priceChange1w" ] } + - { type: Map, actual: <% _data.body %> } + - { type: List, actual: <% _data.body.explorers %> } + - { type: Float, actual: <% _data.body.price %> } + - { type: MapHasKeys, actual: <% _data.body %>, expected: [ "price", "priceBtc", "priceChange1d", "priceChange1h", "priceChange1w" ] } expose: - <% _asserts_response %> diff --git a/tests/resources/storage/spec_docs/validate/simple-external-data-test.chk b/tests/resources/storage/spec_docs/validate/simple-external-data-test.chk new file mode 100644 index 00000000..f53372f8 --- /dev/null +++ b/tests/resources/storage/spec_docs/validate/simple-external-data-test.chk @@ -0,0 +1,10 @@ +--- +version: default:validation:0.7.2 + +asserts: + - { type: Equal, actual: <% _data.code %>, expected: 200 } + - { type: IntegerGreaterOrEqual, actual: <% _data.age %>, other: 18 } + +expose: + - <% _asserts_response %> + - <% _data %> diff --git a/tests/resources/storage/spec_docs/workflow/simple-btc-wf.chk b/tests/resources/storage/spec_docs/workflow/simple-btc-wf.chk new file mode 100644 index 00000000..47af6d4b --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow/simple-btc-wf.chk @@ -0,0 +1,17 @@ +version: default:workflow:0.8.0 + +name: simple Btc workflow +id: simple Btc workflow 1 + +tasks: + - name: Fetch some content from URL + uses: fetch + file: ../fetch/bitcoin-usd.chk +# variables: { "var": 2 } + + - name: Validate the content is okay + uses: validate + file: ../validate/btc-usd-validate.chk +# variables: { "var": 2 } +# arguments: +# data: <% _tasks.0._expose.0 %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/get-req-vars/coinstats-usd-request.chk b/tests/resources/storage/spec_docs/workflow_cases/get-req-vars/coinstats-usd-request.chk new file mode 100644 index 00000000..f368902c --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/get-req-vars/coinstats-usd-request.chk @@ -0,0 +1,16 @@ +--- +version: default:http:0.7.2 + +variables: + convertCurrency: USD + +request: + url: "https://openapiv1.coinstats.app/coins/bitcoin" + url_params: + currency: <% convertCurrency %> + method: GET + headers: + "X-API-KEY": <% _ENV.COINSTATS_API_KEY %> + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/get-req-vars/coinstats-usd-validate.chk b/tests/resources/storage/spec_docs/workflow_cases/get-req-vars/coinstats-usd-validate.chk new file mode 100644 index 00000000..db55b61e --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/get-req-vars/coinstats-usd-validate.chk @@ -0,0 +1,23 @@ +--- +version: default:validation:0.7.2 + +variables: + vdata: ~ + +asserts: + - { type: Equal, actual: <% vdata.code %>, expected: 200, msg_pass: 'Response was successful with code `{type_actual}({value_actual})`', cast_actual_to: int } + - { type: Map, actual: <% vdata.headers %>, msg_pass: 'Header is a list', msg_fail: 'Header is not a list' } + - { type: Str, actual: <% vdata.code %>, msg_pass: 'Code `{type_actual}({value_actual})` is a string', msg_fail: 'Code `{type_actual}({value_actual})` is not a string', cast_actual_to: int } + - { type: StrHave, actual: <% vdata.info %>, other: "200 OK" } + - { type: Map, actual: <% vdata.body %> } + - { type: List, actual: <% vdata.body.explorers %> } + - { type: Float, actual: <% vdata.body.price %> } + - { type: MapHasKeys, actual: <% vdata.body %>, expected: [ "price", "priceBtc", "priceChange1d", "priceChange1h", "priceChange1w" ] } + +expose: + - <% _asserts_response %> + - <% vdata %> + +# --- shell command --- +# pipenv run python -m chk validate -nf tests/resources/storage/spec_docs/validate/btc-usd-validate.chk -D "$(pipenv run python -m chk fetch -nf tests/resources/storage/spec_docs/fetch/bitcoin-usd.chk)" +# --- diff --git a/tests/resources/storage/spec_docs/workflow_cases/get-req-vars/coinstats-usd-workflow.chk b/tests/resources/storage/spec_docs/workflow_cases/get-req-vars/coinstats-usd-workflow.chk new file mode 100644 index 00000000..c091d7a3 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/get-req-vars/coinstats-usd-workflow.chk @@ -0,0 +1,25 @@ +version: default:workflow:0.8.0 + +name: simple Btc workflow +id: BtcWF1 + +variables: + group: coinstats + currency: GBP + +tasks: + - name: Fetch some content from URL + uses: fetch + file: "./<% group %>-usd-request.chk" + variables: + convertCurrency: <% currency %> + + - name: Validate the content is okay + uses: validate + file: "./<% group %>-usd-validate.chk" + variables: + vdata: <% _steps.0._response %> + +expose: + - <% _steps %> + - <% _steps.1.vdata.body %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/get-contacts.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/get-contacts.yml new file mode 100644 index 00000000..75c1556b --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/get-contacts.yml @@ -0,0 +1,9 @@ +--- +version: default:http:0.7.2 + +request: + url: "https://dev-api.lozoacademy.com/api/contact-information" + method: GET + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/me-req.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/me-req.yml new file mode 100644 index 00000000..109a24b5 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/me-req.yml @@ -0,0 +1,16 @@ +--- +version: default:http:0.7.2 + +variables: + jwtToken: ~ + +request: + url: "https://dev-api.lozoacademy.com/api/users/me" + method: GET + headers: + "Content-Type": "application/json; charset=utf-8" + auth .scm=bearer: + token: <% jwtToken %> + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/me-valid.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/me-valid.yml new file mode 100644 index 00000000..69d54565 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/me-valid.yml @@ -0,0 +1,8 @@ +--- +version: default:validation:0.7.2 + +asserts: + - { type: MapHasKeys, actual: <% _data %>, expected: ["id", "username", "name", "email", "provider"] } + +expose: + - <% _asserts_response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/profile-avater-req.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/profile-avater-req.yml new file mode 100644 index 00000000..aaf22144 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/profile-avater-req.yml @@ -0,0 +1,16 @@ +--- +version: default:http:0.7.2 + +variables: + jwtToken: ~ + +request: + url: "https://dev-api.lozoacademy.com/api/profile-avatars" + method: GET + headers: + "Content-Type": "application/json; charset=utf-8" + auth .scm=bearer: + token: <% jwtToken %> + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/register-req.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/register-req.yml new file mode 100644 index 00000000..0cf016b9 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/register-req.yml @@ -0,0 +1,20 @@ +--- +version: default:http:0.7.2 + +variables: + email: ~ + username: ~ + password: "a12345678" + +request: + url: "https://dev-api.lozoacademy.com/api/auth/local/register" + method: POST + body .enc=json: + email: <% email %> + password: <% password %> + username: <% username %> + headers: + "Content-Type": "application/json; charset=utf-8" + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/register-valid.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/register-valid.yml new file mode 100644 index 00000000..f1703077 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/register-valid.yml @@ -0,0 +1,16 @@ +--- +version: default:validation:0.7.2 + +variables: + userName: ~ + +asserts: + - { type: Str, actual: <% _data.body.jwt %>, msg_pass: '`jwt` is string.' } + - { type: Integer, actual: <% _data.body.user.id %>, msg_pass: '`user.id` is an int', msg_fail: '`user.id` is not an int', cast_actual_to: int } + - { type: Equal, actual: <% _data.body.user.username %>, expected: <% userName %> , msg_pass: '`user.username` matches', msg_fail: '`user.username` do not matches'} + - { type: Map, actual: <% _data.body.user %>, msg_pass: '`user` is a map', msg_fail: '`user` is not a map' } + - { type: StrHave, actual: <% _data.body.user.email %>, other: "@gmail.com" } + - { type: MapHasKeys, actual: <% _data.body.user %>, expected: ["id", "username", "email", "provider", "confirmed", "blocked", "gender", "name", "grade", "createdAt", "updatedAt", "age", "phoneNumber", "schoolName", "address", "isDisableOnLeaderBoard", "passwordChangedAt"] } + +expose: + - <% _asserts_response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/register-wf.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/register-wf.yml new file mode 100644 index 00000000..60394879 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/register-wf.yml @@ -0,0 +1,88 @@ +version: default:workflow:0.8.0 + +name: Lozo register workflow + +variables: + email: "hasanlock+lz25@gmail.com" + username: "lz25" + +tasks: + - name: Post user data for register + uses: fetch + file: "./register-req.yml" + variables: + email: <% email %> + username: <% username %> + + - name: Validate register response + uses: validate + file: "./register-valid.yml" + variables: + userName: <% username %> + arguments: + data: <% _steps.0._response %> + + - name: Update user's grade + uses: fetch + file: "./update-grade-req.yml" + variables: + userId: <% _steps.0._response.body.user.id %> + userGrade: 2 + jwtToken: <% _steps.0._response.body.jwt %> + + - name: Validate Update user's grade response + uses: validate + file: "./update-grade-valid.yml" + variables: + userName: <% username %> + arguments: + data: <% _steps.2._response %> + + - name: Get profile avaters + uses: fetch + file: "./profile-avater-req.yml" + variables: + jwtToken: <% _steps.0._response.body.jwt %> + + - name: Update user's profile avatar + uses: fetch + file: "./update-profile-avater-req.yml" + variables: + userId: <% _steps.0._response.body.user.id %> + profileAvatar: 27 + jwtToken: <% _steps.0._response.body.jwt %> + + - name: Validate update user's profile avatar response + uses: validate + file: "./update-grade-valid.yml" + variables: + userName: <% username %> + arguments: + data: <% _steps.5._response %> + + - name: Get user's streaks + uses: fetch + file: "./streaks-req.yml" + variables: + jwtToken: <% _steps.0._response.body.jwt %> + + - name: Validate user's streaks response + uses: validate + file: "./streaks-valid.yml" + arguments: + data: <% _steps.7._response.body %> + + - name: Get user's profile + uses: fetch + file: "./me-req.yml" + variables: + jwtToken: <% _steps.0._response.body.jwt %> + + - name: Validate user's profile response + uses: validate + file: "./me-valid.yml" + arguments: + data: <% _steps.9._response.body %> + +expose: + - <% _steps %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/streaks-req.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/streaks-req.yml new file mode 100644 index 00000000..fb627de9 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/streaks-req.yml @@ -0,0 +1,16 @@ +--- +version: default:http:0.7.2 + +variables: + jwtToken: ~ + +request: + url: "https://dev-api.lozoacademy.com/api/streaks" + method: GET + headers: + "Content-Type": "application/json; charset=utf-8" + auth .scm=bearer: + token: <% jwtToken %> + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/streaks-valid.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/streaks-valid.yml new file mode 100644 index 00000000..ce93fa00 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/streaks-valid.yml @@ -0,0 +1,8 @@ +--- +version: default:validation:0.7.2 + +asserts: + - { type: MapHasKeys, actual: <% _data %>, expected: ["userId", "currentStreak", "longestStreak", "lastStreaksDay"] } + +expose: + - <% _asserts_response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/update-grade-req.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/update-grade-req.yml new file mode 100644 index 00000000..234d1c69 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/update-grade-req.yml @@ -0,0 +1,21 @@ +--- +version: default:http:0.7.2 + +variables: + userGrade: ~ + userId: ~ + jwtToken: ~ + +request: + url: "https://dev-api.lozoacademy.com/api/users/<% userId %>" + method: PUT + body .enc=json: + grade: <% userGrade %> + id: <% userId %> + headers: + "Content-Type": "application/json; charset=utf-8" + auth .scm=bearer: + token: <% jwtToken %> + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/update-grade-valid.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/update-grade-valid.yml new file mode 100644 index 00000000..37891511 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/update-grade-valid.yml @@ -0,0 +1,13 @@ +--- +version: default:validation:0.7.2 + +variables: + userName: ~ + +asserts: + - { type: Equal, actual: <% _data.body.username %>, expected: <% userName %> , msg_pass: '`user.username` matches', msg_fail: '`user.username` do not matches'} + - { type: MapHasKeys, actual: <% _data.body %>, expected: ["id", "username", "email", "provider", "confirmed", "blocked", "gender", "name", "grade", "createdAt", "updatedAt", "age", "phoneNumber", "schoolName", "address", "isDisableOnLeaderBoard", "passwordChangedAt", "role"] } + - { type: MapHasKeys, actual: <% _data.body.role %>, expected: ["id", "name", "description", "type", "createdAt", "updatedAt"] } + +expose: + - <% _asserts_response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/update-profile-avater-req.yml b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/update-profile-avater-req.yml new file mode 100644 index 00000000..d07b4d3b --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/lozoacademy/register-flow/update-profile-avater-req.yml @@ -0,0 +1,21 @@ +--- +version: default:http:0.7.2 + +variables: + profileAvatar: ~ + userId: ~ + jwtToken: ~ + +request: + url: "https://dev-api.lozoacademy.com/api/users/<% userId %>" + method: PUT + body .enc=json: + profileAvatar: <% profileAvatar %> + id: <% userId %> + headers: + "Content-Type": "application/json; charset=utf-8" + auth .scm=bearer: + token: <% jwtToken %> + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/pass_data/coinstats-usd-request.chk b/tests/resources/storage/spec_docs/workflow_cases/pass_data/coinstats-usd-request.chk new file mode 100644 index 00000000..f368902c --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/pass_data/coinstats-usd-request.chk @@ -0,0 +1,16 @@ +--- +version: default:http:0.7.2 + +variables: + convertCurrency: USD + +request: + url: "https://openapiv1.coinstats.app/coins/bitcoin" + url_params: + currency: <% convertCurrency %> + method: GET + headers: + "X-API-KEY": <% _ENV.COINSTATS_API_KEY %> + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/pass_data/coinstats-usd-validate.chk b/tests/resources/storage/spec_docs/workflow_cases/pass_data/coinstats-usd-validate.chk new file mode 100644 index 00000000..fa09f69f --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/pass_data/coinstats-usd-validate.chk @@ -0,0 +1,20 @@ +--- +version: default:validation:0.7.2 + +asserts: + - { type: Equal, actual: <% vdata.code %>, expected: 200, msg_pass: 'Response was successful with code `{type_actual}({value_actual})`', cast_actual_to: int } + - { type: Map, actual: <% vdata.headers %>, msg_pass: 'Header is a list', msg_fail: 'Header is not a list' } + - { type: Str, actual: <% vdata.code %>, msg_pass: 'Code `{type_actual}({value_actual})` is a string', msg_fail: 'Code `{type_actual}({value_actual})` is not a string', cast_actual_to: int } + - { type: StrHave, actual: <% vdata.info %>, other: "200 OK" } + - { type: Map, actual: <% vdata.body %> } + - { type: List, actual: <% vdata.body.explorers %> } + - { type: Float, actual: <% vdata.body.price %> } + - { type: MapHasKeys, actual: <% vdata.body %>, expected: [ "price", "priceBtc", "priceChange1d", "priceChange1h", "priceChange1w" ] } + +expose: + - <% _asserts_response %> + - <% _data %> + +# --- shell command --- +# pipenv run python -m chk validate -nf tests/resources/storage/spec_docs/validate/btc-usd-validate.chk -D "$(pipenv run python -m chk fetch -nf tests/resources/storage/spec_docs/fetch/bitcoin-usd.chk)" +# --- diff --git a/tests/resources/storage/spec_docs/workflow_cases/pass_data/coinstats-usd-workflow.chk b/tests/resources/storage/spec_docs/workflow_cases/pass_data/coinstats-usd-workflow.chk new file mode 100644 index 00000000..1bfbe045 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/pass_data/coinstats-usd-workflow.chk @@ -0,0 +1,25 @@ +version: default:workflow:0.8.0 + +name: simple Btc workflow +id: BtcWF1 + +variables: + group: coinstats + currency: GBP + +tasks: + - name: Fetch some content from URL + uses: fetch + file: "./<% group %>-usd-request.chk" + variables: + convertCurrency: <% currency %> + + - name: Validate the content is okay + uses: validate + file: "./<% group %>-usd-validate.chk" + arguments: + data: <% _steps.0._response %> + +expose: + - <% _steps %> + - <% _steps.1._data.code %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/simple-error-1/coinstats-usd-request.chk b/tests/resources/storage/spec_docs/workflow_cases/simple-error-1/coinstats-usd-request.chk new file mode 100644 index 00000000..1973269f --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/simple-error-1/coinstats-usd-request.chk @@ -0,0 +1,11 @@ +--- +version: default:http:0.7.2 + +request: + url: https://openapiv1.coinstats.app/coins/bitcoin?currency=USD + method: GET + headers: + "X-API-KEY": <% _ENV.COINSTATS_API_KEY %> + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/simple-error-1/coinstats-usd-validate.chk b/tests/resources/storage/spec_docs/workflow_cases/simple-error-1/coinstats-usd-validate.chk new file mode 100644 index 00000000..a2e5113b --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/simple-error-1/coinstats-usd-validate.chk @@ -0,0 +1,19 @@ +--- +version: default:validation:0.7.2 + +asserts: + - { type: Equal, actual: <% _data.code %>, expected: 200, msg_pass: 'Response was successful with code `{type_actual}({value_actual})`' } + - { type: Map, actual: <% _data.headers %>, msg_pass: 'Header is a list', msg_fail: 'Header is not a list' } + - { type: Str, actual: <% _data.code %>, msg_pass: 'Code `{type_actual}({value_actual})` is a string', msg_fail: 'Code `{type_actual}({value_actual})` is not a string', cast_actual_to: int } + - { type: StrHave, actual: <% _data.info %>, other: "200 OK" } + - { type: Map, actual: <% _data.body %> } + - { type: List, actual: <% _data.body.explorers %> } + - { type: Float, actual: <% _data.body.price %> } + - { type: MapHasKeys, actual: <% _data.body %>, expected: [ "price", "priceBtc", "priceChange1d", "priceChange1h", "priceChange1w" ] } + +expose: + - <% _asserts_response %> + +# --- shell command --- +# pipenv run python -m chk validate -nf tests/resources/storage/spec_docs/validate/btc-usd-validate.chk -D "$(pipenv run python -m chk fetch -nf tests/resources/storage/spec_docs/fetch/bitcoin-usd.chk)" +# --- diff --git a/tests/resources/storage/spec_docs/workflow_cases/simple-error-1/coinstats-usd-workflow.chk b/tests/resources/storage/spec_docs/workflow_cases/simple-error-1/coinstats-usd-workflow.chk new file mode 100644 index 00000000..8d2cf381 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/simple-error-1/coinstats-usd-workflow.chk @@ -0,0 +1,22 @@ +version: default:workflow:0.8.0 + +name: simple Btc workflow +id: simple Btc workflow 1 + +tasks: +# - name: Fetch some content from URL +# uses: fetch +# file: ../simple/coinstats-usd-request.chk + + - name: Fetch some content from URL + uses: fetch + file: ./coinstats-usd-request.chk + + - name: Validate the content is okay + uses: validate + file: ./coinstats-usd-validate.chk + arguments: + data: <% _steps.0._response %> + +expose: + - <% _steps %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/simple/coinstats-usd-request.chk b/tests/resources/storage/spec_docs/workflow_cases/simple/coinstats-usd-request.chk new file mode 100644 index 00000000..1973269f --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/simple/coinstats-usd-request.chk @@ -0,0 +1,11 @@ +--- +version: default:http:0.7.2 + +request: + url: https://openapiv1.coinstats.app/coins/bitcoin?currency=USD + method: GET + headers: + "X-API-KEY": <% _ENV.COINSTATS_API_KEY %> + +expose: + - <% _response %> diff --git a/tests/resources/storage/spec_docs/workflow_cases/simple/coinstats-usd-validate.chk b/tests/resources/storage/spec_docs/workflow_cases/simple/coinstats-usd-validate.chk new file mode 100644 index 00000000..6b4f6729 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/simple/coinstats-usd-validate.chk @@ -0,0 +1,20 @@ +--- +version: default:validation:0.7.2 + +asserts: + - { type: Equal, actual: <% _data.code %>, expected: 200, msg_pass: 'Response was successful with code `{type_actual}({value_actual})`' } + - { type: Map, actual: <% _data.headers %>, msg_pass: 'Header is a list', msg_fail: 'Header is not a list' } + - { type: Str, actual: <% _data.code %>, msg_pass: 'Code `{type_actual}({value_actual})` is a string', msg_fail: 'Code `{type_actual}({value_actual})` is not a string', cast_actual_to: int } + - { type: StrHave, actual: <% _data.info %>, other: "200 OK" } + - { type: Map, actual: <% _data.body %> } + - { type: List, actual: <% _data.body.explorers %> } + - { type: Float, actual: <% _data.body.price %> } + - { type: MapHasKeys, actual: <% _data.body %>, expected: [ "price", "priceBtc", "priceChange1d", "priceChange1h", "priceChange1w" ] } + +expose: + - <% _asserts_response %> + - <% _data %> + +# --- shell command --- +# pipenv run python -m chk validate -nf tests/resources/storage/spec_docs/validate/btc-usd-validate.chk -D "$(pipenv run python -m chk fetch -nf tests/resources/storage/spec_docs/fetch/bitcoin-usd.chk)" +# --- diff --git a/tests/resources/storage/spec_docs/workflow_cases/simple/coinstats-usd-workflow.chk b/tests/resources/storage/spec_docs/workflow_cases/simple/coinstats-usd-workflow.chk new file mode 100644 index 00000000..82455d15 --- /dev/null +++ b/tests/resources/storage/spec_docs/workflow_cases/simple/coinstats-usd-workflow.chk @@ -0,0 +1,13 @@ +version: default:workflow:0.8.0 + +name: simple Btc workflow +id: simple Btc workflow 1 + +tasks: + - name: Fetch some content from URL + uses: fetch + file: ./coinstats-usd-request.chk + + - name: Validate the content is okay + uses: validate + file: ./coinstats-usd-validate.chk