diff --git a/docs/autofix_docs.py b/docs/autofix_docs.py index 6d840eaf..d9bee09e 100644 --- a/docs/autofix_docs.py +++ b/docs/autofix_docs.py @@ -288,8 +288,12 @@ def _build_library(url: str = "") -> List[str]: library: Dict[str, List[Tuple]] = defaultdict(list) for path in sorted(builtin_styles()): # type: Path style = BuiltinStyle.from_path(path) + + # When run with tox (invoke ci-build), the path starts with "site-packages" + clean_root = style.path_from_repo_root.replace("site-packages/", "src/") + row = StyleLibraryRow( - style=f"`{style.py_url_without_ext} <{url + style.path_from_repo_root}>`_", + style=f"`{style.py_url_without_ext} <{url + clean_root}>`_", name=f"`{style.name} <{style.url}>`_" if style.url else style.name, ) library[style.identify_tag].append(attr.astuple(row)) diff --git a/docs/styles.rst b/docs/styles.rst index 3883655b..9d1e0f6a 100644 --- a/docs/styles.rst +++ b/docs/styles.rst @@ -105,7 +105,7 @@ Comparing elements on lists .. note:: - At the moment, this feature only works on `the YAML plugin `_. + At the moment, this feature only works on :ref:`the YAML plugin `_. On YAML files, a list (called a "sequence" in the YAML spec) can contain different elements: diff --git a/poetry.lock b/poetry.lock index a4586661..17a8481e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -114,7 +114,7 @@ python-versions = "*" [[package]] name = "charset-normalizer" -version = "2.0.10" +version = "2.0.11" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false @@ -159,7 +159,7 @@ testing = ["sphinx", "flake8", "pytest", "pytest-cov", "pytest-virtualenv", "pyt [[package]] name = "coverage" -version = "6.3" +version = "6.3.1" description = "Code coverage measurement for Python" category = "main" optional = true @@ -211,7 +211,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "dpath" -version = "2.0.5" +version = "2.0.6" description = "Filesystem-like pathing and searching for dictionaries" category = "main" optional = false @@ -263,15 +263,16 @@ python-versions = ">=3.5" python-dateutil = ">=2.7" [[package]] -name = "fuzzywuzzy" -version = "0.18.0" -description = "Fuzzy string matching in python" +name = "furl" +version = "2.1.3" +description = "URL manipulation made simple." category = "main" -optional = true +optional = false python-versions = "*" -[package.extras] -speedup = ["python-levenshtein (>=0.12)"] +[package.dependencies] +orderedmultidict = ">=1.0.1" +six = ">=1.8.0" [[package]] name = "icecream" @@ -289,7 +290,7 @@ pygments = ">=2.2.0" [[package]] name = "identify" -version = "2.4.6" +version = "2.4.8" description = "File identification library for Python" category = "main" optional = false @@ -539,6 +540,17 @@ category = "main" optional = false python-versions = ">=3.5" +[[package]] +name = "orderedmultidict" +version = "1.0.1" +description = "Ordered Multivalue Dictionary" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = ">=1.8.0" + [[package]] name = "packaging" version = "21.3" @@ -848,19 +860,18 @@ use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [[package]] name = "responses" -version = "0.17.0" +version = "0.18.0" description = "A utility library for mocking out the `requests` Python library." category = "main" optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" [package.dependencies] -requests = ">=2.0" -six = "*" +requests = ">=2.0,<3.0" urllib3 = ">=1.25.10" [package.extras] -tests = ["coverage (>=3.7.1,<6.0.0)", "pytest-cov", "pytest-localserver", "flake8", "types-mock", "types-requests", "types-six", "pytest (>=4.6,<5.0)", "pytest (>=4.6)", "mypy"] +tests = ["pytest (>=4.6)", "coverage (>=6.0.0)", "pytest-cov", "pytest-localserver", "flake8", "types-mock", "types-requests", "mypy"] [[package]] name = "ruamel.yaml" @@ -1029,7 +1040,7 @@ test = ["pytest"] [[package]] name = "sphobjinv" -version = "2.1" +version = "2.2.1" description = "Sphinx objects.inv Inspection/Manipulation Tool" category = "main" optional = true @@ -1038,11 +1049,20 @@ python-versions = ">=3.6" [package.dependencies] attrs = ">=19.2" certifi = "*" -fuzzywuzzy = ">=0.8" jsonschema = ">=3.0" +[[package]] +name = "strenum" +version = "0.4.7" +description = "An Enum that inherits from str." +category = "main" +optional = false +python-versions = "*" + [package.extras] -speedup = ["python-levenshtein"] +docs = ["sphinx", "sphinx-rtd-theme", "recommonmark"] +release = ["twine"] +test = ["pytest", "pytest-black", "pytest-cov", "pytest-pylint", "pylint"] [[package]] name = "testfixtures" @@ -1083,7 +1103,7 @@ python-versions = ">=3.7" [[package]] name = "tomlkit" -version = "0.8.0" +version = "0.9.0" description = "Style preserving TOML library" category = "main" optional = false @@ -1209,7 +1229,7 @@ test = ["pytest", "pytest-cov", "testfixtures", "responses", "freezegun", "pytes [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "ecee56d224e6553110bd574c18d2e13f746abdbf8d0b7182ec6bff8cee71c58f" +content-hash = "45ad55a867ae4f88bbc775c46abfc658cd94eff1d567992e8ce5a84de5e50ce8" [metadata.files] alabaster = [ @@ -1258,8 +1278,8 @@ certifi = [ {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, ] charset-normalizer = [ - {file = "charset-normalizer-2.0.10.tar.gz", hash = "sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd"}, - {file = "charset_normalizer-2.0.10-py3-none-any.whl", hash = "sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455"}, + {file = "charset-normalizer-2.0.11.tar.gz", hash = "sha256:98398a9d69ee80548c762ba991a4728bfc3836768ed226b3945908d1a688371c"}, + {file = "charset_normalizer-2.0.11-py3-none-any.whl", hash = "sha256:2842d8f5e82a1f6aa437380934d5e1cd4fcf2003b06fed6940769c164a480a45"}, ] click = [ {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, @@ -1274,50 +1294,47 @@ configupdater = [ {file = "ConfigUpdater-3.0.1.tar.gz", hash = "sha256:372a6a6ef598a118ec17927bec9486a7d36f44ccd3e641e879e0bf998b70924e"}, ] coverage = [ - {file = "coverage-6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e8071e7d9ba9f457fc674afc3de054450be2c9b195c470147fbbc082468d8ff7"}, - {file = "coverage-6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86c91c511853dfda81c2cf2360502cb72783f4b7cebabef27869f00cbe1db07d"}, - {file = "coverage-6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4ce3b647bd1792d4394f5690d9df6dc035b00bcdbc5595099c01282a59ae01"}, - {file = "coverage-6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a491e159294d756e7fc8462f98175e2d2225e4dbe062cca7d3e0d5a75ba6260"}, - {file = "coverage-6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d008e0f67ac800b0ca04d7914b8501312c8c6c00ad8c7ba17754609fae1231a"}, - {file = "coverage-6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4578728c36de2801c1deb1c6b760d31883e62e33f33c7ba8f982e609dc95167d"}, - {file = "coverage-6.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7ee317486593193e066fc5e98ac0ce712178c21529a85c07b7cb978171f25d53"}, - {file = "coverage-6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2bc85664b06ba42d14bb74d6ddf19d8bfc520cb660561d2d9ce5786ae72f71b5"}, - {file = "coverage-6.3-cp310-cp310-win32.whl", hash = "sha256:27a94db5dc098c25048b0aca155f5fac674f2cf1b1736c5272ba28ead2fc267e"}, - {file = "coverage-6.3-cp310-cp310-win_amd64.whl", hash = "sha256:bde4aeabc0d1b2e52c4036c54440b1ad05beeca8113f47aceb4998bb7471e2c2"}, - {file = "coverage-6.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:509c68c3e2015022aeda03b003dd68fa19987cdcf64e9d4edc98db41cfc45d30"}, - {file = "coverage-6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e4ff163602c5c77e7bb4ea81ba5d3b793b4419f8acd296aae149370902cf4e92"}, - {file = "coverage-6.3-cp311-cp311-win_amd64.whl", hash = "sha256:d1675db48490e5fa0b300f6329ecb8a9a37c29b9ab64fa9c964d34111788ca2d"}, - {file = "coverage-6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7eed8459a2b81848cafb3280b39d7d49950d5f98e403677941c752e7e7ee47cb"}, - {file = "coverage-6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b4285fde5286b946835a1a53bba3ad41ef74285ba9e8013e14b5ea93deaeafc"}, - {file = "coverage-6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4748349734110fd32d46ff8897b561e6300d8989a494ad5a0a2e4f0ca974fc7"}, - {file = "coverage-6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:823f9325283dc9565ba0aa2d240471a93ca8999861779b2b6c7aded45b58ee0f"}, - {file = "coverage-6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fff16a30fdf57b214778eff86391301c4509e327a65b877862f7c929f10a4253"}, - {file = "coverage-6.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:da1a428bdbe71f9a8c270c7baab29e9552ac9d0e0cba5e7e9a4c9ee6465d258d"}, - {file = "coverage-6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7d82c610a2e10372e128023c5baf9ce3d270f3029fe7274ff5bc2897c68f1318"}, - {file = "coverage-6.3-cp37-cp37m-win32.whl", hash = "sha256:11e61c5548ecf74ea1f8b059730b049871f0e32b74f88bd0d670c20c819ad749"}, - {file = "coverage-6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8e0c3525b1a182c8ffc9bca7e56b521e0c2b8b3e82f033c8e16d6d721f1b54d6"}, - {file = "coverage-6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a189036c50dcd56100746139a459f0d27540fef95b09aba03e786540b8feaa5f"}, - {file = "coverage-6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32168001f33025fd756884d56d01adebb34e6c8c0b3395ca8584cdcee9c7c9d2"}, - {file = "coverage-6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5d79c9af3f410a2b5acad91258b4ae179ee9c83897eb9de69151b179b0227f5"}, - {file = "coverage-6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:85c5fc9029043cf8b07f73fbb0a7ab6d3b717510c3b5642b77058ea55d7cacde"}, - {file = "coverage-6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7596aa2f2b8fa5604129cfc9a27ad9beec0a96f18078cb424d029fdd707468d"}, - {file = "coverage-6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ce443a3e6df90d692c38762f108fc4c88314bf477689f04de76b3f252e7a351c"}, - {file = "coverage-6.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:012157499ec4f135fc36cd2177e3d1a1840af9b236cbe80e9a5ccfc83d912a69"}, - {file = "coverage-6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a34d313105cdd0d3644c56df2d743fe467270d6ab93b5d4a347eb9fec8924d6"}, - {file = "coverage-6.3-cp38-cp38-win32.whl", hash = "sha256:6e78b1e25e5c5695dea012be473e442f7094d066925604be20b30713dbd47f89"}, - {file = "coverage-6.3-cp38-cp38-win_amd64.whl", hash = "sha256:433b99f7b0613bdcdc0b00cc3d39ed6d756797e3b078d2c43f8a38288520aec6"}, - {file = "coverage-6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9ed3244b415725f08ca3bdf02ed681089fd95e9465099a21c8e2d9c5d6ca2606"}, - {file = "coverage-6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab4fc4b866b279740e0d917402f0e9a08683e002f43fa408e9655818ed392196"}, - {file = "coverage-6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8582e9280f8d0f38114fe95a92ae8d0790b56b099d728cc4f8a2e14b1c4a18c"}, - {file = "coverage-6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c72bb4679283c6737f452eeb9b2a0e570acaef2197ad255fb20162adc80bea76"}, - {file = "coverage-6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca29c352389ea27a24c79acd117abdd8a865c6eb01576b6f0990cd9a4e9c9f48"}, - {file = "coverage-6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:152cc2624381df4e4e604e21bd8e95eb8059535f7b768c1fb8b8ae0b26f47ab0"}, - {file = "coverage-6.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:51372e24b1f7143ee2df6b45cff6a721f3abe93b1e506196f3ffa4155c2497f7"}, - {file = "coverage-6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72d9d186508325a456475dd05b1756f9a204c7086b07fffb227ef8cee03b1dc2"}, - {file = "coverage-6.3-cp39-cp39-win32.whl", hash = "sha256:649df3641eb351cdfd0d5533c92fc9df507b6b2bf48a7ef8c71ab63cbc7b5c3c"}, - {file = "coverage-6.3-cp39-cp39-win_amd64.whl", hash = "sha256:e67ccd53da5958ea1ec833a160b96357f90859c220a00150de011b787c27b98d"}, - {file = "coverage-6.3-pp36.pp37.pp38-none-any.whl", hash = "sha256:27ac7cb84538e278e07569ceaaa6f807a029dc194b1c819a9820b9bb5dbf63ab"}, - {file = "coverage-6.3.tar.gz", hash = "sha256:987a84ff98a309994ca77ed3cc4b92424f824278e48e4bf7d1bb79a63cfe2099"}, + {file = "coverage-6.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeffd96882d8c06d31b65dddcf51db7c612547babc1c4c5db6a011abe9798525"}, + {file = "coverage-6.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:621f6ea7260ea2ffdaec64fe5cb521669984f567b66f62f81445221d4754df4c"}, + {file = "coverage-6.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f2436d6742c01136dd940ee158bfc7cf5ced3da7e4c949662b8703b5cd8145"}, + {file = "coverage-6.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de73fca6fb403dd72d4da517cfc49fcf791f74eee697d3219f6be29adf5af6ce"}, + {file = "coverage-6.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78fbb2be068a13a5d99dce9e1e7d168db880870f7bc73f876152130575bd6167"}, + {file = "coverage-6.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f5a4551dfd09c3bd12fca8144d47fe7745275adf3229b7223c2f9e29a975ebda"}, + {file = "coverage-6.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7bff3a98f63b47464480de1b5bdd80c8fade0ba2832c9381253c9b74c4153c27"}, + {file = "coverage-6.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a06c358f4aed05fa1099c39decc8022261bb07dfadc127c08cfbd1391b09689e"}, + {file = "coverage-6.3.1-cp310-cp310-win32.whl", hash = "sha256:9fff3ff052922cb99f9e52f63f985d4f7a54f6b94287463bc66b7cdf3eb41217"}, + {file = "coverage-6.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:276b13cc085474e482566c477c25ed66a097b44c6e77132f3304ac0b039f83eb"}, + {file = "coverage-6.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:56c4a409381ddd7bbff134e9756077860d4e8a583d310a6f38a2315b9ce301d0"}, + {file = "coverage-6.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eb494070aa060ceba6e4bbf44c1bc5fa97bfb883a0d9b0c9049415f9e944793"}, + {file = "coverage-6.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e15d424b8153756b7c903bde6d4610be0c3daca3986173c18dd5c1a1625e4cd"}, + {file = "coverage-6.3.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d47a897c1e91f33f177c21de897267b38fbb45f2cd8e22a710bcef1df09ac1"}, + {file = "coverage-6.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:25e73d4c81efa8ea3785274a2f7f3bfbbeccb6fcba2a0bdd3be9223371c37554"}, + {file = "coverage-6.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fac0bcc5b7e8169bffa87f0dcc24435446d329cbc2b5486d155c2e0f3b493ae1"}, + {file = "coverage-6.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:72128176fea72012063200b7b395ed8a57849282b207321124d7ff14e26988e8"}, + {file = "coverage-6.3.1-cp37-cp37m-win32.whl", hash = "sha256:1bc6d709939ff262fd1432f03f080c5042dc6508b6e0d3d20e61dd045456a1a0"}, + {file = "coverage-6.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:618eeba986cea7f621d8607ee378ecc8c2504b98b3fdc4952b30fe3578304687"}, + {file = "coverage-6.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ed164af5c9078596cfc40b078c3b337911190d3faeac830c3f1274f26b8320"}, + {file = "coverage-6.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:352c68e233409c31048a3725c446a9e48bbff36e39db92774d4f2380d630d8f8"}, + {file = "coverage-6.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:448d7bde7ceb6c69e08474c2ddbc5b4cd13c9e4aa4a717467f716b5fc938a734"}, + {file = "coverage-6.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9fde6b90889522c220dd56a670102ceef24955d994ff7af2cb786b4ba8fe11e4"}, + {file = "coverage-6.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e647a0be741edbb529a72644e999acb09f2ad60465f80757da183528941ff975"}, + {file = "coverage-6.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a5cdc3adb4f8bb8d8f5e64c2e9e282bc12980ef055ec6da59db562ee9bdfefa"}, + {file = "coverage-6.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2dd70a167843b4b4b2630c0c56f1b586fe965b4f8ac5da05b6690344fd065c6b"}, + {file = "coverage-6.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9ad0a117b8dc2061ce9461ea4c1b4799e55edceb236522c5b8f958ce9ed8fa9a"}, + {file = "coverage-6.3.1-cp38-cp38-win32.whl", hash = "sha256:e92c7a5f7d62edff50f60a045dc9542bf939758c95b2fcd686175dd10ce0ed10"}, + {file = "coverage-6.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:482fb42eea6164894ff82abbcf33d526362de5d1a7ed25af7ecbdddd28fc124f"}, + {file = "coverage-6.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c5b81fb37db76ebea79aa963b76d96ff854e7662921ce742293463635a87a78d"}, + {file = "coverage-6.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a4f923b9ab265136e57cc14794a15b9dcea07a9c578609cd5dbbfff28a0d15e6"}, + {file = "coverage-6.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56d296cbc8254a7dffdd7bcc2eb70be5a233aae7c01856d2d936f5ac4e8ac1f1"}, + {file = "coverage-6.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1245ab82e8554fa88c4b2ab1e098ae051faac5af829efdcf2ce6b34dccd5567c"}, + {file = "coverage-6.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f2b05757c92ad96b33dbf8e8ec8d4ccb9af6ae3c9e9bd141c7cc44d20c6bcba"}, + {file = "coverage-6.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9e3dd806f34de38d4c01416344e98eab2437ac450b3ae39c62a0ede2f8b5e4ed"}, + {file = "coverage-6.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d651fde74a4d3122e5562705824507e2f5b2d3d57557f1916c4b27635f8fbe3f"}, + {file = "coverage-6.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:704f89b87c4f4737da2860695a18c852b78ec7279b24eedacab10b29067d3a38"}, + {file = "coverage-6.3.1-cp39-cp39-win32.whl", hash = "sha256:2aed4761809640f02e44e16b8b32c1a5dee5e80ea30a0ff0912158bde9c501f2"}, + {file = "coverage-6.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:9976fb0a5709988778ac9bc44f3d50fccd989987876dfd7716dee28beed0a9fa"}, + {file = "coverage-6.3.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:463e52616ea687fd323888e86bf25e864a3cc6335a043fad6bbb037dbf49bbe2"}, + {file = "coverage-6.3.1.tar.gz", hash = "sha256:6c3f6158b02ac403868eea390930ae64e9a9a2a5bbfafefbb920d29258d9f2f8"}, ] decorator = [ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, @@ -1335,8 +1352,8 @@ docutils = [ {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, ] dpath = [ - {file = "dpath-2.0.5-py3-none-any.whl", hash = "sha256:e7813fd8a9dd0d4c7cd4014533ce955eff712bcb2e8189be79bb893890a9db01"}, - {file = "dpath-2.0.5.tar.gz", hash = "sha256:ef74321b01479653c812fee69c53922364614d266a8e804d22058c5c02e5674e"}, + {file = "dpath-2.0.6-py3-none-any.whl", hash = "sha256:8c439bb1c3b3222427e9b8812701cd99a0ef3415ddbb7c03a2379f6989a03965"}, + {file = "dpath-2.0.6.tar.gz", hash = "sha256:5a1ddae52233fbc8ef81b15fb85073a81126bb43698d3f3a1b6aaf561a46cdc0"}, ] executing = [ {file = "executing-0.8.2-py2.py3-none-any.whl", hash = "sha256:32fc6077b103bd19e6494a72682d66d5763cf20a106d5aa7c5ccbea4e47b0df7"}, @@ -1354,17 +1371,17 @@ freezegun = [ {file = "freezegun-1.1.0-py2.py3-none-any.whl", hash = "sha256:2ae695f7eb96c62529f03a038461afe3c692db3465e215355e1bb4b0ab408712"}, {file = "freezegun-1.1.0.tar.gz", hash = "sha256:177f9dd59861d871e27a484c3332f35a6e3f5d14626f2bf91be37891f18927f3"}, ] -fuzzywuzzy = [ - {file = "fuzzywuzzy-0.18.0-py2.py3-none-any.whl", hash = "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"}, - {file = "fuzzywuzzy-0.18.0.tar.gz", hash = "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8"}, +furl = [ + {file = "furl-2.1.3-py2.py3-none-any.whl", hash = "sha256:9ab425062c4217f9802508e45feb4a83e54324273ac4b202f1850363309666c0"}, + {file = "furl-2.1.3.tar.gz", hash = "sha256:5a6188fe2666c484a12159c18be97a1977a71d632ef5bb867ef15f54af39cc4e"}, ] icecream = [ {file = "icecream-2.1.1-py2.py3-none-any.whl", hash = "sha256:adc1c48f5a4f83b2171c774a35142f217d317a84fca8cdf3fc65aa1321ff26b6"}, {file = "icecream-2.1.1.tar.gz", hash = "sha256:47e00e3f4e8477996e7dc420b6fa8ba53f8ced17de65320fedb5b15997b76589"}, ] identify = [ - {file = "identify-2.4.6-py2.py3-none-any.whl", hash = "sha256:cf06b1639e0dca0c184b1504d8b73448c99a68e004a80524c7923b95f7b6837c"}, - {file = "identify-2.4.6.tar.gz", hash = "sha256:233679e3f61a02015d4293dbccf16aa0e4996f868bd114688b8c124f18826706"}, + {file = "identify-2.4.8-py2.py3-none-any.whl", hash = "sha256:a55bdd671b6063eb837af938c250ec00bba6e610454265133b0d2db7ae718d0f"}, + {file = "identify-2.4.8.tar.gz", hash = "sha256:97e839c1779f07011b84c92af183e1883d9745d532d83412cca1ca76d3808c1c"}, ] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, @@ -1544,6 +1561,10 @@ more-itertools = [ {file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"}, {file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"}, ] +orderedmultidict = [ + {file = "orderedmultidict-1.0.1-py2.py3-none-any.whl", hash = "sha256:43c839a17ee3cdd62234c47deca1a8508a3f2ca1d0678a3bf791c87cf84adbf3"}, + {file = "orderedmultidict-1.0.1.tar.gz", hash = "sha256:04070bbb5e87291cc9bfa51df413677faf2141c73c61d2a5f7b26bea3cd882ad"}, +] packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, @@ -1665,8 +1686,8 @@ requests = [ {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, ] responses = [ - {file = "responses-0.17.0-py2.py3-none-any.whl", hash = "sha256:e4fc472fb7374fb8f84fcefa51c515ca4351f198852b4eb7fc88223780b472ea"}, - {file = "responses-0.17.0.tar.gz", hash = "sha256:ec675e080d06bf8d1fb5e5a68a1e5cd0df46b09c78230315f650af5e4036bec7"}, + {file = "responses-0.18.0-py3-none-any.whl", hash = "sha256:15c63ad16de13ee8e7182d99c9334f64fd81f1ee79f90748d527c28f7ca9dd51"}, + {file = "responses-0.18.0.tar.gz", hash = "sha256:380cad4c1c1dc942e5e8a8eaae0b4d4edf708f4f010db8b7bcfafad1fcd254ff"}, ] "ruamel.yaml" = [ {file = "ruamel.yaml-0.17.20-py3-none-any.whl", hash = "sha256:810eef9c46523a3f77479c66267a4708255ebe806a2d540078408c2227f011af"}, @@ -1740,7 +1761,11 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] sphobjinv = [ - {file = "sphobjinv-2.1-py3-none-any.whl", hash = "sha256:24c0aa47fcea4f47dc9487ff83776d7dda2c03425927037d7d2a1b292b1c3323"}, + {file = "sphobjinv-2.2.1-py3-none-any.whl", hash = "sha256:7a15569084d85f383336a495c9559b78f1f19f18b2718547e29a49730acce9b1"}, +] +strenum = [ + {file = "StrEnum-0.4.7-py3-none-any.whl", hash = "sha256:28ede0075ea0d990144d334c03509f14ee33e2e732289cb6e496ed7bae73fd1f"}, + {file = "StrEnum-0.4.7.tar.gz", hash = "sha256:6019e9cc1738af6a236c022b82184372178ecadf12438f11f5b680a02a462377"}, ] testfixtures = [ {file = "testfixtures-6.18.3-py2.py3-none-any.whl", hash = "sha256:6ddb7f56a123e1a9339f130a200359092bd0a6455e31838d6c477e8729bb7763"}, @@ -1759,8 +1784,8 @@ tomli = [ {file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"}, ] tomlkit = [ - {file = "tomlkit-0.8.0-py3-none-any.whl", hash = "sha256:b824e3466f1d475b2b5f1c392954c6cb7ea04d64354ff7300dc7c14257dc85db"}, - {file = "tomlkit-0.8.0.tar.gz", hash = "sha256:29e84a855712dfe0e88a48f6d05c21118dbafb283bb2eed614d46f80deb8e9a1"}, + {file = "tomlkit-0.9.0-py3-none-any.whl", hash = "sha256:c1b0fc73abd4f1e77c29ea4061ca0f2e11cbfb77342e17df3d3fdd496fc3f899"}, + {file = "tomlkit-0.9.0.tar.gz", hash = "sha256:5a83672c565f78f5fc8f1e44e5f2726446cc6b765113efd21d03e9331747d9ab"}, ] traitlets = [ {file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"}, diff --git a/pyproject.toml b/pyproject.toml index 60fa1917..2ad6ed17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,8 @@ cachy = "*" importlib-resources = { version = "*", python = ">=3.7, <3.9" } flatten-dict = "*" dpath = "*" +furl = "*" +StrEnum = "*" # TODO: chore: move to dependency groups once the feature is on a stable version of Poetry # https://python-poetry.org/docs/master/managing-dependencies/#dependency-groups diff --git a/src/nitpick/style/core.py b/src/nitpick/style/core.py index eb5c8ded..161fb502 100644 --- a/src/nitpick/style/core.py +++ b/src/nitpick/style/core.py @@ -10,6 +10,7 @@ import dpath.util from flatten_dict import flatten, unflatten +from furl import furl from identify import identify from loguru import logger from more_itertools import always_iterable @@ -27,6 +28,7 @@ PROJECT_NAME, PROJECT_OWNER, PYPROJECT_TOML, + SLASH, TOML_EXTENSION, ) from nitpick.exceptions import QuitComplainingError, pretty_exception @@ -36,9 +38,8 @@ from nitpick.project import Project, glob_files from nitpick.schemas import BaseStyleSchema, flatten_marshmallow_errors from nitpick.style.config import ConfigValidator -from nitpick.style.fetchers import StyleFetcherManager +from nitpick.style.fetchers import Scheme, StyleFetcherManager from nitpick.style.fetchers.github import GitHubURL -from nitpick.style.fetchers.pypackage import PythonPackageProtocol from nitpick.typedefs import JsonDict, StrOrIterable, StrOrList, mypy_property from nitpick.violations import Fuss, Reporter, StyleViolations @@ -83,8 +84,10 @@ def cache_dir(self) -> Path: def get_default_style_url(github=False): """Return the URL of the default style/preset.""" if github: - return GitHubURL(PROJECT_OWNER, PROJECT_NAME, f"v{__version__}", NITPICK_STYLE_TOML, None).long_protocol_url - return f"{PythonPackageProtocol.SHORT.value}://{PROJECT_NAME}/resources/presets/{PROJECT_NAME}" + return GitHubURL(PROJECT_OWNER, PROJECT_NAME, f"v{__version__}", NITPICK_STYLE_TOML).long_protocol_url + + rv = furl(scheme=Scheme.PY, host=PROJECT_NAME, path=SLASH.join(["resources", "presets", PROJECT_NAME])) + return str(rv) def find_initial_styles(self, configured_styles: StrOrIterable) -> Iterator[Fuss]: """Find the initial style(s) and include them.""" diff --git a/src/nitpick/style/fetchers/__init__.py b/src/nitpick/style/fetchers/__init__.py index 5e7c4b23..10cd636d 100644 --- a/src/nitpick/style/fetchers/__init__.py +++ b/src/nitpick/style/fetchers/__init__.py @@ -2,12 +2,14 @@ from __future__ import annotations from dataclasses import dataclass, field +from enum import auto from functools import lru_cache from pathlib import Path from typing import TYPE_CHECKING, Optional, Tuple from urllib.parse import urlparse, uses_netloc, uses_relative from cachy import CacheManager, Repository +from strenum import LowercaseStrEnum from nitpick.generic import is_url @@ -17,6 +19,17 @@ StyleInfo = Tuple[Optional[Path], str] +class Scheme(LowercaseStrEnum): + """URL schemes.""" + + HTTP = auto() + HTTPS = auto() + PY = auto() + PYPACKAGE = auto() + GH = auto() + GITHUB = auto() + + @dataclass(repr=True) class StyleFetcherManager: """Manager that controls which fetcher to be used given a protocol.""" diff --git a/src/nitpick/style/fetchers/base.py b/src/nitpick/style/fetchers/base.py index 91738c1e..e138a158 100644 --- a/src/nitpick/style/fetchers/base.py +++ b/src/nitpick/style/fetchers/base.py @@ -1,8 +1,10 @@ """Base class for fetchers that wrap inner fetchers with caching ability.""" +from __future__ import annotations + from dataclasses import dataclass from datetime import datetime from pathlib import Path -from typing import Dict, Tuple +from typing import Dict from cachy import CacheManager from loguru import logger @@ -22,8 +24,8 @@ class StyleFetcher: cache_option: str requires_connection = False - protocols: Tuple[str, ...] = () - domains: Tuple[str, ...] = () + protocols: tuple = () + domains: tuple[str, ...] = () def fetch(self, url) -> StyleInfo: """Fetch a style form cache or from a specific fetcher.""" diff --git a/src/nitpick/style/fetchers/file.py b/src/nitpick/style/fetchers/file.py index e31d30d4..77428974 100644 --- a/src/nitpick/style/fetchers/file.py +++ b/src/nitpick/style/fetchers/file.py @@ -1,7 +1,8 @@ """Basic local file fetcher.""" +from __future__ import annotations + from dataclasses import dataclass from pathlib import Path -from typing import Tuple from nitpick.style.fetchers.base import StyleFetcher @@ -10,7 +11,7 @@ class FileFetcher(StyleFetcher): # pylint: disable=too-few-public-methods """Fetch a style from a local file.""" - protocols: Tuple[str, ...] = ("file", "") + protocols: tuple = ("file", "") def _do_fetch(self, url): file_path = Path(url).expanduser() diff --git a/src/nitpick/style/fetchers/github.py b/src/nitpick/style/fetchers/github.py index eb119aea..57501c83 100644 --- a/src/nitpick/style/fetchers/github.py +++ b/src/nitpick/style/fetchers/github.py @@ -1,31 +1,21 @@ """Support for ``gh`` and ``github`` schemes.""" +from __future__ import annotations + import os from dataclasses import dataclass -from enum import Enum +from enum import auto from functools import lru_cache -from typing import Tuple -from urllib.parse import parse_qs, urlparse +from furl import furl from requests import Session, get as requests_get -from nitpick.constants import GIT_AT_REFERENCE +from nitpick.constants import GIT_AT_REFERENCE, SLASH +from nitpick.style.fetchers import Scheme from nitpick.style.fetchers.http import HttpFetcher from nitpick.typedefs import mypy_property - -class GitHubProtocol(Enum): - """Protocols for the GitHUb scheme.""" - - SHORT = "gh" - LONG = "github" - - -def _get_token_from_querystr(querystr) -> str: - """Get the value of ``token`` from querystring.""" - if not querystr: - return "" - query_args = parse_qs(querystr) - return query_args.get("token", [""])[0] +GITHUB_COM = "github.com" +QUERY_STRING_TOKEN = "token" # nosec @dataclass(unsafe_hash=True) @@ -36,12 +26,13 @@ class GitHubURL: repository: str git_reference: str path: str - auth_token: str + auth_token: str | None = None + query_string: tuple | None = None def __post_init__(self): """Remove the initial slash from the path.""" self._session = Session() - self.path = self.path.lstrip("/") + self.path = self.path.lstrip(SLASH) @mypy_property @lru_cache() @@ -53,7 +44,7 @@ def default_branch(self) -> str: return get_default_branch(self.api_url) @property - def credentials(self) -> Tuple: + def credentials(self) -> tuple: """Credentials encoded in this URL. A tuple of ``(api_token, '')`` if present, or empty tuple otherwise. If @@ -68,11 +59,11 @@ def credentials(self) -> Tuple: env_token = os.getenv(self.auth_token[1:]) if env_token: - return (env_token, "") + return env_token, "" return () - return (self.auth_token, "") + return self.auth_token, "" @property def git_reference_or_default(self) -> str: @@ -82,30 +73,42 @@ def git_reference_or_default(self) -> str: @property def url(self) -> str: """Default URL built from attributes.""" - return f"https://github.com/{self.owner}/{self.repository}/blob/{self.git_reference_or_default}/{self.path}" + rv = furl( + scheme=Scheme.HTTPS, + host=GITHUB_COM, + path=SLASH.join([self.owner, self.repository, "blob", self.git_reference_or_default, self.path]), + query_params=dict(self.query_string or {}), + ) + return str(rv) @property def raw_content_url(self) -> str: """Raw content URL for this path.""" - return ( - f"https://raw.githubusercontent.com/{self.owner}/{self.repository}" - f"/{self.git_reference_or_default}/{self.path}" + rv = furl( + scheme=Scheme.HTTPS, + host="raw.githubusercontent.com", + path=SLASH.join([self.owner, self.repository, self.git_reference_or_default, self.path]), ) + return str(rv) @classmethod - def parse_url(cls, url: str) -> "GitHubURL": + def parse_url(cls, url: str) -> GitHubURL: """Create an instance by parsing a URL string in any accepted format. See the code for ``test_parsing_github_urls()`` for more examples. """ - parsed_url = urlparse(url) + parsed_url = furl(url) git_reference = "" - auth_token = parsed_url.username or _get_token_from_querystr(parsed_url.query) + auth_token = parsed_url.username or parsed_url.args.get(QUERY_STRING_TOKEN) + + remaining_query_string = parsed_url.args.copy() + if QUERY_STRING_TOKEN in remaining_query_string: + del remaining_query_string[QUERY_STRING_TOKEN] if parsed_url.scheme in GitHubFetcher.protocols: - owner = parsed_url.hostname or "" - repo_with_git_reference, path = parsed_url.path.strip("/").split("/", 1) + owner = parsed_url.host or "" + repo_with_git_reference, path = str(parsed_url.path).strip(SLASH).split(SLASH, 1) if GIT_AT_REFERENCE in repo_with_git_reference: repo, git_reference = repo_with_git_reference.split(GIT_AT_REFERENCE) else: @@ -114,31 +117,35 @@ def parse_url(cls, url: str) -> "GitHubURL": # github.com urls have a useless .../blob/... section, but # raw.githubusercontent.com doesn't, so take the first 2, then last 2 url # components to exclude the blob bit if present. - owner, repo, *_, git_reference, path = parsed_url.path.strip("/").split("/", 4) + owner, repo, *_, git_reference, path = str(parsed_url.path).strip(SLASH).split(SLASH, 4) - return cls(owner, repo, git_reference, path, auth_token) + return cls(owner, repo, git_reference, path, auth_token, tuple(remaining_query_string.items())) @property def api_url(self) -> str: """API URL for this repo.""" - return f"https://api.github.com/repos/{self.owner}/{self.repository}" + rv = furl( + scheme=Scheme.HTTPS, host=f"api.{GITHUB_COM}", path=SLASH.join(["repos", self.owner, self.repository]) + ) + return str(rv) @property def short_protocol_url(self) -> str: """Short protocol URL (``gh``).""" - return self._build_url(GitHubProtocol.SHORT) + return self._build_url(Scheme.GH) @property def long_protocol_url(self) -> str: """Long protocol URL (``github``).""" - return self._build_url(GitHubProtocol.LONG) + return self._build_url(Scheme.GITHUB) - def _build_url(self, protocol: GitHubProtocol): + def _build_url(self, scheme: auto): if self.git_reference and self.git_reference != self.default_branch: at_reference = f"{GIT_AT_REFERENCE}{self.git_reference}" else: at_reference = "" - return f"{protocol.value}://{self.owner}/{self.repository}{at_reference}/{self.path}" + rv = furl(scheme=scheme, host=self.owner, path=SLASH.join([f"{self.repository}{at_reference}", self.path])) + return str(rv) @lru_cache() @@ -163,8 +170,8 @@ def get_default_branch(api_url: str) -> str: class GitHubFetcher(HttpFetcher): # pylint: disable=too-few-public-methods """Fetch styles from GitHub repositories.""" - protocols: Tuple[str, ...] = (GitHubProtocol.SHORT.value, GitHubProtocol.LONG.value) - domains: Tuple[str, ...] = ("github.com",) + protocols: tuple = (Scheme.GH, Scheme.GITHUB) + domains: tuple[str, ...] = (GITHUB_COM,) def _download(self, url, **kwargs) -> str: github_url = GitHubURL.parse_url(url) diff --git a/src/nitpick/style/fetchers/http.py b/src/nitpick/style/fetchers/http.py index dc27ec22..c73b1fa8 100644 --- a/src/nitpick/style/fetchers/http.py +++ b/src/nitpick/style/fetchers/http.py @@ -1,6 +1,7 @@ """Base HTTP fetcher, other fetchers can inherit from this to wrap http errors.""" +from __future__ import annotations + from dataclasses import dataclass, field -from typing import Tuple import click import requests @@ -8,6 +9,7 @@ from requests.sessions import Session from nitpick.enums import OptionEnum +from nitpick.style.fetchers import Scheme from nitpick.style.fetchers.base import StyleFetcher @@ -18,7 +20,7 @@ class HttpFetcher(StyleFetcher): _session: Session = field(init=False) requires_connection = True - protocols: Tuple[str, ...] = ("http", "https") + protocols: tuple = (Scheme.HTTP, Scheme.HTTPS) def __post_init__(self): """Sessions should be per class as children can have custom headers or authentication.""" diff --git a/src/nitpick/style/fetchers/pypackage.py b/src/nitpick/style/fetchers/pypackage.py index 58ba2a1c..060358ce 100644 --- a/src/nitpick/style/fetchers/pypackage.py +++ b/src/nitpick/style/fetchers/pypackage.py @@ -2,7 +2,6 @@ from __future__ import annotations from dataclasses import dataclass -from enum import Enum from functools import lru_cache from itertools import chain from pathlib import Path @@ -14,6 +13,7 @@ from nitpick import PROJECT_NAME, compat from nitpick.constants import DOT, SLASH +from nitpick.style.fetchers import Scheme from nitpick.style.fetchers.base import StyleFetcher @@ -62,13 +62,6 @@ def raw_content_url(self) -> compat.Traversable: return compat.files(self.import_path).joinpath(self.resource_name) -class PythonPackageProtocol(Enum): - """Protocols for the Python package scheme.""" - - SHORT = "py" - LONG = "pypackage" - - @dataclass(repr=True, unsafe_hash=True) class PythonPackageFetcher(StyleFetcher): # pylint: disable=too-few-public-methods """ @@ -81,7 +74,7 @@ class PythonPackageFetcher(StyleFetcher): # pylint: disable=too-few-public-meth E.g. ``py://some_package/path/nitpick.toml``. """ - protocols: tuple[str, ...] = (PythonPackageProtocol.SHORT.value, PythonPackageProtocol.LONG.value) + protocols: tuple = (Scheme.PY, Scheme.PYPACKAGE) def _do_fetch(self, url): package_url = PythonPackageURL.parse_url(url) diff --git a/tasks.py b/tasks.py index e102f6ec..eed9a5b9 100644 --- a/tasks.py +++ b/tasks.py @@ -71,16 +71,15 @@ def html_docs(self): ) @staticmethod - def macos(c, enable: bool): + def config(c) -> str: """Enable/disable macOS on tox.ini.""" if sys.platform != "darwin": - return + return "" - if enable: - # Hack to be able to run `invoke lint` on a macOS machine during development. - c.run("sed -i '' 's/platform = linux/platform = darwin/g' tox.ini") - else: - c.run("sed -i '' 's/platform = darwin/platform = linux/g' tox.ini") + temp_tox_ini = ".temp-tox.ini" + # Hack to be able to run `invoke lint` on a macOS machine during development. + c.run(f"sed 's/platform = linux/platform = darwin/g' tox.ini > {temp_tox_ini}") + return f"-c {temp_tox_ini}" def _python_versions_old_to_new(self) -> List[str]: """Python versions executed in tox.""" @@ -207,9 +206,8 @@ def doc(c, full=False, recreate=False, links=False, browse=False, debug=False): def ci_build(c, full=False, recreate=False, docs=True): """Simulate a CI build.""" tox = ToxCommands() - tox.macos(c, True) - tox_cmd = "tox -r" if recreate else "tox" + tox_cmd = " ".join(["tox", tox.config(c), "-r" if recreate else ""]) if full: c.run(f"rm -rf {DOCS_BUILD_PATH} docs/source") c.run(tox_cmd) @@ -220,19 +218,14 @@ def ci_build(c, full=False, recreate=False, docs=True): envs.append("report") c.run(f"{tox_cmd} -e {','.join(envs)}") - tox.macos(c, False) - @task(help={"recreate": "Recreate tox environment"}) def lint(c, recreate=False): """Lint using tox.""" tox = ToxCommands() - tox.macos(c, True) tox_cmd = "tox -r" if recreate else "tox" - result = c.run(f"{tox_cmd} -e lint", warn=True) - - tox.macos(c, False) + result = c.run(f"{tox_cmd}{tox.config(c)} -e lint", warn=True) # Exit only after restoring tox.ini if result.exited > 0: diff --git a/tests/test_cli.py b/tests/test_cli.py index 6ab2b2e2..13b3c3f2 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3,7 +3,7 @@ from nitpick.constants import DOT_NITPICK_TOML, PYPROJECT_TOML, READ_THE_DOCS_URL, TOOL_NITPICK_KEY from nitpick.style import StyleManager -from nitpick.style.fetchers.pypackage import PythonPackageProtocol +from nitpick.style.fetchers import Scheme from tests.helpers import XFAIL_ON_WINDOWS, ProjectMock @@ -63,7 +63,7 @@ def test_create_basic_dot_nitpick_toml(tmp_path): style = ["{url}"] """, ) - assert url.startswith(PythonPackageProtocol.SHORT.value) + assert url.startswith(f"{Scheme.PY}://") def test_add_tool_nitpick_section_to_pyproject_toml(tmp_path): @@ -89,4 +89,4 @@ def test_add_tool_nitpick_section_to_pyproject_toml(tmp_path): style = ["{url}"] """, ) - assert url.startswith(PythonPackageProtocol.SHORT.value) + assert url.startswith(f"{Scheme.PY}://") diff --git a/tests/test_style.py b/tests/test_style.py index 24668bab..d01ce3dc 100644 --- a/tests/test_style.py +++ b/tests/test_style.py @@ -1,5 +1,6 @@ """Style tests.""" import warnings +from base64 import b64encode from pathlib import Path from textwrap import dedent from unittest import mock @@ -464,26 +465,18 @@ def test_local_style_should_override_settings(tmp_path): def test_fetch_private_github_urls(tmp_path): """Fetch private GitHub URLs with a token on the query string.""" gh_url = "https://github.com/user/private_repo/blob/branch/path/to/nitpick-style" - query_string = "?token=xxx" + file_token = "query-string-token-generated-by-github-for-private-files" full_raw_url = f"https://raw.githubusercontent.com/user/private_repo/branch/path/to/nitpick-style{TOML_EXTENSION}" body = """ ["pyproject.toml".tool.black] missing = "thing" - """ - responses.add( - responses.GET, - full_raw_url, - dedent(body), - # TODO: refactor: fix DeprecationWarning: Argument 'match_querystring' is deprecated. - # Use 'responses.matchers.query_param_matcher' or 'responses.matchers.query_string_matcher' - match_querystring=False, - status=200, - ) + """ + responses.add(responses.GET, full_raw_url, dedent(body), status=200) project = ProjectMock(tmp_path).pyproject_toml( f""" [tool.nitpick] - style = "{gh_url}{query_string}" + style = "{gh_url}?token={file_token}" """ ) project.flake8(offline=False).assert_single_error( @@ -493,8 +486,8 @@ def test_fetch_private_github_urls(tmp_path): missing = "thing"{SUGGESTION_END} """ ) - # 'Basic eHh4Og==' is b64 encoding of "Basic xxx:" - assert responses.calls[0].request.headers["Authorization"] == "Basic eHh4Og==" + token_on_basic_auth = b64encode(f"{file_token}:".encode()).decode().strip() + assert responses.calls[0].request.headers["Authorization"] == f"Basic {token_on_basic_auth}" project.flake8(offline=True).assert_no_errors() @@ -593,7 +586,6 @@ def test_github_url_with_missing_envvar_has_empty_credential(monkeypatch): assert parsed.credentials == () -@pytest.mark.xfail(reason="GithubURL currently doesnt preserve query args") def test_github_url_query_token_retains_other_queryparams(monkeypatch): """Querystring isn't modified by the token switcharoo.""" parsed = GitHubURL.parse_url("https://github.com/foo/bar/blob/branch/filename.toml?leavemealone=ok")