diff --git a/.travis.yml b/.travis.yml index 2f94a62..6fbac08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ env: global: - RANDOM_SEED=0 matrix: - - FLASK_VERSION=1.1.1 + - FLASK_VERSION=2.3.2 before_install: - pip install pipenv diff --git a/Makefile b/Makefile index 121c48f..d89b3e6 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ endif .PHONY: test test: install ## Run unit and integration tests $(NOSE) $(PACKAGE) $(NOSE_OPTIONS) - $(COVERAGESPACE) $(REPOSITORY) overall + $(COVERAGESPACE) update overall .PHONY: read-coverage read-coverage: diff --git a/Pipfile b/Pipfile index ff5cb05..69c54e4 100644 --- a/Pipfile +++ b/Pipfile @@ -18,10 +18,10 @@ flake8 = "~=3.7.9" nose = "*" # Reports -coveragespace = "*" +coveragespace = "~=4.1" # Documentation -mkdocs = "~=0.17.2" +mkdocs = "~=1.2.3" docutils = "*" # Release diff --git a/Pipfile.lock b/Pipfile.lock index f6040f9..3db0df7 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "06c6e9a34817de4ee1793e152bd952ef30fe97bafb85001461f3cb5e5d7e969e" + "sha256": "662759c6b1e37a84f88b13fc4053a0d627d21897ff7f89cae656649393e94549" }, "pipfile-spec": 6, "requires": {}, @@ -14,34 +14,45 @@ ] }, "default": { + "blinker": { + "hashes": [ + "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213", + "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" + ], + "markers": "python_version >= '3.7'", + "version": "==1.6.2" + }, "click": { "hashes": [ - "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", - "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", + "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" ], - "version": "==7.0" + "markers": "python_version >= '3.7'", + "version": "==8.1.3" }, "flask": { "hashes": [ - "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", - "sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6" + "sha256:77fd4e1249d8c9923de34907236b747ced06e5467ecac1a7bb7115ae0e9670b0", + "sha256:8c2f9abd47a9e8df7f0c3f091ce9497d011dc3b31effcf4c85a6e2b50f4114ef" ], "index": "pypi", - "version": "==1.1.1" + "version": "==2.3.2" }, "itsdangerous": { "hashes": [ - "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", - "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" + "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", + "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" ], - "version": "==1.1.0" + "markers": "python_version >= '3.7'", + "version": "==2.1.2" }, "jinja2": { "hashes": [ - "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", - "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de" + "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", + "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" ], - "version": "==2.10.3" + "markers": "python_version >= '3.7'", + "version": "==3.1.2" }, "markdown": { "hashes": [ @@ -53,132 +64,350 @@ }, "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" + "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", + "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", + "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", + "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", + "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", + "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", + "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", + "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", + "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", + "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", + "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", + "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", + "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", + "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", + "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", + "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", + "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", + "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", + "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", + "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", + "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", + "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", + "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", + "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", + "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", + "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", + "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", + "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", + "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", + "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", + "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", + "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", + "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", + "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", + "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", + "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", + "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", + "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", + "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", + "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", + "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", + "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", + "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", + "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", + "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", + "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", + "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", + "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", + "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", + "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.2" }, "werkzeug": { "hashes": [ - "sha256:7280924747b5733b246fe23972186c6b348f9ae29724135a6dfc1e53cea433e7", - "sha256:e5f4a1f98b52b18a93da705a7458e55afb26f32bff83ff5d19189f92462d65c4" + "sha256:1d5a58e0377d1fe39d061a5de4469e414e78ccb1e1e59c0f5ad6fa1c36c52b76", + "sha256:48e5e61472fee0ddee27ebad085614ebedb7af41e88f687aaf881afb723a162f" ], - "version": "==0.16.0" + "markers": "python_version >= '3.8'", + "version": "==2.3.4" } }, "develop": { - "backports.shutil-get-terminal-size": { - "hashes": [ - "sha256:0975ba55054c15e346944b38956a4c9cbee9009391e41b86c68990effb8c1f64", - "sha256:713e7a8228ae80341c70586d1cc0a8caa5207346927e23d09dcbcaf18eadec80" - ], - "version": "==1.0.0" - }, "bleach": { "hashes": [ - "sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", - "sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa" + "sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414", + "sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4" ], - "version": "==3.1.0" + "markers": "python_version >= '3.7'", + "version": "==6.0.0" }, "certifi": { "hashes": [ - "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", - "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" - ], - "version": "==2019.9.11" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" + "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", + "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" + ], + "markers": "python_version >= '3.6'", + "version": "==2023.5.7" + }, + "cffi": { + "hashes": [ + "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", + "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", + "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", + "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", + "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", + "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", + "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", + "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", + "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", + "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", + "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", + "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", + "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", + "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", + "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", + "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", + "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", + "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", + "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", + "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", + "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", + "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", + "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", + "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", + "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", + "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", + "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", + "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", + "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", + "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", + "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", + "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", + "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", + "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", + "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", + "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", + "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", + "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", + "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", + "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", + "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", + "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", + "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", + "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", + "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", + "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", + "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", + "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", + "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", + "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", + "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", + "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", + "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", + "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", + "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", + "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", + "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", + "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", + "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", + "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", + "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", + "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", + "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", + "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" + ], + "version": "==1.15.1" + }, + "charset-normalizer": { + "hashes": [ + "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", + "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", + "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", + "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", + "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", + "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", + "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", + "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", + "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", + "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", + "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", + "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", + "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", + "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", + "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", + "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", + "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", + "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", + "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", + "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", + "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", + "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", + "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", + "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", + "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", + "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", + "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", + "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", + "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", + "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", + "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", + "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", + "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", + "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", + "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", + "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", + "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", + "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", + "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", + "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", + "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", + "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", + "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", + "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", + "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", + "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", + "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", + "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", + "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", + "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", + "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", + "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", + "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", + "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", + "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", + "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", + "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", + "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", + "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", + "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", + "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", + "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", + "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", + "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", + "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", + "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", + "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", + "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", + "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", + "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", + "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", + "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", + "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", + "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", + "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" + ], + "markers": "python_version >= '3.7'", + "version": "==3.1.0" }, "click": { "hashes": [ - "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", - "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", + "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" ], - "version": "==7.0" + "markers": "python_version >= '3.7'", + "version": "==8.1.3" }, "colorama": { "hashes": [ - "sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda", - "sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1" + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" ], - "version": "==0.3.9" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==0.4.6" }, "coverage": { "hashes": [ - "sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6", - "sha256:0be0f1ed45fc0c185cfd4ecc19a1d6532d72f86a2bac9de7e24541febad72650", - "sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5", - "sha256:19e4df788a0581238e9390c85a7a09af39c7b539b29f25c89209e6c3e371270d", - "sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351", - "sha256:245388cda02af78276b479f299bbf3783ef0a6a6273037d7c60dc73b8d8d7755", - "sha256:331cb5115673a20fb131dadd22f5bcaf7677ef758741312bee4937d71a14b2ef", - "sha256:386e2e4090f0bc5df274e720105c342263423e77ee8826002dcffe0c9533dbca", - "sha256:3a794ce50daee01c74a494919d5ebdc23d58873747fa0e288318728533a3e1ca", - "sha256:60851187677b24c6085248f0a0b9b98d49cba7ecc7ec60ba6b9d2e5574ac1ee9", - "sha256:63a9a5fc43b58735f65ed63d2cf43508f462dc49857da70b8980ad78d41d52fc", - "sha256:6b62544bb68106e3f00b21c8930e83e584fdca005d4fffd29bb39fb3ffa03cb5", - "sha256:6ba744056423ef8d450cf627289166da65903885272055fb4b5e113137cfa14f", - "sha256:7494b0b0274c5072bddbfd5b4a6c6f18fbbe1ab1d22a41e99cd2d00c8f96ecfe", - "sha256:826f32b9547c8091679ff292a82aca9c7b9650f9fda3e2ca6bf2ac905b7ce888", - "sha256:93715dffbcd0678057f947f496484e906bf9509f5c1c38fc9ba3922893cda5f5", - "sha256:9a334d6c83dfeadae576b4d633a71620d40d1c379129d587faa42ee3e2a85cce", - "sha256:af7ed8a8aa6957aac47b4268631fa1df984643f07ef00acd374e456364b373f5", - "sha256:bf0a7aed7f5521c7ca67febd57db473af4762b9622254291fbcbb8cd0ba5e33e", - "sha256:bf1ef9eb901113a9805287e090452c05547578eaab1b62e4ad456fcc049a9b7e", - "sha256:c0afd27bc0e307a1ffc04ca5ec010a290e49e3afbe841c5cafc5c5a80ecd81c9", - "sha256:dd579709a87092c6dbee09d1b7cfa81831040705ffa12a1b248935274aee0437", - "sha256:df6712284b2e44a065097846488f66840445eb987eb81b3cc6e4149e7b6982e1", - "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c", - "sha256:e2ede7c1d45e65e209d6093b762e98e8318ddeff95317d07a27a2140b80cfd24", - "sha256:e4ef9c164eb55123c62411f5936b5c2e521b12356037b6e1c2617cef45523d47", - "sha256:eca2b7343524e7ba246cab8ff00cab47a2d6d54ada3b02772e908a45675722e2", - "sha256:eee64c616adeff7db37cc37da4180a3a5b6177f5c46b187894e633f088fb5b28", - "sha256:ef824cad1f980d27f26166f86856efe11eff9912c4fed97d3804820d43fa550c", - "sha256:efc89291bd5a08855829a3c522df16d856455297cf35ae827a37edac45f466a7", - "sha256:fa964bae817babece5aa2e8c1af841bebb6d0b9add8e637548809d040443fee0", - "sha256:ff37757e068ae606659c28c3bd0d923f9d29a85de79bf25b2b34b148473b5025" - ], - "version": "==4.5.4" + "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f", + "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2", + "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a", + "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a", + "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01", + "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6", + "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7", + "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f", + "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02", + "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c", + "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063", + "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a", + "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5", + "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959", + "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97", + "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6", + "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f", + "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9", + "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5", + "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f", + "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562", + "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe", + "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9", + "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f", + "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb", + "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb", + "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1", + "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb", + "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250", + "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e", + "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511", + "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5", + "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59", + "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2", + "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d", + "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3", + "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4", + "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de", + "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9", + "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833", + "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0", + "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9", + "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d", + "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050", + "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d", + "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6", + "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353", + "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb", + "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e", + "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8", + "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495", + "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2", + "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd", + "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27", + "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1", + "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818", + "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4", + "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e", + "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850", + "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3" + ], + "markers": "python_version >= '3.7'", + "version": "==7.2.7" }, "coveragespace": { "hashes": [ - "sha256:498b54ec158a19e1f5647da681dc77fd9d17df11ecff1253d60ac7970209f6e5", - "sha256:7c5ce4641e0f995b9be0e8b53401fd7b6d17db1b8c23bfd06f0c845ad0de5b5f" + "sha256:6dcdee802be5cdaa9820538203ad0e182a1ea56c679cdf65b36d4b85937d2e38", + "sha256:a59fa4227166406f74c0fd89ad871b6d699d35de5468eeca96bf556838af0f0c" ], "index": "pypi", - "version": "==2.1" + "version": "==4.1" + }, + "cryptography": { + "hashes": [ + "sha256:0ddaee209d1cf1f180f1efa338a68c4621154de0afaef92b89486f5f96047c55", + "sha256:14754bcdae909d66ff24b7b5f166d69340ccc6cb15731670435efd5719294895", + "sha256:344c6de9f8bda3c425b3a41b319522ba3208551b70c2ae00099c205f0d9fd3be", + "sha256:34d405ea69a8b34566ba3dfb0521379b210ea5d560fafedf9f800a9a94a41928", + "sha256:3680248309d340fda9611498a5319b0193a8dbdb73586a1acf8109d06f25b92d", + "sha256:3c5ef25d060c80d6d9f7f9892e1d41bb1c79b78ce74805b8cb4aa373cb7d5ec8", + "sha256:4ab14d567f7bbe7f1cdff1c53d5324ed4d3fc8bd17c481b395db224fb405c237", + "sha256:5c1f7293c31ebc72163a9a0df246f890d65f66b4a40d9ec80081969ba8c78cc9", + "sha256:6b71f64beeea341c9b4f963b48ee3b62d62d57ba93eb120e1196b31dc1025e78", + "sha256:7d92f0248d38faa411d17f4107fc0bce0c42cae0b0ba5415505df72d751bf62d", + "sha256:8362565b3835ceacf4dc8f3b56471a2289cf51ac80946f9087e66dc283a810e0", + "sha256:84a165379cb9d411d58ed739e4af3396e544eac190805a54ba2e0322feb55c46", + "sha256:88ff107f211ea696455ea8d911389f6d2b276aabf3231bf72c8853d22db755c5", + "sha256:9f65e842cb02550fac96536edb1d17f24c0a338fd84eaf582be25926e993dde4", + "sha256:a4fc68d1c5b951cfb72dfd54702afdbbf0fb7acdc9b7dc4301bbf2225a27714d", + "sha256:b7f2f5c525a642cecad24ee8670443ba27ac1fab81bba4cc24c7b6b41f2d0c75", + "sha256:b846d59a8d5a9ba87e2c3d757ca019fa576793e8758174d3868aecb88d6fc8eb", + "sha256:bf8fc66012ca857d62f6a347007e166ed59c0bc150cefa49f28376ebe7d992a2", + "sha256:f5d0bf9b252f30a31664b6f64432b4730bb7038339bd18b1fafe129cfc2be9be" + ], + "index": "pypi", + "version": "==41.0.0" }, "docopt": { "hashes": [ @@ -188,18 +417,18 @@ }, "docutils": { "hashes": [ - "sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0", - "sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827", - "sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99" + "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125", + "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61" ], "index": "pypi", - "version": "==0.15.2" + "version": "==0.17.1" }, "entrypoints": { "hashes": [ "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451" ], + "markers": "python_version >= '2.7'", "version": "==0.3" }, "flake8": { @@ -210,47 +439,65 @@ "index": "pypi", "version": "==3.7.9" }, + "ghp-import": { + "hashes": [ + "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", + "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343" + ], + "version": "==2.1.0" + }, "idna": { "hashes": [ - "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", - "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" ], - "version": "==2.8" + "markers": "python_version >= '3.5'", + "version": "==3.4" }, "importlib-metadata": { "hashes": [ - "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", - "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af" + "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed", + "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705" ], - "markers": "python_version < '3.8'", - "version": "==0.23" + "markers": "python_version >= '3.7'", + "version": "==6.6.0" }, - "jinja2": { + "jaraco.classes": { "hashes": [ - "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", - "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de" + "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158", + "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a" ], - "version": "==2.10.3" + "markers": "python_version >= '3.7'", + "version": "==3.2.3" }, - "keyring": { + "jeepney": { "hashes": [ - "sha256:91037ccaf0c9a112a76f7740e4a416b9457a69b66c2799421581bee710a974b3", - "sha256:f5bb20ea6c57c2360daf0c591931c9ea0d7660a8d9e32ca84d63273f131ea605" + "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", + "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755" ], - "version": "==19.2.0" + "markers": "sys_platform == 'linux'", + "version": "==0.8.0" }, - "livereload": { + "jinja2": { "hashes": [ - "sha256:78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b", - "sha256:89254f78d7529d7ea0a3417d224c34287ebfe266b05e67e51facaf82c27f0f66" + "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", + "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" ], - "version": "==2.6.1" + "markers": "python_version >= '3.7'", + "version": "==3.1.2" + }, + "keyring": { + "hashes": [ + "sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd", + "sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678" + ], + "markers": "python_version >= '3.7'", + "version": "==23.13.1" }, "macfsevents": { "hashes": [ "sha256:1324b66b356051de662ba87d84f73ada062acd42b047ed1246e60a449f833e10" ], - "index": "pypi", "markers": "sys_platform == 'darwin'", "version": "==0.8.1" }, @@ -264,36 +511,59 @@ }, "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" + "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", + "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", + "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", + "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", + "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", + "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", + "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", + "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", + "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", + "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", + "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", + "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", + "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", + "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", + "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", + "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", + "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", + "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", + "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", + "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", + "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", + "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", + "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", + "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", + "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", + "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", + "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", + "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", + "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", + "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", + "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", + "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", + "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", + "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", + "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", + "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", + "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", + "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", + "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", + "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", + "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", + "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", + "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", + "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", + "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", + "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", + "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", + "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", + "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", + "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.2" }, "mccabe": { "hashes": [ @@ -302,20 +572,37 @@ ], "version": "==0.6.1" }, + "mergedeep": { + "hashes": [ + "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", + "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307" + ], + "markers": "python_version >= '3.6'", + "version": "==1.3.4" + }, + "minilog": { + "hashes": [ + "sha256:0c48879cc9e72f0127aa2c36b522dc6fa10fa8532956197436b491d31617d5d5", + "sha256:2048a8d381b36ef5f146fb9a657e627729411f8e2ed0047e2c1286cf8e3e58d7" + ], + "markers": "python_version >= '3.7' and python_version < '4.0'", + "version": "==2.1" + }, "mkdocs": { "hashes": [ - "sha256:1b4d46cd1cb517cd743358da96a3efc588fd86f81512fb9c28214597b6dc731f", - "sha256:cd7264ea42d76f5bc1a0bd8b0a2c6c6e6be3a8742f5e78f47104a452dbe93600" + "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1", + "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072" ], "index": "pypi", - "version": "==0.17.5" + "version": "==1.2.3" }, "more-itertools": { "hashes": [ - "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", - "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4" + "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d", + "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3" ], - "version": "==7.2.0" + "markers": "python_version >= '3.7'", + "version": "==9.1.0" }, "nose": { "hashes": [ @@ -326,48 +613,67 @@ "index": "pypi", "version": "==1.3.7" }, + "packaging": { + "hashes": [ + "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", + "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" + ], + "markers": "python_version >= '3.7'", + "version": "==23.1" + }, "pkginfo": { "hashes": [ - "sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb", - "sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32" + "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546", + "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046" ], - "version": "==1.5.0.1" + "markers": "python_version >= '3.6'", + "version": "==1.9.6" }, "pycodestyle": { "hashes": [ "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.5.0" }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" + }, "pyflakes": { "hashes": [ "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.1.1" }, "pygments": { "hashes": [ - "sha256:83ec6c6133ca6b529b7ff5aa826328fd14b5bb02a58c37f4f06384e96a0f94ab", - "sha256:b7949de3d396836085fea596998b135a22610bbcc4f2abfe9e448e44cbc58388" + "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c", + "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1" ], - "version": "==2.5.1" + "markers": "python_version >= '3.7'", + "version": "==2.15.1" }, "pync": { "hashes": [ "sha256:85737aab9fc69cf59dc9fe831adbe94ac224944c05e297c98de3c2413f253530" ], - "index": "pypi", "markers": "sys_platform == 'darwin'", "version": "==1.6.1" }, "python-dateutil": { "hashes": [ - "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", - "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" ], - "version": "==2.8.1" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" }, "python-termstyle": { "hashes": [ @@ -378,49 +684,113 @@ }, "pyyaml": { "hashes": [ - "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", - "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", - "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", - "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", - "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", - "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", - "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", - "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", - "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", - "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", - "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", - "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", - "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8" - ], - "version": "==5.1.2" + "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", + "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", + "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", + "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", + "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", + "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", + "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", + "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", + "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", + "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", + "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", + "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", + "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", + "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", + "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", + "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", + "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", + "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", + "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", + "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", + "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", + "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", + "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", + "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", + "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", + "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", + "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", + "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", + "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", + "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", + "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", + "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", + "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", + "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", + "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", + "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", + "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", + "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", + "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", + "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" + ], + "markers": "python_version >= '3.6'", + "version": "==6.0" + }, + "pyyaml-env-tag": { + "hashes": [ + "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", + "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069" + ], + "markers": "python_version >= '3.6'", + "version": "==0.1" }, "readme-renderer": { "hashes": [ - "sha256:bb16f55b259f27f75f640acf5e00cf897845a8b3e4731b5c1a436e4b8529202f", - "sha256:c8532b79afc0375a85f10433eca157d6b50f7d6990f337fa498c96cd4bfc203d" + "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273", + "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343" ], - "version": "==24.0" + "markers": "python_version >= '3.7'", + "version": "==37.3" }, "requests": { "hashes": [ - "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", - "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], - "version": "==2.22.0" + "markers": "python_version >= '3.7'", + "version": "==2.31.0" }, "requests-toolbelt": { "hashes": [ - "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f", - "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0" + "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", + "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06" ], - "version": "==0.9.1" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.0.0" + }, + "rfc3986": { + "hashes": [ + "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", + "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "secretstorage": { + "hashes": [ + "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", + "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99" + ], + "markers": "sys_platform == 'linux'", + "version": "==3.3.3" + }, + "setuptools": { + "hashes": [ + "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", + "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" + ], + "markers": "python_version >= '3.7'", + "version": "==67.8.0" }, "six": { "hashes": [ - "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", - "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], - "version": "==1.13.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" }, "sniffer": { "hashes": [ @@ -430,37 +800,62 @@ "index": "pypi", "version": "==0.4.1" }, - "tornado": { - "hashes": [ - "sha256:5ef073ac6180038ccf99411fe05ae9aafb675952a2c8db60592d5daf8401f803", - "sha256:6d14e47eab0e15799cf3cdcc86b0b98279da68522caace2bd7ce644287685f0a", - "sha256:92b7ca81e18ba9ec3031a7ee73d4577ac21d41a0c9b775a9182f43301c3b5f8e", - "sha256:ab587996fe6fb9ce65abfda440f9b61e4f9f2cf921967723540679176915e4c3", - "sha256:b36298e9f63f18cad97378db2222c0e0ca6a55f6304e605515e05a25483ed51a" - ], - "version": "==4.5.3" - }, "tqdm": { "hashes": [ - "sha256:5a1f3d58f3eb53264387394387fe23df469d2a3fab98c9e7f99d5c146c119873", - "sha256:f1a1613fee07cc30a253051617f2a219a785c58877f9f6bfa129446cbaf8b4c1" + "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5", + "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671" ], - "version": "==4.39.0" + "markers": "python_version >= '3.7'", + "version": "==4.65.0" }, "twine": { "hashes": [ - "sha256:c1af8ca391e43b0a06bbc155f7f67db0bf0d19d284bfc88d1675da497a946124", - "sha256:d561a5e511f70275e5a485a6275ff61851c16ffcb3a95a602189161112d9f160" + "sha256:16f706f2f1687d7ce30e7effceee40ed0a09b7c33b9abb5ef6434e5551565d83", + "sha256:a56c985264b991dc8a8f4234eb80c5af87fa8080d0c224ad8f2cd05a2c22e83b" ], "index": "pypi", - "version": "==3.1.1" + "version": "==3.4.1" }, "urllib3": { "hashes": [ - "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", - "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745" - ], - "version": "==1.25.7" + "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc", + "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.2" + }, + "watchdog": { + "hashes": [ + "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a", + "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100", + "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8", + "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc", + "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae", + "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41", + "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0", + "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f", + "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c", + "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9", + "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3", + "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709", + "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83", + "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759", + "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9", + "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3", + "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7", + "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f", + "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346", + "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674", + "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397", + "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96", + "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d", + "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a", + "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64", + "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44", + "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33" + ], + "markers": "python_version >= '3.7'", + "version": "==3.0.0" }, "webencodings": { "hashes": [ @@ -471,10 +866,11 @@ }, "zipp": { "hashes": [ - "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", - "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335" + "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", + "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556" ], - "version": "==0.6.0" + "markers": "python_version >= '3.7'", + "version": "==3.15.0" } } } diff --git a/README.md b/README.md index d9eaabf..29166a3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Browsable web APIs for Flask. -[![Unix Build Status](https://img.shields.io/travis/flask-api/flask-api.svg)](https://travis-ci.org/flask-api/flask-api) +[![Unix Build Status](https://img.shields.io/travis/com/flask-api/flask-api.svg)](https://travis-ci.com/flask-api/flask-api) [![Coverage Status](https://img.shields.io/coveralls/flask-api/flask-api.svg)](https://coveralls.io/r/flask-api/flask-api) [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/flask-api/flask-api.svg)](https://scrutinizer-ci.com/g/flask-api/flask-api/) [![PyPI Version](https://img.shields.io/pypi/v/Flask-API.svg)](https://pypi.org/project/Flask-API/) diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index 08b39d2..0000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -www.flaskapi.org diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 8c72f8e..084d595 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -1,5 +1,13 @@ # Release Notes +## Version 3.1 + +- Fixed support for Flask `2.3`. + +## Version 3.0 + +* Dropped support for Flask `<2.0`. + ## Version 2.0 * Dropped support for Python `<3.6`. diff --git a/docs/screenshot.png b/docs/screenshot.png index 9dba138..5d01222 100644 Binary files a/docs/screenshot.png and b/docs/screenshot.png differ diff --git a/example.py b/example.py index 0680147..4b5511c 100644 --- a/example.py +++ b/example.py @@ -1,29 +1,30 @@ from flask import request, url_for -from flask.ext.api import FlaskAPI, status, exceptions +from flask.ext.api import FlaskAPI, exceptions, status app = FlaskAPI(__name__) notes = { - 0: 'do the shopping', - 1: 'build the codez', - 2: 'paint the door', + 0: "do the shopping", + 1: "build the codez", + 2: "paint the door", } + def note_repr(key): return { - 'url': request.host_url.rstrip('/') + url_for('notes_detail', key=key), - 'text': notes[key] + "url": request.host_url.rstrip("/") + url_for("notes_detail", key=key), + "text": notes[key], } -@app.route("/", methods=['GET', 'POST']) +@app.route("/", methods=["GET", "POST"]) def notes_list(): """ List or create notes. """ - if request.method == 'POST': - note = str(request.data.get('text', '')) + if request.method == "POST": + note = str(request.data.get("text", "")) idx = max(notes.keys()) + 1 notes[idx] = note return note_repr(idx), status.HTTP_201_CREATED @@ -32,19 +33,19 @@ def notes_list(): return [note_repr(idx) for idx in sorted(notes.keys())] -@app.route("//", methods=['GET', 'PUT', 'DELETE']) +@app.route("//", methods=["GET", "PUT", "DELETE"]) def notes_detail(key): """ Retrieve, update or delete note instances. """ - if request.method == 'PUT': - note = str(request.data.get('text', '')) + if request.method == "PUT": + note = str(request.data.get("text", "")) notes[key] = note return note_repr(key) - elif request.method == 'DELETE': + elif request.method == "DELETE": notes.pop(key, None) - return '', status.HTTP_204_NO_CONTENT + return "", status.HTTP_204_NO_CONTENT # request.method == 'GET' if key not in notes: diff --git a/flask_api/__init__.py b/flask_api/__init__.py index 7053768..7d4d782 100644 --- a/flask_api/__init__.py +++ b/flask_api/__init__.py @@ -1,3 +1,3 @@ from flask_api.app import FlaskAPI -__version__ = '2.0' +__version__ = "3.1" diff --git a/flask_api/app.py b/flask_api/app.py index 6036c4c..5556445 100644 --- a/flask_api/app.py +++ b/flask_api/app.py @@ -1,23 +1,23 @@ -# coding: utf8 -from __future__ import unicode_literals -from flask import request, Flask, Blueprint -from flask._compat import reraise, string_types, text_type +import re +import sys +from itertools import chain + +from flask import Blueprint, Flask, request +from werkzeug.exceptions import HTTPException + +from flask_api.compat import is_flask_legacy from flask_api.exceptions import APIException from flask_api.request import APIRequest from flask_api.response import APIResponse from flask_api.settings import APISettings from flask_api.status import HTTP_204_NO_CONTENT -from itertools import chain -from werkzeug.exceptions import HTTPException -import re -import sys -from flask_api.compat import is_flask_legacy - api_resources = Blueprint( - 'flask-api', __name__, - url_prefix='/flask-api', - template_folder='templates', static_folder='static' + "flask-api", + __name__, + url_prefix="/flask-api", + template_folder="templates", + static_folder="static", ) @@ -30,15 +30,15 @@ class FlaskAPI(Flask): response_class = APIResponse def __init__(self, *args, **kwargs): - super(FlaskAPI, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.api_settings = APISettings(self.config) self.register_blueprint(api_resources) - self.jinja_env.filters['urlize_quoted_links'] = urlize_quoted_links + self.jinja_env.filters["urlize_quoted_links"] = urlize_quoted_links def preprocess_request(self): request.parser_classes = self.api_settings.DEFAULT_PARSERS request.renderer_classes = self.api_settings.DEFAULT_RENDERERS - return super(FlaskAPI, self).preprocess_request() + return super().preprocess_request() def make_response(self, rv): """ @@ -50,16 +50,16 @@ def make_response(self, rv): rv, status_or_headers, headers = rv + (None,) * (3 - len(rv)) if rv is None and status_or_headers == HTTP_204_NO_CONTENT: - rv = '' + rv = "" if rv is None and status_or_headers: - raise ValueError('View function did not return a response') + raise ValueError("View function did not return a response") if isinstance(status_or_headers, (dict, list)): headers, status_or_headers = status_or_headers, None if not isinstance(rv, self.response_class): - if isinstance(rv, (text_type, bytes, bytearray, list, dict)): + if isinstance(rv, (str, bytes, bytearray, list, dict)): status = status_or_headers rv = self.response_class(rv, headers=headers, status=status) headers = status_or_headers = None @@ -67,7 +67,7 @@ def make_response(self, rv): rv = self.response_class.force_type(rv, request.environ) if status_or_headers is not None: - if isinstance(status_or_headers, string_types): + if isinstance(status_or_headers, str): rv.status = status_or_headers else: rv.status_code = status_or_headers @@ -99,15 +99,16 @@ def handle_user_exception(self, e): if isinstance(e, typecheck): return handler(e) else: - for typecheck, handler in chain(dict(blueprint_handlers).items(), - dict(app_handlers).items()): + for typecheck, handler in chain( + dict(blueprint_handlers).items(), dict(app_handlers).items() + ): if isinstance(e, typecheck): return handler(e) - reraise(exc_type, exc_value, tb) + raise e def handle_api_exception(self, exc): - content = {'message': exc.detail} + content = {"message": exc.detail} status = exc.status_code return self.response_class(content, status=status) @@ -120,13 +121,15 @@ def create_url_adapter(self, request): """ if request is not None: environ = request.environ.copy() - environ['REQUEST_METHOD'] = request.method - return self.url_map.bind_to_environ(environ, - server_name=self.config['SERVER_NAME']) + environ["REQUEST_METHOD"] = request.method + return self.url_map.bind_to_environ( + environ, server_name=self.config["SERVER_NAME"] + ) # We need at the very least the server name to be set for this # to work. - if self.config['SERVER_NAME'] is not None: + if self.config["SERVER_NAME"] is not None: return self.url_map.bind( - self.config['SERVER_NAME'], - script_name=self.config['APPLICATION_ROOT'] or '/', - url_scheme=self.config['PREFERRED_URL_SCHEME']) + self.config["SERVER_NAME"], + script_name=self.config["APPLICATION_ROOT"] or "/", + url_scheme=self.config["PREFERRED_URL_SCHEME"], + ) diff --git a/flask_api/compat.py b/flask_api/compat.py index c59b984..8760df5 100644 --- a/flask_api/compat.py +++ b/flask_api/compat.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals, absolute_import from flask import __version__ as flask_version # Markdown is optional @@ -17,7 +15,6 @@ def apply_markdown(text): md = markdown.Markdown(extensions=extensions) return md.convert(text) - except ImportError: # pragma: no cover - markdown installed for tests apply_markdown = None diff --git a/flask_api/decorators.py b/flask_api/decorators.py index d3a09b2..6b9f5b8 100644 --- a/flask_api/decorators.py +++ b/flask_api/decorators.py @@ -1,4 +1,5 @@ from functools import wraps + from flask import request @@ -11,7 +12,9 @@ def decorated_function(*args, **kwargs): else: request.parser_classes = parsers return func(*args, **kwargs) + return decorated_function + return decorator @@ -24,5 +27,7 @@ def decorated_function(*args, **kwargs): else: request.renderer_classes = renderers return func(*args, **kwargs) + return decorated_function + return decorator diff --git a/flask_api/exceptions.py b/flask_api/exceptions.py index 27b261d..0ac0df1 100644 --- a/flask_api/exceptions.py +++ b/flask_api/exceptions.py @@ -1,10 +1,9 @@ -from __future__ import unicode_literals from flask_api import status class APIException(Exception): status_code = status.HTTP_500_INTERNAL_SERVER_ERROR - detail = '' + detail = "" def __init__(self, detail=None): if detail is not None: @@ -16,27 +15,28 @@ def __str__(self): class ParseError(APIException): status_code = status.HTTP_400_BAD_REQUEST - detail = 'Malformed request.' + detail = "Malformed request." class AuthenticationFailed(APIException): status_code = status.HTTP_401_UNAUTHORIZED - detail = 'Incorrect authentication credentials.' + detail = "Incorrect authentication credentials." class NotAuthenticated(APIException): status_code = status.HTTP_401_UNAUTHORIZED - detail = 'Authentication credentials were not provided.' + detail = "Authentication credentials were not provided." class PermissionDenied(APIException): status_code = status.HTTP_403_FORBIDDEN - detail = 'You do not have permission to perform this action.' + detail = "You do not have permission to perform this action." class NotFound(APIException): status_code = status.HTTP_404_NOT_FOUND - detail = 'This resource does not exist.' + detail = "This resource does not exist." + # class MethodNotAllowed(APIException): # status_code = status.HTTP_405_METHOD_NOT_ALLOWED @@ -48,17 +48,18 @@ class NotFound(APIException): class NotAcceptable(APIException): status_code = status.HTTP_406_NOT_ACCEPTABLE - detail = 'Could not satisfy the request Accept header.' + detail = "Could not satisfy the request Accept header." class UnsupportedMediaType(APIException): status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE - detail = 'Unsupported media type in the request Content-Type header.' + detail = "Unsupported media type in the request Content-Type header." class Throttled(APIException): status_code = status.HTTP_429_TOO_MANY_REQUESTS - detail = 'Request was throttled.' + detail = "Request was throttled." + # def __init__(self, wait=None, detail=None): # if wait is None: diff --git a/flask_api/mediatypes.py b/flask_api/mediatypes.py index 7b9bcb9..e159b37 100644 --- a/flask_api/mediatypes.py +++ b/flask_api/mediatypes.py @@ -1,14 +1,10 @@ -# coding: utf8 -from __future__ import unicode_literals - - -class MediaType(object): +class MediaType: def __init__(self, media_type): self.main_type, self.sub_type, self.params = self._parse(media_type) @property def full_type(self): - return self.main_type + '/' + self.sub_type + return self.main_type + "/" + self.sub_type @property def precedence(self): @@ -20,11 +16,11 @@ def precedence(self): 1. 'type/*' 0. '*/*' """ - if self.main_type == '*': + if self.main_type == "*": return 0 - elif self.sub_type == '*': + elif self.sub_type == "*": return 1 - elif not self.params or list(self.params.keys()) == ['q']: + elif not self.params or list(self.params.keys()) == ["q"]: return 2 return 3 @@ -39,13 +35,21 @@ def satisfies(self, other): '*/*' >= 'text/plain' """ for key in self.params.keys(): - if key != 'q' and other.params.get(key, None) != self.params.get(key, None): + if key != "q" and other.params.get(key, None) != self.params.get(key, None): return False - if self.sub_type != '*' and other.sub_type != '*' and other.sub_type != self.sub_type: + if ( + self.sub_type != "*" + and other.sub_type != "*" + and other.sub_type != self.sub_type + ): return False - if self.main_type != '*' and other.main_type != '*' and other.main_type != self.main_type: + if ( + self.main_type != "*" + and other.main_type != "*" + and other.main_type != self.main_type + ): return False return True @@ -55,15 +59,15 @@ def _parse(self, media_type): Parse a media type string, like "application/json; indent=4" into a three-tuple, like: ('application', 'json', {'indent': 4}) """ - full_type, sep, param_string = media_type.partition(';') + full_type, sep, param_string = media_type.partition(";") params = {} - for token in param_string.strip().split(','): - key, sep, value = [s.strip() for s in token.partition('=')] + for token in param_string.strip().split(","): + key, sep, value = [s.strip() for s in token.partition("=")] if value.startswith('"') and value.endswith('"'): value = value[1:-1] if key: params[key] = value - main_type, sep, sub_type = [s.strip() for s in full_type.partition('/')] + main_type, sep, sub_type = [s.strip() for s in full_type.partition("/")] return (main_type, sub_type, params) def __repr__(self): @@ -75,11 +79,10 @@ def __str__(self): Note that this ensures the params are sorted. """ if self.params: - params_str = ', '.join([ - '%s="%s"' % (key, val) - for key, val in sorted(self.params.items()) - ]) - return self.full_type + '; ' + params_str + params_str = ", ".join( + ['%s="%s"' % (key, val) for key, val in sorted(self.params.items())] + ) + return self.full_type + "; " + params_str return self.full_type def __hash__(self): @@ -87,9 +90,7 @@ def __hash__(self): def __eq__(self, other): # Compare two MediaType instances, ignoring parameter ordering. - return ( - self.full_type == other.full_type and self.params == other.params - ) + return self.full_type == other.full_type and self.params == other.params def parse_accept_header(accept): @@ -105,7 +106,7 @@ def parse_accept_header(accept): ] """ ret = [set(), set(), set(), set()] - for token in accept.split(','): + for token in accept.split(","): media_type = MediaType(token.strip()) ret[3 - media_type.precedence].add(media_type) return [media_types for media_types in ret if media_types] diff --git a/flask_api/negotiation.py b/flask_api/negotiation.py index 9ce0549..299d3a2 100644 --- a/flask_api/negotiation.py +++ b/flask_api/negotiation.py @@ -1,11 +1,10 @@ -# coding: utf8 -from __future__ import unicode_literals from flask import request + from flask_api import exceptions from flask_api.mediatypes import MediaType, parse_accept_header -class BaseNegotiation(object): +class BaseNegotiation: def select_parser(self, parsers): msg = '`select_parser()` method must be implemented for class "%s"' raise NotImplementedError(msg % self.__class__.__name__) @@ -36,7 +35,7 @@ def select_renderer(self, renderers): Determine which renderer to use for rendering the response body. Returns a two-tuple of (renderer, content type). """ - accept_header = request.headers.get('Accept', '*/*') + accept_header = request.headers.get("Accept", "*/*") for client_media_types in parse_accept_header(accept_header): for renderer in renderers: diff --git a/flask_api/parsers.py b/flask_api/parsers.py index 914fc62..56f45f6 100644 --- a/flask_api/parsers.py +++ b/flask_api/parsers.py @@ -1,17 +1,16 @@ -# coding: utf8 -from __future__ import unicode_literals -from flask._compat import text_type -from flask_api import exceptions +import json + from werkzeug.formparser import MultiPartParser as WerkzeugMultiPartParser from werkzeug.formparser import default_stream_factory from werkzeug.urls import url_decode_stream -import json + +from flask_api import exceptions -class BaseParser(object): +class BaseParser: media_type = None handles_file_uploads = False # If set then 'request.files' will be populated. - handles_form_data = False # If set then 'request.form' will be populated. + handles_form_data = False # If set then 'request.form' will be populated. def parse(self, stream, media_type, **options): msg = '`parse()` method must be implemented for class "%s"' @@ -19,46 +18,50 @@ def parse(self, stream, media_type, **options): class JSONParser(BaseParser): - media_type = 'application/json' + media_type = "application/json" def parse(self, stream, media_type, **options): - data = stream.read().decode('utf-8') + data = stream.read().decode("utf-8") try: return json.loads(data) except ValueError as exc: - msg = 'JSON parse error - %s' % text_type(exc) + msg = "JSON parse error - %s" % str(exc) raise exceptions.ParseError(msg) class MultiPartParser(BaseParser): - media_type = 'multipart/form-data' + media_type = "multipart/form-data" handles_file_uploads = True handles_form_data = True def parse(self, stream, media_type, **options): - boundary = media_type.params.get('boundary') + boundary = media_type.params.get("boundary") if boundary is None: - msg = 'Multipart message missing boundary in Content-Type header' + msg = "Multipart message missing boundary in Content-Type header" raise exceptions.ParseError(msg) - boundary = boundary.encode('ascii') + boundary = boundary.encode("ascii") - content_length = options.get('content_length') - assert content_length is not None, 'MultiPartParser.parse() requires `content_length` argument' + content_length = options.get("content_length") + assert ( + content_length is not None + ), "MultiPartParser.parse() requires `content_length` argument" buffer_size = content_length while buffer_size % 4 or buffer_size < 1024: buffer_size += 1 - multipart_parser = WerkzeugMultiPartParser(default_stream_factory, buffer_size=buffer_size) + multipart_parser = WerkzeugMultiPartParser( + default_stream_factory, buffer_size=buffer_size + ) try: return multipart_parser.parse(stream, boundary, content_length) except ValueError as exc: - msg = 'Multipart parse error - %s' % text_type(exc) + msg = "Multipart parse error - %s" % str(exc) raise exceptions.ParseError(msg) class URLEncodedParser(BaseParser): - media_type = 'application/x-www-form-urlencoded' + media_type = "application/x-www-form-urlencoded" handles_form_data = True def parse(self, stream, media_type, **options): diff --git a/flask_api/renderers.py b/flask_api/renderers.py index f84b366..a9aed23 100644 --- a/flask_api/renderers.py +++ b/flask_api/renderers.py @@ -1,13 +1,12 @@ -# coding: utf8 -from __future__ import unicode_literals -from flask import request, render_template, current_app -from flask.globals import _request_ctx_stack -from flask_api.mediatypes import MediaType -from flask_api.compat import apply_markdown -import json import pydoc import re +from flask import current_app, render_template, request +from flask.globals import _request_ctx_stack + +from flask_api.compat import apply_markdown +from flask_api.mediatypes import MediaType + def dedent(content): """ @@ -18,26 +17,29 @@ def dedent(content): as it fails to dedent multiline docstrings that include unindented text on the initial line. """ - whitespace_counts = [len(line) - len(line.lstrip(' ')) - for line in content.splitlines()[1:] if line.lstrip()] + whitespace_counts = [ + len(line) - len(line.lstrip(" ")) + for line in content.splitlines()[1:] + if line.lstrip() + ] # unindent the content if needed if whitespace_counts: - whitespace_pattern = '^' + (' ' * min(whitespace_counts)) - content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content) + whitespace_pattern = "^" + (" " * min(whitespace_counts)) + content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), "", content) return content.strip() def convert_to_title(name): - for char in ['-', '_', '.']: - name = name.replace(char, ' ') + for char in ["-", "_", "."]: + name = name.replace(char, " ") return name.capitalize() -class BaseRenderer(object): +class BaseRenderer: media_type = None - charset = 'utf-8' + charset = "utf-8" handles_empty_responses = False def render(self, data, media_type, **options): @@ -46,44 +48,47 @@ def render(self, data, media_type, **options): class JSONRenderer(BaseRenderer): - media_type = 'application/json' + media_type = "application/json" charset = None def render(self, data, media_type, **options): # Requested indentation may be set in the Accept header. try: - indent = max(min(int(media_type.params['indent']), 8), 0) + indent = max(min(int(media_type.params["indent"]), 8), 0) except (KeyError, ValueError, TypeError): indent = None # Indent may be set explicitly, eg when rendered by the browsable API. - indent = options.get('indent', indent) - return json.dumps(data, cls=current_app.json_encoder, ensure_ascii=False, indent=indent) + indent = options.get("indent", indent) + return current_app.json.dumps( + data, ensure_ascii=False, indent=indent + ) -class HTMLRenderer(object): - media_type = 'text/html' - charset = 'utf-8' +class HTMLRenderer: + media_type = "text/html" + charset = "utf-8" def render(self, data, media_type, **options): return data.encode(self.charset) class BrowsableAPIRenderer(BaseRenderer): - media_type = 'text/html' + media_type = "text/html" handles_empty_responses = True - template = 'base.html' + template = "base.html" def render(self, data, media_type, **options): # Render the content as it would have been if the client # had requested 'Accept: */*'. available_renderers = [ - renderer for renderer in request.renderer_classes + renderer + for renderer in request.renderer_classes if not issubclass(renderer, BrowsableAPIRenderer) ] - assert available_renderers, 'BrowsableAPIRenderer cannot be the only renderer' + assert available_renderers, "BrowsableAPIRenderer cannot be the only renderer" mock_renderer = available_renderers[0]() mock_media_type = MediaType(mock_renderer.media_type) - if data == '' and not mock_renderer.handles_empty_responses: + if data == "" and not mock_renderer.handles_empty_responses: mock_content = None else: text = mock_renderer.render(data, mock_media_type, indent=4) @@ -104,30 +109,26 @@ def render(self, data, media_type, **options): view_description = dedent(view_description) view_description = pydoc.html.preformat(view_description) - status = options['status'] - headers = options['headers'] - headers['Content-Type'] = str(mock_media_type) + status = options["status"] + headers = options["headers"] + headers["Content-Type"] = str(mock_media_type) from flask_api import __version__ context = { - 'status': status, - 'headers': headers, - 'content': mock_content, - 'allowed_methods': allowed_methods, - 'view_name': convert_to_title(view_name), - 'view_description': view_description, - 'version': __version__ + "status": status, + "headers": headers, + "content": mock_content, + "allowed_methods": allowed_methods, + "view_name": convert_to_title(view_name), + "view_description": view_description, + "version": __version__, } return render_template(self.template, **context) @staticmethod def _html_escape(text): - escape_table = [ - ("&", "&"), - ("<", "<"), - (">", ">") - ] + escape_table = [("&", "&"), ("<", "<"), (">", ">")] for char, replacement in escape_table: text = text.replace(char, replacement) diff --git a/flask_api/request.py b/flask_api/request.py index d9d291a..33c3eaa 100644 --- a/flask_api/request.py +++ b/flask_api/request.py @@ -1,13 +1,12 @@ -# coding: utf8 -from __future__ import unicode_literals +import io + from flask import Request -from flask_api.negotiation import DefaultNegotiation -from flask_api.settings import default_settings from werkzeug.datastructures import MultiDict from werkzeug.urls import url_decode_stream from werkzeug.wsgi import get_content_length -from werkzeug._compat import to_unicode -import io + +from flask_api.negotiation import DefaultNegotiation +from flask_api.settings import default_settings class APIRequest(Request): @@ -20,25 +19,25 @@ class APIRequest(Request): @property def data(self): - if not hasattr(self, '_data'): + if not hasattr(self, "_data"): self._parse() return self._data @property def form(self): - if not hasattr(self, '_form'): + if not hasattr(self, "_form"): self._parse() return self._form @property def files(self): - if not hasattr(self, '_files'): + if not hasattr(self, "_files"): self._parse() return self._files def _parse(self): """ - Parse the body of the request, using whichever parser satifies the + Parse the body of the request, using whichever parser satisfies the client 'Content-Type' header. """ if not self.content_type or not self.content_length: @@ -58,7 +57,9 @@ def _parse(self): raise e from None if parser.handles_file_uploads: - assert isinstance(ret, tuple) and len(ret) == 2, 'Expected a two-tuple of (data, files)' + assert ( + isinstance(ret, tuple) and len(ret) == 2 + ), "Expected a two-tuple of (data, files)" self._data, self._files = ret else: self._data = ret @@ -70,7 +71,7 @@ def _get_parser_options(self): """ Any additional information to pass to the parser. """ - return {'content_length': self.content_length} + return {"content_length": self.content_length} def _set_empty_data(self): """ @@ -84,13 +85,13 @@ def _set_empty_data(self): @property def accepted_renderer(self): - if not hasattr(self, '_accepted_renderer'): + if not hasattr(self, "_accepted_renderer"): self._perform_content_negotiation() return self._accepted_renderer @property def accepted_media_type(self): - if not hasattr(self, '_accepted_media_type'): + if not hasattr(self, "_accepted_media_type"): self._perform_content_negotiation() return self._accepted_media_type @@ -101,31 +102,37 @@ def _perform_content_negotiation(self): """ negotiator = self.negotiator_class() renderers = [renderer() for renderer in self.renderer_classes] - self._accepted_renderer, self._accepted_media_type = negotiator.select_renderer(renderers) + self._accepted_renderer, self._accepted_media_type = negotiator.select_renderer( + renderers + ) # Method and content type overloading. @property def method(self): - if not hasattr(self, '_method'): + if not hasattr(self, "_method"): self._perform_method_overloading() return self._method + @method.setter + def method(self, value): + self._method = value + @property def content_type(self): - if not hasattr(self, '_content_type'): + if not hasattr(self, "_content_type"): self._perform_method_overloading() return self._content_type @property def content_length(self): - if not hasattr(self, '_content_length'): + if not hasattr(self, "_content_length"): self._perform_method_overloading() return self._content_length @property def stream(self): - if not hasattr(self, '_stream'): + if not hasattr(self, "_stream"): self._perform_method_overloading() return self._stream @@ -134,29 +141,33 @@ def _perform_method_overloading(self): Perform method and content type overloading. Provides support for browser PUT, PATCH, DELETE & other requests, - by specifing a '_method' form field. + by specifying a '_method' form field. Also provides support for browser non-form requests (eg JSON), - by specifing '_content' and '_content_type' form fields. + by specifying '_content' and '_content_type' form fields. """ - self._method = super(APIRequest, self).method - self._stream = super(APIRequest, self).stream - self._content_type = self.headers.get('Content-Type') + if not hasattr(self, "_method"): + self.method = super().method + self._stream = super().stream + self._content_type = self.headers.get("Content-Type") self._content_length = get_content_length(self.environ) - if (self._method == 'POST' and self._content_type == 'application/x-www-form-urlencoded'): + if ( + self._method == "POST" + and self._content_type == "application/x-www-form-urlencoded" + ): # Read the request data, then push it back onto the stream again. body = self.get_data() data = url_decode_stream(io.BytesIO(body)) self._stream = io.BytesIO(body) - if '_method' in data: + if "_method" in data: # Support browser forms with PUT, PATCH, DELETE & other methods. - self._method = data['_method'] - if '_content' in data and '_content_type' in data: + self._method = data["_method"] + if "_content" in data and "_content_type" in data: # Support browser forms with non-form data, such as JSON. - body = data['_content'].encode('utf8') + body = data["_content"].encode("utf8") self._stream = io.BytesIO(body) - self._content_type = data['_content_type'] + self._content_type = data["_content_type"] self._content_length = len(body) # Misc... @@ -169,7 +180,7 @@ def full_path(self): """ if not self.query_string: return self.path - return self.path + u'?' + to_unicode(self.query_string, self.url_charset) + return self.path + "?" + self.query_string.decode() # @property # def auth(self): diff --git a/flask_api/response.py b/flask_api/response.py index 3551158..701c299 100644 --- a/flask_api/response.py +++ b/flask_api/response.py @@ -1,7 +1,4 @@ -# coding: utf8 -from __future__ import unicode_literals -from flask import request, Response -from flask._compat import text_type +from flask import Response, request class APIResponse(Response): @@ -9,12 +6,12 @@ class APIResponse(Response): api_return_types = (list, dict) def __init__(self, content=None, *args, **kwargs): - super(APIResponse, self).__init__(None, *args, **kwargs) + super().__init__(None, *args, **kwargs) media_type = None - if isinstance(content, self.api_return_types) or content == '': + if isinstance(content, self.api_return_types) or content == "": renderer = request.accepted_renderer - if content != '' or renderer.handles_empty_responses: + if content != "" or renderer.handles_empty_responses: media_type = request.accepted_media_type options = self.get_renderer_options() content = renderer.render(content, media_type, **options) @@ -24,17 +21,17 @@ def __init__(self, content=None, *args, **kwargs): # From `werkzeug.wrappers.BaseResponse` if content is None: content = [] - if isinstance(content, (text_type, bytes, bytearray)): + if isinstance(content, (str, bytes, bytearray)): self.set_data(content) else: self.response = content if media_type is not None: - self.headers['Content-Type'] = str(media_type) + self.headers["Content-Type"] = str(media_type) def get_renderer_options(self): return { - 'status': self.status, - 'status_code': self.status_code, - 'headers': self.headers + "status": self.status, + "status_code": self.status_code, + "headers": self.headers, } diff --git a/flask_api/settings.py b/flask_api/settings.py index b48044e..58f02d4 100644 --- a/flask_api/settings.py +++ b/flask_api/settings.py @@ -1,4 +1,3 @@ -from flask._compat import string_types import importlib @@ -7,7 +6,7 @@ def perform_imports(val, setting_name): If the given setting is a string import notation, then perform the necessary import or imports. """ - if isinstance(val, string_types): + if isinstance(val, str): return import_from_string(val, setting_name) elif isinstance(val, (list, tuple)): return [perform_imports(item, setting_name) for item in val] @@ -20,8 +19,8 @@ def import_from_string(val, setting_name): """ try: # Nod to tastypie's use of importlib. - parts = val.split('.') - module_path, class_name = '.'.join(parts[:-1]), parts[-1] + parts = val.split(".") + module_path, class_name = ".".join(parts[:-1]), parts[-1] module = importlib.import_module(module_path) return getattr(module, class_name) except ImportError as exc: @@ -30,28 +29,28 @@ def import_from_string(val, setting_name): raise ImportError(msg) -class APISettings(object): +class APISettings: def __init__(self, user_config=None): self.user_config = user_config or {} @property def DEFAULT_PARSERS(self): default = [ - 'flask_api.parsers.JSONParser', - 'flask_api.parsers.URLEncodedParser', - 'flask_api.parsers.MultiPartParser' + "flask_api.parsers.JSONParser", + "flask_api.parsers.URLEncodedParser", + "flask_api.parsers.MultiPartParser", ] - val = self.user_config.get('DEFAULT_PARSERS', default) - return perform_imports(val, 'DEFAULT_PARSERS') + val = self.user_config.get("DEFAULT_PARSERS", default) + return perform_imports(val, "DEFAULT_PARSERS") @property def DEFAULT_RENDERERS(self): default = [ - 'flask_api.renderers.JSONRenderer', - 'flask_api.renderers.BrowsableAPIRenderer' + "flask_api.renderers.JSONRenderer", + "flask_api.renderers.BrowsableAPIRenderer", ] - val = self.user_config.get('DEFAULT_RENDERERS', default) - return perform_imports(val, 'DEFAULT_RENDERERS') + val = self.user_config.get("DEFAULT_RENDERERS", default) + return perform_imports(val, "DEFAULT_RENDERERS") default_settings = APISettings() diff --git a/flask_api/status.py b/flask_api/status.py index 422c0ef..ffb78a0 100644 --- a/flask_api/status.py +++ b/flask_api/status.py @@ -1,4 +1,3 @@ -# coding: utf8 """ Descriptive HTTP status codes, for code readability. @@ -7,7 +6,6 @@ RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC 6585: http://tools.ietf.org/html/rfc6585 """ -from __future__ import unicode_literals def is_informational(code): diff --git a/flask_api/templates/base.html b/flask_api/templates/base.html index c80d7fc..0603943 100644 --- a/flask_api/templates/base.html +++ b/flask_api/templates/base.html @@ -31,7 +31,7 @@