From ac571289d412e0ecf9024d268805050b238398b6 Mon Sep 17 00:00:00 2001 From: a Date: Sun, 25 Dec 2022 18:32:57 -0600 Subject: [PATCH] add pylint and lint the project --- pdm.lock | 197 +++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 21 ++++- webfinger/__init__.py | 8 +- webfinger/__main__.py | 5 +- webfinger/io.py | 79 +++++++++++++---- webfinger/lookup.py | 138 +++++++++++++---------------- webfinger/models.py | 66 ++++++++++++++ 7 files changed, 414 insertions(+), 100 deletions(-) create mode 100644 webfinger/models.py diff --git a/pdm.lock b/pdm.lock index 3dc3d19..a987847 100644 --- a/pdm.lock +++ b/pdm.lock @@ -8,6 +8,17 @@ dependencies = [ "sniffio>=1.1", ] +[[package]] +name = "astroid" +version = "2.12.13" +requires_python = ">=3.7.2" +summary = "An abstract syntax tree for Python with inference support." +dependencies = [ + "lazy-object-proxy>=1.4.0", + "wrapt<2,>=1.11; python_version < \"3.11\"", + "wrapt<2,>=1.14; python_version >= \"3.11\"", +] + [[package]] name = "click" version = "8.1.3" @@ -23,6 +34,12 @@ version = "0.4.6" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Cross-platform colored terminal text." +[[package]] +name = "dill" +version = "0.3.6" +requires_python = ">=3.7" +summary = "serialize all of python" + [[package]] name = "fastapi" version = "0.88.0" @@ -51,6 +68,30 @@ version = "3.4" requires_python = ">=3.5" summary = "Internationalized Domain Names in Applications (IDNA)" +[[package]] +name = "isort" +version = "5.11.4" +requires_python = ">=3.7.0" +summary = "A Python utility / library to sort Python imports." + +[[package]] +name = "lazy-object-proxy" +version = "1.8.0" +requires_python = ">=3.7" +summary = "A fast and thorough lazy object proxy." + +[[package]] +name = "mccabe" +version = "0.7.0" +requires_python = ">=3.6" +summary = "McCabe checker, plugin for flake8" + +[[package]] +name = "platformdirs" +version = "2.6.0" +requires_python = ">=3.7" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." + [[package]] name = "pydantic" version = "1.10.2" @@ -60,6 +101,23 @@ dependencies = [ "typing-extensions>=4.1.0", ] +[[package]] +name = "pylint" +version = "2.15.9" +requires_python = ">=3.7.2" +summary = "python code static checker" +dependencies = [ + "astroid<=2.14.0-dev0,>=2.12.13", + "colorama>=0.4.5; sys_platform == \"win32\"", + "dill>=0.2; python_version < \"3.11\"", + "dill>=0.3.6; python_version >= \"3.11\"", + "isort<6,>=4.2.5", + "mccabe<0.8,>=0.6", + "platformdirs>=2.2.0", + "tomli>=1.1.0; python_version < \"3.11\"", + "tomlkit>=0.10.1", +] + [[package]] name = "python-dotenv" version = "0.21.0" @@ -87,6 +145,18 @@ dependencies = [ "anyio<5,>=3.4.0", ] +[[package]] +name = "tomli" +version = "2.0.1" +requires_python = ">=3.7" +summary = "A lil' TOML parser" + +[[package]] +name = "tomlkit" +version = "0.11.6" +requires_python = ">=3.6" +summary = "Style preserving TOML library" + [[package]] name = "typing-extensions" version = "4.4.0" @@ -141,15 +211,25 @@ version = "10.4" requires_python = ">=3.7" summary = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +[[package]] +name = "wrapt" +version = "1.14.1" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +summary = "Module for decorators, wrappers and monkey patching." + [metadata] lock_version = "4.1" -content_hash = "sha256:b322cb00d8d7fb1b82a352ce22f81402bfde370223353b1df64cd11ec9639a92" +content_hash = "sha256:9a10f6941e9a68f98e1a2ce7e300b98d1c7cc79f71526d28a1030d59c7e560bb" [metadata.files] "anyio 3.6.2" = [ {url = "https://files.pythonhosted.org/packages/77/2b/b4c0b7a3f3d61adb1a1e0b78f90a94e2b6162a043880704b7437ef297cad/anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, {url = "https://files.pythonhosted.org/packages/8b/94/6928d4345f2bc1beecbff03325cad43d320717f51ab74ab5a571324f4f5a/anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, ] +"astroid 2.12.13" = [ + {url = "https://files.pythonhosted.org/packages/61/d0/e7cfca72ec7d6c5e0da725c003db99bb056e9b6c2f4ee6fae1145adf28a6/astroid-2.12.13.tar.gz", hash = "sha256:1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7"}, + {url = "https://files.pythonhosted.org/packages/b1/61/42e075b7d29ed4d452d91cbaaca142710d50d04e68eb7161ce5807a00a30/astroid-2.12.13-py3-none-any.whl", hash = "sha256:10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907"}, +] "click 8.1.3" = [ {url = "https://files.pythonhosted.org/packages/59/87/84326af34517fca8c58418d148f2403df25303e02736832403587318e9e8/click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, {url = "https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, @@ -158,6 +238,10 @@ content_hash = "sha256:b322cb00d8d7fb1b82a352ce22f81402bfde370223353b1df64cd11ec {url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +"dill 0.3.6" = [ + {url = "https://files.pythonhosted.org/packages/7c/e7/364a09134e1062d4d5ff69b853a56cf61c223e0afcc6906b6832bcd51ea8/dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, + {url = "https://files.pythonhosted.org/packages/be/e3/a84bf2e561beed15813080d693b4b27573262433fced9c1d1fea59e60553/dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, +] "fastapi 0.88.0" = [ {url = "https://files.pythonhosted.org/packages/d8/09/ce090f6d53ce8b6335954488087210fa1e054c4a65f74d5f76aed254c159/fastapi-0.88.0-py3-none-any.whl", hash = "sha256:263b718bb384422fe3d042ffc9a0c8dece5e034ab6586ff034f6b4b1667c3eee"}, {url = "https://files.pythonhosted.org/packages/f0/d1/55559f6b09ac336cbb4640ab4c2da0fd79a955eed31f475f0a5ba2ad102e/fastapi-0.88.0.tar.gz", hash = "sha256:915bf304180a0e7c5605ec81097b7d4cd8826ff87a02bb198e336fb9f3b5ff02"}, @@ -213,6 +297,39 @@ content_hash = "sha256:b322cb00d8d7fb1b82a352ce22f81402bfde370223353b1df64cd11ec {url = "https://files.pythonhosted.org/packages/8b/e1/43beb3d38dba6cb420cefa297822eac205a277ab43e5ba5d5c46faf96438/idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, {url = "https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, ] +"isort 5.11.4" = [ + {url = "https://files.pythonhosted.org/packages/76/46/004e2dd6c312e8bb7cb40a6c01b770956e0ef137857e82d47bd9c829356b/isort-5.11.4.tar.gz", hash = "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6"}, + {url = "https://files.pythonhosted.org/packages/91/3b/a63bafb8141b67c397841b36ad46e7469716af2b2d00cb0be2dfb9667130/isort-5.11.4-py3-none-any.whl", hash = "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b"}, +] +"lazy-object-proxy 1.8.0" = [ + {url = "https://files.pythonhosted.org/packages/0a/68/5839136508651d813c1adce568e2f7417bb66083dc8d604a69d465ee53c0/lazy_object_proxy-1.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c"}, + {url = "https://files.pythonhosted.org/packages/30/c3/81c176ce53d9107947d355b273f9661a4f4cad6d56d1daf1c9d6969902e8/lazy_object_proxy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25"}, + {url = "https://files.pythonhosted.org/packages/34/c5/1ef17ab530068f7a5549ab376726f83fe2221db592dbdfd4f8fd4662e45d/lazy_object_proxy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e"}, + {url = "https://files.pythonhosted.org/packages/46/35/55c3650f29858869596871b7fedf4a6b211b61dcc4dd8e8d5702eb85370e/lazy_object_proxy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f"}, + {url = "https://files.pythonhosted.org/packages/60/c1/bf324cf9a0577b0e3781b1a38696405235ac784c4a6d889f97a36dcedc70/lazy_object_proxy-1.8.0-cp37-cp37m-win32.whl", hash = "sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd"}, + {url = "https://files.pythonhosted.org/packages/64/ed/ad47931e7780a5c39f7439de9124438794137840ffdb5f3ffd2995228071/lazy_object_proxy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b"}, + {url = "https://files.pythonhosted.org/packages/65/08/836c9e4e6edf3a267e5b1d0c03923a70ee1a233baf6eb00bfab88d795c51/lazy_object_proxy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7"}, + {url = "https://files.pythonhosted.org/packages/74/37/591f89e8a09ae4574391bdf8a5eecd34a3dbe545917333e625c9de9a66b0/lazy-object-proxy-1.8.0.tar.gz", hash = "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156"}, + {url = "https://files.pythonhosted.org/packages/7c/0f/60db0efe9a1d61fc830cfd2806d54c5fb64761e8009b9d163bf0ede5b12d/lazy_object_proxy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe"}, + {url = "https://files.pythonhosted.org/packages/80/aa/71f82fd17211767419d6b1fc3dc00ba4641c11f9c9358f7acc5222e693b9/lazy_object_proxy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f"}, + {url = "https://files.pythonhosted.org/packages/95/97/44ee4e0247754bcb878886baf2e06856ff268b0d67e86f1d750f251e3c87/lazy_object_proxy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada"}, + {url = "https://files.pythonhosted.org/packages/9d/23/7e78292a5b72121a8bdfff128fcfb8d3630af74336855d3e527f73eaa4c0/lazy_object_proxy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288"}, + {url = "https://files.pythonhosted.org/packages/9d/d1/6dd90b049748d02d9120a496c3649220ac4f6803dd74c9ae48f2bb001239/lazy_object_proxy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0"}, + {url = "https://files.pythonhosted.org/packages/b9/a2/e6b92d1ce6da768a1570d436616f4c565420fcf1c4b2b5246cf77624fe36/lazy_object_proxy-1.8.0-pp37-pypy37_pp73-any.whl", hash = "sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891"}, + {url = "https://files.pythonhosted.org/packages/d7/8a/7bf9154dd7e6e9bda733a105e3baca3636abe72091cd1dcbf636979b667f/lazy_object_proxy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d"}, + {url = "https://files.pythonhosted.org/packages/e0/d3/0cdabfa685eb152a9f4d179fa95f121b3810171f246e8e51f45d100b345c/lazy_object_proxy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c"}, + {url = "https://files.pythonhosted.org/packages/e3/90/4c8d2ce638791874f48894761e305afa2bf6f85f315f1d51946eb1e2b18f/lazy_object_proxy-1.8.0-pp38-pypy38_pp73-any.whl", hash = "sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec"}, + {url = "https://files.pythonhosted.org/packages/f5/dc/11168f6697ed68ec29a4f0887308c0d7836d96148a81eb0abb7b8e77b8e8/lazy_object_proxy-1.8.0-pp39-pypy39_pp73-any.whl", hash = "sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8"}, + {url = "https://files.pythonhosted.org/packages/f6/71/e0dbe4172135aca4b4f657cf15fefd34247b5392ae42cf2ca2583dfa332f/lazy_object_proxy-1.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858"}, +] +"mccabe 0.7.0" = [ + {url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] +"platformdirs 2.6.0" = [ + {url = "https://files.pythonhosted.org/packages/87/69/cd019a9473bcdfb38983e2d550ccb239264fc4c2fc32c42ac1b1cc2506b6/platformdirs-2.6.0-py3-none-any.whl", hash = "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca"}, + {url = "https://files.pythonhosted.org/packages/ec/4c/9af851448e55c57b30a13a72580306e628c3b431d97fdae9e0b8d4fa3685/platformdirs-2.6.0.tar.gz", hash = "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"}, +] "pydantic 1.10.2" = [ {url = "https://files.pythonhosted.org/packages/13/e3/5b83cba317390c9125e049a5328b8e19475098362d398a65936aaab3f00f/pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"}, {url = "https://files.pythonhosted.org/packages/22/53/196c9a5752e30d682e493d7c00ea0a02377446578e577ae5e085010dc0bd/pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"}, @@ -251,6 +368,10 @@ content_hash = "sha256:b322cb00d8d7fb1b82a352ce22f81402bfde370223353b1df64cd11ec {url = "https://files.pythonhosted.org/packages/fe/5b/6f77e6ebc93e5e3c7fd480e1b171a6547407eba901a56a65d2745df24144/pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"}, {url = "https://files.pythonhosted.org/packages/fe/fd/8f7f8271d526378c927babd1229501e576760cef9a509909a3415eec3c0d/pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"}, ] +"pylint 2.15.9" = [ + {url = "https://files.pythonhosted.org/packages/68/3a/1e61444eb8276ad962a7f300b6920b7ad391f4fbe551d34443f093a18899/pylint-2.15.9.tar.gz", hash = "sha256:18783cca3cfee5b83c6c5d10b3cdb66c6594520ffae61890858fe8d932e1c6b4"}, + {url = "https://files.pythonhosted.org/packages/7d/df/0e50d5640ed4c6a492cdc6df0c281afee3f85d98209e7ec7b31243838b40/pylint-2.15.9-py3-none-any.whl", hash = "sha256:349c8cd36aede4d50a0754a8c0218b43323d13d5d88f4b2952ddfe3e169681eb"}, +] "python-dotenv 0.21.0" = [ {url = "https://files.pythonhosted.org/packages/2d/10/ff4f2f5b2a420fd09e1331d63cc87cf4367c5745c0a4ce99cea92b1cbacb/python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5"}, {url = "https://files.pythonhosted.org/packages/87/8d/ab7352188f605e3f663f34692b2ed7457da5985857e9e4c2335cd12fb3c9/python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"}, @@ -305,6 +426,14 @@ content_hash = "sha256:b322cb00d8d7fb1b82a352ce22f81402bfde370223353b1df64cd11ec {url = "https://files.pythonhosted.org/packages/1d/4e/30eda84159d5b3ad7fe663c40c49b16dd17436abe838f10a56c34bee44e8/starlette-0.22.0-py3-none-any.whl", hash = "sha256:b5eda991ad5f0ee5d8ce4c4540202a573bb6691ecd0c712262d0bc85cf8f2c50"}, {url = "https://files.pythonhosted.org/packages/f7/ff/64bd3362f80e81402f21e0f1ba55f8801cd899ad3f2a1bcc4a94d284c786/starlette-0.22.0.tar.gz", hash = "sha256:b092cbc365bea34dd6840b42861bdabb2f507f8671e642e8272d2442e08ea4ff"}, ] +"tomli 2.0.1" = [ + {url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +"tomlkit 0.11.6" = [ + {url = "https://files.pythonhosted.org/packages/2b/df/971fa5db3250bb022105d17f340339370f73d502e65e687a94ca1a4c4b1f/tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, + {url = "https://files.pythonhosted.org/packages/ff/04/58b4c11430ed4b7b8f1723a5e4f20929d59361e9b17f0872d69681fd8ffd/tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, +] "typing-extensions 4.4.0" = [ {url = "https://files.pythonhosted.org/packages/0b/8e/f1a0a5a76cfef77e1eb6004cb49e5f8d72634da638420b9ea492ce8305e8/typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, {url = "https://files.pythonhosted.org/packages/e3/a7/8f4e456ef0adac43f452efc2d0e4b242ab831297f1bac60ac815d37eb9cf/typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, @@ -436,3 +565,69 @@ content_hash = "sha256:b322cb00d8d7fb1b82a352ce22f81402bfde370223353b1df64cd11ec {url = "https://files.pythonhosted.org/packages/f9/15/ab0e9155700d3037ffe4a146a719f3e68ee025c9d45d6a39b027e928db52/websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"}, {url = "https://files.pythonhosted.org/packages/fd/42/07f31d9f9e142b38cde8d3ea0c8ea1bacf9bc366f2f573eca57086e9f2a6/websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"}, ] +"wrapt 1.14.1" = [ + {url = "https://files.pythonhosted.org/packages/00/61/04422b7469534650b622d5baa1dd335c4b91d35c8d33548b272f33060519/wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, + {url = "https://files.pythonhosted.org/packages/03/c6/d864b8da8afa57a638b12596c3a58dfe3471acda900961c02a904010e0e9/wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, + {url = "https://files.pythonhosted.org/packages/07/06/2b4aaaa4403f766c938f9780c700d7399726bce3dfd94f5a57c4e5b9dc68/wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, + {url = "https://files.pythonhosted.org/packages/0a/61/330f24065b8f2fc02f94321092a24e0c30aefcbac89ab5c860e180366c9f/wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, + {url = "https://files.pythonhosted.org/packages/0d/dc/3f588e42e09fb5170349924366587319e1e49d50a1a58dbe78d6046ca812/wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, + {url = "https://files.pythonhosted.org/packages/11/eb/e06e77394d6cf09977d92bff310cb0392930c08a338f99af6066a5a98f92/wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, + {url = "https://files.pythonhosted.org/packages/12/cd/da6611401655ac2b8496b316ad9e21a3fd4f8e62e2c3b3e3c50207770517/wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, + {url = "https://files.pythonhosted.org/packages/1b/77/9f3660dca3d6b7079c3b1b64ad0795db3603cb9345fba3ca580ccdc3fef5/wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, + {url = "https://files.pythonhosted.org/packages/21/55/42ff84a671415db8fc87a1c301c6c7f52b978669324059bdb8dbd7d3f0ce/wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, + {url = "https://files.pythonhosted.org/packages/23/8b/e4de40ac2fa6d53e694310c576e160bec3db8a282fbdcd5596544f6bc69e/wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, + {url = "https://files.pythonhosted.org/packages/2a/86/c9ef2fa4899ec069c8efe43fc92ca2ba0c5a7921cfaf83090030cf7b1487/wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, + {url = "https://files.pythonhosted.org/packages/30/31/c3f80ed75bec31fc3b4e3193f660b96da8fef70811f0ed67a4dc873412bc/wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, + {url = "https://files.pythonhosted.org/packages/33/cd/7335d8b82ff0a442581ab37a8d275ad76b4c1f33ace63c1a4d7c23791eee/wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, + {url = "https://files.pythonhosted.org/packages/36/ee/944dc7e5462662270e8a379755bcc543fc8f09029866288060dc163ed5b4/wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, + {url = "https://files.pythonhosted.org/packages/38/38/5b338163b3b4f1ab718306984678c3d180b85a25d72654ea4c61aa6b0968/wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, + {url = "https://files.pythonhosted.org/packages/39/4d/34599a47c8a41b3ea4986e14f728c293a8a96cd6c23663fe33657c607d34/wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, + {url = "https://files.pythonhosted.org/packages/39/a1/9b4d07b6836a62c6999e8bb5cefced5b34a26fb03941a19c27af98eecec0/wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, + {url = "https://files.pythonhosted.org/packages/40/f4/7be7124a06c14b92be53912f93c8dc84247f1cb93b4003bed460a430d1de/wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, + {url = "https://files.pythonhosted.org/packages/49/a8/528295a24655f901148177355edb6a22b84abb2abfadacc1675643c1434a/wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, + {url = "https://files.pythonhosted.org/packages/4b/07/782463e367a7c6b418af231ded753e4b2dd3293a157d9b0bb010806fc0c0/wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, + {url = "https://files.pythonhosted.org/packages/4b/5b/3cf79a5fce7a91c0c10275835199fafdf30c1b8c7008fa671af3c4e8046c/wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, + {url = "https://files.pythonhosted.org/packages/4f/83/2669bf2cb4cc2b346c40799478d29749ccd17078cb4f69b4a9f95921ff6d/wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, + {url = "https://files.pythonhosted.org/packages/50/d5/bf619c4d204fe8888460f65222b465c7ecfa43590fdb31864fe0e266da29/wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, + {url = "https://files.pythonhosted.org/packages/5b/02/5ac7ea3b6722c84a2882d349ac581a9711b4047fe7a58475903832caa295/wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, + {url = "https://files.pythonhosted.org/packages/5c/46/b91791db2ac7cc4c186408b7aed37b994463970f2397d0548f38b2b47aca/wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, + {url = "https://files.pythonhosted.org/packages/5e/d3/bd44864e0274b7e162e2a68c71fffbd8b3a7b620efd23320fd0f70333cff/wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, + {url = "https://files.pythonhosted.org/packages/67/b4/b5504dddcb2ff9486f8569953938591e0013cca09c912b28747d1d9cb04f/wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, + {url = "https://files.pythonhosted.org/packages/6a/12/76bbe26dc39d05f1a7be8d570d91c87bb79297e08e885148ed670ed17b7b/wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, + {url = "https://files.pythonhosted.org/packages/72/24/490a0bbc67135f737d2eb4b270bfc91e54cc3f0b5e97b4ceec91a44bb898/wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, + {url = "https://files.pythonhosted.org/packages/79/9c/f5d1209c8e4e091e250eb3ed099056e7e1ad0ec1e9ca46f6d88389e2d6d4/wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, + {url = "https://files.pythonhosted.org/packages/82/27/1eac9e63b9ef0e0929e00e17872d45de9d7d965c7f49b933e2daa22c7896/wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, + {url = "https://files.pythonhosted.org/packages/88/ef/05655df7648752ae0a57fe2b9820e340ff025cecec9341aad7936c589a2f/wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, + {url = "https://files.pythonhosted.org/packages/92/b5/788b92550804405424e0d0b1a95250137cbf0e050bb5c461e8ad0fefdc86/wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, + {url = "https://files.pythonhosted.org/packages/93/12/b20ae4dbefa94ef5d667ba71324763d870b86064a944c8ec9533042a41fc/wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, + {url = "https://files.pythonhosted.org/packages/93/8c/1bbba9357142e6f9bcf55c79e2aa6fd5f4066c331e731376705777a0077f/wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, + {url = "https://files.pythonhosted.org/packages/93/b1/007fd8d5c8c366ee1c1b93a99962de5fd34f81dae679ee2bf6a6e0ffc8f0/wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, + {url = "https://files.pythonhosted.org/packages/94/4b/ff8d58aee32ed91744f1ff4970e590f0c8fdda3fa6d702dc82281e0309bd/wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, + {url = "https://files.pythonhosted.org/packages/94/56/fd707fb8e1ea86e72503d823549fb002a0f16cb4909619748996daeb3a82/wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, + {url = "https://files.pythonhosted.org/packages/94/59/60b2fe919ffb190cf8cae0307bafdaf1695eac8655921f59768ce3bf1084/wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, + {url = "https://files.pythonhosted.org/packages/98/0f/3db7e01896b726e68fa2ba918ed0d79f3cc2da2ce928799282264d14c6f6/wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, + {url = "https://files.pythonhosted.org/packages/a2/a7/dd6e91c68d76328d09dd61a7aadac19d49ec509a07e853173036dc05fb79/wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, + {url = "https://files.pythonhosted.org/packages/a7/0d/a52a0268c98a687785c5452324e10f9462d289e850066e281aa327505aa7/wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, + {url = "https://files.pythonhosted.org/packages/b1/ca/ec539e402932bb64814a039f471d327d0deb4612199506094ca60821b94c/wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, + {url = "https://files.pythonhosted.org/packages/bb/70/73c54e24ea69a8b06ae9649e61d5e64f2b4bdfc6f202fc7794abeac1ed20/wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, + {url = "https://files.pythonhosted.org/packages/c0/1e/e5a5ac09e92fd112d50e1793e5b9982dc9e510311ed89dacd2e801f82967/wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, + {url = "https://files.pythonhosted.org/packages/c7/1b/0cdff572d22600fcf47353e8eb1077d83cab3f161ebfb4843565c6e07e66/wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, + {url = "https://files.pythonhosted.org/packages/c8/03/b36a48dcb6f6332d754017b2dd617757687984a6c433e44ca59bb7fefd4c/wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, + {url = "https://files.pythonhosted.org/packages/ca/16/e79e786d930b69a20481174c7bc97e989fb67d2a181a5043e1d3c70c9b21/wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, + {url = "https://files.pythonhosted.org/packages/cd/ec/383d9552df0641e9915454b03139571e0c6e055f5d414d8f3d04f3892f38/wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, + {url = "https://files.pythonhosted.org/packages/d9/3b/f6b760bf04d13e5ddb70d019779466c22952637cf0f606a26d5f784f27ff/wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, + {url = "https://files.pythonhosted.org/packages/d9/ab/3ba5816dd466ffd7242913708771d258569825ab76fd29d7fd85b9361311/wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, + {url = "https://files.pythonhosted.org/packages/da/f4/7af9e01b6c1126b2daef72d5ba2cbf59a7229fd57c5b23166f694d758a8f/wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, + {url = "https://files.pythonhosted.org/packages/e0/20/9716fb522d17a726364c4d032c8806ffe312268773dd46a394436b2787cc/wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, + {url = "https://files.pythonhosted.org/packages/e0/6a/3c660fa34c8106aa9719f2a6636c1c3ea7afd5931ae665eb197fdf4def84/wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, + {url = "https://files.pythonhosted.org/packages/e0/80/af9da7379ee6df583875d0aeb80f9d5f0bd5f081dd1ee5ce06587d8bfec7/wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, + {url = "https://files.pythonhosted.org/packages/e6/57/d5673f5201ccbc287e70a574868319267735de3041e496e1e26b48d8f653/wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, + {url = "https://files.pythonhosted.org/packages/e7/a1/a9596c5858c4a58be8cdd5e8b0e5f53f9c1c17f0616b47edde8de1a356fe/wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, + {url = "https://files.pythonhosted.org/packages/e8/f6/7e30a8c53d27ef8c1ff872dc4fb75247c99eb73d834c91a49a55d046c127/wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, + {url = "https://files.pythonhosted.org/packages/f0/db/2a9ea49cd8bdde87a85262e517563d42b9e5b760473597b9da511fcbd54d/wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, + {url = "https://files.pythonhosted.org/packages/f1/96/d22461ba08d61a859c45cda5064b878f2baa61f142d3acfa8adabd82bf07/wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, + {url = "https://files.pythonhosted.org/packages/f7/92/121147bb2f9ed1aa35a8780c636d5da9c167545f97737f0860b4c6c92086/wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, + {url = "https://files.pythonhosted.org/packages/f8/c4/3f8130d646bfc89382966adfb3d6428f26d0f752543a7e2fd92c1e493be6/wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, + {url = "https://files.pythonhosted.org/packages/f9/3c/110e52b9da396a4ef3a0521552a1af9c7875a762361f48678c1ac272fd7e/wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, + {url = "https://files.pythonhosted.org/packages/fd/70/8a133c88a394394dd57159083b86a564247399440b63f2da0ad727593570/wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, +] diff --git a/pyproject.toml b/pyproject.toml index ef9c4a5..564e01e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,11 +3,11 @@ name = "webfinger" version = "1" description = "Simple WebFinger server that returns static resources. Written with Python and FastAPI." authors = [ - {name = "a", email = "a@trwnh.com"}, + {name = "a", email = "a@trwnh.com"}, ] dependencies = [ - "fastapi>=0.88.0", - "uvicorn[standard]>=0.20.0", + "fastapi>=0.88.0", + "uvicorn[standard]>=0.20.0", ] requires-python = ">=3.10" license = {text = "AGPL"} @@ -19,3 +19,18 @@ build-backend = "pdm.pep517.api" [tool.pdm.scripts] dev = "uvicorn webfinger:app --port 7033 --reload" start = "python -m webfinger" +lint = "pylint webfinger" + +[tool.pdm.dev-dependencies] +dev = [ + "pylint>=2.15.9", +] + +[tool.pylint.format] +indent-string = "\t" + +[tool.pylint.messages_control] +extension-pkg-whitelist = "pydantic" +disable = [ + "missing-final-newline" +] \ No newline at end of file diff --git a/webfinger/__init__.py b/webfinger/__init__.py index 13a57a5..daf1b61 100644 --- a/webfinger/__init__.py +++ b/webfinger/__init__.py @@ -1,3 +1,7 @@ +""" +Initialize the WebFinger server with FastAPI +""" + from fastapi import FastAPI import webfinger.lookup @@ -9,9 +13,9 @@ app = FastAPI( app.include_router(webfinger.lookup.router) -def cleanup_openapi(app: FastAPI) -> None: +def cleanup_openapi(fastapi_app: FastAPI) -> None: """Cleanup inconsistencies in the OpenAPI documentation""" - openapi_schema = app.openapi() + openapi_schema = fastapi_app.openapi() # Remove 422 Validation Error from Webfinger lookup (RFC7033 uses 400 Bad Request) webfinger_responses: dict = openapi_schema["paths"]["/.well-known/webfinger"]["get"]["responses"] diff --git a/webfinger/__main__.py b/webfinger/__main__.py index 61c5066..f363134 100644 --- a/webfinger/__main__.py +++ b/webfinger/__main__.py @@ -1,4 +1,7 @@ -from webfinger import app +""" +Run the FastAPI app with Uvicorn if the package is run as a module +""" import uvicorn +from webfinger import app uvicorn.run(app=app, port=7033) \ No newline at end of file diff --git a/webfinger/io.py b/webfinger/io.py index b847f5a..4418f51 100644 --- a/webfinger/io.py +++ b/webfinger/io.py @@ -1,11 +1,21 @@ +""" +Functions that perform read-write operations +""" + import json from os import environ as env from urllib.parse import quote_plus as url_encode from pathlib import Path -def get_jrd(resource: str) -> dict[str, str | list[str] | dict[str,str]]: +from webfinger.models import JRD, Link + + +DictJRD = dict[str, str | list[str] | dict[str,str] | list[dict[str,str]]] + + +def get_jrd_as_dict(resource: str) -> DictJRD: """ - Obtain a JSON Resource Descriptor (JRD) + Obtain a JSON Resource Descriptor (JRD) as a dictionary A JRD is a JSON object containing the following: - subject (string value; SHOULD be present) @@ -15,9 +25,9 @@ def get_jrd(resource: str) -> dict[str, str | list[str] | dict[str,str]]: Parameters: resource (str): The URI of the resouce - + Returns: - jrd (dict): Parsed JRD + jrd (dict): Parsed JRD dictionary Raises: FileNotFoundError: No JRD file exists in the resource directory @@ -25,30 +35,69 @@ def get_jrd(resource: str) -> dict[str, str | list[str] | dict[str,str]]: json.JSONDecodeError: A file exists, but does not contain a valid JSON object """ # Get a filename for the resource document. - dir = env.get("RESOURCE_DIR") or "resource" + directory = env.get("RESOURCE_DIR") or "resource" filename = resource - path = f"{dir}/{filename}.jrd" + path = f"{directory}/{filename}.jrd" # If we can't get a file, try percent-encoding if not Path(path).is_file(): filename = url_encode(resource) - path = f"{dir}/{filename}.jrd" + path = f"{directory}/{filename}.jrd" # Try plain JSON if not Path(path).is_file(): filename = resource - path = f"{dir}/{filename}.json" + path = f"{directory}/{filename}.json" # Try plain JSON and percent-encoding if not Path(path).is_file(): filename = resource - path = f"{dir}/{filename}.json" + path = f"{directory}/{filename}.json" # Open the file and load the JSON as a dictionary. - try: - with open(path, "r") as file: - jrd: dict[str, str | list[str] | dict[str,str]] = json.loads(file.read()) - except: - raise + # This may fail and raise an exception. + with open(path, mode="r", encoding="utf-8") as file: + jrd: DictJRD = json.loads(file.read()) - return jrd \ No newline at end of file + return {k: v for k, v in jrd.items() if v} # remove null values + + +def get_jrd(resource: str) -> JRD: + """ + Obtain a JSON Resource Descriptor (JRD) as a Pydantic model + + A JRD is a JSON object containing the following: + - subject (string value; SHOULD be present) + - aliases (array of string values; OPTIONAL) + - properties (object containing key-value pairs as strings; OPTIONAL) + - links (array of objects containing link relation information; OPTIONAL) + + Parameters: + resource (str): The URI of the resouce + + Returns: + jrd (JRD): Parsed and valid JRD model + + Raises: + FileNotFoundError: No JRD file exists in the resource directory + OSError: A file may exist, but it is not readable + json.JSONDecodeError: A file exists, but does not contain a valid JSON object + pydantic.ValidationError: A file exists and was parsed, but some properties failed validation + """ + + jrd = get_jrd_as_dict(resource) + return JRD( + subject = jrd.get('subject') or resource, + aliases = jrd.get('aliases'), + properties = jrd.get('properties'), + links = [ + Link( + rel = link.get('rel'), + type = link.get('type'), + href = link.get('href'), + titles = link.get('titles'), + properties = link.get('properties'), + ) + for link in jrd.get('links', []) + ], + ) \ No newline at end of file diff --git a/webfinger/lookup.py b/webfinger/lookup.py index 67925c3..364a938 100644 --- a/webfinger/lookup.py +++ b/webfinger/lookup.py @@ -1,11 +1,17 @@ -from fastapi import APIRouter, Response +""" +Perform WebFinger lookups according to RFC7033 +""" + +import json + +from fastapi import APIRouter from fastapi.responses import PlainTextResponse, JSONResponse from fastapi.params import Query -from pydantic import BaseModel -import json +from pydantic import ValidationError from webfinger.io import get_jrd +from webfinger.models import JRD, JRDResponse router = APIRouter( @@ -21,62 +27,23 @@ HEADERS = {'Access-Control-Allow-Origin': '*'} # headers.update({'Access-Control-Allow-Origin': allowed_origins}) -## Pydantic models for OpenAPI schema - -class Link(BaseModel): - rel: str - type: str | None - href: str | None - titles: str | None - properties: str | None - - class Config: - schema_extra = { - "example": r'''{ - "rel": "https://webfinger.net/rel/profile-page/", - "type": "text/html", - "href": "https://trwnh.com/~a" - }''' - } - -class JRD(BaseModel): - subject: str - aliases: list[str] | None - links: list[Link] | None - - class Config: - schema_extra = { - "example": r'''{ - "subject": "acct:a@trwnh.com", - "aliases": ["https://ap.trwnh.com/actors/7057bc10-db1c-4ebe-9e00-22cf04be4e5e", "https://trwnh.com/~a", "acct:trwnh@ap.trwnh.com"], - "links": [ - { - "rel": "self", - "type": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", - "href": "https://trwnh.com/actors/7057bc10-db1c-4ebe-9e00-22cf04be4e5e" - }, - { - "rel": "https://webfinger.net/rel/profile-page/", - "type": "text/html", - "href": "https://trwnh.com/~a" - } - ] - }''' - } - -class JRDResponse(Response): - media_type = "application/jrd+json" - - ## Example responses for OpenAPI schema -RESOURCE_NOT_PROVIDED = "Query ?resource={resource} to obtain information about that resource." -RESOURCE_NOT_FOUND = "Resource not found" -RESOURCE_NOT_PARSED = "Resource contains invalid JSON or was unreadable" +RESOURCE_NOT_PROVIDED = \ + "Query ?resource={resource} to obtain information about that resource." +RESOURCE_NOT_FOUND = \ + "Resource not found" +RESOURCE_NOT_PARSED = \ + "Resource contains invalid JSON or was unreadable" +RESOURCE_NOT_VALID = \ + "Some errors occurred while validating the JRD" RESPONSES = { 200: { - "description": "OK: The resource you requested was found, and has the returned resource document.", + "description": ( + "OK:" + " The resource you requested was found," + " and has the returned resource document."), "model": JRD, "content": { "application/jrd+json": { @@ -85,7 +52,9 @@ RESPONSES = { }, }, 400: { - "description": "Bad Request: No resource was provided.", + "description": ( + "Bad Request:" + " No resource was provided."), "content": { "text/plain": { "example": RESOURCE_NOT_PROVIDED @@ -93,7 +62,9 @@ RESPONSES = { }, }, 404: { - "description": "Not Found: Could not find a document for the provided resource.", + "description": ( + "Not Found:" + " Could not find a document for the provided resource."), "content": { "text/plain": { "example": RESOURCE_NOT_FOUND @@ -101,7 +72,10 @@ RESPONSES = { }, }, 500: { - "description": "Server Error: The resource exists, but contains invalid JSON or was unreadable.", + "description": ( + "Server Error:" + " The resource exists," + " but contains invalid JSON or was unreadable."), "content": { "text/plain": { "example": RESOURCE_NOT_PARSED @@ -116,7 +90,9 @@ RESPONSES = { @router.get( "", summary = "Lookup a Webfinger resource", - description = "Query the Webfinger service for a resource URI and obtain its associated resource document", + description = ( + "Query the Webfinger service for a resource URI" + " and obtain its associated resource document"), tags = ["RFC7033"], response_model = JRD, response_class = JRDResponse, @@ -126,12 +102,18 @@ async def lookup( resource: str = Query( None, title = "Resource URI", - description = "The subject whose document you are looking up. If not provided, you will get a 400 Bad Request." + description = ( + "The subject whose document you are looking up." + " If not provided, you will get a 400 Bad Request." + ), ), rel: list[str] = Query( None, title = "Link relation", - description = "If provided, filter the links array for this rel-value only. This parameter may be included multiple times." + description = ( + "If provided, filter the links array for this rel-value only." + " This parameter may be included multiple times." + ), ) ): """ @@ -144,43 +126,43 @@ async def lookup( status_code=400, headers = HEADERS, ) - + # Otherwise, try to read the resource document. try: jrd = get_jrd(resource) - except FileNotFoundError: # JRD file does not exist + except FileNotFoundError: + # JRD file does not exist return PlainTextResponse( content = RESOURCE_NOT_FOUND, status_code = 404, headers = HEADERS, ) - except: # JRD file could not be read or parsed + except (OSError, json.JSONDecodeError): + # JRD file could not be read or parsed return PlainTextResponse( content = RESOURCE_NOT_PARSED, status_code = 500, headers = HEADERS, ) - - # Obtain values from the resource document. - subject: str = jrd.get('subject', None) or resource - aliases: list[str] = [alias for alias in jrd.get('aliases', [])] - properties: dict[str, str] = jrd.get('properties', {}) - links: list[dict[str,str]] = [link for link in jrd.get('links', [])] + except ValidationError as exception: + # JRD was parsed but contained type or value errors + errors = [RESOURCE_NOT_VALID] + for error in exception.errors(): + print(error) + errors.append(f"{error['msg']} for {error['loc'][0]}") + return PlainTextResponse( + content = '\n- '.join(errors), + status_code=500, + headers = HEADERS, + ) # If optional rel is provided, then filter only for links with that rel-type. if rel: - links = [link for link in links if link.get('rel', '') in rel] + jrd.links = [link for link in jrd.links if link.rel in rel] # Construct JSON response and return it. - content = { - "subject": subject, - "aliases": aliases, - "properties": properties, - "links": links, - } - content = {k: v for k, v in content.items() if v} # remove null values return JSONResponse( - content = content, + content = jrd.dict(exclude_none=True), media_type = "application/jrd+json", headers = HEADERS, ) \ No newline at end of file diff --git a/webfinger/models.py b/webfinger/models.py new file mode 100644 index 0000000..91852f9 --- /dev/null +++ b/webfinger/models.py @@ -0,0 +1,66 @@ +""" +Models used for validation and OpenAPI responses +""" + +from pydantic import BaseModel, StrictStr +from fastapi import Response + +class Link(BaseModel): + """ + Describes an entry in the JRD `link` array + """ + rel: StrictStr + type: StrictStr | None + href: StrictStr | None + titles: StrictStr | None + properties: dict[StrictStr, StrictStr] | None + + class Config: # pylint: disable=too-few-public-methods + """ + Provide an example `link` item + """ + schema_extra = { + "example": r'''{ + "rel": "https://webfinger.net/rel/profile-page/", + "type": "text/html", + "href": "https://trwnh.com/~a" + }''' + } + +class JRD(BaseModel): + """ + Describes a JSON Resource Descriptor document + """ + subject: StrictStr + aliases: list[StrictStr] | None + links: list[Link] | None + properties: dict[StrictStr, StrictStr] | None + + class Config: # pylint: disable=too-few-public-methods + """ + Provide an example JRD document + """ + schema_extra = { + "example": r'''{ + "subject": "acct:a@trwnh.com", + "aliases": ["https://ap.trwnh.com/actors/7057bc10-db1c-4ebe-9e00-22cf04be4e5e", "https://trwnh.com/~a", "acct:trwnh@ap.trwnh.com"], + "links": [ + { + "rel": "self", + "type": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", + "href": "https://trwnh.com/actors/7057bc10-db1c-4ebe-9e00-22cf04be4e5e" + }, + { + "rel": "https://webfinger.net/rel/profile-page/", + "type": "text/html", + "href": "https://trwnh.com/~a" + } + ] + }''' + } + +class JRDResponse(Response): + """ + Specify JRD Content-Type for responding to WebFinger lookup requests + """ + media_type = "application/jrd+json" \ No newline at end of file