diff --git a/.travis.yml b/.travis.yml index 21a05de..7aa9cd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,20 @@ dist: xenial +sudo: false language: python -python: - - 3.5 - - 3.6 - - 3.7 +matrix: + include: + - python: 3.5 + env: TOXENV=py35 + - python: 3.6 + env: TOXENV=py36 + - python: 3.7 + env: TOXENV=py37 +before_install: + - sudo rm -f /etc/boto.cfg install: - - pip install -r requirements.txt - - pip install -r requirements-dev.txt -script: - - make + - pip install tox pipenv + - pipenv install +script: tox deploy: provider: pypi user: jacobi diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c9deaf7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Jacobi Petrucciani + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..bf7e4ff --- /dev/null +++ b/Pipfile @@ -0,0 +1,11 @@ +[[source]] +url = "https://pypi.python.org/simple" +verify_ssl = true + +[packages] +boto3 = "*" + +[dev-packages] +tox = "*" +moto = "*" +pytest = "*" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..fce8cc5 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,603 @@ +{ + "_meta": { + "hash": { + "sha256": "195aa7e2804abd9e36418735dee976c496a2307e205f2320dd4a56c62bc8b686" + }, + "pipfile-spec": 6, + "requires": {}, + "sources": [ + { + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "boto3": { + "hashes": [ + "sha256:04a32520a546ceb4bd97b4dea87d7c079b01c6ec55280bce86ca12fc80ac7e32", + "sha256:24aa9be2a87bb0a7ac1dff2dc5471dde837fcb96ffc30876d688971171889301" + ], + "version": "==1.9.192" + }, + "botocore": { + "hashes": [ + "sha256:ecf8d872baec661e292e01d45f666fdc4a4eca716bbc6a7876223899a1a9d9d8", + "sha256:fbb621329cb41f3fcaa996ab938837ad78a4db6ad1d1a8ef48ec59a56f379049" + ], + "version": "==1.12.192" + }, + "docutils": { + "hashes": [ + "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", + "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", + "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6" + ], + "version": "==0.14" + }, + "jmespath": { + "hashes": [ + "sha256:3720a4b1bd659dd2eecad0666459b9788813e032b83e7ba58578e48254e0a0e6", + "sha256:bde2aef6f44302dfb30320115b17d030798de8c4110e28d5cf6cf91a7a31074c" + ], + "version": "==0.9.4" + }, + "python-dateutil": { + "hashes": [ + "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", + "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e" + ], + "markers": "python_version >= '2.7'", + "version": "==2.8.0" + }, + "s3transfer": { + "hashes": [ + "sha256:6efc926738a3cd576c2a79725fed9afde92378aa5c6a957e3af010cb019fac9d", + "sha256:b780f2411b824cb541dbcd2c713d0cb61c7d1bcadae204cdddda2b35cef493ba" + ], + "version": "==0.2.1" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "urllib3": { + "hashes": [ + "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", + "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" + ], + "markers": "python_version >= '3.4'", + "version": "==1.25.3" + } + }, + "develop": { + "asn1crypto": { + "hashes": [ + "sha256:2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87", + "sha256:9d5c20441baf0cb60a4ac34cc447c6c189024b6b4c6cd7877034f4965c464e49" + ], + "version": "==0.24.0" + }, + "atomicwrites": { + "hashes": [ + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + ], + "version": "==1.3.0" + }, + "attrs": { + "hashes": [ + "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", + "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + ], + "version": "==19.1.0" + }, + "aws-sam-translator": { + "hashes": [ + "sha256:4f6c4a0b8f416c9336be8465f7e252560738308cfb2fa840d5e77257b5608945" + ], + "version": "==1.12.0" + }, + "aws-xray-sdk": { + "hashes": [ + "sha256:75cbce8c777b7d8055719ee1a0db6043e53c44e8f1a62a956bd84db87c4a4c7c", + "sha256:ce4adb60fe67ebe91f2fc57d5067b4e44df6e233652987be4fb2e549688cf9fe" + ], + "version": "==2.4.2" + }, + "boto": { + "hashes": [ + "sha256:147758d41ae7240dc989f0039f27da8ca0d53734be0eb869ef16e3adcfa462e8", + "sha256:ea0d3b40a2d852767be77ca343b58a9e3a4b00d9db440efb8da74b4e58025e5a" + ], + "version": "==2.49.0" + }, + "boto3": { + "hashes": [ + "sha256:04a32520a546ceb4bd97b4dea87d7c079b01c6ec55280bce86ca12fc80ac7e32", + "sha256:24aa9be2a87bb0a7ac1dff2dc5471dde837fcb96ffc30876d688971171889301" + ], + "version": "==1.9.192" + }, + "botocore": { + "hashes": [ + "sha256:ecf8d872baec661e292e01d45f666fdc4a4eca716bbc6a7876223899a1a9d9d8", + "sha256:fbb621329cb41f3fcaa996ab938837ad78a4db6ad1d1a8ef48ec59a56f379049" + ], + "version": "==1.12.192" + }, + "certifi": { + "hashes": [ + "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", + "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" + ], + "version": "==2019.6.16" + }, + "cffi": { + "hashes": [ + "sha256:041c81822e9f84b1d9c401182e174996f0bae9991f33725d059b771744290774", + "sha256:046ef9a22f5d3eed06334d01b1e836977eeef500d9b78e9ef693f9380ad0b83d", + "sha256:066bc4c7895c91812eff46f4b1c285220947d4aa46fa0a2651ff85f2afae9c90", + "sha256:066c7ff148ae33040c01058662d6752fd73fbc8e64787229ea8498c7d7f4041b", + "sha256:2444d0c61f03dcd26dbf7600cf64354376ee579acad77aef459e34efcb438c63", + "sha256:300832850b8f7967e278870c5d51e3819b9aad8f0a2c8dbe39ab11f119237f45", + "sha256:34c77afe85b6b9e967bd8154e3855e847b70ca42043db6ad17f26899a3df1b25", + "sha256:46de5fa00f7ac09f020729148ff632819649b3e05a007d286242c4882f7b1dc3", + "sha256:4aa8ee7ba27c472d429b980c51e714a24f47ca296d53f4d7868075b175866f4b", + "sha256:4d0004eb4351e35ed950c14c11e734182591465a33e960a4ab5e8d4f04d72647", + "sha256:4e3d3f31a1e202b0f5a35ba3bc4eb41e2fc2b11c1eff38b362de710bcffb5016", + "sha256:50bec6d35e6b1aaeb17f7c4e2b9374ebf95a8975d57863546fa83e8d31bdb8c4", + "sha256:55cad9a6df1e2a1d62063f79d0881a414a906a6962bc160ac968cc03ed3efcfb", + "sha256:5662ad4e4e84f1eaa8efce5da695c5d2e229c563f9d5ce5b0113f71321bcf753", + "sha256:59b4dc008f98fc6ee2bb4fd7fc786a8d70000d058c2bbe2698275bc53a8d3fa7", + "sha256:73e1ffefe05e4ccd7bcea61af76f36077b914f92b76f95ccf00b0c1b9186f3f9", + "sha256:a1f0fd46eba2d71ce1589f7e50a9e2ffaeb739fb2c11e8192aa2b45d5f6cc41f", + "sha256:a2e85dc204556657661051ff4bab75a84e968669765c8a2cd425918699c3d0e8", + "sha256:a5457d47dfff24882a21492e5815f891c0ca35fefae8aa742c6c263dac16ef1f", + "sha256:a8dccd61d52a8dae4a825cdbb7735da530179fea472903eb871a5513b5abbfdc", + "sha256:ae61af521ed676cf16ae94f30fe202781a38d7178b6b4ab622e4eec8cefaff42", + "sha256:b012a5edb48288f77a63dba0840c92d0504aa215612da4541b7b42d849bc83a3", + "sha256:d2c5cfa536227f57f97c92ac30c8109688ace8fa4ac086d19d0af47d134e2909", + "sha256:d42b5796e20aacc9d15e66befb7a345454eef794fdb0737d1af593447c6c8f45", + "sha256:dee54f5d30d775f525894d67b1495625dd9322945e7fee00731952e0368ff42d", + "sha256:e070535507bd6aa07124258171be2ee8dfc19119c28ca94c9dfb7efd23564512", + "sha256:e1ff2748c84d97b065cc95429814cdba39bcbd77c9c85c89344b317dc0d9cbff", + "sha256:ed851c75d1e0e043cbf5ca9a8e1b13c4c90f3fbd863dacb01c0808e2b5204201" + ], + "version": "==1.12.3" + }, + "cfn-lint": { + "hashes": [ + "sha256:050084d75e8987e50380bb5735db3cd6143bf409236e8e4579d210581332a7a7", + "sha256:3ee51094c23a0ca3914f70205fdb84c00050ffed87f0a5ede8f80be584a8c199" + ], + "version": "==0.22.3" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "cryptography": { + "hashes": [ + "sha256:24b61e5fcb506424d3ec4e18bca995833839bf13c59fc43e530e488f28d46b8c", + "sha256:25dd1581a183e9e7a806fe0543f485103232f940fcfc301db65e630512cce643", + "sha256:3452bba7c21c69f2df772762be0066c7ed5dc65df494a1d53a58b683a83e1216", + "sha256:41a0be220dd1ed9e998f5891948306eb8c812b512dc398e5a01846d855050799", + "sha256:5751d8a11b956fbfa314f6553d186b94aa70fdb03d8a4d4f1c82dcacf0cbe28a", + "sha256:5f61c7d749048fa6e3322258b4263463bfccefecb0dd731b6561cb617a1d9bb9", + "sha256:72e24c521fa2106f19623a3851e9f89ddfdeb9ac63871c7643790f872a305dfc", + "sha256:7b97ae6ef5cba2e3bb14256625423413d5ce8d1abb91d4f29b6d1a081da765f8", + "sha256:961e886d8a3590fd2c723cf07be14e2a91cf53c25f02435c04d39e90780e3b53", + "sha256:96d8473848e984184b6728e2c9d391482008646276c3ff084a1bd89e15ff53a1", + "sha256:ae536da50c7ad1e002c3eee101871d93abdc90d9c5f651818450a0d3af718609", + "sha256:b0db0cecf396033abb4a93c95d1602f268b3a68bb0a9cc06a7cff587bb9a7292", + "sha256:cfee9164954c186b191b91d4193989ca994703b2fff406f71cf454a2d3c7327e", + "sha256:e6347742ac8f35ded4a46ff835c60e68c22a536a8ae5c4422966d06946b6d4c6", + "sha256:f27d93f0139a3c056172ebb5d4f9056e770fdf0206c2f422ff2ebbad142e09ed", + "sha256:f57b76e46a58b63d1c6375017f4564a28f19a5ca912691fd2e4261b3414b618d" + ], + "version": "==2.7" + }, + "datetime": { + "hashes": [ + "sha256:371dba07417b929a4fa685c2f7a3eaa6a62d60c02947831f97d4df9a9e70dfd0", + "sha256:5cef605bab8259ff61281762cdf3290e459fbf0b4719951d5fab967d5f2ea0ea" + ], + "version": "==4.3" + }, + "docker": { + "hashes": [ + "sha256:acf51b5e3e0d056925c3b780067a6f753c915fffaa46c5f2d79eb0fc1cbe6a01", + "sha256:cc5b2e94af6a2b1e1ed9d7dcbdc77eff56c36081757baf9ada6e878ea0213164" + ], + "version": "==4.0.2" + }, + "docutils": { + "hashes": [ + "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", + "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", + "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6" + ], + "version": "==0.14" + }, + "ecdsa": { + "hashes": [ + "sha256:20c17e527e75acad8f402290e158a6ac178b91b881f941fc6ea305bfdfb9657c", + "sha256:5c034ffa23413ac923541ceb3ac14ec15a0d2530690413bff58c12b80e56d884" + ], + "version": "==0.13.2" + }, + "filelock": { + "hashes": [ + "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", + "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" + ], + "version": "==3.0.12" + }, + "future": { + "hashes": [ + "sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8" + ], + "version": "==0.17.1" + }, + "idna": { + "hashes": [ + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + ], + "version": "==2.8" + }, + "importlib-metadata": { + "hashes": [ + "sha256:6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7", + "sha256:cb6ee23b46173539939964df59d3d72c3e0c1b5d54b84f1d8a7e912fe43612db" + ], + "version": "==0.18" + }, + "jinja2": { + "hashes": [ + "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", + "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b" + ], + "version": "==2.10.1" + }, + "jmespath": { + "hashes": [ + "sha256:3720a4b1bd659dd2eecad0666459b9788813e032b83e7ba58578e48254e0a0e6", + "sha256:bde2aef6f44302dfb30320115b17d030798de8c4110e28d5cf6cf91a7a31074c" + ], + "version": "==0.9.4" + }, + "jsondiff": { + "hashes": [ + "sha256:7e18138aecaa4a8f3b7ac7525b8466234e6378dd6cae702b982c9ed851d2ae21" + ], + "version": "==1.1.2" + }, + "jsonpatch": { + "hashes": [ + "sha256:49f29cab70e9068db3b1dc6b656cbe2ee4edf7dfe9bf5a0055f17a4b6804a4b9", + "sha256:8bf92fa26bc42c346c03bd4517722a8e4f429225dbe775ac774b2c70d95dbd33" + ], + "version": "==1.23" + }, + "jsonpickle": { + "hashes": [ + "sha256:d0c5a4e6cb4e58f6d5406bdded44365c2bcf9c836c4f52910cc9ba7245a59dc2", + "sha256:d3e922d781b1d0096df2dad89a2e1f47177d7969b596aea806a9d91b4626b29b" + ], + "version": "==1.2" + }, + "jsonpointer": { + "hashes": [ + "sha256:c192ba86648e05fdae4f08a17ec25180a9aef5008d973407b581798a83975362", + "sha256:ff379fa021d1b81ab539f5ec467c7745beb1a5671463f9dcc2b2d458bd361c1e" + ], + "version": "==2.0" + }, + "jsonschema": { + "hashes": [ + "sha256:0c0a81564f181de3212efa2d17de1910f8732fa1b71c42266d983cd74304e20d", + "sha256:a5f6559964a3851f59040d3b961de5e68e70971afb88ba519d27e6a039efff1a" + ], + "version": "==3.0.1" + }, + "markupsafe": { + "hashes": [ + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" + ], + "version": "==1.1.1" + }, + "mock": { + "hashes": [ + "sha256:83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3", + "sha256:d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8" + ], + "version": "==3.0.5" + }, + "more-itertools": { + "hashes": [ + "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", + "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4" + ], + "version": "==7.2.0" + }, + "moto": { + "hashes": [ + "sha256:95d48d8ebaad47fb5bb4233854cf1cf8523ec5307d50eb1e4017ce10f1960b66" + ], + "version": "==1.3.13" + }, + "packaging": { + "hashes": [ + "sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af", + "sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3" + ], + "version": "==19.0" + }, + "pluggy": { + "hashes": [ + "sha256:0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc", + "sha256:b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c" + ], + "version": "==0.12.0" + }, + "py": { + "hashes": [ + "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", + "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" + ], + "version": "==1.8.0" + }, + "pyasn1": { + "hashes": [ + "sha256:da2420fe13a9452d8ae97a0e478adde1dee153b11ba832a95b223a2ba01c10f7", + "sha256:da6b43a8c9ae93bc80e2739efb38cc776ba74a886e3e9318d65fe81a8b8a2c6e" + ], + "version": "==0.4.5" + }, + "pycparser": { + "hashes": [ + "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3" + ], + "version": "==2.19" + }, + "pyparsing": { + "hashes": [ + "sha256:530d8bf8cc93a34019d08142593cf4d78a05c890da8cf87ffa3120af53772238", + "sha256:f78e99616b6f1a4745c0580e170251ef1bbafc0d0513e270c4bd281bf29d2800" + ], + "version": "==2.4.1" + }, + "pyrsistent": { + "hashes": [ + "sha256:50cffebc87ca91b9d4be2dcc2e479272bcb466b5a0487b6c271f7ddea6917e14" + ], + "version": "==0.15.3" + }, + "pytest": { + "hashes": [ + "sha256:6ef6d06de77ce2961156013e9dff62f1b2688aa04d0dc244299fe7d67e09370d", + "sha256:a736fed91c12681a7b34617c8fcefe39ea04599ca72c608751c31d89579a3f77" + ], + "version": "==5.0.1" + }, + "python-dateutil": { + "hashes": [ + "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", + "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e" + ], + "markers": "python_version >= '2.7'", + "version": "==2.8.0" + }, + "python-jose": { + "hashes": [ + "sha256:29701d998fe560e52f17246c3213a882a4a39da7e42c7015bcc1f7823ceaff1c", + "sha256:ed7387f0f9af2ea0ddc441d83a6eb47a5909bd0c8a72ac3250e75afec2cc1371" + ], + "version": "==3.0.1" + }, + "pytz": { + "hashes": [ + "sha256:303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda", + "sha256:d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141" + ], + "version": "==2019.1" + }, + "pyyaml": { + "hashes": [ + "sha256:57acc1d8533cbe51f6662a55434f0dbecfa2b9eaf115bede8f6fd00115a0c0d3", + "sha256:588c94b3d16b76cfed8e0be54932e5729cc185caffaa5a451e7ad2f7ed8b4043", + "sha256:68c8dd247f29f9a0d09375c9c6b8fdc64b60810ebf07ba4cdd64ceee3a58c7b7", + "sha256:70d9818f1c9cd5c48bb87804f2efc8692f1023dac7f1a1a5c61d454043c1d265", + "sha256:86a93cccd50f8c125286e637328ff4eef108400dd7089b46a7be3445eecfa391", + "sha256:a0f329125a926876f647c9fa0ef32801587a12328b4a3c741270464e3e4fa778", + "sha256:a3c252ab0fa1bb0d5a3f6449a4826732f3eb6c0270925548cac342bc9b22c225", + "sha256:b4bb4d3f5e232425e25dda21c070ce05168a786ac9eda43768ab7f3ac2770955", + "sha256:cd0618c5ba5bda5f4039b9398bb7fb6a317bb8298218c3de25c47c4740e4b95e", + "sha256:ceacb9e5f8474dcf45b940578591c7f3d960e82f926c707788a570b51ba59190", + "sha256:fe6a88094b64132c4bb3b631412e90032e8cfe9745a58370462240b8cb7553cd" + ], + "version": "==5.1.1" + }, + "requests": { + "hashes": [ + "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", + "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" + ], + "version": "==2.22.0" + }, + "responses": { + "hashes": [ + "sha256:502d9c0c8008439cfcdef7e251f507fcfdd503b56e8c0c87c3c3e3393953f790", + "sha256:97193c0183d63fba8cd3a041c75464e4b09ea0aff6328800d1546598567dde0b" + ], + "version": "==0.10.6" + }, + "rsa": { + "hashes": [ + "sha256:14ba45700ff1ec9eeb206a2ce76b32814958a98e372006c8fb76ba820211be66", + "sha256:1a836406405730121ae9823e19c6e806c62bbad73f890574fff50efa4122c487" + ], + "version": "==4.0" + }, + "s3transfer": { + "hashes": [ + "sha256:6efc926738a3cd576c2a79725fed9afde92378aa5c6a957e3af010cb019fac9d", + "sha256:b780f2411b824cb541dbcd2c713d0cb61c7d1bcadae204cdddda2b35cef493ba" + ], + "version": "==0.2.1" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "sshpubkeys": { + "hashes": [ + "sha256:9f73d51c2ef1e68cd7bde0825df29b3c6ec89f4ce24ebca3bf9eaa4a23a284db", + "sha256:b388399caeeccdc145f06fd0d2665eeecc545385c60b55c282a15a022215af80" + ], + "version": "==3.1.0" + }, + "toml": { + "hashes": [ + "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", + "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" + ], + "version": "==0.10.0" + }, + "tox": { + "hashes": [ + "sha256:dab0b0160dd187b654fc33d690ee1d7bf328bd5b8dc6ef3bb3cc468969c659ba", + "sha256:ee35ffce74933a6c6ac10c9a0182e41763140a5a5070e21b114feca56eaccdcd" + ], + "version": "==3.13.2" + }, + "urllib3": { + "hashes": [ + "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", + "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" + ], + "markers": "python_version >= '3.4'", + "version": "==1.25.3" + }, + "virtualenv": { + "hashes": [ + "sha256:861bbce3a418110346c70f5c7a696fdcf23a261424e1d28aa4f9362fc2ccbc19", + "sha256:ba8ce6a961d842320681fb90a3d564d0e5134f41dacd0e2bae7f02441dde2d52" + ], + "version": "==16.6.2" + }, + "wcwidth": { + "hashes": [ + "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", + "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" + ], + "version": "==0.1.7" + }, + "websocket-client": { + "hashes": [ + "sha256:1151d5fb3a62dc129164292e1227655e4bbc5dd5340a5165dfae61128ec50aa9", + "sha256:1fd5520878b68b84b5748bb30e592b10d0a91529d5383f74f4964e72b297fd3a" + ], + "version": "==0.56.0" + }, + "werkzeug": { + "hashes": [ + "sha256:87ae4e5b5366da2347eb3116c0e6c681a0e939a33b2805e2c0cbd282664932c4", + "sha256:a13b74dd3c45f758d4ebdb224be8f1ab8ef58b3c0ffc1783a8c7d9f4f50227e6" + ], + "version": "==0.15.5" + }, + "wrapt": { + "hashes": [ + "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1" + ], + "version": "==1.11.2" + }, + "xmltodict": { + "hashes": [ + "sha256:50d8c638ed7ecb88d90561beedbf720c9b4e851a9fa6c47ebd64e99d166d8a21", + "sha256:8bbcb45cc982f48b2ca8fe7e7827c5d792f217ecf1792626f808bf41c3b86051" + ], + "version": "==0.12.0" + }, + "zipp": { + "hashes": [ + "sha256:4970c3758f4e89a7857a973b1e2a5d75bcdc47794442f2e2dd4fe8e0466e809a", + "sha256:8a5712cfd3bb4248015eb3b0b3c54a5f6ee3f2425963ef2a0125b8bc40aafaec" + ], + "version": "==0.5.2" + }, + "zope.interface": { + "hashes": [ + "sha256:086707e0f413ff8800d9c4bc26e174f7ee4c9c8b0302fbad68d083071822316c", + "sha256:1157b1ec2a1f5bf45668421e3955c60c610e31913cc695b407a574efdbae1f7b", + "sha256:11ebddf765bff3bbe8dbce10c86884d87f90ed66ee410a7e6c392086e2c63d02", + "sha256:14b242d53f6f35c2d07aa2c0e13ccb710392bcd203e1b82a1828d216f6f6b11f", + "sha256:1b3d0dcabc7c90b470e59e38a9acaa361be43b3a6ea644c0063951964717f0e5", + "sha256:20a12ab46a7e72b89ce0671e7d7a6c3c1ca2c2766ac98112f78c5bddaa6e4375", + "sha256:298f82c0ab1b182bd1f34f347ea97dde0fffb9ecf850ecf7f8904b8442a07487", + "sha256:2f6175722da6f23dbfc76c26c241b67b020e1e83ec7fe93c9e5d3dd18667ada2", + "sha256:3b877de633a0f6d81b600624ff9137312d8b1d0f517064dfc39999352ab659f0", + "sha256:4265681e77f5ac5bac0905812b828c9fe1ce80c6f3e3f8574acfb5643aeabc5b", + "sha256:550695c4e7313555549aa1cdb978dc9413d61307531f123558e438871a883d63", + "sha256:5f4d42baed3a14c290a078e2696c5f565501abde1b2f3f1a1c0a94fbf6fbcc39", + "sha256:62dd71dbed8cc6a18379700701d959307823b3b2451bdc018594c48956ace745", + "sha256:7040547e5b882349c0a2cc9b50674b1745db551f330746af434aad4f09fba2cc", + "sha256:7e099fde2cce8b29434684f82977db4e24f0efa8b0508179fce1602d103296a2", + "sha256:7e5c9a5012b2b33e87980cee7d1c82412b2ebabcb5862d53413ba1a2cfde23aa", + "sha256:81295629128f929e73be4ccfdd943a0906e5fe3cdb0d43ff1e5144d16fbb52b1", + "sha256:95cc574b0b83b85be9917d37cd2fad0ce5a0d21b024e1a5804d044aabea636fc", + "sha256:968d5c5702da15c5bf8e4a6e4b67a4d92164e334e9c0b6acf080106678230b98", + "sha256:9e998ba87df77a85c7bed53240a7257afe51a07ee6bc3445a0bf841886da0b97", + "sha256:a0c39e2535a7e9c195af956610dba5a1073071d2d85e9d2e5d789463f63e52ab", + "sha256:a15e75d284178afe529a536b0e8b28b7e107ef39626a7809b4ee64ff3abc9127", + "sha256:a6a6ff82f5f9b9702478035d8f6fb6903885653bff7ec3a1e011edc9b1a7168d", + "sha256:b639f72b95389620c1f881d94739c614d385406ab1d6926a9ffe1c8abbea23fe", + "sha256:bad44274b151d46619a7567010f7cde23a908c6faa84b97598fd2f474a0c6891", + "sha256:bbcef00d09a30948756c5968863316c949d9cedbc7aabac5e8f0ffbdb632e5f1", + "sha256:d788a3999014ddf416f2dc454efa4a5dbeda657c6aba031cf363741273804c6b", + "sha256:eed88ae03e1ef3a75a0e96a55a99d7937ed03e53d0cffc2451c208db445a2966", + "sha256:f99451f3a579e73b5dd58b1b08d1179791d49084371d9a47baad3b22417f0317" + ], + "version": "==4.6.0" + } + } +} diff --git a/README.rst b/README.rst index 695d3b8..c2d40a7 100644 --- a/README.rst +++ b/README.rst @@ -1,9 +1,39 @@ +qoo: a simple SQS client +======================== -qoo -======== +.. image:: https://travis-ci.org/jpetrucciani/qoo.svg?branch=master + :target: https://travis-ci.org/jpetrucciani/qoo -Quick start ------------ + +.. image:: https://badge.fury.io/py/qoo.svg + :target: https://badge.fury.io/py/qoo + :alt: PyPI version + + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + :alt: Code style: black + + +.. image:: https://img.shields.io/badge/python-3.5+-blue.svg + :target: https://www.python.org/downloads/release/python-350/ + :alt: Python 3.5+ supported + + +**qoo** is a very simple Amazon SQS client, written in Python. It +aims to be much more straight-forward to use than boto3, and specializes +only in Amazon SQS, ignoring the rest of the AWS ecosystem. + + +Features +-------- + +- Easier interaction with SQS queues +- Automatic support for ``AWS_ACCESS_KEY_ID``, ``AWS_SECRET_ACCESS_KEY``, and ``AWS_DEFAULT_REGION`` environment variables. +- automatic useful message/job metadata + +Usage +----- Installation ^^^^^^^^^^^^ @@ -17,17 +47,44 @@ Basic Usage .. code-block:: python - pass + import qoo + + # list bucket names + qoo.list() + + # get an existing bucket + queue = qoo.get("$QUEUE_NAME") + + # or create a queue + queue = qoo.create("$QUEUE_NAME") + + # send a job, pass info/keys as kwargs + queue.send(info="foo", user_id="test_user") # etc. + + # get an approximate count of messages in the queue + len(queue) # approximate total messages + queue.approx_not_visible # approximate number of message in the visibility timeout + + # get a job + job = queue.receive(wait_time=1) + job.elapsed # time between sending the job and receiving it + job.md5_matches # boolean property to show that the md5 of the job matches what was sent + + # and the data from the job is automatically converted into attrs + job.info # the string "foo" + job.user_id # the string "test_user" + + # delete the job from the SQS queue + del job + job.delete() Testing ------- -I'm currently working on rewriting many of the tests with `pytest `_\ . +Tests can be run with tox! .. code-block:: bash # run tests - make - # or - make test_all + tox diff --git a/qoo/__init__.py b/qoo/__init__.py index 9b3e9e6..34e745b 100644 --- a/qoo/__init__.py +++ b/qoo/__init__.py @@ -1,3 +1,76 @@ """ qoo module """ +import boto3 +import os +from qoo.errors import FailedToCreateQueue +from qoo.queues import Job, Queue # noqa +from typing import List + + +AWS_DEFAULT_REGION = "us-east-1" + + +def _client(region: str = ""): + """returns a boto3 sqs client""" + region_name = region or os.environ.get("AWS_DEFAULT_REGION", AWS_DEFAULT_REGION) + return boto3.client("sqs", region_name=region_name) + + +def login( + access_key_id: str, secret_access_key: str, region: str = AWS_DEFAULT_REGION +) -> None: + """sets environment variables for boto3.""" + os.environ["AWS_ACCESS_KEY_ID"] = access_key_id + os.environ["AWS_SECRET_ACCESS_KEY"] = secret_access_key + os.environ["AWS_DEFAULT_REGION"] = region + + +def get(queue_name: str, **kwargs) -> Queue: + """gets a qoo Queue object by SQS queue_name""" + return Queue(queue_name, **kwargs) + + +def list(region: str = "", verbose: bool = False) -> List[str]: + """ + list all queues in the default or given region + :note: this will only list up to 1000 queue names + """ + sqs_client = _client(region=region) + return [ + (x if verbose else x.split("/")[-1]) + for x in sqs_client.list_queues()["QueueUrls"] + ] + + +def create( + queue_name: str, + region: str = "", + delay_seconds: int = 0, + maximum_message_size: int = 262144, + message_retention_period: int = 345600, + visibility_timeout: int = 30, + fifo: bool = False, + receive_message_wait_time_seconds: int = 0, + **additional_attributes +) -> Queue: + """ + attempt to create an SQS queue and return it + :note: most of the common params are here, but you can pass additional_attributes if needed + """ + sqs_client = _client(region=region) + new_queue_url = sqs_client.create_queue( + QueueName=queue_name, + Attributes=dict( + DelaySeconds=str(delay_seconds), + MaximumMessageSize=str(maximum_message_size), + MessageRetentionPeriod=str(message_retention_period), + ReceiveMessageWaitTimeSeconds=str(receive_message_wait_time_seconds), + VisibilityTimeout=str(visibility_timeout), + FifoQueue=str(fifo).lower(), + **additional_attributes + ), + ) + if not new_queue_url: + raise FailedToCreateQueue() + return get(new_queue_url["QueueUrl"].split("/")[-1]) diff --git a/qoo/errors.py b/qoo/errors.py index f6fa06c..8e56c2d 100644 --- a/qoo/errors.py +++ b/qoo/errors.py @@ -20,3 +20,7 @@ def __init__(self, *args, **kwargs): # type: ignore class InvalidCredentials(QooException): """invalid credentials were passed for a queue object""" + + +class FailedToCreateQueue(QooException): + """attempting to create a queue has failed""" diff --git a/qoo/globals.py b/qoo/globals.py deleted file mode 100644 index 8a77f40..0000000 --- a/qoo/globals.py +++ /dev/null @@ -1,6 +0,0 @@ -""" -global variables for qoo -""" - - -__version__ = "0.0.1" diff --git a/qoo/queues.py b/qoo/queues.py index f51cb02..bcc5bc8 100644 --- a/qoo/queues.py +++ b/qoo/queues.py @@ -6,18 +6,21 @@ import time import hashlib from qoo.utils import jsond, jsonl +from typing import List, Optional class Job(object): """a single unit of work""" - def __init__(self, sqs_message: dict) -> None: + def __init__(self, sqs_message: dict, queue: "Queue") -> None: """job constructor""" + self._queue = queue self._data = sqs_message self._md5 = self._data["MD5OfBody"] self._id = self._data["MessageId"] self._body = jsonl(self._data["Body"]) - self._received_at = int(time.time()) + self._received_at = float(time.time()) + self.elapsed = time.time() - self._body["created_at"] for key in self._body: setattr(self, key, self._body[key]) self.handle = self._data["ReceiptHandle"] @@ -30,6 +33,14 @@ def __repr__(self) -> str: """repr""" return self.__str__() + def __del__(self) -> dict: + """del keyword for the job""" + return self.delete() + + def delete(self) -> dict: + """delete this object""" + return self._queue.delete_job(self.handle) + @property def md5_matches(self) -> bool: """verify contents of the message""" @@ -82,14 +93,22 @@ def __repr__(self) -> str: """repr""" return self.__str__() + def __len__(self) -> int: + """ + attempt to get how many messages are in the queue + :note: this is an approximate + """ + self._update_attributes() + return self.approx_messages + def _update_attributes(self) -> None: """pull the latest attributes and parse them into class attributes""" self._attributes = self._client.get_queue_attributes( QueueUrl=self._queue_url, AttributeNames=["All"] )["Attributes"] self.arn = self._attributes["QueueArn"] - self.created_at = int(self._attributes["CreatedTimestamp"]) - self.updated_at = int(self._attributes["LastModifiedTimestamp"]) + self.created_at = float(self._attributes["CreatedTimestamp"]) + self.updated_at = float(self._attributes["LastModifiedTimestamp"]) self.visibility_timeout = int(self._attributes["VisibilityTimeout"]) self.delay_seconds = int(self._attributes["DelaySeconds"]) self.maximum_message_size = int(self._attributes["MaximumMessageSize"]) @@ -104,26 +123,43 @@ def _update_attributes(self) -> None: self.approx_delayed = int( self._attributes["ApproximateNumberOfMessagesDelayed"] ) + self.fifo = "FifoQueue" in self._attributes + + def send(self, **attributes) -> str: + """shorthand for send_job""" + return self.send_job(**attributes) def send_job(self, **attributes) -> str: - """using the kwarg attributes, send a job to this queue.""" + """ + using the kwarg attributes, send a job to this queue. + pass job attributes to set the message body + """ attributes.update({"created_at": int(time.time())}) response = self._client.send_message( MessageBody=jsond(attributes), QueueUrl=self._queue_url ) return response["MessageId"] - def receive_jobs(self) -> list: + def receive_jobs( + self, max_messages: int = None, wait_time: int = None + ) -> List[Job]: """receive a list of jobs from the queue""" jobs = self._client.receive_message( QueueUrl=self._queue_url, - MaxNumberOfMessages=self._max_messages, - WaitTimeSeconds=self._wait_time, + MaxNumberOfMessages=max_messages if max_messages else self._max_messages, + WaitTimeSeconds=wait_time if wait_time else self._wait_time, ) if "Messages" not in jobs: return [] - return [Job(x) for x in jobs["Messages"]] + return [Job(x, self) for x in jobs["Messages"]] + + def receive(self, wait_time: int = None) -> Optional[Job]: + """receive a single job from the queue""" + jobs = self.receive_jobs(max_messages=1, wait_time=wait_time) + return jobs[0] if jobs else None - def delete_job(self, handle: str) -> None: - """delete a job""" - self._client.delete_message(QueueUrl=self._queue_url, ReceiptHandle=handle) + def delete_job(self, handle: str) -> dict: + """delete a job by the message handle""" + return self._client.delete_message( + QueueUrl=self._queue_url, ReceiptHandle=handle + ) diff --git a/qoo/workers.py b/qoo/workers.py index 14da3f4..57cfb68 100644 --- a/qoo/workers.py +++ b/qoo/workers.py @@ -1,5 +1,5 @@ """ -worker class +worker class - not yet used """ from time import sleep @@ -27,6 +27,5 @@ def work(self, burst=False, wait_seconds=5): job.run() queue.remove_job(job) - if burst: - break - sleep(wait_seconds) + if not burst: + sleep(wait_seconds) diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index da47343..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,2 +0,0 @@ -pytest==4.5.0 -pytest-cov==2.7.1 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d93f295..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -boto3>=1.7.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 99e0c9f..77b22bb 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,6 @@ """ pip setup file """ -from qoo.globals import __version__ from setuptools import setup @@ -10,23 +9,28 @@ LONG_DESCRIPTION = readme.read() +REQUIRED = ["boto3"] + setup( name="qoo", - version=__version__, - description=("A powerful but simple job queue wrapper around SQS."), + version="0.0.1", + description=("A simple library for interacting with Amazon SQS."), long_description=LONG_DESCRIPTION, author="Jacobi Petrucciani", author_email="jacobi@mimirhq.com", url="https://github.com/jpetrucciani/qoo.git", download_url="https://github.com/jpetrucciani/qoo.git", - license="LICENSE", + license="MIT", packages=["qoo"], - install_requires=[], + install_requires=REQUIRED, classifiers=[ + "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", ], zip_safe=False, ) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..29e70a5 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,3 @@ +""" +tests for qoo +""" diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..74d3474 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,49 @@ +""" +qoo pytest configuration +""" +import qoo +import os +import pytest +import time +from moto import mock_sqs +from typing import Generator + + +# this is to attempt to hack our way around boto issues +os.environ["AWS_DEFAULT_REGION"] = "us-east-1" + + +@pytest.fixture(autouse=True) +def login() -> Generator: + """fixture that will automatically set the login variables.""" + qoo.login("access_key", "secret_key") + yield + + +@pytest.fixture +def queue() -> Generator: + """fixture that provides an SQS qoo.""" + with mock_sqs(): + yield qoo.create("qoo") + + +@pytest.fixture +def fifo_queue() -> Generator: + """fixture that provides a fifo SQS qoo.""" + with mock_sqs(): + yield qoo.create("qoo-fifo", fifo=True) + + +@pytest.fixture +def queue_with_job(queue: qoo.Queue) -> qoo.Queue: + """fixture that provides a job""" + queue.send_job(test="test message", sent_at=int(time.time())) + return queue + + +@pytest.fixture +def queue_with_jobs(queue: qoo.Queue) -> qoo.Queue: + """fixture that provides a queue and many jobs""" + for x in range(10): + queue.send_job(test="test message {}".format(x), sent_at=int(time.time())) + return queue diff --git a/tests/test_qoo.py b/tests/test_qoo.py new file mode 100644 index 0000000..7bd6f28 --- /dev/null +++ b/tests/test_qoo.py @@ -0,0 +1,81 @@ +""" +pytest the qoo functionality +""" +import os +import qoo +import pytest +import time +from moto import mock_sqs + + +def test_login(): + """ + Ensure that login sets the correct environment variables. + + The ``login`` fixture sets these automatically. + """ + assert os.environ["AWS_ACCESS_KEY_ID"] == "access_key" + assert os.environ["AWS_SECRET_ACCESS_KEY"] == "secret_key" + assert os.environ["AWS_DEFAULT_REGION"] == "us-east-1" + + +@mock_sqs +def test_queues_can_be_created(): + """test that we can create a queue""" + queue = qoo.create("test_queue") + assert queue.name == "test_queue" + assert queue.created_at < time.time() + assert queue.maximum_message_size == 262144 + assert not queue.fifo + + +@mock_sqs +def test_fifo_queues_can_be_created(): + """test that we can create a queue""" + queue = qoo.create("test_queue", fifo=True) + assert queue.name == "test_queue" + assert queue.created_at < time.time() + assert queue.maximum_message_size == 262144 + assert queue.fifo + + +@mock_sqs +def test_queues_are_not_auto_created(): + """tests that we are not creating queues on init""" + sqs = qoo._client() + with pytest.raises(sqs.exceptions.QueueDoesNotExist): + qoo.get("this_isnt_a_queue") + + +def test_can_send_job(queue): + """test that we can send a job into the queue""" + queue.send(info="test_job") + assert len(queue) > 0 + + +def test_can_send_and_receive_job(queue): + """test that we can send a job into the queue, and pull it back out""" + queue.send(info="test_job") + assert len(queue) > 0 + job = queue.receive() + assert job + assert job.md5_matches + + +def test_can_delete_job(queue_with_job): + """test that we can delete a job from the queue""" + job = queue_with_job.receive() + del job + next_job = queue_with_job.receive(wait_time=1) + assert not next_job + + +def test_pull_two_jobs(queue_with_jobs): + """test that we can pull 2 jobs in one call""" + assert len(queue_with_jobs) == 10 + job_0 = queue_with_jobs.receive() + job_1 = queue_with_jobs.receive() + assert job_0.test + assert job_1.test + assert job_0.test == "test message 0" + assert job_1.test == "test message 1" diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..1f2d333 --- /dev/null +++ b/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py{35,36,37} + +[testenv] +commands = + pytest --cov qoo --cov-report term [] +deps = + pytest + pytest-cov + moto