diff --git a/.isort.cfg b/.isort.cfg index eae966e..f21b05a 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,2 +1,3 @@ [settings] known_third_party = atlassian,boto3,botocore,click,deepdiff,google,hcl2,jinja2,mergedeep,moto,pkg_resources,pytest,pytest_lazyfixture,tenacity,yaml +profile = black diff --git a/Makefile b/Makefile index 72dd7cf..01691d3 100644 --- a/Makefile +++ b/Makefile @@ -8,15 +8,15 @@ lint: init format: init poetry run black tfworker tests - @poetry run seed-isort-config || echo "known_third_party setting changed. Please commit pyproject.toml" + poetry run seed-isort-config || echo "known_third_party setting changed. Please commit pyproject.toml" poetry run isort tfworker tests test: init - poetry run pytest -p no:warnings + poetry run pytest -p no:warnings --disable-socket poetry run coverage report --fail-under=60 -m --skip-empty dep-test: init - poetry run pytest + poetry run pytest --disable-socket poetry run coverage report --fail-under=60 -m --skip-empty clean: diff --git a/poetry.lock b/poetry.lock index 39d1e15..9e9e607 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "alabaster" @@ -62,18 +62,18 @@ kerberos = ["requests-kerberos"] [[package]] name = "awscli" -version = "1.32.70" +version = "1.32.113" description = "Universal Command Line Environment for AWS." optional = false python-versions = ">=3.8" files = [ - {file = "awscli-1.32.70-py3-none-any.whl", hash = "sha256:630a6c8925c121fa658015a9ed52d53d3526e6005d7bd361dadb83c6f4ecc7c2"}, - {file = "awscli-1.32.70.tar.gz", hash = "sha256:41eaf622b5953d7c03838af34df85a6e23b4f811feaefeaddcee6d0ca59dce49"}, + {file = "awscli-1.32.113-py3-none-any.whl", hash = "sha256:755f98d8b24c64d4368a6df308ff2a6600b214d5e164eb68c19c5c28e0d5a5cd"}, + {file = "awscli-1.32.113.tar.gz", hash = "sha256:f24d485e16738927cd28f04718815cd2b47c7410c2cfd46b107f6f056492d6f8"}, ] [package.dependencies] -botocore = "1.34.70" -colorama = ">=0.2.5,<0.4.5" +botocore = "1.34.113" +colorama = ">=0.2.5,<0.4.7" docutils = ">=0.10,<0.17" PyYAML = ">=3.10,<6.1" rsa = ">=3.1.2,<4.8" @@ -81,13 +81,13 @@ s3transfer = ">=0.10.0,<0.11.0" [[package]] name = "babel" -version = "2.14.0" +version = "2.15.0" description = "Internationalization utilities" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, + {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, + {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, ] [package.extras] @@ -141,17 +141,17 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.34.70" +version = "1.34.113" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.70-py3-none-any.whl", hash = "sha256:8d7902e2c0c62837457ba18146e3feaf1dec62018617edc5c0336b65b305b682"}, - {file = "boto3-1.34.70.tar.gz", hash = "sha256:54150a52eb93028b8e09df00319e8dcb68be7459333d5da00d706d75ba5130d6"}, + {file = "boto3-1.34.113-py3-none-any.whl", hash = "sha256:7e59f0a848be477a4c98a90e7a18a0e284adfb643f7879d2b303c5f493661b7a"}, + {file = "boto3-1.34.113.tar.gz", hash = "sha256:009cd143509f2ff4c37582c3f45d50f28c95eed68e8a5c36641206bdb597a9ea"}, ] [package.dependencies] -botocore = ">=1.34.70,<1.35.0" +botocore = ">=1.34.113,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -160,13 +160,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.70" +version = "1.34.113" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.70-py3-none-any.whl", hash = "sha256:c86944114e85c8a8d5da06fb84f2609ed3bd23cd2fc06b30250bef7e37e8c589"}, - {file = "botocore-1.34.70.tar.gz", hash = "sha256:fa03d4972cd57d505e6c0eb5d7c7a1caeb7dd49e84f963f7ebeca41fe8ab736e"}, + {file = "botocore-1.34.113-py3-none-any.whl", hash = "sha256:8ca87776450ef41dd25c327eb6e504294230a5756940d68bcfdedc4a7cdeca97"}, + {file = "botocore-1.34.113.tar.gz", hash = "sha256:449912ba3c4ded64f21d09d428146dd9c05337b2a112e15511bf2c4888faae79"}, ] [package.dependencies] @@ -175,7 +175,7 @@ python-dateutil = ">=2.1,<3.0.0" urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} [package.extras] -crt = ["awscrt (==0.19.19)"] +crt = ["awscrt (==0.20.9)"] [[package]] name = "cachetools" @@ -378,74 +378,74 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "colorama" -version = "0.4.4" +version = "0.4.6" description = "Cross-platform colored terminal text." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" -version = "7.4.4" +version = "7.5.2" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, - {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, - {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, - {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, - {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, - {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, - {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, - {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, - {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, - {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, - {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, - {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, - {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, - {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, + {file = "coverage-7.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:554c7327bf0fd688050348e22db7c8e163fb7219f3ecdd4732d7ed606b417263"}, + {file = "coverage-7.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d0305e02e40c7cfea5d08d6368576537a74c0eea62b77633179748d3519d6705"}, + {file = "coverage-7.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:829fb55ad437d757c70d5b1c51cfda9377f31506a0a3f3ac282bc6a387d6a5f1"}, + {file = "coverage-7.5.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:894b1acded706f1407a662d08e026bfd0ff1e59e9bd32062fea9d862564cfb65"}, + {file = "coverage-7.5.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe76d6dee5e4febefa83998b17926df3a04e5089e3d2b1688c74a9157798d7a2"}, + {file = "coverage-7.5.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c7ebf2a37e4f5fea3c1a11e1f47cea7d75d0f2d8ef69635ddbd5c927083211fc"}, + {file = "coverage-7.5.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20e611fc36e1a0fc7bbf957ef9c635c8807d71fbe5643e51b2769b3cc0fb0b51"}, + {file = "coverage-7.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c5c5b7ae2763533152880d5b5b451acbc1089ade2336b710a24b2b0f5239d20"}, + {file = "coverage-7.5.2-cp310-cp310-win32.whl", hash = "sha256:1e4225990a87df898e40ca31c9e830c15c2c53b1d33df592bc8ef314d71f0281"}, + {file = "coverage-7.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:976cd92d9420e6e2aa6ce6a9d61f2b490e07cb468968adf371546b33b829284b"}, + {file = "coverage-7.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5997d418c219dcd4dcba64e50671cca849aaf0dac3d7a2eeeb7d651a5bd735b8"}, + {file = "coverage-7.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ec27e93bbf5976f0465e8936f02eb5add99bbe4e4e7b233607e4d7622912d68d"}, + {file = "coverage-7.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f11f98753800eb1ec872562a398081f6695f91cd01ce39819e36621003ec52a"}, + {file = "coverage-7.5.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e34680049eecb30b6498784c9637c1c74277dcb1db75649a152f8004fbd6646"}, + {file = "coverage-7.5.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e12536446ad4527ac8ed91d8a607813085683bcce27af69e3b31cd72b3c5960"}, + {file = "coverage-7.5.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3d3f7744b8a8079d69af69d512e5abed4fb473057625588ce126088e50d05493"}, + {file = "coverage-7.5.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:431a3917e32223fcdb90b79fe60185864a9109631ebc05f6c5aa03781a00b513"}, + {file = "coverage-7.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a7c6574225f34ce45466f04751d957b5c5e6b69fca9351db017c9249786172ce"}, + {file = "coverage-7.5.2-cp311-cp311-win32.whl", hash = "sha256:2b144d142ec9987276aeff1326edbc0df8ba4afbd7232f0ca10ad57a115e95b6"}, + {file = "coverage-7.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:900532713115ac58bc3491b9d2b52704a05ed408ba0918d57fd72c94bc47fba1"}, + {file = "coverage-7.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9a42970ce74c88bdf144df11c52c5cf4ad610d860de87c0883385a1c9d9fa4ab"}, + {file = "coverage-7.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26716a1118c6ce2188283b4b60a898c3be29b480acbd0a91446ced4fe4e780d8"}, + {file = "coverage-7.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60b66b0363c5a2a79fba3d1cd7430c25bbd92c923d031cae906bdcb6e054d9a2"}, + {file = "coverage-7.5.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d22eba19273b2069e4efeff88c897a26bdc64633cbe0357a198f92dca94268"}, + {file = "coverage-7.5.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bb5b92a0ab3d22dfdbfe845e2fef92717b067bdf41a5b68c7e3e857c0cff1a4"}, + {file = "coverage-7.5.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1aef719b6559b521ae913ddeb38f5048c6d1a3d366865e8b320270b7bc4693c2"}, + {file = "coverage-7.5.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8809c0ea0e8454f756e3bd5c36d04dddf222989216788a25bfd6724bfcee342c"}, + {file = "coverage-7.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1acc2e2ef098a1d4bf535758085f508097316d738101a97c3f996bccba963ea5"}, + {file = "coverage-7.5.2-cp312-cp312-win32.whl", hash = "sha256:97de509043d3f0f2b2cd171bdccf408f175c7f7a99d36d566b1ae4dd84107985"}, + {file = "coverage-7.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:8941e35a0e991a7a20a1fa3e3182f82abe357211f2c335a9e6007067c3392fcf"}, + {file = "coverage-7.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5662bf0f6fb6757f5c2d6279c541a5af55a39772c2362ed0920b27e3ce0e21f7"}, + {file = "coverage-7.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d9c62cff2ffb4c2a95328488fd7aa96a7a4b34873150650fe76b19c08c9c792"}, + {file = "coverage-7.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74eeaa13e8200ad72fca9c5f37395fb310915cec6f1682b21375e84fd9770e84"}, + {file = "coverage-7.5.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f29bf497d51a5077994b265e976d78b09d9d0dff6ca5763dbb4804534a5d380"}, + {file = "coverage-7.5.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f96aa94739593ae0707eda9813ce363a0a0374a810ae0eced383340fc4a1f73"}, + {file = "coverage-7.5.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:51b6cee539168a912b4b3b040e4042b9e2c9a7ad9c8546c09e4eaeff3eacba6b"}, + {file = "coverage-7.5.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:59a75e6aa5c25b50b5a1499f9718f2edff54257f545718c4fb100f48d570ead4"}, + {file = "coverage-7.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29da75ce20cb0a26d60e22658dd3230713c6c05a3465dd8ad040ffc991aea318"}, + {file = "coverage-7.5.2-cp38-cp38-win32.whl", hash = "sha256:23f2f16958b16152b43a39a5ecf4705757ddd284b3b17a77da3a62aef9c057ef"}, + {file = "coverage-7.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:9e41c94035e5cdb362beed681b58a707e8dc29ea446ea1713d92afeded9d1ddd"}, + {file = "coverage-7.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06d96b9b19bbe7f049c2be3c4f9e06737ec6d8ef8933c7c3a4c557ef07936e46"}, + {file = "coverage-7.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:878243e1206828908a6b4a9ca7b1aa8bee9eb129bf7186fc381d2646f4524ce9"}, + {file = "coverage-7.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:482df956b055d3009d10fce81af6ffab28215d7ed6ad4a15e5c8e67cb7c5251c"}, + {file = "coverage-7.5.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a35c97af60a5492e9e89f8b7153fe24eadfd61cb3a2fb600df1a25b5dab34b7e"}, + {file = "coverage-7.5.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24bb4c7859a3f757a116521d4d3a8a82befad56ea1bdacd17d6aafd113b0071e"}, + {file = "coverage-7.5.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e1046aab24c48c694f0793f669ac49ea68acde6a0798ac5388abe0a5615b5ec8"}, + {file = "coverage-7.5.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:448ec61ea9ea7916d5579939362509145caaecf03161f6f13e366aebb692a631"}, + {file = "coverage-7.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4a00bd5ba8f1a4114720bef283cf31583d6cb1c510ce890a6da6c4268f0070b7"}, + {file = "coverage-7.5.2-cp39-cp39-win32.whl", hash = "sha256:9f805481d5eff2a96bac4da1570ef662bf970f9a16580dc2c169c8c3183fa02b"}, + {file = "coverage-7.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:2c79f058e7bec26b5295d53b8c39ecb623448c74ccc8378631f5cb5c16a7e02c"}, + {file = "coverage-7.5.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:40dbb8e7727560fe8ab65efcddfec1ae25f30ef02e2f2e5d78cfb52a66781ec5"}, + {file = "coverage-7.5.2.tar.gz", hash = "sha256:13017a63b0e499c59b5ba94a8542fb62864ba3016127d1e4ef30d354fc2b00e9"}, ] [package.dependencies] @@ -456,43 +456,43 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "42.0.5" +version = "42.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, - {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, - {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, - {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, - {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, - {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, - {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, - {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"}, + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"}, + {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"}, + {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"}, + {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"}, + {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"}, + {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"}, + {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"}, ] [package.dependencies] @@ -554,6 +554,28 @@ wrapt = ">=1.10,<2" [package.extras] dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] +[[package]] +name = "docker" +version = "7.1.0" +description = "A Python library for the Docker Engine API." +optional = false +python-versions = ">=3.8" +files = [ + {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, + {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, +] + +[package.dependencies] +pywin32 = {version = ">=304", markers = "sys_platform == \"win32\""} +requests = ">=2.26.0" +urllib3 = ">=1.26.0" + +[package.extras] +dev = ["coverage (==7.2.7)", "pytest (==7.4.2)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.1.0)", "ruff (==0.1.8)"] +docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"] +ssh = ["paramiko (>=2.4.3)"] +websockets = ["websocket-client (>=1.3.0)"] + [[package]] name = "docutils" version = "0.16" @@ -567,13 +589,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.0" +version = "1.2.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, ] [package.extras] @@ -625,13 +647,13 @@ rewrite = ["tokenize-rt (>=3)"] [[package]] name = "google-api-core" -version = "2.18.0" +version = "2.19.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.18.0.tar.gz", hash = "sha256:62d97417bfc674d6cef251e5c4d639a9655e00c45528c4364fbfebb478ce72a9"}, - {file = "google_api_core-2.18.0-py3-none-any.whl", hash = "sha256:5a63aa102e0049abe85b5b88cb9409234c1f70afcda21ce1e40b285b9629c1d6"}, + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, ] [package.dependencies] @@ -826,13 +848,13 @@ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] [[package]] name = "idna" -version = "3.6" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] @@ -859,13 +881,13 @@ files = [ [[package]] name = "ipython" -version = "8.22.2" +version = "8.24.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" files = [ - {file = "ipython-8.22.2-py3-none-any.whl", hash = "sha256:3c86f284c8f3d8f2b6c662f885c4889a91df7cd52056fd02b7d8d6195d7f56e9"}, - {file = "ipython-8.22.2.tar.gz", hash = "sha256:2dcaad9049f9056f1fef63514f176c7d41f930daa78d05b82a176202818f2c14"}, + {file = "ipython-8.24.0-py3-none-any.whl", hash = "sha256:d7bf2f6c4314984e3e02393213bab8703cf163ede39672ce5918c51fe253a2a3"}, + {file = "ipython-8.24.0.tar.gz", hash = "sha256:010db3f8a728a578bb641fdd06c063b9fb8e96a9464c63aec6310fbcb5e80501"}, ] [package.dependencies] @@ -879,18 +901,20 @@ prompt-toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" stack-data = "*" traitlets = ">=5.13.0" +typing-extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} [package.extras] -all = ["ipython[black,doc,kernel,nbconvert,nbformat,notebook,parallel,qtconsole,terminal]", "ipython[test,test-extra]"] +all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] doc = ["docrepr", "exceptiongroup", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "stack-data", "typing-extensions"] kernel = ["ipykernel"] +matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "testpath"] +test = ["pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] [[package]] @@ -928,13 +952,13 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -1022,13 +1046,13 @@ files = [ [[package]] name = "matplotlib-inline" -version = "0.1.6" +version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, - {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, + {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, + {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, ] [package.dependencies] @@ -1058,46 +1082,49 @@ files = [ [[package]] name = "moto" -version = "4.2.14" +version = "5.0.8" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "moto-4.2.14-py2.py3-none-any.whl", hash = "sha256:6d242dbbabe925bb385ddb6958449e5c827670b13b8e153ed63f91dbdb50372c"}, - {file = "moto-4.2.14.tar.gz", hash = "sha256:8f9263ca70b646f091edcc93e97cda864a542e6d16ed04066b1370ed217bd190"}, + {file = "moto-5.0.8-py2.py3-none-any.whl", hash = "sha256:7d1035e366434bfa9fcc0621f07d5aa724b6846408071d540137a0554c46f214"}, + {file = "moto-5.0.8.tar.gz", hash = "sha256:517fb808dc718bcbdda54c6ffeaca0adc34cf6e10821bfb01216ce420a31765c"}, ] [package.dependencies] boto3 = ">=1.9.201" -botocore = ">=1.12.201" +botocore = ">=1.14.0" cryptography = ">=3.3.1" +docker = {version = ">=3.0.0", optional = true, markers = "extra == \"dynamodb\""} Jinja2 = ">=2.10.1" +py-partiql-parser = {version = "0.5.5", optional = true, markers = "extra == \"dynamodb\" or extra == \"s3\""} python-dateutil = ">=2.1,<3.0.0" +PyYAML = {version = ">=5.1", optional = true, markers = "extra == \"s3\""} requests = ">=2.5" -responses = ">=0.13.0" +responses = ">=0.15.0" werkzeug = ">=0.5,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1" xmltodict = "*" [package.extras] -all = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] -apigateway = ["PyYAML (>=5.1)", "ecdsa (!=0.15)", "openapi-spec-validator (>=0.5.0)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"] -apigatewayv2 = ["PyYAML (>=5.1)"] +all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] +apigateway = ["PyYAML (>=5.1)", "joserfc (>=0.9.0)", "openapi-spec-validator (>=0.5.0)"] +apigatewayv2 = ["PyYAML (>=5.1)", "openapi-spec-validator (>=0.5.0)"] appsync = ["graphql-core"] awslambda = ["docker (>=3.0.0)"] batch = ["docker (>=3.0.0)"] -cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] -cognitoidp = ["ecdsa (!=0.15)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"] -dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.0)"] -dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.0)"] -ec2 = ["sshpubkeys (>=3.1.0)"] +cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] +cognitoidp = ["joserfc (>=0.9.0)"] +dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.5)"] +dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.5)"] glue = ["pyparsing (>=3.0.7)"] iotdata = ["jsondiff (>=1.1.2)"] -proxy = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] -resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"] -s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.0)"] -s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.0)"] -server = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] +proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] +resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)"] +s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.5)"] +s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.5)"] +server = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] ssm = ["PyYAML (>=5.1)"] +stepfunctions = ["antlr4-python3-runtime", "jsonpath-ng"] xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] @@ -1113,20 +1140,20 @@ files = [ [[package]] name = "networkx" -version = "3.2.1" +version = "3.3" description = "Python package for creating and manipulating graphs and networks" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, - {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, + {file = "networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2"}, + {file = "networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9"}, ] [package.extras] -default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] -developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] -doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] -extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] +default = ["matplotlib (>=3.6)", "numpy (>=1.23)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["myst-nb (>=1.0)", "numpydoc (>=1.7)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] +extra = ["lxml (>=4.6)", "pydot (>=2.0)", "pygraphviz (>=1.12)", "sympy (>=1.10)"] test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] [[package]] @@ -1172,18 +1199,18 @@ files = [ [[package]] name = "parso" -version = "0.8.3" +version = "0.8.4" description = "A Python Parser" optional = false python-versions = ">=3.6" files = [ - {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, - {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, ] [package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] [[package]] name = "pathspec" @@ -1212,28 +1239,29 @@ ptyprocess = ">=0.5" [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -1242,13 +1270,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "prompt-toolkit" -version = "3.0.43" +version = "3.0.44" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, - {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, + {file = "prompt_toolkit-3.0.44-py3-none-any.whl", hash = "sha256:205a20669633d042d3722a528b8e7cd3f4dbd9e1450935f596c2cc61166762dd"}, + {file = "prompt_toolkit-3.0.44.tar.gz", hash = "sha256:c1dfd082c4259964bc8bcce1f8460d9dbeb5d4a37bfc25b8082bc02cd41c8af6"}, ] [package.dependencies] @@ -1316,30 +1344,44 @@ files = [ [package.extras] tests = ["pytest"] +[[package]] +name = "py-partiql-parser" +version = "0.5.5" +description = "Pure Python PartiQL Parser" +optional = false +python-versions = "*" +files = [ + {file = "py_partiql_parser-0.5.5-py2.py3-none-any.whl", hash = "sha256:90d278818385bd60c602410c953ee78f04ece599d8cd21c656fc5e47399577a1"}, + {file = "py_partiql_parser-0.5.5.tar.gz", hash = "sha256:ed07f8edf4b55e295cab4f5fd3e2ba3196cee48a43fe210d53ddd6ffce1cf1ff"}, +] + +[package.extras] +dev = ["black (==22.6.0)", "flake8", "mypy", "pytest"] + [[package]] name = "pyasn1" -version = "0.5.1" +version = "0.6.0" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = ">=3.8" files = [ - {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, - {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, + {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, + {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, ] [[package]] name = "pyasn1-modules" -version = "0.3.0" +version = "0.4.0" description = "A collection of ASN.1-based protocols modules" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = ">=3.8" files = [ - {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, - {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, + {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, + {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, ] [package.dependencies] -pyasn1 = ">=0.4.6,<0.6.0" +pyasn1 = ">=0.4.6,<0.7.0" [[package]] name = "pycodestyle" @@ -1354,13 +1396,13 @@ files = [ [[package]] name = "pycparser" -version = "2.21" +version = "2.22" description = "C parser in Python" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] [[package]] @@ -1376,17 +1418,16 @@ files = [ [[package]] name = "pygments" -version = "2.17.2" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] -plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] @@ -1460,6 +1501,20 @@ files = [ [package.dependencies] pytest = ">=3.2.5" +[[package]] +name = "pytest-socket" +version = "0.7.0" +description = "Pytest Plugin to disable socket calls during tests" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "pytest_socket-0.7.0-py3-none-any.whl", hash = "sha256:7e0f4642177d55d317bbd58fc68c6bd9048d6eadb2d46a89307fa9221336ce45"}, + {file = "pytest_socket-0.7.0.tar.gz", hash = "sha256:71ab048cbbcb085c15a4423b73b619a8b35d6a307f46f78ea46be51b1b7e11b3"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + [[package]] name = "pytest-timeout" version = "2.1.0" @@ -1490,18 +1545,41 @@ six = ">=1.5" [[package]] name = "python-hcl2" -version = "4.3.2" +version = "4.3.3" description = "A parser for HCL2" optional = false python-versions = ">=3.7.0" files = [ - {file = "python-hcl2-4.3.2.tar.gz", hash = "sha256:7122661438be27ccd8b8f3db71969d8ef2cce3b3cf183e88f8172575e7405a65"}, - {file = "python_hcl2-4.3.2-py3-none-any.whl", hash = "sha256:e958fe52ca0519e3500eb621caa16be6be9c27870cbcd1879d8c538fb85067ea"}, + {file = "python-hcl2-4.3.3.tar.gz", hash = "sha256:03d1df5a025c7430ddde789695c845701269faae3bdbe37049201c0f57bbcbde"}, + {file = "python_hcl2-4.3.3-py3-none-any.whl", hash = "sha256:8fda0b8769ceda87f2e06b1ecf63343a8e83c4f0a2b410798a81aa3b9689a25c"}, ] [package.dependencies] lark = ">=1,<2" +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + [[package]] name = "pyyaml" version = "6.0.1" @@ -1514,6 +1592,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1521,8 +1600,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1539,6 +1626,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1546,6 +1634,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1553,13 +1642,13 @@ files = [ [[package]] name = "requests" -version = "2.31.0" +version = "2.32.2" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, + {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, ] [package.dependencies] @@ -1841,17 +1930,18 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "tenacity" -version = "8.2.3" +version = "8.3.0" description = "Retry code until it succeeds" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, - {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, + {file = "tenacity-8.3.0-py3-none-any.whl", hash = "sha256:3649f6443dbc0d9b01b9d8020a9c4ec7a1ff5f6f3c6c8a036ef371f573fe9185"}, + {file = "tenacity-8.3.0.tar.gz", hash = "sha256:953d4e6ad24357bceffbc9707bc74349aca9d245f68eb65419cf0c249a1949a2"}, ] [package.extras] -doc = ["reno", "sphinx", "tornado (>=4.5)"] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] [[package]] name = "tomli" @@ -1866,28 +1956,28 @@ files = [ [[package]] name = "traitlets" -version = "5.14.2" +version = "5.14.3" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" files = [ - {file = "traitlets-5.14.2-py3-none-any.whl", hash = "sha256:fcdf85684a772ddeba87db2f398ce00b40ff550d1528c03c14dbf6a02003cd80"}, - {file = "traitlets-5.14.2.tar.gz", hash = "sha256:8cdd83c040dab7d1dee822678e5f5d100b514f7b72b01615b26fc5718916fdf9"}, + {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, + {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, ] [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.1)", "pytest-mock", "pytest-mypy-testing"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.12.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, + {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, ] [[package]] @@ -2039,4 +2129,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "42b1e3342bf064466eabcbd15a001be18770e485f70fe8bdb0cb5768170a23e5" +content-hash = "48dd23624a37c26ef765864da8ade5d2bdbd40c3f6b234ed0bc1f1cc511110f8" diff --git a/pyproject.toml b/pyproject.toml index 7c462bf..e4b2281 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,10 +52,11 @@ seed-isort-config = "^2.2.0" flake8 = "^6.0.0" wheel = "^0.40" pytest-depends = "^1.0.1" +pytest-socket = "^0.7.0" pytest-lazy-fixture = "^0.6.3" coverage = "^7.2" pytest-cov = "^4.0.0" -moto = {extras = ["sts"], version = "^4.1.4"} +moto = {extras = ["sts","dynamodb", "s3"], version = "^5.0.8"} deepdiff = "^6.2.0" Sphinx = "5.1.1" diff --git a/tests/authenticators/test_aws_auth.py b/tests/authenticators/test_aws_auth.py index d8ddb1e..04c5ccd 100644 --- a/tests/authenticators/test_aws_auth.py +++ b/tests/authenticators/test_aws_auth.py @@ -16,10 +16,9 @@ import pytest from botocore.credentials import Credentials -from moto import mock_sts +from moto import mock_aws -from tfworker.authenticators.aws import (AWSAuthenticator, - MissingArgumentException) +from tfworker.authenticators.aws import AWSAuthenticator, MissingArgumentException from tfworker.commands.root import RootCommand from tfworker.constants import DEFAULT_BACKEND_PREFIX @@ -69,7 +68,7 @@ def test_with_no_backend_bucket(self): AWSAuthenticator(state_args={}, deployment="deployfu") assert "backend_bucket" in str(e.value) - @mock_sts + @mock_aws def test_with_access_key_pair_creds( self, sts_client, state_args, aws_access_key_id, aws_secret_access_key ): @@ -78,7 +77,7 @@ def test_with_access_key_pair_creds( assert auth.secret_access_key == aws_secret_access_key assert auth.session_token is None - @mock_sts + @mock_aws def test_with_access_key_pair_creds_and_role_arn( self, sts_client, state_args_with_role_arn, aws_secret_access_key ): @@ -110,7 +109,7 @@ def test_with_profile( assert auth.secret_access_key == aws_credentials_instance.secret_key assert auth.session_token is None - @mock_sts + @mock_aws def test_with_prefix(self, state_args): auth = AWSAuthenticator(state_args, deployment="deployfu") assert auth.prefix == DEFAULT_BACKEND_PREFIX.format(deployment="deployfu") diff --git a/tests/backends/test_s3.py b/tests/backends/test_s3.py index f518c19..31ae6c2 100644 --- a/tests/backends/test_s3.py +++ b/tests/backends/test_s3.py @@ -11,12 +11,19 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import os import random import string +from unittest.mock import MagicMock, patch import pytest +from botocore.exceptions import ClientError +from moto import mock_aws +from tests.conftest import MockAWSAuth +from tfworker.backends import S3Backend from tfworker.backends.base import BackendError +from tfworker.handlers import HandlerError STATE_BUCKET = "test_bucket" STATE_PREFIX = "terraform" @@ -25,6 +32,7 @@ EMPTY_STATE = f"{STATE_PREFIX}/{STATE_DEPLOYMENT}/empty/terraform.tfstate" OCCUPIED_STATE = f"{STATE_PREFIX}/{STATE_DEPLOYMENT}/occupied/terraform.tfstate" LOCK_DIGEST = "1234123412341234" +NO_SUCH_BUCKET = "no_such_bucket" @pytest.fixture(scope="class") @@ -159,6 +167,191 @@ def test_clean_locking_state(self, basec, state_setup, dynamodb_client): ) +class TestS3BackendInit: + def setup_method(self, method): + self.authenticators = {"aws": MockAWSAuth()} + self.definitions = {} + + def test_no_session(self): + self.authenticators["aws"]._session = None + with pytest.raises(BackendError): + result = S3Backend(self.authenticators, self.definitions) + + def test_no_backend_session(self): + self.authenticators["aws"]._backend_session = None + with pytest.raises(BackendError): + result = S3Backend(self.authenticators, self.definitions) + + @patch("tfworker.backends.S3Backend._ensure_locking_table", return_value=None) + @patch("tfworker.backends.S3Backend._ensure_backend_bucket", return_value=None) + @patch("tfworker.backends.S3Backend._get_bucket_files", return_value={}) + def test_deployment_undefined( + self, + mock_get_bucket_files, + mock_ensure_backend_bucket, + mock_ensure_locking_table, + ): + # arrange + result = S3Backend(self.authenticators, self.definitions) + assert result._deployment == "undefined" + assert mock_get_bucket_files.called + assert mock_ensure_backend_bucket.called + assert mock_ensure_locking_table.called + + @patch("tfworker.backends.S3Backend._ensure_locking_table", return_value=None) + @patch("tfworker.backends.S3Backend._ensure_backend_bucket", return_value=None) + @patch("tfworker.backends.S3Backend._get_bucket_files", return_value={}) + @patch("tfworker.backends.s3.S3Handler", side_effect=HandlerError("message")) + def test_handler_error( + self, + mock_get_bucket_files, + mock_ensure_backend_bucket, + mock_ensure_locking_table, + mock_handler, + ): + with pytest.raises(SystemExit): + result = S3Backend(self.authenticators, self.definitions) + + +class TestS3BackendEnsureBackendBucket: + from botocore.exceptions import ClientError + + @pytest.fixture(autouse=True) + def setup_class(self, state_setup): + pass + + @patch("tfworker.backends.S3Backend._ensure_locking_table", return_value=None) + @patch("tfworker.backends.S3Backend._ensure_backend_bucket", return_value=None) + @patch("tfworker.backends.S3Backend._get_bucket_files", return_value={}) + def setup_method( + self, + method, + mock_get_bucket_files, + mock_ensure_backend_bucket, + mock_ensure_locking_table, + ): + with mock_aws(): + self.authenticators = {"aws": MockAWSAuth()} + self.definitions = {} + self.backend = S3Backend(self.authenticators, self.definitions) + self.backend._authenticator.bucket = STATE_BUCKET + self.backend._authenticator.backend_region = STATE_REGION + + def teardown_method(self, method): + with mock_aws(): + try: + self.backend._s3_client.delete_bucket(Bucket=NO_SUCH_BUCKET) + except Exception: + pass + + @mock_aws + def test_check_bucket_does_not_exist(self): + result = self.backend._check_bucket_exists(NO_SUCH_BUCKET) + assert result is False + + @mock_aws + def test_check_bucket_exists(self): + result = self.backend._check_bucket_exists(STATE_BUCKET) + assert result is True + + @mock_aws + def test_check_bucket_exists_error(self): + self.backend._s3_client = MagicMock() + self.backend._s3_client.head_bucket.side_effect = ClientError( + {"Error": {"Code": "403", "Message": "Unauthorized"}}, "head_bucket" + ) + + with pytest.raises(ClientError): + result = self.backend._check_bucket_exists(STATE_BUCKET) + assert self.backend._s3_client.head_bucket.called + + @mock_aws + def test_bucket_not_exist_no_create(self, capfd): + self.backend._authenticator.create_backend_bucket = False + self.backend._authenticator.bucket = NO_SUCH_BUCKET + with pytest.raises(BackendError): + result = self.backend._ensure_backend_bucket() + assert ( + "Backend bucket not found and --no-create-backend-bucket specified." + in capfd.readouterr().out + ) + + @mock_aws + def test_create_bucket(self): + self.backend._authenticator.create_backend_bucket = True + self.backend._authenticator.bucket = NO_SUCH_BUCKET + assert NO_SUCH_BUCKET not in [ + x["Name"] for x in self.backend._s3_client.list_buckets()["Buckets"] + ] + result = self.backend._ensure_backend_bucket() + assert result is None + assert NO_SUCH_BUCKET in [ + x["Name"] for x in self.backend._s3_client.list_buckets()["Buckets"] + ] + + @mock_aws + def test_create_bucket_invalid_location_constraint(self, capsys): + self.backend._authenticator.create_backend_bucket = True + self.backend._authenticator.bucket = NO_SUCH_BUCKET + self.backend._authenticator.backend_region = "us-west-1" + # moto doesn't properly raise a location constraint when the session doesn't match the region + # so we'll just do it manually + assert self.backend._authenticator.backend_session.region_name != "us-west-1" + assert self.backend._authenticator.backend_region == "us-west-1" + assert NO_SUCH_BUCKET not in [ + x["Name"] for x in self.backend._s3_client.list_buckets()["Buckets"] + ] + self.backend._s3_client = MagicMock() + self.backend._s3_client.create_bucket.side_effect = ClientError( + { + "Error": { + "Code": "InvalidLocationConstraint", + "Message": "InvalidLocationConstraint", + } + }, + "create_bucket", + ) + + with pytest.raises(SystemExit): + result = self.backend._create_bucket(NO_SUCH_BUCKET) + assert "InvalidLocationConstraint" in capsys.readouterr().out + + assert NO_SUCH_BUCKET not in [ + x["Name"] for x in self.backend._s3_client.list_buckets()["Buckets"] + ] + + # This test can not be enabled until several other tests are refactored to not create the bucket needlessly + # as the method itself skips this check when being run through a test, the same also applies to "BucketAlreadyOwnedByYou" + # @mock_aws + # def test_create_bucket_already_exists(self, capsys): + # self.backend._authenticator.create_backend_bucket = True + # self.backend._authenticator.bucket = STATE_BUCKET + # assert STATE_BUCKET in [ x['Name'] for x in self.backend._s3_client.list_buckets()['Buckets'] ] + + # with pytest.raises(SystemExit): + # result = self.backend._create_bucket(STATE_BUCKET) + # assert f"Bucket {STATE_BUCKET} already exists" in capsys.readouterr().out + + def test_create_bucket_error(self): + self.backend._authenticator.create_backend_bucket = True + self.backend._authenticator.bucket = NO_SUCH_BUCKET + self.backend._s3_client = MagicMock() + self.backend._s3_client.create_bucket.side_effect = ClientError( + {"Error": {"Code": "403", "Message": "Unauthorized"}}, "create_bucket" + ) + + with pytest.raises(ClientError): + result = self.backend._create_bucket(NO_SUCH_BUCKET) + assert self.backend._s3_client.create_bucket.called + + +def test_backend_remotes(basec, state_setup): + remotes = basec.backend.remotes() + assert len(remotes) == 2 + assert "empty" in remotes + assert "occupied" in remotes + + def test_backend_clean_all(basec, request, state_setup, dynamodb_client, s3_client): # this function should trigger an exit with pytest.raises(SystemExit): diff --git a/tests/commands/test_root.py b/tests/commands/test_root.py index 2de7692..11fb8ad 100644 --- a/tests/commands/test_root.py +++ b/tests/commands/test_root.py @@ -105,7 +105,7 @@ def test_config_loader(self, rootc, capfd): invalidrc.load_config() assert e.value.code == 1 out, err = capfd.readouterr() - assert "can not read" in out + assert "configuration file does not exist" in out # a j2 template with invalid substitutions should raise an error invalidrc = tfworker.commands.root.RootCommand( @@ -245,3 +245,50 @@ def test_get_platform( assert machine == actual_machine mock1.assert_called_once() mock2.assert_called_once() + + +import io + +import pytest + +from tfworker.commands.root import ordered_config_load + + +class TestOrderedConfigLoad: + def test_ordered_config_load(self): + config = """ + key1: value1 + key2: value2 + key3: + - item1 + - item2 + """ + result = ordered_config_load(config) + assert result == { + "key1": "value1", + "key2": "value2", + "key3": ["item1", "item2"], + } + + def test_ordered_config_load_invalid(self, capfd): + config = """ + key1: value1 + key2: value2 + key3: + - item1 + - item2 + key4: + subkey1: value1 + subkey1a: value2 + - ohnoimalistnow + """ + expected_error_out = "" + for i, line in enumerate(config.split("\n")): + expected_error_out += f"{i+1}: {line}\n" + with pytest.raises(SystemExit) as e: + ordered_config_load(config) + out, err = capfd.readouterr() + assert e.value.code == 1 + assert "error loading yaml/json" in out + assert "the configuration that caused the error was" in out + assert expected_error_out in out diff --git a/tests/commands/test_version.py b/tests/commands/test_version.py index 2ba195e..25bae6f 100644 --- a/tests/commands/test_version.py +++ b/tests/commands/test_version.py @@ -25,14 +25,8 @@ def mock_get_distribution(package: str): class TestVersionCommand: def test_exec(self, capsys): - VersionCommand().exec() + vc = VersionCommand() + vc._version = "1.2.3" + vc.exec() text = capsys.readouterr() - assert text.out.startswith("terraform-worker version") - - with mock.patch( - "tfworker.commands.version.get_distribution", - side_effect=mock_get_distribution, - ): - VersionCommand().exec() - text = capsys.readouterr() - assert text.out == "terraform-worker version unknown\n" + assert text.out == "terraform-worker version 1.2.3\n" diff --git a/tests/conftest.py b/tests/conftest.py index 04d9d02..ac0052a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,7 +20,7 @@ import boto3 import pytest -from moto import mock_dynamodb, mock_s3, mock_sts +from moto import mock_aws from pytest_lazyfixture import lazy_fixture import tfworker @@ -70,19 +70,19 @@ def aws_credentials(): @pytest.fixture(scope="class") def s3_client(aws_credentials): - with mock_s3(): + with mock_aws(): yield boto3.client("s3", region_name="us-west-2") @pytest.fixture(scope="class") def dynamodb_client(aws_credentials): - with mock_dynamodb(): + with mock_aws(): yield boto3.client("dynamodb", region_name="us-west-2") @pytest.fixture(scope="class") def sts_client(aws_credentials): - with mock_sts(): + with mock_aws(): yield boto3.client("sts", region_name="us-west-2") @@ -93,8 +93,10 @@ class MockAWSAuth: (cross account assumed roles, user identity, etc...) """ + @mock_aws def __init__(self): self._session = boto3.Session() + self._backend_session = self._session self.bucket = "test_bucket" self.prefix = "terraform/test-0001" @@ -102,6 +104,10 @@ def __init__(self): def session(self): return self._session + @property + def backend_session(self): + return self._backend_session + @pytest.fixture() def grootc(): diff --git a/tests/test_cli.py b/tests/test_cli.py index 975461b..f264bb1 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -161,7 +161,7 @@ def test_cli_invalid_config(self): assert result.exit_code == 1 # the expected result is: configuration file {config_file} not found" the # exception is being handled in the wrong place - assert "not read" in result.output + assert "configuration file does not exist" in result.output @patch("tfworker.cli.CleanCommand", autospec=True) def test_cli_clean_command(self, mock_request, test_config_file): diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 623e612..f2208b4 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -68,6 +68,7 @@ def does_not_raise(): class TestPlugins: + @pytest.mark.enable_socket @pytest.mark.depends(on="get_url") def test_plugin_download(self, rootc): plugins = tfworker.plugins.PluginsCollection( diff --git a/tests/util/test_copier.py b/tests/util/test_copier.py index c58ac30..a4851e3 100644 --- a/tests/util/test_copier.py +++ b/tests/util/test_copier.py @@ -23,8 +23,7 @@ import pytest -from tfworker.util.copier import (Copier, CopyFactory, FileSystemCopier, - GitCopier) +from tfworker.util.copier import Copier, CopyFactory, FileSystemCopier, GitCopier C_CONFLICTS = ["test.txt", "foo", "test.tf"] C_SOURCE = "test_source" diff --git a/tests/util/test_system.py b/tests/util/test_system.py index 07cea2e..f0cda0f 100644 --- a/tests/util/test_system.py +++ b/tests/util/test_system.py @@ -17,7 +17,7 @@ import pytest -from tfworker.util.system import pipe_exec, which +from tfworker.util.system import get_version, pipe_exec, strip_ansi, which # context manager to allow testing exceptions in parameterized tests @@ -34,6 +34,12 @@ def mock_tf_version(args: str): return (0, args.encode(), "".encode()) +def mock_distribution(*args, **kwargs): + Class = mock.MagicMock() + Class.version = "1.2.3" + return Class + + class TestUtilSystem: @pytest.mark.parametrize( "commands, exit_code, cwd, stdin, stdout, stderr, stream_output", @@ -109,3 +115,41 @@ def test_which(self): ): with mock.patch("os.access", side_effect=lambda x, y: True): assert which("terraform") is not None + + def test_which_full_path(self): + with mock.patch( + "os.path.isfile", + side_effect=lambda x: True, + ): + with mock.patch("os.access", side_effect=lambda x, y: True): + assert which("/full/path/to/file") is not None + + def test_which_not_found(self): + with mock.patch( + "os.path.isfile", + side_effect=lambda x: False, + ): + with mock.patch("os.access", side_effect=lambda x, y: False): + assert which("terraform") is None + + def test_get_version(self): + with mock.patch( + "tfworker.util.system.get_distribution", + side_effect=mock_distribution, + ): + assert get_version() == "1.2.3" + + def test_get_version_unknown(self): + from pkg_resources import DistributionNotFound + + with mock.patch( + "tfworker.util.system.get_distribution", + side_effect=DistributionNotFound, + ): + assert get_version() == "unknown" + + def test_strip_ansi(self): + assert strip_ansi("\x1B[31mHello\x1B[0m") == "Hello" + assert strip_ansi("\x1B[32mWorld\x1B[0m") == "World" + assert strip_ansi("\x1B[33mFoo\x1B[0m") == "Foo" + assert strip_ansi("\x1B[34mBar\x1B[0m") == "Bar" diff --git a/tfworker/backends/base.py b/tfworker/backends/base.py index 6fa40cc..550de42 100644 --- a/tfworker/backends/base.py +++ b/tfworker/backends/base.py @@ -18,7 +18,16 @@ class BackendError(Exception): - pass + # add custom "help" parameter to the exception + def __init__(self, message, help=None): + super().__init__(message) + self._help = help + + @property + def help(self): + if self._help is None: + return "No help available" + return self._help class BaseBackend(metaclass=ABCMeta): diff --git a/tfworker/backends/s3.py b/tfworker/backends/s3.py index c7f33bb..b1f9564 100644 --- a/tfworker/backends/s3.py +++ b/tfworker/backends/s3.py @@ -34,103 +34,32 @@ class S3Backend(BaseBackend): plan_storage = False def __init__(self, authenticators, definitions, deployment=None): - self._authenticator = authenticators[self.auth_tag] + # print the module name for debugging self._definitions = definitions - self._deployment = "undefined" - self._handlers = None - - if deployment: - self._deployment = deployment - - self._ddb_client = boto3.client( - "dynamodb", - region_name=self._authenticator.backend_region, - aws_access_key_id=self._authenticator.access_key_id, - aws_secret_access_key=self._authenticator.secret_access_key, - aws_session_token=self._authenticator.session_token, - ) - - locking_table_name = f"terraform-{deployment}" - - # Check locking table for aws backend - click.secho( - f"Checking backend locking table: {locking_table_name}", fg="yellow" - ) + self._authenticator = authenticators[self.auth_tag] + if not self._authenticator.session: + raise BackendError( + "AWS session not available", + help="Either provide AWS credentials or a profile, see --help for more information.", + ) + if not self._authenticator.backend_session: + raise BackendError( + "AWS backend session not available", + help="Either provide AWS credentials or a profile, see --help for more information.", + ) - if self._check_table_exists(locking_table_name): - click.secho("DynamoDB lock table found, continuing.", fg="yellow") + if deployment is None: + self._deployment = "undefined" else: - click.secho( - "DynamoDB lock table not found, creating, please wait...", fg="yellow" - ) - self._create_table(locking_table_name) + self._deployment = deployment - # Initialize s3 client and create bucket if necessary. + # Setup AWS clients and ensure backend resources are available + self._ddb_client = self._authenticator.backend_session.client("dynamodb") self._s3_client = self._authenticator.backend_session.client("s3") - try: - self._s3_client.head_bucket(Bucket=self._authenticator.bucket) - except botocore.exceptions.ClientError as err: - err_str = str(err) - if "Not Found" not in err_str: - raise err - if self._authenticator.create_backend_bucket: - try: - self._s3_client.create_bucket( - Bucket=self._authenticator.bucket, - CreateBucketConfiguration={ - "LocationConstraint": self._authenticator.backend_region - }, - ACL="private", - ) - except botocore.exceptions.ClientError as err: - err_str = str(err) - if "InvalidLocationConstraint" in err_str: - click.secho( - "InvalidLocationConstraint raised when trying to create a bucket. " - "Verify the AWS Region passed to the worker matches the AWS region " - "in the profile.", - fg="red", - ) - elif "BucketAlreadyExists" in err_str: - # Ignore when testing - if "PYTEST_CURRENT_TEST" not in os.environ: - click.secho(err_str, fg="red") - sys.exit(4) - elif "BucketAlreadyOwnedByYou" not in err_str: - raise err - - # Block public access - self._s3_client.put_public_access_block( - Bucket=self._authenticator.bucket, - PublicAccessBlockConfiguration={ - "BlockPublicAcls": True, - "IgnorePublicAcls": True, - "BlockPublicPolicy": True, - "RestrictPublicBuckets": True, - }, - ) + self._ensure_locking_table() + self._ensure_backend_bucket() + self._bucket_files = self._get_bucket_files() - # Enable versioning on the bucket - s3_resource = self._authenticator.backend_session.resource("s3") - versioning = s3_resource.BucketVersioning(self._authenticator.bucket) - versioning.enable() - else: - raise BackendError( - "Backend bucket not found and --no-create-backend-bucket specified." - ) - - # Generate a list of all files in the bucket, at the desired prefix for the deployment, used for "--all-remote-states" option and clean - s3_paginator = self._s3_client.get_paginator("list_objects_v2").paginate( - Bucket=self._authenticator.bucket, - Prefix=self._authenticator.prefix, - ) - - self._bucket_files = set() - for page in s3_paginator: - if "Contents" in page: - for key in page["Contents"]: - # just append the last part of the prefix to the list - self._bucket_files.add(key["Key"].split("/")[-2]) try: self._handlers = S3Handler(self._authenticator) self.plan_storage = True @@ -149,12 +78,6 @@ def remotes(self) -> list: """return a list of the remote bucket keys""" return list(self._bucket_files) - def _check_table_exists(self, name: str) -> bool: - """check if a supplied dynamodb table exists""" - if name in self._ddb_client.list_tables()["TableNames"]: - return True - return False - def _clean_bucket_state(self, definition=None): """ clean_state validates all of the terraform states are empty, @@ -210,6 +133,33 @@ def _clean_locking_state(self, deployment, definition=None): fg="yellow", ) + def _ensure_locking_table(self) -> None: + """ + _ensure_locking_table checks for the existence of the locking table, and + creates it if it doesn't exist + """ + # get dynamodb client from backend session + locking_table_name = f"terraform-{self._deployment}" + + # Check locking table for aws backend + click.secho( + f"Checking backend locking table: {locking_table_name}", fg="yellow" + ) + + if self._check_table_exists(locking_table_name): + click.secho("DynamoDB lock table found, continuing.", fg="yellow") + else: + click.secho( + "DynamoDB lock table not found, creating, please wait...", fg="yellow" + ) + self._create_table(locking_table_name) + + def _check_table_exists(self, name: str) -> bool: + """check if a supplied dynamodb table exists""" + if name in self._ddb_client.list_tables()["TableNames"]: + return True + return False + def _create_table( self, name: str, read_capacity: int = 1, write_capacity: int = 1 ) -> None: @@ -233,6 +183,119 @@ def _create_table( TableName=name, WaiterConfig={"Delay": 10, "MaxAttempts": 30} ) + def _ensure_backend_bucket(self) -> None: + """ + _ensure_backend_bucket checks for the existence of the backend bucket, and + creates it if it doesn't exist, along with setting the appropriate bucket + permissions + """ + bucket_present = self._check_bucket_exists(self._authenticator.bucket) + + if bucket_present: + click.secho( + f"Backend bucket: {self._authenticator.bucket} found", fg="yellow" + ) + return + + if not self._authenticator.create_backend_bucket and not bucket_present: + raise BackendError( + "Backend bucket not found and --no-create-backend-bucket specified." + ) + + self._create_bucket(self._authenticator.bucket) + self._create_bucket_versioning(self._authenticator.bucket) + self._create_bucket_public_access_block(self._authenticator.bucket) + + def _create_bucket(self, name: str) -> None: + """ + _create_bucket creates a new s3 bucket + """ + try: + click.secho(f"Creating backend bucket: {name}", fg="yellow") + self._s3_client.create_bucket( + Bucket=name, + CreateBucketConfiguration={ + "LocationConstraint": self._authenticator.backend_region + }, + ACL="private", + ) + except botocore.exceptions.ClientError as err: + err_str = str(err) + if "InvalidLocationConstraint" in err_str: + click.secho( + "InvalidLocationConstraint raised when trying to create a bucket. " + "Verify the AWS backend region passed to the worker matches the " + "backend AWS region in the profile.", + fg="red", + ) + raise SystemExit(1) + elif "BucketAlreadyExists" in err_str: + # Ignore when testing + if "PYTEST_CURRENT_TEST" not in os.environ: + click.secho( + f"Bucket {name} already exists, this is not expected since a moment ago it did not", + fg="red", + ) + raise SystemExit(1) + elif "BucketAlreadyOwnedByYou" not in err_str: + raise err + + def _create_bucket_versioning(self, name: str) -> None: + """ + _create_bucket_versioning enables versioning on the bucket + """ + click.secho(f"Enabling versioning on bucket: {name}", fg="yellow") + self._s3_client.put_bucket_versioning( + Bucket=name, VersioningConfiguration={"Status": "Enabled"} + ) + + def _create_bucket_public_access_block(self, name: str) -> None: + """ + _create_bucket_public_access_block blocks public access to the bucket + """ + click.secho(f"Blocking public access to bucket: {name}", fg="yellow") + self._s3_client.put_public_access_block( + Bucket=name, + PublicAccessBlockConfiguration={ + "BlockPublicAcls": True, + "IgnorePublicAcls": True, + "BlockPublicPolicy": True, + "RestrictPublicBuckets": True, + }, + ) + + def _check_bucket_exists(self, name: str) -> bool: + """ + check if a supplied bucket exists + """ + try: + self._s3_client.head_bucket(Bucket=name) + return True + except botocore.exceptions.ClientError as err: + err_str = str(err) + if "Not Found" in err_str: + return False + raise err + + def _get_bucket_files(self) -> set: + """ + _get_bucket_files returns a set of the keys in the bucket + """ + bucket_files = set() + s3_paginator = self._s3_client.get_paginator("list_objects_v2").paginate( + Bucket=self._authenticator.bucket, + Prefix=self._authenticator.prefix, + ) + + for page in s3_paginator: + if "Contents" in page: + for key in page["Contents"]: + # just append the last part of the prefix to the list, as they + # are relative to the base path, and deployment name + bucket_files.add(key["Key"].split("/")[-2]) + + return bucket_files + def _delete_with_versions(self, key): """ _delete_with_versions should handle object deletions, and all references / versions of the object diff --git a/tfworker/cli.py b/tfworker/cli.py index 1463bb6..b0bc11d 100644 --- a/tfworker/cli.py +++ b/tfworker/cli.py @@ -229,11 +229,7 @@ def cli(context, **kwargs): validate_host() validate_working_dir(kwargs.get("working_dir", None)) config_file = kwargs["config_file"] - try: - context.obj = RootCommand(args=kwargs) - except FileNotFoundError: - click.secho(f"configuration file {config_file} not found", fg="red", err=True) - raise SystemExit(1) + context.obj = RootCommand(args=kwargs) @cli.command() diff --git a/tfworker/commands/base.py b/tfworker/commands/base.py index a06765e..8f72f0c 100644 --- a/tfworker/commands/base.py +++ b/tfworker/commands/base.py @@ -23,7 +23,7 @@ from tfworker.handlers.exceptions import HandlerError, UnknownHandler from tfworker.plugins import PluginsCollection from tfworker.providers import ProvidersCollection -from tfworker.util.system import pipe_exec, which +from tfworker.util.system import get_version, pipe_exec, which class MissingDependencyException(Exception): @@ -36,7 +36,7 @@ def __init__(self, rootc, deployment="undefined", limit=tuple(), **kwargs): self._args_dict = dict(kwargs) self._args_dict.update(self._rootc.args.__dict__) - self._version = None + self._version = get_version() self._providers = None self._definitions = None self._backend = None @@ -115,6 +115,7 @@ def __init__(self, rootc, deployment="undefined", limit=tuple(), **kwargs): ) except BackendError as e: click.secho(e, fg="red") + click.secho(e.help, fg="red") raise SystemExit(1) # if backend_plans is requested, check if backend supports it diff --git a/tfworker/commands/root.py b/tfworker/commands/root.py index bc83049..5cf32bc 100644 --- a/tfworker/commands/root.py +++ b/tfworker/commands/root.py @@ -19,6 +19,8 @@ import re import shutil import tempfile +from pathlib import Path +from typing import Union import click import hcl2 @@ -29,7 +31,12 @@ class RootCommand: def __init__(self, args={}): - """Setup state with args that are passed.""" + """ + Initialize the RootCommand with the given arguments. + + Args: + args (dict, optional): A dictionary of arguments to initialize the RootCommand with. Defaults to {}. + """ self.working_dir = args.get("working_dir", None) # the default behavior of --clean/--no-clean varies depending on if working-dir is passed @@ -52,7 +59,9 @@ def __init__(self, args={}): self.add_args(args) def __del__(self): - """Cleanup the temporary directory after execution.""" + """ + Cleanup the temporary directory after execution. + """ if self.clean: # the affect of remove_top being true is removing the top level directory, for a temporary @@ -66,18 +75,60 @@ def __del__(self): pass def add_args(self, args): - """Add a dictionary of args.""" + """ + Add a dictionary of args. + + Args: + args (dict): A dictionary of arguments to add. + """ for k, v in args.items(): self.add_arg(k, v) def add_arg(self, k, v): - """Add an argument to the state args.""" + """ + Add an argument to the state args. + + Args: + k (str): The key of the argument. + v (any): The value of the argument. + """ setattr(self.args, k, v) return None def load_config(self): + """ + Load the configuration file. + """ if not self.config_file: return + + self._config_file_exists() + rendered_config = self._process_template() + + if self.config_file.endswith(".hcl"): + self.config = ordered_config_load_hcl(rendered_config) + else: + self.config = ordered_config_load(rendered_config) + + # Decorate the RootCommand with the config values + self.tf = self.config.get("terraform", dict()) + self._pullup_keys() + self._merge_args() + + def _config_file_exists(self): + """ + Check if the configuration file exists. + """ + if not os.path.exists(self.config_file): + click.secho( + f"configuration file does not exist: {self.config_file}", fg="red" + ) + raise SystemExit(1) + + def _process_template(self) -> str: + """ + Process the Jinja2 template. + """ try: template_reader = io.StringIO() jinja_env = jinja2.Environment( @@ -89,26 +140,9 @@ def load_config(self): template_config = jinja_env.get_template( pathlib.Path(self.config_file).name ) - # maybe get_env should be optional? template_config.stream( **self.args.template_items(return_as_dict=True, get_env=True) ).dump(template_reader) - if self.config_file.endswith(".hcl"): - self.config = ordered_config_load_hcl( - template_reader.getvalue(), self.args - ) - else: - self.config = ordered_config_load(template_reader.getvalue(), self.args) - - # A little arbitrary, but decorate the top two levels - # directly on self object - self.tf = self.config.get("terraform", dict()) - self._pullup_keys() - self._merge_args() - except jinja2.exceptions.TemplateNotFound as e: - path = pathlib.Path(self.config_file).parents[0] - click.secho(f"can not read template file: {path}/{e}", fg="red") - raise SystemExit(1) except jinja2.exceptions.UndefinedError as e: click.secho( f"configuration file contains invalid template substitutions: {e}", @@ -116,9 +150,12 @@ def load_config(self): ) raise SystemExit(1) + return template_reader.getvalue() + def _pullup_keys(self): - """_pullup_keys is a utility function to place keys from the loaded config file - directly on the RootCommand instance.""" + """ + A utility function to place keys from the loaded config file directly on the RootCommand instance. + """ for k in [ "definitions", "providers", @@ -134,11 +171,16 @@ def _pullup_keys(self): setattr(self, f"{k}_odict", None) def _merge_args(self): + """ + Merge the worker options from the config file with the command line arguments. + """ for k, v in self.worker_options_odict.items(): self.add_arg(k, v) class StateArgs: - """A class to hold arguments in the state for easier access.""" + """ + A class to hold arguments in the state for easier access. + """ def __iter__(self): return iter(self.__dict__) @@ -183,8 +225,13 @@ def template_items(self, return_as_dict=False, get_env=False): def get_config_var_dict(config_vars): """ - get_config_var_dict returns a dictionary of of key=value for each item - provided as a command line substitution + Returns a dictionary of of key=value for each item provided as a command line substitution. + + Args: + config_vars (list): A list of command line substitutions. + + Returns: + dict: A dictionary of key=value pairs. """ return_vars = dict() for cv in config_vars: @@ -196,25 +243,34 @@ def get_config_var_dict(config_vars): return return_vars -def ordered_config_load_hcl(stream, args) -> dict: +def ordered_config_load_hcl(config: str) -> dict: """ Load an hcl config, and replace templated items. """ - return hcl2.loads(stream) + return hcl2.loads(config) -def ordered_config_load(stream, args) -> dict: +def ordered_config_load(config: str) -> dict: """ since python 3.7 the yaml loader is deterministic, so we can use the standard yaml loader """ - return yaml.load(stream, Loader=yaml.FullLoader) + try: + return yaml.load(config, Loader=yaml.FullLoader) + except yaml.YAMLError as e: + click.secho(f"error loading yaml/json: {e}", fg="red") + click.secho(f"the configuration that caused the error was\n:", fg="red") + for i, line in enumerate(config.split("\n")): + click.secho(f"{i+1}: {line}", fg="red") + raise SystemExit(1) def get_platform(): """ - get_platform will return a formatted operating system / architecture - tuple that is consistent with common distribution creation tools + Returns a formatted operating system / architecture tuple that is consistent with common distribution creation tools. + + Returns: + tuple: A tuple containing the operating system and architecture. """ # strip off "2" which only appears on old linux kernels @@ -233,11 +289,15 @@ def get_platform(): return (opsys, machine) -def rm_tree(base_path, inner=False): +def rm_tree(base_path: Union[str, Path], inner: bool = False) -> None: """ - rm_tree recursively removes all files and directories + Recursively removes all files and directories. + + Args: + base_path (Union[str, Path]): The base path to start removing files and directories from. + inner (bool, optional): Controls recrusion, if True only the inner files and directories are removed. Defaults to False. """ - parent = pathlib.Path(base_path) + parent: Path = Path(base_path) for child in parent.glob("*"): if child.is_file() or child.is_symlink(): diff --git a/tfworker/commands/version.py b/tfworker/commands/version.py index 7c35e59..e8c1c52 100644 --- a/tfworker/commands/version.py +++ b/tfworker/commands/version.py @@ -11,19 +11,13 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - -from pkg_resources import DistributionNotFound, get_distribution - from tfworker.commands.base import BaseCommand +from tfworker.util.system import get_version class VersionCommand(BaseCommand): def __init__(self): - try: - pkg_info = get_distribution("terraform-worker") - self._version = pkg_info.version - except DistributionNotFound: - self._version = "unknown" + self._version = get_version() def exec(self): print(f"terraform-worker version {self._version}") diff --git a/tfworker/plugins.py b/tfworker/plugins.py index 0ea234b..1587092 100644 --- a/tfworker/plugins.py +++ b/tfworker/plugins.py @@ -21,8 +21,14 @@ import zipfile import click -from tenacity import (RetryError, retry, retry_if_not_exception_message, - stop_after_attempt, wait_chain, wait_fixed) +from tenacity import ( + RetryError, + retry, + retry_if_not_exception_message, + stop_after_attempt, + wait_chain, + wait_fixed, +) from tfworker.commands.root import get_platform diff --git a/tfworker/util/system.py b/tfworker/util/system.py index c48596b..bc21766 100644 --- a/tfworker/util/system.py +++ b/tfworker/util/system.py @@ -17,6 +17,7 @@ import subprocess import click +from pkg_resources import DistributionNotFound, get_distribution def strip_ansi(line): @@ -147,3 +148,14 @@ def is_exe(fpath): if is_exe(exe_file): return exe_file return None + + +def get_version() -> str: + """ + Get the version of the current package + """ + try: + pkg_info = get_distribution("terraform-worker") + return pkg_info.version + except DistributionNotFound: + return "unknown"