diff --git a/.flake8 b/.flake8 index 6d6814e..40b49db 100644 --- a/.flake8 +++ b/.flake8 @@ -1,10 +1,21 @@ [flake8] exclude = shulkr/DecompilerMC +extend-ignore = + # Indentation — black handles + E1 + W1 + # Whitespace — black handles + E2 + W2 + # Blank lines — black handles + E3 + W3 + # Line length — black handles + E5 + W5 per-file-ignores = # line too long tests/*: E501 -use-flake8-tabs = true -indent-style = tab +max-line-length = 88 # continuation-style = hanging -INDENT-TABS-DEF = 1 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 550c307..b7a459a 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ - "ms-python.flake8" + "ms-python.flake8", + "ms-python.black-formatter" ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index f51726e..b1faf7c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,6 @@ ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, - "editor.defaultFormatter": "ms-python.flake8", + "editor.defaultFormatter": "ms-python.black-formatter", "editor.insertSpaces": false } \ No newline at end of file diff --git a/Pipfile b/Pipfile index 4d438c2..a26ef1d 100644 --- a/Pipfile +++ b/Pipfile @@ -18,13 +18,13 @@ pytest = "*" pytest-mock = "*" twine = "*" flake8 = "*" -flake8-tabs = "*" bump2version = "*" keepachangelog = "*" python-lsp-server = "*" pyls-mypy = {git = "https://github.com/tomv564/pyls-mypy.git"} # Needed to run tests on windows atomicwrites = "*" +black = "*" [requires] python_version = "3.9" @@ -36,6 +36,7 @@ test-unit = "python -m pytest tests/command/unit tests/gradle/unit tests/mint/un test-smoke = "python -m pytest tests/command/smoke tests/gradle/smoke tests/mint/smoke tests/shulkr/smoke" test-func = "python -m pytest tests/java/functional tests/shulkr/functional" lint = "python -m flake8 src" +format = "black ." profile = "python -m tests.profile" bump = "python -m scripts.bump" build = "python setup.py clean --all sdist bdist_wheel" diff --git a/Pipfile.lock b/Pipfile.lock index 653c9da..cb148f2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "78c492d1b694180948f9e4719be6c206d5ae30804bb82b29fe1e681dafab3528" + "sha256": "d42db0c23f1c30660570319048c53e3c294cca6e5ba9d8e5c6748ff0e9c6521e" }, "pipfile-spec": 6, "requires": { @@ -18,108 +18,125 @@ "default": { "certifi": { "hashes": [ - "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", - "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" + "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", + "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" ], "markers": "python_version >= '3.6'", - "version": "==2023.7.22" + "version": "==2024.2.2" }, "charset-normalizer": { "hashes": [ - "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", - "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", - "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710", - "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706", - "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020", - "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252", - "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad", - "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329", - "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a", - "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f", - "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6", - "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4", - "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a", - "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46", - "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2", - "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23", - "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace", - "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd", - "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982", - "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10", - "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2", - "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea", - "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09", - "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5", - "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149", - "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489", - "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9", - "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80", - "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592", - "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3", - "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6", - "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed", - "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c", - "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200", - "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a", - "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e", - "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d", - "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6", - "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623", - "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669", - "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3", - "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa", - "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9", - "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2", - "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f", - "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1", - "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4", - "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a", - "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8", - "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3", - "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029", - "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f", - "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959", - "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22", - "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7", - "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952", - "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346", - "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e", - "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d", - "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299", - "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd", - "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a", - "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3", - "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037", - "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94", - "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c", - "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858", - "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a", - "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449", - "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c", - "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918", - "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1", - "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c", - "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac", - "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa" - ], - "markers": "python_version >= '3.7'", - "version": "==3.2.0" + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" }, "click": { "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" ], "index": "pypi", - "version": "==8.1.3" + "markers": "python_version >= '3.7'", + "version": "==8.1.7" }, "colorama": { "hashes": [ - "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da", - "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4" + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" ], "index": "pypi", - "version": "==0.4.5" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==0.4.6" }, "gitdb": { "hashes": [ @@ -131,19 +148,18 @@ }, "gitpython": { "hashes": [ - "sha256:c36b6634d069b3f719610175020a9aed919421c87552185b085e04fbbdb10b7c", - "sha256:ed66e624884f76df22c8e16066d567aaa5a37d5b5fa19db2c6df6f7156db9048" + "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c", + "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==3.1.41" + "version": "==3.1.43" }, "idna": { "hashes": [ "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" ], - "index": "pypi", "markers": "python_version >= '3.5'", "version": "==3.7" }, @@ -162,6 +178,7 @@ "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.31.0" }, "six": { @@ -186,24 +203,24 @@ "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], "index": "pypi", + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.10.2" }, "unidiff": { "hashes": [ - "sha256:2bbcbc986e1fb97f04b1d7b864aa6002ab02f4d8a996bf03aa6e5a81447d1fc5", - "sha256:688622add422f84a873498cc4ff9bf50da5ea6c23dea908f19d2190fa39a8e39" + "sha256:2e5f0162052248946b9f0970a40e9e124236bf86c82b70821143a6fc1dea2574", + "sha256:c93bf2265cc1ba2a520e415ab05da587370bc2a3ae9e0414329f54f0c2fc09e8" ], "index": "pypi", - "version": "==0.7.4" + "version": "==0.7.5" }, "urllib3": { "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" + "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", + "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19" ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.0.7" + "markers": "python_version >= '3.8'", + "version": "==2.2.1" } }, "develop": { @@ -214,13 +231,42 @@ "index": "pypi", "version": "==1.4.1" }, - "attrs": { + "backports.tarfile": { "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" + "sha256:73e0179647803d3726d82e76089d01d8549ceca9bace469953fcb4d97cf2d417", + "sha256:9c2ef9696cb73374f7164e17fc761389393ca76777036f5aad42e8b93fcd8009" ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" + "markers": "python_version < '3.12'", + "version": "==1.1.1" + }, + "black": { + "hashes": [ + "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474", + "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1", + "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0", + "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8", + "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96", + "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1", + "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04", + "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021", + "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94", + "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d", + "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c", + "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7", + "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c", + "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc", + "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7", + "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d", + "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c", + "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741", + "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce", + "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb", + "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063", + "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==24.4.2" }, "bump2version": { "hashes": [ @@ -228,240 +274,170 @@ "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6" ], "index": "pypi", + "markers": "python_version >= '3.5'", "version": "==1.0.1" }, "certifi": { "hashes": [ - "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", - "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" + "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", + "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" ], "markers": "python_version >= '3.6'", - "version": "==2023.7.22" - }, - "cffi": { - "hashes": [ - "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", - "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", - "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", - "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", - "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", - "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", - "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", - "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", - "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", - "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", - "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", - "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", - "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", - "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", - "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", - "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", - "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", - "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", - "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", - "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", - "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", - "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", - "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", - "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", - "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", - "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", - "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", - "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", - "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", - "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", - "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", - "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", - "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", - "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", - "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", - "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", - "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", - "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", - "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", - "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", - "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", - "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", - "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", - "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", - "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", - "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", - "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", - "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", - "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", - "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", - "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", - "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" - ], - "markers": "platform_python_implementation != 'PyPy'", - "version": "==1.16.0" + "version": "==2024.2.2" }, "charset-normalizer": { "hashes": [ - "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", - "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", - "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710", - "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706", - "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020", - "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252", - "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad", - "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329", - "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a", - "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f", - "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6", - "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4", - "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a", - "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46", - "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2", - "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23", - "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace", - "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd", - "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982", - "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10", - "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2", - "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea", - "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09", - "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5", - "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149", - "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489", - "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9", - "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80", - "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592", - "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3", - "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6", - "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed", - "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c", - "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200", - "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a", - "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e", - "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d", - "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6", - "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623", - "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669", - "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3", - "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa", - "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9", - "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2", - "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f", - "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1", - "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4", - "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a", - "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8", - "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3", - "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029", - "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f", - "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959", - "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22", - "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7", - "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952", - "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346", - "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e", - "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d", - "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299", - "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd", - "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a", - "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3", - "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037", - "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94", - "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c", - "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858", - "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a", - "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449", - "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c", - "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918", - "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1", - "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c", - "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac", - "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa" - ], - "markers": "python_version >= '3.7'", - "version": "==3.2.0" + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" }, - "cryptography": { - "hashes": [ - "sha256:01911714117642a3f1792c7f376db572aadadbafcd8d75bb527166009c9f1d1b", - "sha256:0e89f7b84f421c56e7ff69f11c441ebda73b8a8e6488d322ef71746224c20fce", - "sha256:12d341bd42cdb7d4937b0cabbdf2a94f949413ac4504904d0cdbdce4a22cbf88", - "sha256:15a1fb843c48b4a604663fa30af60818cd28f895572386e5f9b8a665874c26e7", - "sha256:1cdcdbd117681c88d717437ada72bdd5be9de117f96e3f4d50dab3f59fd9ab20", - "sha256:1df6fcbf60560d2113b5ed90f072dc0b108d64750d4cbd46a21ec882c7aefce9", - "sha256:3c6048f217533d89f2f8f4f0fe3044bf0b2090453b7b73d0b77db47b80af8dff", - "sha256:3e970a2119507d0b104f0a8e281521ad28fc26f2820687b3436b8c9a5fcf20d1", - "sha256:44a64043f743485925d3bcac548d05df0f9bb445c5fcca6681889c7c3ab12764", - "sha256:4e36685cb634af55e0677d435d425043967ac2f3790ec652b2b88ad03b85c27b", - "sha256:5f8907fcf57392cd917892ae83708761c6ff3c37a8e835d7246ff0ad251d9298", - "sha256:69b22ab6506a3fe483d67d1ed878e1602bdd5912a134e6202c1ec672233241c1", - "sha256:6bfadd884e7280df24d26f2186e4e07556a05d37393b0f220a840b083dc6a824", - "sha256:6d0fbe73728c44ca3a241eff9aefe6496ab2656d6e7a4ea2459865f2e8613257", - "sha256:6ffb03d419edcab93b4b19c22ee80c007fb2d708429cecebf1dd3258956a563a", - "sha256:810bcf151caefc03e51a3d61e53335cd5c7316c0a105cc695f0959f2c638b129", - "sha256:831a4b37accef30cccd34fcb916a5d7b5be3cbbe27268a02832c3e450aea39cb", - "sha256:887623fe0d70f48ab3f5e4dbf234986b1329a64c066d719432d0698522749929", - "sha256:a0298bdc6e98ca21382afe914c642620370ce0470a01e1bef6dd9b5354c36854", - "sha256:a1327f280c824ff7885bdeef8578f74690e9079267c1c8bd7dc5cc5aa065ae52", - "sha256:c1f25b252d2c87088abc8bbc4f1ecbf7c919e05508a7e8628e6875c40bc70923", - "sha256:c3a5cbc620e1e17009f30dd34cb0d85c987afd21c41a74352d1719be33380885", - "sha256:ce8613beaffc7c14f091497346ef117c1798c202b01153a8cc7b8e2ebaaf41c0", - "sha256:d2a27aca5597c8a71abbe10209184e1a8e91c1fd470b5070a2ea60cafec35bcd", - "sha256:dad9c385ba8ee025bb0d856714f71d7840020fe176ae0229de618f14dae7a6e2", - "sha256:db4b65b02f59035037fde0998974d84244a64c3265bdef32a827ab9b63d61b18", - "sha256:e09469a2cec88fb7b078e16d4adec594414397e8879a4341c6ace96013463d5b", - "sha256:e53dc41cda40b248ebc40b83b31516487f7db95ab8ceac1f042626bc43a2f992", - "sha256:f1e85a178384bf19e36779d91ff35c7617c885da487d689b05c1366f9933ad74", - "sha256:f47be41843200f7faec0683ad751e5ef11b9a56a220d57f300376cd8aba81660", - "sha256:fb0cef872d8193e487fc6bdb08559c3aa41b659a7d9be48b2e10747f47863925", - "sha256:ffc73996c4fca3d2b6c1c8c12bfd3ad00def8621da24f547626bf06441400449" + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==42.0.4" + "version": "==8.1.7" }, - "docutils": { + "docstring-to-markdown": { "hashes": [ - "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6", - "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b" + "sha256:27afb3faedba81e34c33521c32bbd258d7fbb79eedf7d29bc4e81080e854aec0", + "sha256:e146114d9c50c181b1d25505054a8d0f7a476837f0da2c19f07e06eaed52b73d" ], - "markers": "python_version >= '3.7'", - "version": "==0.20.1" + "markers": "python_version >= '3.6'", + "version": "==0.15" }, - "editorconfig": { + "docutils": { "hashes": [ - "sha256:57f8ce78afcba15c8b18d46b5170848c88d56fd38f05c2ec60dbbfcb8996e89e", - "sha256:6b0851425aa875b08b16789ee0eeadbd4ab59666e9ebe728e526314c4a2e52c1" + "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", + "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2" ], - "version": "==0.12.3" + "markers": "python_version >= '3.9'", + "version": "==0.21.2" }, - "flake8": { + "exceptiongroup": { "hashes": [ - "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b", - "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907" + "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad", + "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16" ], - "index": "pypi", - "version": "==3.9.2" + "markers": "python_version < '3.11'", + "version": "==1.2.1" }, - "flake8-tabs": { + "flake8": { "hashes": [ - "sha256:e36c0f2038d709961510b9a80a685b28db87df6cfb1309b111d6be41e79a669b", - "sha256:f11c4b8a1537d67014c24f5296118e54b78fc43204b740e28283fa8e165130ef" + "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132", + "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3" ], "index": "pypi", - "version": "==2.3.2" + "markers": "python_full_version >= '3.8.1'", + "version": "==7.0.0" }, "idna": { "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", + "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" ], "markers": "python_version >= '3.5'", - "version": "==3.4" + "version": "==3.7" }, "importlib-metadata": { "hashes": [ - "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb", - "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743" + "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", + "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2" ], "markers": "python_version >= '3.8'", - "version": "==6.8.0" + "version": "==7.1.0" }, "iniconfig": { "hashes": [ @@ -473,11 +449,27 @@ }, "jaraco.classes": { "hashes": [ - "sha256:10afa92b6743f25c0cf5f37c6bb6e18e2c5bb84a16527ccfc0040ea377e7aaeb", - "sha256:c063dd08e89217cee02c8d5e5ec560f2c8ce6cdc2fcdc2e68f7b2e5547ed3621" + "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", + "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790" ], "markers": "python_version >= '3.8'", - "version": "==3.3.0" + "version": "==3.4.0" + }, + "jaraco.context": { + "hashes": [ + "sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266", + "sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2" + ], + "markers": "python_version >= '3.8'", + "version": "==5.3.0" + }, + "jaraco.functools": { + "hashes": [ + "sha256:3b24ccb921d6b593bdceb56ce14799204f473976e2a9d4b15b04d0f2c2326664", + "sha256:d33fa765374c0611b52f8b3a795f8900869aa88c84769d4d1746cd68fb28c3e8" + ], + "markers": "python_version >= '3.8'", + "version": "==4.0.1" }, "jedi": { "hashes": [ @@ -487,29 +479,22 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==0.17.2" }, - "jeepney": { - "hashes": [ - "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", - "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755" - ], - "markers": "sys_platform == 'linux'", - "version": "==0.8.0" - }, "keepachangelog": { "hashes": [ "sha256:0d3331f6601aeccf73620319782f0e993b1147b1fcba0ee2a534e4eef4b252b4", "sha256:1b73548ce85518b73009ce71af79c617c663437268cb085071973440c0505158" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==1.0.0" }, "keyring": { "hashes": [ - "sha256:4901caaf597bfd3bbd78c9a0c7c4c29fcd8310dab2cffefe749e916b6527acd6", - "sha256:ca0746a19ec421219f4d713f848fa297a661a8a8c1504867e55bfb5e09091509" + "sha256:19f17d40335444aab84b19a0d16a77ec0758a9c384e3446ae2ed8bd6d53b67a5", + "sha256:7045f367268ce42dba44745050164b431e46f6e92f99ef2937dfadaef368d8cf" ], "markers": "python_version >= '3.8'", - "version": "==24.2.0" + "version": "==25.2.0" }, "markdown-it-py": { "hashes": [ @@ -521,10 +506,11 @@ }, "mccabe": { "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" ], - "version": "==0.6.1" + "markers": "python_version >= '3.6'", + "version": "==0.7.0" }, "mdurl": { "hashes": [ @@ -536,44 +522,44 @@ }, "more-itertools": { "hashes": [ - "sha256:626c369fa0eb37bac0291bce8259b332fd59ac792fa5497b59837309cd5b114a", - "sha256:64e0735fcfdc6f3464ea133afe8ea4483b1c5fe3a3d69852e6503b43a0b222e6" + "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684", + "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1" ], "markers": "python_version >= '3.8'", - "version": "==10.1.0" + "version": "==10.2.0" }, "mypy": { "hashes": [ - "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315", - "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0", - "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373", - "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a", - "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161", - "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275", - "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693", - "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb", - "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65", - "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4", - "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb", - "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243", - "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14", - "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4", - "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1", - "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a", - "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160", - "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25", - "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12", - "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d", - "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92", - "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770", - "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2", - "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70", - "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb", - "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5", - "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f" + "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061", + "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99", + "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de", + "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a", + "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9", + "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec", + "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1", + "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131", + "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f", + "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821", + "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5", + "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee", + "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e", + "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746", + "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2", + "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0", + "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b", + "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53", + "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30", + "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda", + "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051", + "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2", + "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7", + "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee", + "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727", + "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976", + "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4" ], "markers": "python_version >= '3.8'", - "version": "==1.5.1" + "version": "==1.10.0" }, "mypy-extensions": { "hashes": [ @@ -585,32 +571,32 @@ }, "nh3": { "hashes": [ - "sha256:116c9515937f94f0057ef50ebcbcc10600860065953ba56f14473ff706371873", - "sha256:18415df36db9b001f71a42a3a5395db79cf23d556996090d293764436e98e8ad", - "sha256:203cac86e313cf6486704d0ec620a992c8bc164c86d3a4fd3d761dd552d839b5", - "sha256:2b0be5c792bd43d0abef8ca39dd8acb3c0611052ce466d0401d51ea0d9aa7525", - "sha256:377aaf6a9e7c63962f367158d808c6a1344e2b4f83d071c43fbd631b75c4f0b2", - "sha256:525846c56c2bcd376f5eaee76063ebf33cf1e620c1498b2a40107f60cfc6054e", - "sha256:5529a3bf99402c34056576d80ae5547123f1078da76aa99e8ed79e44fa67282d", - "sha256:7771d43222b639a4cd9e341f870cee336b9d886de1ad9bec8dddab22fe1de450", - "sha256:88c753efbcdfc2644a5012938c6b9753f1c64a5723a67f0301ca43e7b85dcf0e", - "sha256:93a943cfd3e33bd03f77b97baa11990148687877b74193bf777956b67054dcc6", - "sha256:9be2f68fb9a40d8440cbf34cbf40758aa7f6093160bfc7fb018cce8e424f0c3a", - "sha256:a0c509894fd4dccdff557068e5074999ae3b75f4c5a2d6fb5415e782e25679c4", - "sha256:ac8056e937f264995a82bf0053ca898a1cb1c9efc7cd68fa07fe0060734df7e4", - "sha256:aed56a86daa43966dd790ba86d4b810b219f75b4bb737461b6886ce2bde38fd6", - "sha256:e8986f1dd3221d1e741fda0a12eaa4a273f1d80a35e31a1ffe579e7c621d069e", - "sha256:f99212a81c62b5f22f9e7c3e347aa00491114a5647e1f13bbebd79c3e5f08d75" - ], - "version": "==0.2.14" + "sha256:0316c25b76289cf23be6b66c77d3608a4fdf537b35426280032f432f14291b9a", + "sha256:1a814dd7bba1cb0aba5bcb9bebcc88fd801b63e21e2450ae6c52d3b3336bc911", + "sha256:1aa52a7def528297f256de0844e8dd680ee279e79583c76d6fa73a978186ddfb", + "sha256:22c26e20acbb253a5bdd33d432a326d18508a910e4dcf9a3316179860d53345a", + "sha256:40015514022af31975c0b3bca4014634fa13cb5dc4dbcbc00570acc781316dcc", + "sha256:40d0741a19c3d645e54efba71cb0d8c475b59135c1e3c580f879ad5514cbf028", + "sha256:551672fd71d06cd828e282abdb810d1be24e1abb7ae2543a8fa36a71c1006fe9", + "sha256:66f17d78826096291bd264f260213d2b3905e3c7fae6dfc5337d49429f1dc9f3", + "sha256:85cdbcca8ef10733bd31f931956f7fbb85145a4d11ab9e6742bbf44d88b7e351", + "sha256:a3f55fabe29164ba6026b5ad5c3151c314d136fd67415a17660b4aaddacf1b10", + "sha256:b4427ef0d2dfdec10b641ed0bdaf17957eb625b2ec0ea9329b3d28806c153d71", + "sha256:ba73a2f8d3a1b966e9cdba7b211779ad8a2561d2dba9674b8a19ed817923f65f", + "sha256:c21bac1a7245cbd88c0b0e4a420221b7bfa838a2814ee5bb924e9c2f10a1120b", + "sha256:c551eb2a3876e8ff2ac63dff1585236ed5dfec5ffd82216a7a174f7c5082a78a", + "sha256:c790769152308421283679a142dbdb3d1c46c79c823008ecea8e8141db1a2062", + "sha256:d7a25fd8c86657f5d9d576268e3b3767c5cd4f42867c9383618be8517f0f022a" + ], + "version": "==0.2.17" }, "packaging": { "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" + "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9" ], "markers": "python_version >= '3.7'", - "version": "==23.1" + "version": "==24.0" }, "parso": { "hashes": [ @@ -620,61 +606,61 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.7.1" }, + "pathspec": { + "hashes": [ + "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", + "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" + ], + "markers": "python_version >= '3.8'", + "version": "==0.12.1" + }, "pkginfo": { "hashes": [ - "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546", - "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046" + "sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297", + "sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097" ], "markers": "python_version >= '3.6'", - "version": "==1.9.6" + "version": "==1.10.0" }, - "pluggy": { + "platformdirs": { "hashes": [ - "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", - "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf", + "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1" ], "markers": "python_version >= '3.8'", - "version": "==1.3.0" + "version": "==4.2.1" }, - "py": { + "pluggy": { "hashes": [ - "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", - "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" + "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", + "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.11.0" + "markers": "python_version >= '3.8'", + "version": "==1.5.0" }, "pycodestyle": { "hashes": [ - "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068", - "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.7.0" - }, - "pycparser": { - "hashes": [ - "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", - "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" + "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f", + "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67" ], "markers": "python_version >= '3.8'", - "version": "==2.22" + "version": "==2.11.1" }, "pyflakes": { "hashes": [ - "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3", - "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db" + "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", + "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.3.1" + "markers": "python_version >= '3.8'", + "version": "==3.2.0" }, "pygments": { "hashes": [ - "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692", - "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29" + "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", + "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367" ], "markers": "python_version >= '3.7'", - "version": "==2.16.1" + "version": "==2.17.2" }, "pyls-mypy": { "git": "https://github.com/tomv564/pyls-mypy.git", @@ -682,19 +668,21 @@ }, "pytest": { "hashes": [ - "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7", - "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39" + "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233", + "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f" ], "index": "pypi", - "version": "==7.1.3" + "markers": "python_version >= '3.8'", + "version": "==8.2.0" }, "pytest-mock": { "hashes": [ - "sha256:77f03f4554392558700295e05aed0b1096a20d4a60a4f3ddcde58b0c31c8fca2", - "sha256:8a9e226d6c0ef09fcf20c94eb3405c388af438a90f3e39687f84166da82d5948" + "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", + "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0" ], "index": "pypi", - "version": "==3.8.2" + "markers": "python_version >= '3.8'", + "version": "==3.14.0" }, "python-jsonrpc-server": { "hashes": [ @@ -712,27 +700,28 @@ }, "python-lsp-jsonrpc": { "hashes": [ - "sha256:05cc914aa5ee2e7f52eb762014ba4996a8cc52459bfddb81fb96c56ace927908", - "sha256:2bd7771bd5e23ba2edb17101432c5fd680bbacedb8dddeb472a8250ca8d936d6" + "sha256:4688e453eef55cd952bff762c705cedefa12055c0aec17a06f595bcc002cc912", + "sha256:7339c2e9630ae98903fdaea1ace8c47fba0484983794d6aafd0bd8989be2b03c" ], "markers": "python_version >= '3.8'", - "version": "==1.1.1" + "version": "==1.1.2" }, "python-lsp-server": { "hashes": [ - "sha256:77aac9861b0e23cafb251f116448157e6c45957e625097ed4846891281cffc18", - "sha256:e5c094c19925022a27c4068f414b2bb653243f8fb0d768e39735289d7a89380d" + "sha256:278cb41ea69ca9f84ec99d4edc96ff5f2f9e795d240771dc46dc1653f56ddfe3", + "sha256:89edd6fb3f7852e4bf5a3d1d95ea41484d1a28fa94b6e3cbff12b9db123b8e86" ], "index": "pypi", - "version": "==1.5.0" + "markers": "python_version >= '3.8'", + "version": "==1.11.0" }, "readme-renderer": { "hashes": [ - "sha256:13d039515c1f24de668e2c93f2e877b9dbe6c6c32328b90a40a49d8b2b85f36d", - "sha256:2d55489f83be4992fe4454939d1a051c33edbab778e82761d060c9fc6b308cd1" + "sha256:1818dd28140813509eeed8d62687f7cd4f7bad90d4db586001c5dc09d4fde311", + "sha256:19db308d86ecd60e5affa3b2a98f017af384678c63c88e5d4556a380e674f3f9" ], "markers": "python_version >= '3.8'", - "version": "==42.0" + "version": "==43.0" }, "requests": { "hashes": [ @@ -740,6 +729,7 @@ "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.31.0" }, "requests-toolbelt": { @@ -760,134 +750,123 @@ }, "rich": { "hashes": [ - "sha256:87b43e0543149efa1253f485cd845bb7ee54df16c9617b8a893650ab84b4acb6", - "sha256:9257b468badc3d347e146a4faa268ff229039d4c2d176ab0cffb4c4fbc73d5d9" - ], - "markers": "python_version >= '3.7'", - "version": "==13.5.3" - }, - "secretstorage": { - "hashes": [ - "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", - "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99" + "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", + "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432" ], - "markers": "sys_platform == 'linux'", - "version": "==3.3.3" - }, - "setuptools": { - "hashes": [ - "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87", - "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a" - ], - "markers": "python_version >= '3.8'", - "version": "==68.2.2" + "markers": "python_full_version >= '3.7.0'", + "version": "==13.7.1" }, "tomli": { "hashes": [ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], - "markers": "python_version >= '3.7'", + "markers": "python_version < '3.11'", "version": "==2.0.1" }, "twine": { "hashes": [ - "sha256:42026c18e394eac3e06693ee52010baa5313e4811d5a11050e7d48436cf41b9e", - "sha256:96b1cf12f7ae611a4a40b6ae8e9570215daff0611828f5fe1f37a16255ab24a0" + "sha256:89b0cc7d370a4b66421cc6102f269aa910fe0f1861c124f573cf2ddedbc10cf4", + "sha256:a262933de0b484c53408f9edae2e7821c1c45a3314ff2df9bdd343aa7ab8edc0" ], "index": "pypi", - "version": "==4.0.1" + "markers": "python_version >= '3.8'", + "version": "==5.0.0" }, "typing-extensions": { "hashes": [ - "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", - "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" + "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0", + "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a" ], - "markers": "python_version >= '3.8'", - "version": "==4.8.0" + "markers": "python_version < '3.11'", + "version": "==4.11.0" }, "ujson": { "hashes": [ - "sha256:07d459aca895eb17eb463b00441986b021b9312c6c8cc1d06880925c7f51009c", - "sha256:0be81bae295f65a6896b0c9030b55a106fb2dec69ef877253a87bc7c9c5308f7", - "sha256:0fe1b7edaf560ca6ab023f81cbeaf9946a240876a993b8c5a21a1c539171d903", - "sha256:102bf31c56f59538cccdfec45649780ae00657e86247c07edac434cb14d5388c", - "sha256:11da6bed916f9bfacf13f4fc6a9594abd62b2bb115acfb17a77b0f03bee4cfd5", - "sha256:16fde596d5e45bdf0d7de615346a102510ac8c405098e5595625015b0d4b5296", - "sha256:193349a998cd821483a25f5df30b44e8f495423840ee11b3b28df092ddfd0f7f", - "sha256:20768961a6a706170497129960762ded9c89fb1c10db2989c56956b162e2a8a3", - "sha256:27a2a3c7620ebe43641e926a1062bc04e92dbe90d3501687957d71b4bdddaec4", - "sha256:2873d196725a8193f56dde527b322c4bc79ed97cd60f1d087826ac3290cf9207", - "sha256:299a312c3e85edee1178cb6453645217ba23b4e3186412677fa48e9a7f986de6", - "sha256:2a64cc32bb4a436e5813b83f5aab0889927e5ea1788bf99b930fad853c5625cb", - "sha256:2b852bdf920fe9f84e2a2c210cc45f1b64f763b4f7d01468b33f7791698e455e", - "sha256:2e72ba76313d48a1a3a42e7dc9d1db32ea93fac782ad8dde6f8b13e35c229130", - "sha256:3659deec9ab9eb19e8646932bfe6fe22730757c4addbe9d7d5544e879dc1b721", - "sha256:3b27a8da7a080add559a3b73ec9ebd52e82cc4419f7c6fb7266e62439a055ed0", - "sha256:3f9b63530a5392eb687baff3989d0fb5f45194ae5b1ca8276282fb647f8dcdb3", - "sha256:407d60eb942c318482bbfb1e66be093308bb11617d41c613e33b4ce5be789adc", - "sha256:40931d7c08c4ce99adc4b409ddb1bbb01635a950e81239c2382cfe24251b127a", - "sha256:48c7d373ff22366eecfa36a52b9b55b0ee5bd44c2b50e16084aa88b9de038916", - "sha256:4ddeabbc78b2aed531f167d1e70387b151900bc856d61e9325fcdfefb2a51ad8", - "sha256:5ac97b1e182d81cf395ded620528c59f4177eee024b4b39a50cdd7b720fdeec6", - "sha256:5ce24909a9c25062e60653073dd6d5e6ec9d6ad7ed6e0069450d5b673c854405", - "sha256:69b3104a2603bab510497ceabc186ba40fef38ec731c0ccaa662e01ff94a985c", - "sha256:6a4dafa9010c366589f55afb0fd67084acd8added1a51251008f9ff2c3e44042", - "sha256:6d230d870d1ce03df915e694dcfa3f4e8714369cce2346686dbe0bc8e3f135e7", - "sha256:78e318def4ade898a461b3d92a79f9441e7e0e4d2ad5419abed4336d702c7425", - "sha256:7a42baa647a50fa8bed53d4e242be61023bd37b93577f27f90ffe521ac9dc7a3", - "sha256:7cba16b26efe774c096a5e822e4f27097b7c81ed6fb5264a2b3f5fd8784bab30", - "sha256:7d8283ac5d03e65f488530c43d6610134309085b71db4f675e9cf5dff96a8282", - "sha256:7ecc33b107ae88405aebdb8d82c13d6944be2331ebb04399134c03171509371a", - "sha256:9249fdefeb021e00b46025e77feed89cd91ffe9b3a49415239103fc1d5d9c29a", - "sha256:9399eaa5d1931a0ead49dce3ffacbea63f3177978588b956036bfe53cdf6af75", - "sha256:94c7bd9880fa33fcf7f6d7f4cc032e2371adee3c5dba2922b918987141d1bf07", - "sha256:9571de0c53db5cbc265945e08f093f093af2c5a11e14772c72d8e37fceeedd08", - "sha256:9721cd112b5e4687cb4ade12a7b8af8b048d4991227ae8066d9c4b3a6642a582", - "sha256:9ab282d67ef3097105552bf151438b551cc4bedb3f24d80fada830f2e132aeb9", - "sha256:9d9707e5aacf63fb919f6237d6490c4e0244c7f8d3dc2a0f84d7dec5db7cb54c", - "sha256:a70f776bda2e5072a086c02792c7863ba5833d565189e09fabbd04c8b4c3abba", - "sha256:a89cf3cd8bf33a37600431b7024a7ccf499db25f9f0b332947fbc79043aad879", - "sha256:a8c91b6f4bf23f274af9002b128d133b735141e867109487d17e344d38b87d94", - "sha256:ad24ec130855d4430a682c7a60ca0bc158f8253ec81feed4073801f6b6cb681b", - "sha256:ae7f4725c344bf437e9b881019c558416fe84ad9c6b67426416c131ad577df67", - "sha256:b748797131ac7b29826d1524db1cc366d2722ab7afacc2ce1287cdafccddbf1f", - "sha256:bdf04c6af3852161be9613e458a1fb67327910391de8ffedb8332e60800147a2", - "sha256:bf5737dbcfe0fa0ac8fa599eceafae86b376492c8f1e4b84e3adf765f03fb564", - "sha256:c4e7bb7eba0e1963f8b768f9c458ecb193e5bf6977090182e2b4f4408f35ac76", - "sha256:d524a8c15cfc863705991d70bbec998456a42c405c291d0f84a74ad7f35c5109", - "sha256:d53039d39de65360e924b511c7ca1a67b0975c34c015dd468fca492b11caa8f7", - "sha256:d6f84a7a175c75beecde53a624881ff618e9433045a69fcfb5e154b73cdaa377", - "sha256:e0147d41e9fb5cd174207c4a2895c5e24813204499fd0839951d4c8784a23bf5", - "sha256:e3673053b036fd161ae7a5a33358ccae6793ee89fd499000204676baafd7b3aa", - "sha256:e54578fa8838ddc722539a752adfce9372474114f8c127bb316db5392d942f8b", - "sha256:eb0142f6f10f57598655340a3b2c70ed4646cbe674191da195eb0985a9813b83", - "sha256:efeddf950fb15a832376c0c01d8d7713479fbeceaed1eaecb2665aa62c305aec", - "sha256:f26629ac531d712f93192c233a74888bc8b8212558bd7d04c349125f10199fcf", - "sha256:f2e385a7679b9088d7bc43a64811a7713cc7c33d032d020f757c54e7d41931ae", - "sha256:f3554eaadffe416c6f543af442066afa6549edbc34fe6a7719818c3e72ebfe95", - "sha256:f4511560d75b15ecb367eef561554959b9d49b6ec3b8d5634212f9fed74a6df1", - "sha256:f504117a39cb98abba4153bf0b46b4954cc5d62f6351a14660201500ba31fe7f", - "sha256:fb87decf38cc82bcdea1d7511e73629e651bdec3a43ab40985167ab8449b769c" + "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd", + "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd", + "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e", + "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5", + "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c", + "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437", + "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21", + "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e", + "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851", + "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562", + "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34", + "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60", + "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b", + "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4", + "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096", + "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9", + "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30", + "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320", + "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01", + "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c", + "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617", + "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0", + "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21", + "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e", + "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f", + "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120", + "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99", + "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d", + "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5", + "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad", + "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c", + "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908", + "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c", + "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164", + "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532", + "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d", + "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d", + "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378", + "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399", + "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e", + "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f", + "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b", + "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa", + "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427", + "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f", + "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae", + "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106", + "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd", + "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f", + "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36", + "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf", + "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c", + "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81", + "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c", + "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d", + "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc", + "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402", + "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f", + "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b", + "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95", + "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844", + "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124", + "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e", + "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e", + "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1" ], "markers": "python_version >= '3.8'", - "version": "==5.8.0" + "version": "==5.9.0" }, "urllib3": { "hashes": [ - "sha256:13abf37382ea2ce6fb744d4dad67838eec857c9f4f57009891805e0b5e123594", - "sha256:ef16afa8ba34a1f989db38e1dbbe0c302e4289a47856990d0682e374563ce35e" + "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", + "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19" ], - "markers": "python_version >= '3.7'", - "version": "==2.0.5" + "markers": "python_version >= '3.8'", + "version": "==2.2.1" }, "zipp": { "hashes": [ - "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31", - "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0" + "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b", + "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715" ], "markers": "python_version >= '3.8'", - "version": "==3.17.0" + "version": "==3.18.1" } } } diff --git a/scripts/bump/__main__.py b/scripts/bump/__main__.py index c9d9da1..732b872 100644 --- a/scripts/bump/__main__.py +++ b/scripts/bump/__main__.py @@ -2,47 +2,47 @@ import keepachangelog -CHANGELOG_PATH = 'docs/changelog.md' +CHANGELOG_PATH = "docs/changelog.md" def current_version(): - changes = keepachangelog.to_dict(CHANGELOG_PATH) - return sorted(changes.keys())[-1] + changes = keepachangelog.to_dict(CHANGELOG_PATH) + return sorted(changes.keys())[-1] def main(): - repo = Repo('.') + repo = Repo(".") - old_version = current_version() + old_version = current_version() - # Release to get predicted version - keepachangelog.release(CHANGELOG_PATH) - predicted_version = current_version() + # Release to get predicted version + keepachangelog.release(CHANGELOG_PATH) + predicted_version = current_version() - # Confirm version - user_input = input('New version [{}]: '.format(predicted_version)) - new_version = user_input or predicted_version + # Confirm version + user_input = input("New version [{}]: ".format(predicted_version)) + new_version = user_input or predicted_version - # Undo temporary release - repo.git.restore(CHANGELOG_PATH) + # Undo temporary release + repo.git.restore(CHANGELOG_PATH) - # Release with confirmed version - keepachangelog.release(CHANGELOG_PATH, new_version) + # Release with confirmed version + keepachangelog.release(CHANGELOG_PATH, new_version) - # Update setup.py - with open('setup.py', 'r') as setuppy: - setuppy_code = setuppy.read() - new_setuppy_code = setuppy_code.replace(old_version, new_version) - with open('setup.py', 'w') as setuppy: - setuppy.write(new_setuppy_code) + # Update setup.py + with open("setup.py", "r") as setuppy: + setuppy_code = setuppy.read() + new_setuppy_code = setuppy_code.replace(old_version, new_version) + with open("setup.py", "w") as setuppy: + setuppy.write(new_setuppy_code) - # Commit to git - commit_message = f'chore(*): release version {new_version}\n\nBump version {old_version} → {new_version}' - repo.git.commit(CHANGELOG_PATH, 'setup.py', message=commit_message) - repo.git.tag(f'v{new_version}', annotate=True, message=f'version {new_version}') + # Commit to git + commit_message = f"chore(*): release version {new_version}\n\nBump version {old_version} → {new_version}" + repo.git.commit(CHANGELOG_PATH, "setup.py", message=commit_message) + repo.git.tag(f"v{new_version}", annotate=True, message=f"version {new_version}") - print(f'Bumped version {old_version} → {new_version}') + print(f"Bumped version {old_version} → {new_version}") -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index 6f051cf..2d5c630 100644 --- a/setup.py +++ b/setup.py @@ -3,40 +3,38 @@ script_dir = os.path.dirname(os.path.realpath(__file__)) -with open(os.path.join(script_dir, 'README.md'), 'r') as f: - README = f.read() +with open(os.path.join(script_dir, "README.md"), "r") as f: + README = f.read() classifiers = [ - 'Development Status :: 3 - Alpha', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Topic :: Games/Entertainment', - 'Topic :: Scientific/Engineering', - 'Topic :: Software Development :: Version Control :: Git', - 'Topic :: Utilities' + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Topic :: Games/Entertainment", + "Topic :: Scientific/Engineering", + "Topic :: Software Development :: Version Control :: Git", + "Topic :: Utilities", ] setup( - name='shulkr', - version='0.7.1', - description='Diff decompiled versions of Minecraft', - long_description=README, - long_description_content_type='text/markdown', - url='https://github.com/clabe45/shulkr', - author='Caleb Sacks', - license='GPLv3', - classifiers=classifiers, - keywords=['minecraft', 'git', 'decompile', 'game'], - package_dir={'': 'src'}, - packages=find_packages(where='src'), - py_modules=['colorama', 'java', 'minecraft', 'mint', 'shulkr'], - install_requires=['gitpython', 'javalang', 'unidiff', 'requests', 'toml', 'click'], - entry_points={ - 'console_scripts': ['shulkr=shulkr.__main__:main'] - } -) \ No newline at end of file + name="shulkr", + version="0.7.1", + description="Diff decompiled versions of Minecraft", + long_description=README, + long_description_content_type="text/markdown", + url="https://github.com/clabe45/shulkr", + author="Caleb Sacks", + license="GPLv3", + classifiers=classifiers, + keywords=["minecraft", "git", "decompile", "game"], + package_dir={"": "src"}, + packages=find_packages(where="src"), + py_modules=["colorama", "java", "minecraft", "mint", "shulkr"], + install_requires=["gitpython", "javalang", "unidiff", "requests", "toml", "click"], + entry_points={"console_scripts": ["shulkr=shulkr.__main__:main"]}, +) diff --git a/src/command/__init__.py b/src/command/__init__.py index 7b17b9f..6c8c2ae 100644 --- a/src/command/__init__.py +++ b/src/command/__init__.py @@ -5,127 +5,121 @@ class BaseCommandError(Exception): - pass + pass class CommandError(BaseCommandError): - def __init__(self, command: str, stderr: str, *args: object) -> None: - super().__init__(*args) + def __init__(self, command: str, stderr: str, *args: object) -> None: + super().__init__(*args) - self.command = command - self.stderr = stderr + self.command = command + self.stderr = stderr - def __str__(self) -> str: - return f'{self.command}:\n{self.stderr}' + def __str__(self) -> str: + return f"{self.command}:\n{self.stderr}" class CommandNotFoundError(BaseCommandError): - def __init__(self, command: str, *args: object) -> None: - super().__init__(*args) + def __init__(self, command: str, *args: object) -> None: + super().__init__(*args) - self.command = command + self.command = command - def __str__(self) -> str: - return f'Command not found: {self.command}' + def __str__(self) -> str: + return f"Command not found: {self.command}" class Command: - """ - Context for running git commands - - Sample usage: - git = GitCommand(PATH_TO_REPO) - if not git.status(porcelain=True): - git.commit(message='empty commit', allow_empty=True) - - Currently, options that require an '=' between the key and the value must be - supplied as positional arguments: - git.log('--format=%B') - """ - - def __init__( - self, - executabale: str, - working_dir: str = None, - capture_output: bool = True, - error=CommandError - ) -> None: - - if not shutil.which(executabale): - raise CommandNotFoundError(executabale) - - self._executable = executabale - - if working_dir is None: - working_dir = os.getcwd() - - self._working_dir = working_dir - self._capture_output = capture_output - - self._error = error - - def _run_command(self, command: List[str]) -> str: - try: - proc = subprocess.run( - command, - cwd=self._working_dir, - check=True, - capture_output=self._capture_output, - text=True - ) - - if self._capture_output: - return proc.stdout.strip() - - except subprocess.CalledProcessError as e: - # Convert the error to to the user-specified error type - raise self._error(command, e.stderr) from e - - def __getattr__(self, name: str): - """ - Return the specified git subcommand as a function - - Args: - name (str): Name of the subcommand - """ - - def func(*args, **kwargs): - subcommand = name.replace('_', '-') - - command = self._raw_command( - subcommand, - args, - kwargs - ) - return self._run_command(command) - - return func - - @staticmethod - def _format_option(key: str, value: Any) -> List[str]: - option = key.replace('_', '-') - prefix = '-' if len(option) == 1 else '--' - - if value is True: - return [f'{prefix}{option}'] - - elif value is False: - return [] - - else: - # Non-boolean value - return [f'{prefix}{option}', str(value)] - - def _raw_command( - self, - subcommand: str, - args: List[Any], - kwargs: Dict[str, Any] - ) -> List[str]: - - options = [ - token for key, value in kwargs.items() - for token in Command._format_option(key, value) - ] - args = [str(arg) for arg in args] - return [self._executable, subcommand, *options, *args] + """ + Context for running git commands + + Sample usage: + git = GitCommand(PATH_TO_REPO) + if not git.status(porcelain=True): + git.commit(message='empty commit', allow_empty=True) + + Currently, options that require an '=' between the key and the value must be + supplied as positional arguments: + git.log('--format=%B') + """ + + def __init__( + self, + executabale: str, + working_dir: str = None, + capture_output: bool = True, + error=CommandError, + ) -> None: + + if not shutil.which(executabale): + raise CommandNotFoundError(executabale) + + self._executable = executabale + + if working_dir is None: + working_dir = os.getcwd() + + self._working_dir = working_dir + self._capture_output = capture_output + + self._error = error + + def _run_command(self, command: List[str]) -> str: + try: + proc = subprocess.run( + command, + cwd=self._working_dir, + check=True, + capture_output=self._capture_output, + text=True, + ) + + if self._capture_output: + return proc.stdout.strip() + + except subprocess.CalledProcessError as e: + # Convert the error to to the user-specified error type + raise self._error(command, e.stderr) from e + + def __getattr__(self, name: str): + """ + Return the specified git subcommand as a function + + Args: + name (str): Name of the subcommand + """ + + def func(*args, **kwargs): + subcommand = name.replace("_", "-") + + command = self._raw_command(subcommand, args, kwargs) + return self._run_command(command) + + return func + + @staticmethod + def _format_option(key: str, value: Any) -> List[str]: + option = key.replace("_", "-") + prefix = "-" if len(option) == 1 else "--" + + if value is True: + return [f"{prefix}{option}"] + + elif value is False: + return [] + + else: + # Non-boolean value + return [f"{prefix}{option}", str(value)] + + def _raw_command( + self, subcommand: str, args: List[Any], kwargs: Dict[str, Any] + ) -> List[str]: + + options = [ + token + for key, value in kwargs.items() + for token in Command._format_option(key, value) + ] + args = [str(arg) for arg in args] + return [self._executable, subcommand, *options, *args] diff --git a/src/gradle/command.py b/src/gradle/command.py index e8691e6..9eeb854 100644 --- a/src/gradle/command.py +++ b/src/gradle/command.py @@ -4,41 +4,37 @@ class Gradle(Command): - # `capture_output` is set to False by default because gradle commands can - # be very verbose and can cause the command to hang on windows - def __init__(self, project_dir: str, capture_output=False) -> None: - exec = Gradle._executable(project_dir) - super().__init__( - exec, - working_dir=project_dir, - capture_output=capture_output - ) - - def __getattr__(self, name: str): - super_func = super().__getattr__(name) - - def func(*args, **kwargs): - return super_func( - name, - *args, - # `--quiet` prevents the commands from being run interactively - quiet=True, - **kwargs - ) - - return func - - @staticmethod - def _executable(project_dir: str) -> bool: - """ - Get the path to gradlew or gradle (if gradlew is not found) - """ - - gradlew_exec = os.path.join( - os.path.abspath(project_dir), - 'gradlew.bat' if os.name == 'nt' else 'gradlew' - ) - if os.path.exists(gradlew_exec): - return gradlew_exec - - return 'gradle' + # `capture_output` is set to False by default because gradle commands can + # be very verbose and can cause the command to hang on windows + def __init__(self, project_dir: str, capture_output=False) -> None: + exec = Gradle._executable(project_dir) + super().__init__(exec, working_dir=project_dir, capture_output=capture_output) + + def __getattr__(self, name: str): + super_func = super().__getattr__(name) + + def func(*args, **kwargs): + return super_func( + name, + *args, + # `--quiet` prevents the commands from being run interactively + quiet=True, + **kwargs + ) + + return func + + @staticmethod + def _executable(project_dir: str) -> bool: + """ + Get the path to gradlew or gradle (if gradlew is not found) + """ + + gradlew_exec = os.path.join( + os.path.abspath(project_dir), + "gradlew.bat" if os.name == "nt" else "gradlew", + ) + if os.path.exists(gradlew_exec): + return gradlew_exec + + return "gradle" diff --git a/src/gradle/error.py b/src/gradle/error.py index fa99de8..83873fc 100644 --- a/src/gradle/error.py +++ b/src/gradle/error.py @@ -2,4 +2,4 @@ class GradleError(CommandError): - pass + pass diff --git a/src/gradle/project.py b/src/gradle/project.py index 94b1895..04c3636 100644 --- a/src/gradle/project.py +++ b/src/gradle/project.py @@ -2,16 +2,16 @@ class Project: - def __init__(self, project_dir: str, capture_output=False) -> None: - self.project_dir = project_dir - self.gradle = Gradle(self.project_dir, capture_output=capture_output) + def __init__(self, project_dir: str, capture_output=False) -> None: + self.project_dir = project_dir + self.gradle = Gradle(self.project_dir, capture_output=capture_output) - @staticmethod - def init(project_dir: str, capture_output=False) -> 'Project': - """ - Create a new gradle project - """ + @staticmethod + def init(project_dir: str, capture_output=False) -> "Project": + """ + Create a new gradle project + """ - project = Project(project_dir, capture_output=capture_output) - project.gradle.init() - return project + project = Project(project_dir, capture_output=capture_output) + project.gradle.init() + return project diff --git a/src/java/__init__.py b/src/java/__init__.py index 3ed2ffc..0d2ce17 100644 --- a/src/java/__init__.py +++ b/src/java/__init__.py @@ -11,412 +11,405 @@ class JavaAnalyzationError(Exception): - pass + pass def _ast_nodes_equal( - node1: Node, - node2: Node, - recursive: bool, - matches: List[Tuple[any, any]], - _first_level=True + node1: Node, + node2: Node, + recursive: bool, + matches: List[Tuple[any, any]], + _first_level=True, ) -> bool: - # Non-recursive comparison; we're done now that the first node has been - # processed - if not recursive and not _first_level: - return True + # Non-recursive comparison; we're done now that the first node has been + # processed + if not recursive and not _first_level: + return True - if type(node1) is not type(node2): - return False + if type(node1) is not type(node2): + return False - if type(node1) is list: - if len(node1) != len(node2): - return False + if type(node1) is list: + if len(node1) != len(node2): + return False - for i in range(len(node1)): - if not _ast_nodes_equal(node1[i], node2[i], recursive, matches, False): - return False + for i in range(len(node1)): + if not _ast_nodes_equal(node1[i], node2[i], recursive, matches, False): + return False - return True + return True - elif isinstance(node1, Node): - ignored_attrs = [ - attrs for a, b, attrs in matches if node1 in (a, b) or node2 in (a, b) - ] - for attr in node1.attrs: - if attr in ignored_attrs: - continue + elif isinstance(node1, Node): + ignored_attrs = [ + attrs for a, b, attrs in matches if node1 in (a, b) or node2 in (a, b) + ] + for attr in node1.attrs: + if attr in ignored_attrs: + continue - attr1 = getattr(node1, attr) - attr2 = getattr(node2, attr) + attr1 = getattr(node1, attr) + attr2 = getattr(node2, attr) - if not _ast_nodes_equal(attr1, attr2, recursive, matches, False): - return False + if not _ast_nodes_equal(attr1, attr2, recursive, matches, False): + return False - return True + return True - else: - # if node1 != node2: - # print('XD', node1, node2, '\n') - return node1 == node2 + else: + # if node1 != node2: + # print('XD', node1, node2, '\n') + return node1 == node2 def ast_nodes_equal( - node1: Node, - node2: Node, - recursive=True, - matches: List[Tuple[any, any]] = tuple() + node1: Node, node2: Node, recursive=True, matches: List[Tuple[any, any]] = tuple() ) -> bool: - return _ast_nodes_equal(node1, node2, recursive, matches) + return _ast_nodes_equal(node1, node2, recursive, matches) def ast_paths_equal( - path1: List[Union[Node, List[Node]]], - path2: List[Union[Node, List[Node]]], - matches: List[Tuple[any, any]] = tuple() + path1: List[Union[Node, List[Node]]], + path2: List[Union[Node, List[Node]]], + matches: List[Tuple[any, any]] = tuple(), ) -> bool: - """ - Determine if two AST node paths are equal, ignoring other parts of the file + """ + Determine if two AST node paths are equal, ignoring other parts of the file - Args: - path1 (List[Union[Node, List[Node]]]): [description] - path2 (List[Union[Node, List[Node]]]): [description] - matches (List[Tuple[any, any]], optional): List of node pairs to - consider equal. Defaults to tuple(). + Args: + path1 (List[Union[Node, List[Node]]]): [description] + path2 (List[Union[Node, List[Node]]]): [description] + matches (List[Tuple[any, any]], optional): List of node pairs to + consider equal. Defaults to tuple(). - Returns: - bool: [description] - """ + Returns: + bool: [description] + """ - if len(path1) != len(path2): - return False + if len(path1) != len(path2): + return False - # Skip list entries in the path (because they contain irrelevant parts) - for i in range(len(path1)): - if not ast_nodes_equal(path1[i], path2[i], recursive=False, matches=matches): - return False + # Skip list entries in the path (because they contain irrelevant parts) + for i in range(len(path1)): + if not ast_nodes_equal(path1[i], path2[i], recursive=False, matches=matches): + return False - return True + return True def _filter_ast_node(node: Node, t: type, _path=[]) -> List[Union[Node, str]]: - if node is None: - return + if node is None: + return - elif type(node) is list: - for item in node: - for path, child in _filter_ast_node(item, t, _path=_path): - yield path, child + elif type(node) is list: + for item in node: + for path, child in _filter_ast_node(item, t, _path=_path): + yield path, child - elif isinstance(node, Node): - if isinstance(node, t): - yield _path, node + elif isinstance(node, Node): + if isinstance(node, t): + yield _path, node - for attr in node.attrs: - yield from _filter_ast_node( - getattr(node, attr), - t, - _path=_path + [node, attr] - ) + for attr in node.attrs: + yield from _filter_ast_node( + getattr(node, attr), t, _path=_path + [node, attr] + ) def filter_ast_node(node: Node, t: type) -> List[Union[Node, str]]: - return _filter_ast_node(node, t) + return _filter_ast_node(node, t) def chunk_ast_nodes_by_path( - nodes: List[Tuple[List, Node]] + nodes: List[Tuple[List, Node]] ) -> List[Tuple[List, List[Node]]]: - chunks = [] - curr_path = None - curr_chunk = None - for path, node in nodes: - if path != curr_path: - if curr_path is not None: - chunks.append((curr_path, curr_chunk)) + chunks = [] + curr_path = None + curr_chunk = None + for path, node in nodes: + if path != curr_path: + if curr_path is not None: + chunks.append((curr_path, curr_chunk)) - curr_path = path - curr_chunk = [] + curr_path = path + curr_chunk = [] - curr_chunk.append(node) + curr_chunk.append(node) - if curr_path is not None: - chunks.append((curr_path, curr_chunk)) + if curr_path is not None: + chunks.append((curr_path, curr_chunk)) - return chunks + return chunks def _have_same_references( - a_var: str, - b_var: str, - a_refs: List[Tuple], - b_refs: List[Tuple], - matches: List[Tuple] + a_var: str, + b_var: str, + a_refs: List[Tuple], + b_refs: List[Tuple], + matches: List[Tuple], ) -> bool: - ar = [ - (path, ref) - for path, refs in a_refs - for ref in refs - if ref.member == a_var - ] - - br = [ - (path, ref) - for path, refs in b_refs - for ref in refs - if ref.member == b_var - ] - - if len(ar) != len(br): - return False - - for i in range(len(ar)): - a_ref_path, a_ref_node = ar[i] - b_ref_path, b_ref_node = br[i] - ref_match = (a_ref_node, b_ref_node, 'name') - - if not ast_paths_equal( - a_ref_path, - b_ref_path, - matches=matches + [ref_match] - ): - return False - - return True - - -def get_renamed_variables( - source_code: str, - target_code: str -) -> Dict[str, str]: - - try: - source_tree = javalang.parse.parse(source_code) - target_tree = javalang.parse.parse(target_code) - except javalang.parser.JavaSyntaxError: - return None - - source_declarations = chunk_ast_nodes_by_path( - filter_ast_node(source_tree, VariableDeclaration) - ) - - target_declarations = chunk_ast_nodes_by_path( - filter_ast_node(target_tree, VariableDeclaration) - ) - - target_var_names = [ - declarator.name - for path, declarations in target_declarations - for declaration in declarations - for declarator in declaration.declarators - ] - - source_references = chunk_ast_nodes_by_path( - filter_ast_node(source_tree, MemberReference) - ) - - target_references = chunk_ast_nodes_by_path( - filter_ast_node(target_tree, MemberReference) - ) - - renamed_var_names = [] - matches = [] - - # Find all the semantically equalivalent variables that have different - # names. For the variable declarations a and b to be semantically - # equalivalent: - # - # - They must have be in the same semantic section of the file - # - They must have the same references and assignments - - # Iterate over every pair of chunks, including the pair of the same chunk. - for i in range(len(source_declarations)): - source_decn_path, source_decn_nodes = source_declarations[i] - for j in range(i, len(target_declarations)): - target_decn_path, target_decn_nodes = target_declarations[j] - - # 1. Make sure paths would be equal if source_node (a) and - # target_node (b) were the same. - - # 1a. Must be in the same part of the file - # First get all previously discovered renamed variables - prev_matches_here = [ - (old, new, attrs) - for path, v in matches - for old, new, attrs in v - if path in (source_decn_path, target_decn_path) - ] - - # Now make sure the paths are equal (ignoring the previously found - # renamed variables and the declarations themselves) - if not ast_paths_equal( - source_decn_path, - target_decn_path, - matches=prev_matches_here - ): - continue - - for k in range(len(source_decn_nodes)): - source_decn_node = source_decn_nodes[k] - for m in range(len(source_decn_node.declarators)): - source_declarator = source_decn_node.declarators[m] - # 1b. Must have different names - if source_declarator.name in target_var_names: - continue - - sr = [ - (path, ref) - for path, refs in source_references - for ref in refs - if ref.member == source_declarator.name - ] - - for n in range(k, len(target_decn_nodes)): - target_decn_node = target_decn_nodes[n] - for q in range(len(target_decn_node.declarators)): - target_declarator = target_decn_node.declarators[q] - # 1c. All attributes except the name should be equal - declarator_match = (source_declarator, target_declarator, 'name') - if not ast_nodes_equal( - source_declarator, - target_declarator, - recursive=True, - matches=prev_matches_here + [declarator_match] - ): - continue - - # 1d. Must have the same references - if not _have_same_references( - source_declarator.name, - target_declarator.name, - source_references, - target_references, - prev_matches_here - ): - continue - - tr = [ - (path, ref) - for path, refs in target_references - for ref in refs - if ref.member == target_declarator.name - ] - - # (sr and tr have the same length, so we can do this safely) - for i in range(len(sr)): - source_ref_path, source_ref_node = sr[i] - target_ref_path, target_ref_node = tr[i] - ref_match = (source_ref_node, target_ref_node, 'name') - - for path, mappings in matches: - if path == target_ref_path: - mappings.append(ref_match) - - else: - matches.append((target_ref_path, [ref_match])) - - # 1d. (The function cannot return multiple mappings - # containing the same variable at the same path) - other_renames_at_path = False - never_used = True - for path, mappings in renamed_var_names: - if path != target_decn_path: - continue - - for old, new in mappings: - if old == source_declarator.name or new == target_declarator.name: - never_used = False - break - - if not never_used: - break - - mappings.append((source_declarator.name, target_declarator.name)) - other_renames_at_path = True - break - - if not never_used: - continue - - if not other_renames_at_path: - mappings = [(source_declarator.name, target_declarator.name)] - renamed_var_names.append((target_decn_path, mappings)) - - for path, v in matches: - if path == target_decn_path: - for old, new, _ in v: - if old == source_declarator: - raise JavaAnalyzationError(f'duplicate declarator in source: {old}') - - if new == target_declarator: - raise JavaAnalyzationError(f'duplicate declarator in target: {new}') - - v.append(declarator_match) - - else: - matches.append((target_decn_path, [declarator_match])) - - return renamed_var_names + ar = [(path, ref) for path, refs in a_refs for ref in refs if ref.member == a_var] + + br = [(path, ref) for path, refs in b_refs for ref in refs if ref.member == b_var] + + if len(ar) != len(br): + return False + + for i in range(len(ar)): + a_ref_path, a_ref_node = ar[i] + b_ref_path, b_ref_node = br[i] + ref_match = (a_ref_node, b_ref_node, "name") + + if not ast_paths_equal(a_ref_path, b_ref_path, matches=matches + [ref_match]): + return False + + return True + + +def get_renamed_variables(source_code: str, target_code: str) -> Dict[str, str]: + + try: + source_tree = javalang.parse.parse(source_code) + target_tree = javalang.parse.parse(target_code) + except javalang.parser.JavaSyntaxError: + return None + + source_declarations = chunk_ast_nodes_by_path( + filter_ast_node(source_tree, VariableDeclaration) + ) + + target_declarations = chunk_ast_nodes_by_path( + filter_ast_node(target_tree, VariableDeclaration) + ) + + target_var_names = [ + declarator.name + for path, declarations in target_declarations + for declaration in declarations + for declarator in declaration.declarators + ] + + source_references = chunk_ast_nodes_by_path( + filter_ast_node(source_tree, MemberReference) + ) + + target_references = chunk_ast_nodes_by_path( + filter_ast_node(target_tree, MemberReference) + ) + + renamed_var_names = [] + matches = [] + + # Find all the semantically equalivalent variables that have different + # names. For the variable declarations a and b to be semantically + # equalivalent: + # + # - They must have be in the same semantic section of the file + # - They must have the same references and assignments + + # Iterate over every pair of chunks, including the pair of the same chunk. + for i in range(len(source_declarations)): + source_decn_path, source_decn_nodes = source_declarations[i] + for j in range(i, len(target_declarations)): + target_decn_path, target_decn_nodes = target_declarations[j] + + # 1. Make sure paths would be equal if source_node (a) and + # target_node (b) were the same. + + # 1a. Must be in the same part of the file + # First get all previously discovered renamed variables + prev_matches_here = [ + (old, new, attrs) + for path, v in matches + for old, new, attrs in v + if path in (source_decn_path, target_decn_path) + ] + + # Now make sure the paths are equal (ignoring the previously found + # renamed variables and the declarations themselves) + if not ast_paths_equal( + source_decn_path, target_decn_path, matches=prev_matches_here + ): + continue + + for k in range(len(source_decn_nodes)): + source_decn_node = source_decn_nodes[k] + for m in range(len(source_decn_node.declarators)): + source_declarator = source_decn_node.declarators[m] + # 1b. Must have different names + if source_declarator.name in target_var_names: + continue + + sr = [ + (path, ref) + for path, refs in source_references + for ref in refs + if ref.member == source_declarator.name + ] + + for n in range(k, len(target_decn_nodes)): + target_decn_node = target_decn_nodes[n] + for q in range(len(target_decn_node.declarators)): + target_declarator = target_decn_node.declarators[q] + # 1c. All attributes except the name should be equal + declarator_match = ( + source_declarator, + target_declarator, + "name", + ) + if not ast_nodes_equal( + source_declarator, + target_declarator, + recursive=True, + matches=prev_matches_here + [declarator_match], + ): + continue + + # 1d. Must have the same references + if not _have_same_references( + source_declarator.name, + target_declarator.name, + source_references, + target_references, + prev_matches_here, + ): + continue + + tr = [ + (path, ref) + for path, refs in target_references + for ref in refs + if ref.member == target_declarator.name + ] + + # (sr and tr have the same length, so we can do this safely) + for i in range(len(sr)): + source_ref_path, source_ref_node = sr[i] + target_ref_path, target_ref_node = tr[i] + ref_match = (source_ref_node, target_ref_node, "name") + + for path, mappings in matches: + if path == target_ref_path: + mappings.append(ref_match) + + else: + matches.append((target_ref_path, [ref_match])) + + # 1d. (The function cannot return multiple mappings + # containing the same variable at the same path) + other_renames_at_path = False + never_used = True + for path, mappings in renamed_var_names: + if path != target_decn_path: + continue + + for old, new in mappings: + if ( + old == source_declarator.name + or new == target_declarator.name + ): + never_used = False + break + + if not never_used: + break + + mappings.append( + (source_declarator.name, target_declarator.name) + ) + other_renames_at_path = True + break + + if not never_used: + continue + + if not other_renames_at_path: + mappings = [ + (source_declarator.name, target_declarator.name) + ] + renamed_var_names.append((target_decn_path, mappings)) + + for path, v in matches: + if path == target_decn_path: + for old, new, _ in v: + if old == source_declarator: + raise JavaAnalyzationError( + f"duplicate declarator in source: {old}" + ) + + if new == target_declarator: + raise JavaAnalyzationError( + f"duplicate declarator in target: {new}" + ) + + v.append(declarator_match) + + else: + matches.append((target_decn_path, [declarator_match])) + + return renamed_var_names def undo_variable_renames(code: str, renamed_var_names: List[Tuple]) -> str: - lines = code.split('\n') - tokens = tokenize(code) + lines = code.split("\n") + tokens = tokenize(code) - for token in tokens: - if not isinstance(token, Identifier): - continue + for token in tokens: + if not isinstance(token, Identifier): + continue - for path, mappings in renamed_var_names: - for old, new in mappings: - if token.value == new: - line, col = token.position - line -= 1 - col -= 1 - lines[line] = lines[line][:col] + old + lines[line][col + len(new):] + for path, mappings in renamed_var_names: + for old, new in mappings: + if token.value == new: + line, col = token.position + line -= 1 + col -= 1 + lines[line] = ( + lines[line][:col] + old + lines[line][col + len(new) :] + ) - return '\n'.join(lines) + return "\n".join(lines) def undo_renames(repo: git.Repo) -> None: - # Since mint does not parse commits into objects, the repo needs to be a # - # gitpython repo + # Since mint does not parse commits into objects, the repo needs to be a # + # gitpython repo - commit1 = repo.commit('HEAD') - commit2 = None # working tree - diff_index = commit1.diff(commit2) + commit1 = repo.commit("HEAD") + commit2 = None # working tree + diff_index = commit1.diff(commit2) - updated_count = 0 + updated_count = 0 - # For each file changed (in reverse, with indices, so we can remove elements - # in the for loop) - for diff in diff_index: - # Only process modified files (no new, deleted, ... files) - if diff.change_type != 'M': - continue + # For each file changed (in reverse, with indices, so we can remove elements + # in the for loop) + for diff in diff_index: + # Only process modified files (no new, deleted, ... files) + if diff.change_type != "M": + continue - # Only process Java files; leave everything else unchanged - if not diff.a_path.endswith('.java'): - continue + # Only process Java files; leave everything else unchanged + if not diff.a_path.endswith(".java"): + continue - source = get_blob(repo, commit1, diff.a_path) - target = get_blob(repo, commit2, diff.b_path) + source = get_blob(repo, commit1, diff.a_path) + target = get_blob(repo, commit2, diff.b_path) - try: - renamed_variables = get_renamed_variables(source, target) - except JavaAnalyzationError as e: - raise Exception(f'{e} [{diff.a_path} -> {diff.b_path}]') + try: + renamed_variables = get_renamed_variables(source, target) + except JavaAnalyzationError as e: + raise Exception(f"{e} [{diff.a_path} -> {diff.b_path}]") - if renamed_variables is not None: - updated_target = undo_variable_renames(target, renamed_variables) - p = os.path.join(repo.working_tree_dir, diff.a_path) - with open(p, 'w') as f: - f.write(updated_target) + if renamed_variables is not None: + updated_target = undo_variable_renames(target, renamed_variables) + p = os.path.join(repo.working_tree_dir, diff.a_path) + with open(p, "w") as f: + f.write(updated_target) - updated_count += 1 + updated_count += 1 - print(f'+ Updated {updated_count} files') + print(f"+ Updated {updated_count} files") diff --git a/src/java/blob.py b/src/java/blob.py index 33f8225..0ef62ee 100644 --- a/src/java/blob.py +++ b/src/java/blob.py @@ -5,16 +5,16 @@ def get_blob(repo: git.Repo, commit: Optional[git.Commit], path: str) -> bytes: - if commit is None: - p = os.path.join(repo.working_tree_dir, path) - with open(p, 'r') as f: - return f.read() + if commit is None: + p = os.path.join(repo.working_tree_dir, path) + with open(p, "r") as f: + return f.read() - parts = path.split('/') - curr = commit.tree + parts = path.split("/") + curr = commit.tree - while len(parts) > 0: - name = parts.pop(0) - curr = curr[name] + while len(parts) > 0: + name = parts.pop(0) + curr = curr[name] - return curr.data_stream.read().decode() + return curr.data_stream.read().decode() diff --git a/src/minecraft/source.py b/src/minecraft/source.py index 8a47ffb..dbcf80b 100644 --- a/src/minecraft/source.py +++ b/src/minecraft/source.py @@ -10,129 +10,119 @@ from minecraft.version import Version -DECOMPILER_MC_REMOTE_URL = 'https://github.com/hube12/DecompilerMC.git' -YARN_REMOTE_URL = 'https://github.com/FabricMC/yarn.git' +DECOMPILER_MC_REMOTE_URL = "https://github.com/hube12/DecompilerMC.git" +YARN_REMOTE_URL = "https://github.com/FabricMC/yarn.git" def _setup_decompiler(local_dir: str, remote_url: str) -> Repo: - if os.path.exists( - os.path.join(local_dir, '.git') - ): - # Used cached yarn repo - return Repo(local_dir) - else: - # Clone the yarn repo - click.echo(f'Cloning {remote_url} into {local_dir}') - return Repo.clone(remote_url, local_dir) + if os.path.exists(os.path.join(local_dir, ".git")): + # Used cached yarn repo + return Repo(local_dir) + else: + # Clone the yarn repo + click.echo(f"Cloning {remote_url} into {local_dir}") + return Repo.clone(remote_url, local_dir) def _generate_sources_with_yarn(version: Version, path: str) -> None: - # Remove decompiler generated by previous versions of shulkr - old_decompiler_path = os.path.join(path, '.yarn') - if os.path.exists(old_decompiler_path): - shutil.rmtree(old_decompiler_path) + # Remove decompiler generated by previous versions of shulkr + old_decompiler_path = os.path.join(path, ".yarn") + if os.path.exists(old_decompiler_path): + shutil.rmtree(old_decompiler_path) - decompiler_path = os.path.join(path, 'yarn') - decompiler_repo = _setup_decompiler(decompiler_path, YARN_REMOTE_URL) + decompiler_path = os.path.join(path, "yarn") + decompiler_repo = _setup_decompiler(decompiler_path, YARN_REMOTE_URL) - click.echo(f'Updating mappings to version {version}') + click.echo(f"Updating mappings to version {version}") - # Get latest versions from remote - decompiler_repo.git.fetch(prune=True) + # Get latest versions from remote + decompiler_repo.git.fetch(prune=True) - decompiler_repo.git.reset('HEAD', hard=True) - decompiler_repo.git.clean(force=True, d=True) + decompiler_repo.git.reset("HEAD", hard=True) + decompiler_repo.git.clean(force=True, d=True) - # Checkout version branch - decompiler_repo.git.checkout(f'origin/{version}') + # Checkout version branch + decompiler_repo.git.checkout(f"origin/{version}") - click.echo('Running decompiler') + click.echo("Running decompiler") - # Generate source code - # Create gradle project - decompiler_project = Project(decompiler_repo.path) + # Generate source code + # Create gradle project + decompiler_project = Project(decompiler_repo.path) - # Call gradle task to generate sources - decompiler_project.gradle.decompileCFR() + # Call gradle task to generate sources + decompiler_project.gradle.decompileCFR() - click.echo('Moving generated sources') + click.echo("Moving generated sources") - src_path = os.path.join(path, 'src') + src_path = os.path.join(path, "src") - # Remove existing top-level destination directory - if os.path.exists(src_path): - shutil.rmtree(src_path) + # Remove existing top-level destination directory + if os.path.exists(src_path): + shutil.rmtree(src_path) - # Move the generated source code to $repo_dir/src - shutil.move( - os.path.join(decompiler_repo.path, 'src' if version >= Version.of('1.20.5') else 'namedSrc'), - src_path - ) + # Move the generated source code to $repo_dir/src + shutil.move( + os.path.join( + decompiler_repo.path, + "src" if version >= Version.of("1.20.5") else "namedSrc", + ), + src_path, + ) def _generate_sources_with_mojang(version: Version, path: str) -> None: - """ - Decompiles a version of the Minecraft client with DecompilerMC (which uses - Mojang's official mappings) - - Args: - version (Version): - - Raises: - Exception: If DecompilerMC fails - """ - - # Remove decompiler generated by previous versions of shulkr - old_decompiler_path = os.path.join(path, '.DecompilerMC') - if os.path.exists(old_decompiler_path): - shutil.rmtree(old_decompiler_path) - - decompiler_path = os.path.join(path, 'DecompilerMC') - decompiler_repo = _setup_decompiler(decompiler_path, DECOMPILER_MC_REMOTE_URL) - - click.echo('Running decompiler') - - # Decompile client - p = subprocess.run( - [ - 'python3', - 'main.py', - '--mcv', - str(version), - '-s', - 'client', - '-c', - '-f', - '-q' - ], - stderr=subprocess.PIPE, - cwd=decompiler_repo.path - ) - if p.returncode != 0: - raise Exception(p.stderr.decode()) - - click.echo('Moving generated sources') - - # Sources directory - dest_src_dir = os.path.join(path, 'src') - - # Remove existing top-level destination directory - if os.path.exists(dest_src_dir): - shutil.rmtree(dest_src_dir) - - # Move the generated source code to $dest_dir/src - shutil.move( - os.path.join(decompiler_repo.path, 'src', str(version), 'client'), - dest_src_dir - ) + """ + Decompiles a version of the Minecraft client with DecompilerMC (which uses + Mojang's official mappings) + + Args: + version (Version): + + Raises: + Exception: If DecompilerMC fails + """ + + # Remove decompiler generated by previous versions of shulkr + old_decompiler_path = os.path.join(path, ".DecompilerMC") + if os.path.exists(old_decompiler_path): + shutil.rmtree(old_decompiler_path) + + decompiler_path = os.path.join(path, "DecompilerMC") + decompiler_repo = _setup_decompiler(decompiler_path, DECOMPILER_MC_REMOTE_URL) + + click.echo("Running decompiler") + + # Decompile client + p = subprocess.run( + ["python3", "main.py", "--mcv", str(version), "-s", "client", "-c", "-f", "-q"], + stderr=subprocess.PIPE, + cwd=decompiler_repo.path, + ) + if p.returncode != 0: + raise Exception(p.stderr.decode()) + + click.echo("Moving generated sources") + + # Sources directory + dest_src_dir = os.path.join(path, "src") + + # Remove existing top-level destination directory + if os.path.exists(dest_src_dir): + shutil.rmtree(dest_src_dir) + + # Move the generated source code to $dest_dir/src + shutil.move( + os.path.join(decompiler_repo.path, "src", str(version), "client"), dest_src_dir + ) def generate_sources(version: Version, mappings: str, path: str) -> None: - if mappings == 'mojang': - _generate_sources_with_mojang(version, path) + if mappings == "mojang": + _generate_sources_with_mojang(version, path) - elif mappings == 'yarn': - _generate_sources_with_yarn(version, path) + elif mappings == "yarn": + _generate_sources_with_yarn(version, path) - else: - raise ValueError(f"Invalid mapping type '{mappings}'") + else: + raise ValueError(f"Invalid mapping type '{mappings}'") diff --git a/src/minecraft/version.py b/src/minecraft/version.py index 2ea392b..6f6a056 100644 --- a/src/minecraft/version.py +++ b/src/minecraft/version.py @@ -3,279 +3,270 @@ import requests from typing import Dict, List, Optional -MANIFEST_LOCATION = "https://launchermeta.mojang.com/mc/game/version_manifest.json" # noqa: E501 -EARLIEST_SUPPORTED_VERSION_ID = '19w36a' +MANIFEST_LOCATION = ( + "https://launchermeta.mojang.com/mc/game/version_manifest.json" # noqa: E501 +) +EARLIEST_SUPPORTED_VERSION_ID = "19w36a" class VersionError(Exception): - pass + pass class NoSuchVersionError(VersionError): - pass + pass class Version: - def __init__( - self, - id: str, - index: int, - next: Optional[Version] = None - ) -> None: - - self.id = id - self._index = index - self.next = next - - def __str__(self) -> str: - return self.id - - def __lt__(self, other) -> bool: - if not isinstance(other, Version): - return False - - return self._index < other._index - - def __gt__(self, other) -> bool: - if not isinstance(other, Version): - return False - - return self._index > other._index - - def __le__(self, other) -> bool: - return self < other or self == other - - def __ge__(self, other) -> bool: - return self > other or self == other - - def to(self, other: Optional[Version], snapshots=True) -> List[Version]: - """Iterate over all the versions from this version (inclusive) to - `other` (exclusive) - - Args: - other (Optional[Version]): Stop before reaching this version - include_snapshots (bool, optional): Defaults to True. - - Returns: - List[Version]: All the versions since this version, before `other` - """ - - if other < self: - dots = '...' if snapshots else '..' - raise ValueError( - 'Version B cannot have been released sooner than version A in' - + f' A{dots}B syntax' - ) - - r = [] - curr = self - while curr is not None and (other is None or curr != other.next): - if snapshots or isinstance(curr, Release): - r.append(curr) - - curr = curr.next - - return r - - @staticmethod - def parse(raw: Dict, index: int) -> Version: - id = raw['id'] - type_ = raw['type'] - - if type_ == 'snapshot': - return Snapshot(id, index) - elif type_ == 'release': - return Release(id, index) - elif type_ == 'old_alpha': - return OldAlphaVersion(id, index) - elif type_ == 'old_beta': - return OldBetaVersion(id, index) - else: - raise Exception(f'Unknown version type: {type_}') - - @staticmethod - def of(id: Optional[str]) -> Version: - if id is None: - return manifest.latest_snapshot - - if id not in manifest.version_for_id: - raise NoSuchVersionError(f"Unsupported Minecraft version '{id}'") - - return manifest.version_for_id[id] - - @staticmethod - def pattern(p: str, latest_in_repo: Version = None) -> List[Version]: - # First, check if the pattern is a range - m = re.match(r'(.*[^.])?(\.\.\.?)(.*)', p) - if m is not None: - a_id, dots, b_id = m.groups() - snapshots = dots == '...' - - if a_id: - a = Version.of(a_id) - else: - # Get the next version after the latest committed one - if latest_in_repo: - a = latest_in_repo.next - if not snapshots: - # Find the next release - while a is not None and not isinstance(a, Release): - a = a.next - else: - raise ValueError('No commits from which to derive current version') - - if a is None: - # No more versions or releases to generate - return [] - - if b_id: - b = Version.of(b_id) - else: - # Get all the commits since `a` - b = None - - return a.to(b, snapshots) - - else: - # Then, try to treat it as a version - return [Version.of(p)] - - @staticmethod - def patterns( - patterns: List[str], - latest_in_repo: Version = None - ) -> List[Version]: - - # Parse versions - versions = set() - for pattern in patterns: - if pattern.startswith('-'): - versions -= set(Version.pattern(pattern[1:], latest_in_repo)) - else: - versions |= set(Version.pattern(pattern, latest_in_repo)) - - # Sort - versions = list(versions) - versions.sort() - - return versions + def __init__(self, id: str, index: int, next: Optional[Version] = None) -> None: + + self.id = id + self._index = index + self.next = next + + def __str__(self) -> str: + return self.id + + def __lt__(self, other) -> bool: + if not isinstance(other, Version): + return False + + return self._index < other._index + + def __gt__(self, other) -> bool: + if not isinstance(other, Version): + return False + + return self._index > other._index + + def __le__(self, other) -> bool: + return self < other or self == other + + def __ge__(self, other) -> bool: + return self > other or self == other + + def to(self, other: Optional[Version], snapshots=True) -> List[Version]: + """Iterate over all the versions from this version (inclusive) to + `other` (exclusive) + + Args: + other (Optional[Version]): Stop before reaching this version + include_snapshots (bool, optional): Defaults to True. + + Returns: + List[Version]: All the versions since this version, before `other` + """ + + if other < self: + dots = "..." if snapshots else ".." + raise ValueError( + "Version B cannot have been released sooner than version A in" + + f" A{dots}B syntax" + ) + + r = [] + curr = self + while curr is not None and (other is None or curr != other.next): + if snapshots or isinstance(curr, Release): + r.append(curr) + + curr = curr.next + + return r + + @staticmethod + def parse(raw: Dict, index: int) -> Version: + id = raw["id"] + type_ = raw["type"] + + if type_ == "snapshot": + return Snapshot(id, index) + elif type_ == "release": + return Release(id, index) + elif type_ == "old_alpha": + return OldAlphaVersion(id, index) + elif type_ == "old_beta": + return OldBetaVersion(id, index) + else: + raise Exception(f"Unknown version type: {type_}") + + @staticmethod + def of(id: Optional[str]) -> Version: + if id is None: + return manifest.latest_snapshot + + if id not in manifest.version_for_id: + raise NoSuchVersionError(f"Unsupported Minecraft version '{id}'") + + return manifest.version_for_id[id] + + @staticmethod + def pattern(p: str, latest_in_repo: Version = None) -> List[Version]: + # First, check if the pattern is a range + m = re.match(r"(.*[^.])?(\.\.\.?)(.*)", p) + if m is not None: + a_id, dots, b_id = m.groups() + snapshots = dots == "..." + + if a_id: + a = Version.of(a_id) + else: + # Get the next version after the latest committed one + if latest_in_repo: + a = latest_in_repo.next + if not snapshots: + # Find the next release + while a is not None and not isinstance(a, Release): + a = a.next + else: + raise ValueError("No commits from which to derive current version") + + if a is None: + # No more versions or releases to generate + return [] + + if b_id: + b = Version.of(b_id) + else: + # Get all the commits since `a` + b = None + + return a.to(b, snapshots) + + else: + # Then, try to treat it as a version + return [Version.of(p)] + + @staticmethod + def patterns(patterns: List[str], latest_in_repo: Version = None) -> List[Version]: + + # Parse versions + versions = set() + for pattern in patterns: + if pattern.startswith("-"): + versions -= set(Version.pattern(pattern[1:], latest_in_repo)) + else: + versions |= set(Version.pattern(pattern, latest_in_repo)) + + # Sort + versions = list(versions) + versions.sort() + + return versions class Release(Version): - @staticmethod - def of(id: Optional[str]) -> Release: - if id is None: - return manifest.latest_release + @staticmethod + def of(id: Optional[str]) -> Release: + if id is None: + return manifest.latest_release - else: - version = Version.of(id) - if not isinstance(version, Release): - raise ValueError('Version is not a release') + else: + version = Version.of(id) + if not isinstance(version, Release): + raise ValueError("Version is not a release") - return version + return version class Snapshot(Version): - pass + pass class OldVersion(Version): - pass + pass class OldAlphaVersion(Version): - pass + pass class OldBetaVersion(Version): - pass + pass class Manifest: - def __init__( - self, - versions: List[Version], - earliest_release: Release, - earliest_snapshot: Version, - latest_release: Release, - latest_snapshot: Version - ) -> None: - - self.versions = versions - self.version_for_id = {version.id: version for version in versions} - self.earliest_release = earliest_release - self.earliest_snapshot = earliest_snapshot - self.latest_release = latest_release - self.latest_snapshot = latest_snapshot - - def __iter__(self) -> Version: - return self.versions - - @staticmethod - def parse( - raw: Dict, - earliest_supported_version_id: Optional[str] - ) -> Manifest: - - # Reverse to get ascending chronological order - reversed_versions = reversed(raw['versions']) - - current_version_supported = False - earliest_release = None - earliest_snapshot = None - versions = [] - version_for_id = {} - prev = None - - for i, raw_version in enumerate(reversed_versions): - id = raw_version['id'] - if id == earliest_supported_version_id: - current_version_supported = True - - if not current_version_supported: - continue - - version = Version.parse(raw_version, i) - versions.append(version) - version_for_id[id] = version - - if prev is not None: - prev.next = version - prev = version - - if earliest_snapshot is None: - earliest_snapshot = version - if earliest_release is None and isinstance(version, Release): - earliest_release = version - - latest_release = version_for_id[raw['latest']['release']] - latest_snapshot = version_for_id[raw['latest']['snapshot']] - - return Manifest( - versions, - earliest_release, - earliest_snapshot, - latest_release, - latest_snapshot - ) + def __init__( + self, + versions: List[Version], + earliest_release: Release, + earliest_snapshot: Version, + latest_release: Release, + latest_snapshot: Version, + ) -> None: + + self.versions = versions + self.version_for_id = {version.id: version for version in versions} + self.earliest_release = earliest_release + self.earliest_snapshot = earliest_snapshot + self.latest_release = latest_release + self.latest_snapshot = latest_snapshot + + def __iter__(self) -> Version: + return self.versions + + @staticmethod + def parse(raw: Dict, earliest_supported_version_id: Optional[str]) -> Manifest: + + # Reverse to get ascending chronological order + reversed_versions = reversed(raw["versions"]) + + current_version_supported = False + earliest_release = None + earliest_snapshot = None + versions = [] + version_for_id = {} + prev = None + + for i, raw_version in enumerate(reversed_versions): + id = raw_version["id"] + if id == earliest_supported_version_id: + current_version_supported = True + + if not current_version_supported: + continue + + version = Version.parse(raw_version, i) + versions.append(version) + version_for_id[id] = version + + if prev is not None: + prev.next = version + prev = version + + if earliest_snapshot is None: + earliest_snapshot = version + if earliest_release is None and isinstance(version, Release): + earliest_release = version + + latest_release = version_for_id[raw["latest"]["release"]] + latest_snapshot = version_for_id[raw["latest"]["snapshot"]] + + return Manifest( + versions, + earliest_release, + earliest_snapshot, + latest_release, + latest_snapshot, + ) def load_manifest( - raw: Optional[Dict] = None, - earliest_supported_version_id: Optional[str] = EARLIEST_SUPPORTED_VERSION_ID + raw: Optional[Dict] = None, + earliest_supported_version_id: Optional[str] = EARLIEST_SUPPORTED_VERSION_ID, ): - global manifest + global manifest - if raw is None: - raw = requests.get(MANIFEST_LOCATION).json() - manifest = Manifest.parse(raw, earliest_supported_version_id) + if raw is None: + raw = requests.get(MANIFEST_LOCATION).json() + manifest = Manifest.parse(raw, earliest_supported_version_id) def clear_manifest(): - global manifest + global manifest - manifest = None + manifest = None manifest = None diff --git a/src/mint/error.py b/src/mint/error.py index cc67f1a..38d01dd 100644 --- a/src/mint/error.py +++ b/src/mint/error.py @@ -2,4 +2,4 @@ class GitError(CommandError): - pass + pass diff --git a/src/mint/repo.py b/src/mint/repo.py index dc416a4..64445d5 100644 --- a/src/mint/repo.py +++ b/src/mint/repo.py @@ -8,67 +8,67 @@ class NoSuchRepoError(Exception): - def __init__(self, path: str, *args: object) -> None: - super().__init__(*args) + def __init__(self, path: str, *args: object) -> None: + super().__init__(*args) - self.path = path + self.path = path - def __str__(self) -> str: - return f'{self.path} is not a git repo' + def __str__(self) -> str: + return f"{self.path} is not a git repo" class Repo: - """ - Git repo + """ + Git repo - Creating a Repo object: - # Use an existing repo - repo = Repo(PATH) + Creating a Repo object: + # Use an existing repo + repo = Repo(PATH) - # Create an empty repo - repo = Repo.init(PATH) + # Create an empty repo + repo = Repo.init(PATH) - # Clone a repo - repo = Repo.clone(REMOTE_URL, DESTINATION) + # Clone a repo + repo = Repo.clone(REMOTE_URL, DESTINATION) - Using a Repo object: - repo.git.commit('src', message='Some commit') # or m='Some commit' - """ + Using a Repo object: + repo.git.commit('src', message='Some commit') # or m='Some commit' + """ - def __init__(self, path: str, check_path=True) -> None: - if check_path: - Repo._ensure_repo_path_is_valid(path) + def __init__(self, path: str, check_path=True) -> None: + if check_path: + Repo._ensure_repo_path_is_valid(path) - self.path = path - self.git = Command('git', working_dir=path, error=GitError) + self.path = path + self.git = Command("git", working_dir=path, error=GitError) - def to_gitpython(self) -> git.Repo: - return git.Repo(self.path) + def to_gitpython(self) -> git.Repo: + return git.Repo(self.path) - @staticmethod - def _ensure_repo_path_is_valid(path: str): - if not os.path.exists(path): - raise FileNotFoundError(path) + @staticmethod + def _ensure_repo_path_is_valid(path: str): + if not os.path.exists(path): + raise FileNotFoundError(path) - # Make sure the path is a directory or a symlink - if os.path.isfile(path): - raise NotADirectoryError(path) + # Make sure the path is a directory or a symlink + if os.path.isfile(path): + raise NotADirectoryError(path) - git_dir = os.path.join(path, '.git') - if not os.path.exists(git_dir) or os.path.isfile(git_dir): - raise NoSuchRepoError(path) + git_dir = os.path.join(path, ".git") + if not os.path.exists(git_dir) or os.path.isfile(git_dir): + raise NoSuchRepoError(path) - @staticmethod - def init(path: str, **kwargs) -> Repo: - if not os.path.exists(path): - os.mkdir(path) + @staticmethod + def init(path: str, **kwargs) -> Repo: + if not os.path.exists(path): + os.mkdir(path) - repo = Repo(path, check_path=False) - repo.git.init(**kwargs) - return repo + repo = Repo(path, check_path=False) + repo.git.init(**kwargs) + return repo - @staticmethod - def clone(remote: str, dest: str, **kwargs) -> Repo: - git = Command('git', error=GitError) - git.clone(remote, dest, **kwargs) - return Repo(dest) + @staticmethod + def clone(remote: str, dest: str, **kwargs) -> Repo: + git = Command("git", error=GitError) + git.clone(remote, dest, **kwargs) + return Repo(dest) diff --git a/src/shulkr/__main__.py b/src/shulkr/__main__.py index d611bda..0df5a47 100644 --- a/src/shulkr/__main__.py +++ b/src/shulkr/__main__.py @@ -2,8 +2,8 @@ def main() -> None: - cli() + cli() -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() diff --git a/src/shulkr/app.py b/src/shulkr/app.py index c1cd565..82f8e38 100644 --- a/src/shulkr/app.py +++ b/src/shulkr/app.py @@ -3,11 +3,7 @@ from typing import List import click -from minecraft.version import ( - NoSuchVersionError, - Version, - load_manifest -) +from minecraft.version import NoSuchVersionError, Version, load_manifest from shulkr.compatibility import is_compatible from shulkr.config import init_config @@ -17,75 +13,70 @@ def run( - versions: List[str], - mappings: str, - repo_path: str, - message_template: str, - tags: bool, - undo_renamed_vars: bool + versions: List[str], + mappings: str, + repo_path: str, + message_template: str, + tags: bool, + undo_renamed_vars: bool, ) -> None: - load_manifest() - - full_repo_path = os.path.join( - os.getcwd(), - repo_path - ) - - init_output = not init_repo(full_repo_path) - - if not is_compatible(): - click.secho( - 'This repo is not compatible with the current version of shulkr - ' + - 'please create a new repo or downgrade shulkr.', - err=True, - fg='yellow' - ) - sys.exit(4) - - init_output = not init_config( - full_repo_path, - mappings, - message_template, - tags, - undo_renamed_vars - ) or init_output - init_output = not ensure_gitignore_exists() or init_output - - # If we printed anything in the initialization step, print a newline - if init_output: - click.echo() - - try: - resolved_versions = Version.patterns( - versions, - latest_in_repo=get_latest_generated_version() - ) - - except NoSuchVersionError as e: - click.secho(e, err=True, fg='red') - sys.exit(1) - - if len(resolved_versions) == 0: - click.secho('No versions selected', color='yellow') - sys.exit(0) - - if resolved_versions[0] < get_latest_generated_version(): - click.secho( - 'The latest version in the repo is ' + - get_latest_generated_version().id + - ', but you selected ' + - resolved_versions[0].id + - '. Please select a version that is newer than the latest ' + - 'version in the repo.', - err=True, - fg='red' - ) - sys.exit(3) - - for i, version in enumerate(resolved_versions): - create_version(version) - - # Print line between the output of generating each version - if i < len(resolved_versions) - 1: - click.echo() + load_manifest() + + full_repo_path = os.path.join(os.getcwd(), repo_path) + + init_output = not init_repo(full_repo_path) + + if not is_compatible(): + click.secho( + "This repo is not compatible with the current version of shulkr - " + + "please create a new repo or downgrade shulkr.", + err=True, + fg="yellow", + ) + sys.exit(4) + + init_output = ( + not init_config( + full_repo_path, mappings, message_template, tags, undo_renamed_vars + ) + or init_output + ) + init_output = not ensure_gitignore_exists() or init_output + + # If we printed anything in the initialization step, print a newline + if init_output: + click.echo() + + try: + resolved_versions = Version.patterns( + versions, latest_in_repo=get_latest_generated_version() + ) + + except NoSuchVersionError as e: + click.secho(e, err=True, fg="red") + sys.exit(1) + + if len(resolved_versions) == 0: + click.secho("No versions selected", color="yellow") + sys.exit(0) + + if resolved_versions[0] < get_latest_generated_version(): + click.secho( + "The latest version in the repo is " + + get_latest_generated_version().id + + ", but you selected " + + resolved_versions[0].id + + ". Please select a version that is newer than the latest " + + "version in the repo.", + err=True, + fg="red", + ) + sys.exit(3) + + for i, version in enumerate(resolved_versions): + create_version(version) + + # Print line between the output of generating each version + if i < len(resolved_versions) - 1: + click.echo() diff --git a/src/shulkr/cli.py b/src/shulkr/cli.py index 814fc1c..0e17270 100644 --- a/src/shulkr/cli.py +++ b/src/shulkr/cli.py @@ -7,68 +7,57 @@ @click.command( - name='shulkr', - help='Generate multiple versions of the Minecraft source code' -) -@click.help_option( - '-h', - '--help', - is_flag=True, - help='Show this message and exit' -) -@click.option( - '--mappings', - '-p', - type=click.Choice(['yarn', 'mojang']), - default='yarn', - help="Mappings for deobfuscation (defaults to 'yarn')" + name="shulkr", help="Generate multiple versions of the Minecraft source code" ) +@click.help_option("-h", "--help", is_flag=True, help="Show this message and exit") @click.option( - '--repo', - '-p', - type=click.Path(), - default='.', - help='Path to the Minecraft repo (defaults to the current working directory)' + "--mappings", + "-p", + type=click.Choice(["yarn", "mojang"]), + default="yarn", + help="Mappings for deobfuscation (defaults to 'yarn')", ) @click.option( - '--message', - '-m', - type=str, - default='version {}', - help="Commit message template (defaults to 'version {}')" + "--repo", + "-p", + type=click.Path(), + default=".", + help="Path to the Minecraft repo (defaults to the current working directory)", ) @click.option( - '--no-tags', - '-T', - is_flag=True, - help='Do not tag commits' + "--message", + "-m", + type=str, + default="version {}", + help="Commit message template (defaults to 'version {}')", ) +@click.option("--no-tags", "-T", is_flag=True, help="Do not tag commits") @click.option( - '--undo-renamed-vars', - '-u', - is_flag=True, - help=( - 'Revert local variables that were renamed in new versions to their ' - 'original names (experimental)' - ) + "--undo-renamed-vars", + "-u", + is_flag=True, + help=( + "Revert local variables that were renamed in new versions to their " + "original names (experimental)" + ), ) -@click.argument('versions', nargs=-1, type=click.STRING) +@click.argument("versions", nargs=-1, type=click.STRING) def cli( - versions: List[str], - mappings: str, - repo: str, - message: str, - no_tags: bool, - undo_renamed_vars: bool + versions: List[str], + mappings: str, + repo: str, + message: str, + no_tags: bool, + undo_renamed_vars: bool, ) -> None: - tags = not no_tags - try: - run(versions, mappings, repo, message, tags, undo_renamed_vars) + tags = not no_tags + try: + run(versions, mappings, repo, message, tags, undo_renamed_vars) - except ValueError as e: - click.secho(e, err=True, fg='red') - sys.exit(2) + except ValueError as e: + click.secho(e, err=True, fg="red") + sys.exit(2) - except KeyboardInterrupt: - click.echo('Aborted!', err=True) + except KeyboardInterrupt: + click.echo("Aborted!", err=True) diff --git a/src/shulkr/compatibility.py b/src/shulkr/compatibility.py index 239aa7b..dde1815 100644 --- a/src/shulkr/compatibility.py +++ b/src/shulkr/compatibility.py @@ -5,18 +5,18 @@ def _repo_has_commits() -> bool: - try: - get_repo().git.rev_parse('HEAD') - return True + try: + get_repo().git.rev_parse("HEAD") + return True - except GitError: - return False + except GitError: + return False def is_compatible() -> bool: - """ - Check if the current repo is compatible with the current shulkr version. - """ + """ + Check if the current repo is compatible with the current shulkr version. + """ - repo = get_repo() - return config_exists(repo.path) or not _repo_has_commits() + repo = get_repo() + return config_exists(repo.path) or not _repo_has_commits() diff --git a/src/shulkr/config.py b/src/shulkr/config.py index 335e204..7e8b9c7 100644 --- a/src/shulkr/config.py +++ b/src/shulkr/config.py @@ -8,150 +8,136 @@ class Config: - def __init__( - self, - repo_path: str, - mappings: str, - message_template: str, - tag: bool, - undo_renamed_vars: bool - ) -> None: - - self.repo_path = repo_path - self.mappings = mappings - self.message_template = message_template - self.tag = tag - self.undo_renamed_vars = undo_renamed_vars - - def save(self) -> None: - """ - Write this configuration as TOML to the .shulkr file in the - corresponding shulkr repo - """ - - raw_config = { - 'mappings': self.mappings, - 'message': self.message_template, - 'tag': self.tag, - 'experimental': { - 'undo_renamed_vars': self.undo_renamed_vars - } - # No need to store the repo path (since it is supplied to the CLI - # and defaults to the CWD) - } - - config_path = _config_path_for_repo(self.repo_path) - with open(config_path, 'w+') as config_file: - toml.dump(raw_config, config_file) + def __init__( + self, + repo_path: str, + mappings: str, + message_template: str, + tag: bool, + undo_renamed_vars: bool, + ) -> None: + + self.repo_path = repo_path + self.mappings = mappings + self.message_template = message_template + self.tag = tag + self.undo_renamed_vars = undo_renamed_vars + + def save(self) -> None: + """ + Write this configuration as TOML to the .shulkr file in the + corresponding shulkr repo + """ + + raw_config = { + "mappings": self.mappings, + "message": self.message_template, + "tag": self.tag, + "experimental": {"undo_renamed_vars": self.undo_renamed_vars}, + # No need to store the repo path (since it is supplied to the CLI + # and defaults to the CWD) + } + + config_path = _config_path_for_repo(self.repo_path) + with open(config_path, "w+") as config_file: + toml.dump(raw_config, config_file) def _config_path_for_repo(repo_path: str) -> str: - return os.path.join(repo_path, '.shulkr') + return os.path.join(repo_path, ".shulkr") def _load_config(repo_path: str) -> Config: - config_path = _config_path_for_repo(repo_path) - with open(config_path, 'r') as config_file: - raw_config = toml.load(config_file) + config_path = _config_path_for_repo(repo_path) + with open(config_path, "r") as config_file: + raw_config = toml.load(config_file) - return Config( - repo_path=repo_path, - mappings=raw_config['mappings'], - message_template=raw_config['message'], - tag=raw_config['tag'], - undo_renamed_vars=raw_config['experimental']['undo_renamed_vars'] - ) + return Config( + repo_path=repo_path, + mappings=raw_config["mappings"], + message_template=raw_config["message"], + tag=raw_config["tag"], + undo_renamed_vars=raw_config["experimental"]["undo_renamed_vars"], + ) def _commit_config() -> None: - repo = get_repo() + repo = get_repo() - repo.git.add('.shulkr') - repo.git.commit(message='add .shulkr') + repo.git.add(".shulkr") + repo.git.commit(message="add .shulkr") def _create_config( - repo_path: str, - mappings: str, - message_template: str, - tag: bool, - undo_renamed_vars: bool + repo_path: str, + mappings: str, + message_template: str, + tag: bool, + undo_renamed_vars: bool, ) -> Config: - global config + global config - click.echo('Saving config') + click.echo("Saving config") - config = Config( - repo_path, - mappings, - message_template, - tag, - undo_renamed_vars - ) - config.save() - _commit_config() + config = Config(repo_path, mappings, message_template, tag, undo_renamed_vars) + config.save() + _commit_config() - return config + return config def config_exists(repo_path: str) -> bool: - return os.path.exists( - _config_path_for_repo(repo_path) - ) + return os.path.exists(_config_path_for_repo(repo_path)) def init_config( - repo_path: str, - mappings: str, - message_template: str, - tag: bool, - undo_renamed_vars: bool + repo_path: str, + mappings: str, + message_template: str, + tag: bool, + undo_renamed_vars: bool, ) -> bool: - """ - Initialize the config state + """ + Initialize the config state - If a .shulkr file exists for the current repo, it will be loaded. - Otherwise, a new one will be created with the specified mappings. + If a .shulkr file exists for the current repo, it will be loaded. + Otherwise, a new one will be created with the specified mappings. - Args: - repo_path (str): _description_ - mappings (str): _description_ + Args: + repo_path (str): _description_ + mappings (str): _description_ - Returns: - bool: True if a config was loaded. False if a new one was created. - """ + Returns: + bool: True if a config was loaded. False if a new one was created. + """ - global config + global config - if config_exists(repo_path): - config = _load_config(repo_path) - return True - else: - config = _create_config( - repo_path, - mappings, - message_template, - tag, - undo_renamed_vars - ) - return False + if config_exists(repo_path): + config = _load_config(repo_path) + return True + else: + config = _create_config( + repo_path, mappings, message_template, tag, undo_renamed_vars + ) + return False def clear_config() -> None: - """ - Unload the config from memory + """ + Unload the config from memory - Used in tests - """ + Used in tests + """ - global config + global config - config = None + config = None def get_config(): - return config + return config config = None diff --git a/src/shulkr/gitignore.py b/src/shulkr/gitignore.py index 9a4d903..3896619 100644 --- a/src/shulkr/gitignore.py +++ b/src/shulkr/gitignore.py @@ -6,35 +6,35 @@ def _gitignore_path() -> str: - repo = get_repo() + repo = get_repo() - return os.path.join(repo.path, '.gitignore') + return os.path.join(repo.path, ".gitignore") def _create_gitignore() -> None: - click.echo('Creating gitignore') + click.echo("Creating gitignore") - repo = get_repo() + repo = get_repo() - with open(_gitignore_path(), 'w+') as gitignore: - to_ignore = ['yarn', 'DecompilerMC'] - gitignore.write('\n'.join(to_ignore) + '\n') + with open(_gitignore_path(), "w+") as gitignore: + to_ignore = ["yarn", "DecompilerMC"] + gitignore.write("\n".join(to_ignore) + "\n") - repo.git.add('.gitignore') - repo.git.commit(message='add .gitignore') + repo.git.add(".gitignore") + repo.git.commit(message="add .gitignore") def ensure_gitignore_exists() -> bool: - """ - Create and commit a .gitignore file if one does not exist + """ + Create and commit a .gitignore file if one does not exist - Returns: - bool: True if a .gitignore file was found. False if one was created. - """ + Returns: + bool: True if a .gitignore file was found. False if one was created. + """ - if not os.path.isfile(_gitignore_path()): - _create_gitignore() - return False + if not os.path.isfile(_gitignore_path()): + _create_gitignore() + return False - else: - return True + else: + return True diff --git a/src/shulkr/repo.py b/src/shulkr/repo.py index ae21e4d..87e8027 100644 --- a/src/shulkr/repo.py +++ b/src/shulkr/repo.py @@ -6,45 +6,45 @@ def init_repo(repo_path: str) -> bool: - """ - Load information about the current shulkr/git repo + """ + Load information about the current shulkr/git repo - Must be called before get_repo() + Must be called before get_repo() - Args: - repo_path (str): Path to the working tree of the repo + Args: + repo_path (str): Path to the working tree of the repo - Returns: - bool: True if a repo was found, False if a new repo was created - loaded. - """ + Returns: + bool: True if a repo was found, False if a new repo was created + loaded. + """ - global repo + global repo - try: - repo = Repo(repo_path) + try: + repo = Repo(repo_path) - # The repo already exists - return True + # The repo already exists + return True - except FileNotFoundError: - click.echo('Initializing git') - repo = Repo.init(repo_path) + except FileNotFoundError: + click.echo("Initializing git") + repo = Repo.init(repo_path) - except NotADirectoryError: - click.echo('Initializing git') - repo = Repo.init(repo_path) + except NotADirectoryError: + click.echo("Initializing git") + repo = Repo.init(repo_path) - except NoSuchRepoError: - click.echo('Initializing git') - repo = Repo.init(repo_path) + except NoSuchRepoError: + click.echo("Initializing git") + repo = Repo.init(repo_path) - # We created the repo - return False + # We created the repo + return False def get_repo() -> Repo: - return repo + return repo repo = None diff --git a/src/shulkr/version.py b/src/shulkr/version.py index 248dbc6..852c321 100644 --- a/src/shulkr/version.py +++ b/src/shulkr/version.py @@ -16,112 +16,112 @@ def _commit_version(version: Version) -> None: - repo = get_repo() - message_template = get_config().message_template + repo = get_repo() + message_template = get_config().message_template - commit_msg = message_template.strip().replace('{}', str(version)) - if get_config().undo_renamed_vars and head_has_versions(): - commit_msg += '\n\nRenamed variables reverted' + commit_msg = message_template.strip().replace("{}", str(version)) + if get_config().undo_renamed_vars and head_has_versions(): + commit_msg += "\n\nRenamed variables reverted" - repo.git.add('src') + repo.git.add("src") - repo.git.commit(message=commit_msg) + repo.git.commit(message=commit_msg) def _tag_version(version: Version) -> None: - repo = get_repo() + repo = get_repo() - repo.git.tag(version) + repo.git.tag(version) def create_version(version: Version) -> None: - """ - Generate the sources for a Minecraft version and commit to the repo - - Args: - version (Version): Version to create - undo_renamed_vars (bool): If set, this function will attempt to revert - any variables that were renamed in the new version - message_template (str): Template for commit messages ('{}'s will be - replaced with the version name) - tag (bool): If set, the commit will be tagged - """ - - # 1. Generate source code for the current version - click.secho(f'Generating sources for Minecraft {version}', bold=True) - - repo = get_repo() - mappings = get_config().mappings - repo_path = repo.path - - try: - generate_sources(version, mappings, repo_path) - except BaseException as e: - # Undo src/ deletions - if head_has_versions(): - repo.git.restore('src') - else: - path = os.path.join(repo.path, 'src') - if os.path.exists(path): - shutil.rmtree(path) - - raise e - - # 2. If there are any previous versions, undo the renamed variables - if get_config().undo_renamed_vars and head_has_versions(): - click.echo('Undoing renamed variables') - undo_renames(get_repo().to_gitpython()) - - # 3. Commit the new version to git - click.echo('Committing to git') - _commit_version(version) - - # 4. Tag - if get_config().tag: - _tag_version(version) + """ + Generate the sources for a Minecraft version and commit to the repo + + Args: + version (Version): Version to create + undo_renamed_vars (bool): If set, this function will attempt to revert + any variables that were renamed in the new version + message_template (str): Template for commit messages ('{}'s will be + replaced with the version name) + tag (bool): If set, the commit will be tagged + """ + + # 1. Generate source code for the current version + click.secho(f"Generating sources for Minecraft {version}", bold=True) + + repo = get_repo() + mappings = get_config().mappings + repo_path = repo.path + + try: + generate_sources(version, mappings, repo_path) + except BaseException as e: + # Undo src/ deletions + if head_has_versions(): + repo.git.restore("src") + else: + path = os.path.join(repo.path, "src") + if os.path.exists(path): + shutil.rmtree(path) + + raise e + + # 2. If there are any previous versions, undo the renamed variables + if get_config().undo_renamed_vars and head_has_versions(): + click.echo("Undoing renamed variables") + undo_renames(get_repo().to_gitpython()) + + # 3. Commit the new version to git + click.echo("Committing to git") + _commit_version(version) + + # 4. Tag + if get_config().tag: + _tag_version(version) def head_has_versions() -> bool: - """ - Check if any versions have been generated on the current branch + """ + Check if any versions have been generated on the current branch - Raises: - e: + Raises: + e: - Returns: - bool: True if at least one version was found on the current branch - """ + Returns: + bool: True if at least one version was found on the current branch + """ - repo = get_repo() + repo = get_repo() - try: - # List tags reachable by HEAD - repo.git.describe(tags=True) + try: + # List tags reachable by HEAD + repo.git.describe(tags=True) - # If we made it here, there is at least one tag. - return True + # If we made it here, there is at least one tag. + return True - except GitError as e: - if 'fatal: No names found, cannot describe anything.' in e.stderr: - return False + except GitError as e: + if "fatal: No names found, cannot describe anything." in e.stderr: + return False - raise e + raise e def get_latest_generated_version() -> Version: - """ - Get the most recent version commit on the current branch + """ + Get the most recent version commit on the current branch - Returns: - Version: - """ + Returns: + Version: + """ - if not head_has_versions(): - return None + if not head_has_versions(): + return None - repo = get_repo() + repo = get_repo() - # Get most recent tag reachable by HEAD - tag_name = repo.git.describe(tags=True) + # Get most recent tag reachable by HEAD + tag_name = repo.git.describe(tags=True) - return Version.of(tag_name) + return Version.of(tag_name) diff --git a/tests/command/smoke/conftest.py b/tests/command/smoke/conftest.py index f4ef7f1..f9de6b2 100644 --- a/tests/command/smoke/conftest.py +++ b/tests/command/smoke/conftest.py @@ -8,15 +8,15 @@ @pytest.fixture def tempdir(): - with TemporaryDirectory() as tempdir: - yield tempdir + with TemporaryDirectory() as tempdir: + yield tempdir @pytest.fixture def repo(tempdir): - yield Repo.init(tempdir) + yield Repo.init(tempdir) @pytest.fixture def git(repo): - return Command('git', working_dir=repo.path) + return Command("git", working_dir=repo.path) diff --git a/tests/command/smoke/test_command.py b/tests/command/smoke/test_command.py index 791971e..e478fb8 100644 --- a/tests/command/smoke/test_command.py +++ b/tests/command/smoke/test_command.py @@ -1,13 +1,15 @@ class TestCommand: - def test_getattr_returns_git_output(self, git, repo): - # Make one commit so git knows which branch is checked out - git.commit(message='dummy commit', allow_empty=True) + def test_getattr_returns_git_output(self, git, repo): + # Make one commit so git knows which branch is checked out + git.commit(message="dummy commit", allow_empty=True) - # Now, make sure the result of 'git status' is correct - branch = git.rev_parse('HEAD', abbrev_ref=True) - assert git.status() == f'On branch {branch}\nnothing to commit, working tree clean' + # Now, make sure the result of 'git status' is correct + branch = git.rev_parse("HEAD", abbrev_ref=True) + assert ( + git.status() == f"On branch {branch}\nnothing to commit, working tree clean" + ) - def test_commit_messages_with_spaces_are_not_wrapped_with_quotes(self, git, repo): - git.commit(message='dummy commit', allow_empty=True) + def test_commit_messages_with_spaces_are_not_wrapped_with_quotes(self, git, repo): + git.commit(message="dummy commit", allow_empty=True) - assert git.log('--format=%B') == 'dummy commit' + assert git.log("--format=%B") == "dummy commit" diff --git a/tests/command/unit/test_command.py b/tests/command/unit/test_command.py index 99524db..a2de3bc 100644 --- a/tests/command/unit/test_command.py +++ b/tests/command/unit/test_command.py @@ -8,123 +8,116 @@ SUBPROCESS_ANY_ARGS = { - 'cwd': ANY, - 'check': ANY, - 'capture_output': ANY, - 'text': ANY, + "cwd": ANY, + "check": ANY, + "capture_output": ANY, + "text": ANY, } class GitError(CommandError): - pass + pass @pytest.fixture(autouse=True) def processes(mocker) -> None: - mocker.patch('command.subprocess.run') - mocker.patch('command.shutil.which', return_value=True) + mocker.patch("command.subprocess.run") + mocker.patch("command.shutil.which", return_value=True) @pytest.fixture def git() -> Command: - return Command('git', working_dir='/foo/bar', error=GitError) + return Command("git", working_dir="/foo/bar", error=GitError) @pytest.fixture def silent_git() -> Command: - return Command('git', working_dir='/foo/bar', capture_output=False, error=GitError) + return Command("git", working_dir="/foo/bar", capture_output=False, error=GitError) class TestGitCommand: - def test_getattr_calls_subprocess_with_cwd_set_to_repo_path(self, git): - git.status() - - subprocess_args = { - **SUBPROCESS_ANY_ARGS, - 'cwd': '/foo/bar' - } - - command.subprocess.run.assert_called_once_with( - ANY, - **subprocess_args - ) - - def test_getattr_with_no_arguments_calls_corresponding_git_subcommand(self, git): - git.status() - - command.subprocess.run.assert_called_once_with( - ['git', 'status'], - **SUBPROCESS_ANY_ARGS - ) - - def test_getattr_with_one_positional_argument_calls_corresponding_git_subcommand_concatenated_with_argument(self, git): - git.status('src') - - command.subprocess.run.assert_called_once_with( - ['git', 'status', 'src'], - **SUBPROCESS_ANY_ARGS - ) - - def test_getattr_with_one_nonboolean_keyword_argument_calls_corresponding_git_subcommand_concatenated_with_formatted_keyword_argument(self, git): - git.log(n=3) - - command.subprocess.run.assert_called_once_with( - ['git', 'log', '-n', '3'], - **SUBPROCESS_ANY_ARGS - ) - - def test_getattr_with_one_keyword_argument_set_to_1_sets_the_corresponding_option_to_1(self, git): - git.log(n=1) - - # It should not be 'git log -n' - it should be 'git log -n 1' - command.subprocess.run.assert_called_once_with( - ['git', 'log', '-n', '1'], - **SUBPROCESS_ANY_ARGS - ) - - def test_getattr_with_one_keyword_argument_set_to_true_calls_corresponding_git_subcommand_concatenated_with_corresponding_flag(self, git): - git.log(oneline=True) - - command.subprocess.run.assert_called_once_with( - ['git', 'log', '--oneline'], - **SUBPROCESS_ANY_ARGS - ) - - def test_getattr_with_one_keyword_argument_set_to_false_calls_corresponding_git_subcommand_concatenated_without_corresponding_flag(self, git): - git.log(oneline=False) - - command.subprocess.run.assert_called_once_with( - ['git', 'log'], - **SUBPROCESS_ANY_ARGS - ) - - def test_getattr_with_one_positional_argument_and_one_keyword_argument_calls_git_with_the_keyword_argument_before_the_positional_argument(self, git): - git.log('HEAD', oneline=True) - - command.subprocess.run.assert_called_once_with( - ['git', 'log', '--oneline', 'HEAD'], - **SUBPROCESS_ANY_ARGS - ) - - def test_getattr_raises_correct_error_when_subprocess_raises_an_error(self, git): - command.subprocess.run.side_effect = CalledProcessError( - 1, - 'git', - 'some error message' - ) - - with pytest.raises(GitError): - git.status() - - def test_getattr_with_capture_output_set_to_false_does_not_capture_output(self, silent_git): - silent_git.status() - - subprocess_args = { - **SUBPROCESS_ANY_ARGS, - 'capture_output': False - } - - command.subprocess.run.assert_called_once_with( - ANY, - **subprocess_args - ) + def test_getattr_calls_subprocess_with_cwd_set_to_repo_path(self, git): + git.status() + + subprocess_args = {**SUBPROCESS_ANY_ARGS, "cwd": "/foo/bar"} + + command.subprocess.run.assert_called_once_with(ANY, **subprocess_args) + + def test_getattr_with_no_arguments_calls_corresponding_git_subcommand(self, git): + git.status() + + command.subprocess.run.assert_called_once_with( + ["git", "status"], **SUBPROCESS_ANY_ARGS + ) + + def test_getattr_with_one_positional_argument_calls_corresponding_git_subcommand_concatenated_with_argument( + self, git + ): + git.status("src") + + command.subprocess.run.assert_called_once_with( + ["git", "status", "src"], **SUBPROCESS_ANY_ARGS + ) + + def test_getattr_with_one_nonboolean_keyword_argument_calls_corresponding_git_subcommand_concatenated_with_formatted_keyword_argument( + self, git + ): + git.log(n=3) + + command.subprocess.run.assert_called_once_with( + ["git", "log", "-n", "3"], **SUBPROCESS_ANY_ARGS + ) + + def test_getattr_with_one_keyword_argument_set_to_1_sets_the_corresponding_option_to_1( + self, git + ): + git.log(n=1) + + # It should not be 'git log -n' - it should be 'git log -n 1' + command.subprocess.run.assert_called_once_with( + ["git", "log", "-n", "1"], **SUBPROCESS_ANY_ARGS + ) + + def test_getattr_with_one_keyword_argument_set_to_true_calls_corresponding_git_subcommand_concatenated_with_corresponding_flag( + self, git + ): + git.log(oneline=True) + + command.subprocess.run.assert_called_once_with( + ["git", "log", "--oneline"], **SUBPROCESS_ANY_ARGS + ) + + def test_getattr_with_one_keyword_argument_set_to_false_calls_corresponding_git_subcommand_concatenated_without_corresponding_flag( + self, git + ): + git.log(oneline=False) + + command.subprocess.run.assert_called_once_with( + ["git", "log"], **SUBPROCESS_ANY_ARGS + ) + + def test_getattr_with_one_positional_argument_and_one_keyword_argument_calls_git_with_the_keyword_argument_before_the_positional_argument( + self, git + ): + git.log("HEAD", oneline=True) + + command.subprocess.run.assert_called_once_with( + ["git", "log", "--oneline", "HEAD"], **SUBPROCESS_ANY_ARGS + ) + + def test_getattr_raises_correct_error_when_subprocess_raises_an_error(self, git): + command.subprocess.run.side_effect = CalledProcessError( + 1, "git", "some error message" + ) + + with pytest.raises(GitError): + git.status() + + def test_getattr_with_capture_output_set_to_false_does_not_capture_output( + self, silent_git + ): + silent_git.status() + + subprocess_args = {**SUBPROCESS_ANY_ARGS, "capture_output": False} + + command.subprocess.run.assert_called_once_with(ANY, **subprocess_args) diff --git a/tests/gradle/smoke/conftest.py b/tests/gradle/smoke/conftest.py index 70a42ea..891c0f9 100644 --- a/tests/gradle/smoke/conftest.py +++ b/tests/gradle/smoke/conftest.py @@ -6,10 +6,10 @@ @pytest.fixture def tempdir(): - with TemporaryDirectory() as tempdir: - yield tempdir + with TemporaryDirectory() as tempdir: + yield tempdir @pytest.fixture def project(tempdir): - yield Project.init(tempdir) + yield Project.init(tempdir) diff --git a/tests/gradle/smoke/test_commands.py b/tests/gradle/smoke/test_commands.py index c3813a8..93429bc 100644 --- a/tests/gradle/smoke/test_commands.py +++ b/tests/gradle/smoke/test_commands.py @@ -2,8 +2,8 @@ def test_gradle_build(project: Project) -> None: - """ - Run a gradle build - """ + """ + Run a gradle build + """ - assert project.gradle.build() is None + assert project.gradle.build() is None diff --git a/tests/gradle/unit/test_command.py b/tests/gradle/unit/test_command.py index 274151c..1242109 100644 --- a/tests/gradle/unit/test_command.py +++ b/tests/gradle/unit/test_command.py @@ -3,39 +3,37 @@ class TestGradle: - def test__init__without_gradlew(self, mocker): - """ - Gradle constructor - """ - - Command__init__ = mocker.patch('gradle.command.Command.__init__') - mocker.patch('gradle.command.os.path.exists', return_value=False) - - Gradle('.', capture_output=True) - - Command__init__.assert_called_once_with( - 'gradle', - working_dir='.', - capture_output=True - ) - - def test__init__with_gradlew(self, mocker): - """ - Gradle constructor - """ - - gradlew_path = 'path/to/gradlew.bat' if os.name == 'nt' else 'path/to/gradlew' - - Command__init__ = mocker.patch('gradle.command.Command.__init__') - mocker.patch('gradle.command.os.path.exists', return_value=True) - mocker.patch('gradle.command.os.path.join', return_value=gradlew_path) - - Gradle('.', capture_output=True) - - Command__init__.assert_called_once_with( - # Mocking `os.name` causes pytest to crash on unix, so we have to - # check the os name - gradlew_path, - working_dir='.', - capture_output=True - ) \ No newline at end of file + def test__init__without_gradlew(self, mocker): + """ + Gradle constructor + """ + + Command__init__ = mocker.patch("gradle.command.Command.__init__") + mocker.patch("gradle.command.os.path.exists", return_value=False) + + Gradle(".", capture_output=True) + + Command__init__.assert_called_once_with( + "gradle", working_dir=".", capture_output=True + ) + + def test__init__with_gradlew(self, mocker): + """ + Gradle constructor + """ + + gradlew_path = "path/to/gradlew.bat" if os.name == "nt" else "path/to/gradlew" + + Command__init__ = mocker.patch("gradle.command.Command.__init__") + mocker.patch("gradle.command.os.path.exists", return_value=True) + mocker.patch("gradle.command.os.path.join", return_value=gradlew_path) + + Gradle(".", capture_output=True) + + Command__init__.assert_called_once_with( + # Mocking `os.name` causes pytest to crash on unix, so we have to + # check the os name + gradlew_path, + working_dir=".", + capture_output=True, + ) diff --git a/tests/gradle/unit/test_project.py b/tests/gradle/unit/test_project.py index ff66859..6861bf2 100644 --- a/tests/gradle/unit/test_project.py +++ b/tests/gradle/unit/test_project.py @@ -2,24 +2,24 @@ class TestProject: - def test__init__creates_gradle_instance(self, mocker): - """ - Test Project constructor - """ + def test__init__creates_gradle_instance(self, mocker): + """ + Test Project constructor + """ - Gradle = mocker.patch('gradle.project.Gradle') + Gradle = mocker.patch("gradle.project.Gradle") - Project('.', capture_output=True) + Project(".", capture_output=True) - Gradle.assert_called_once_with('.', capture_output=True) + Gradle.assert_called_once_with(".", capture_output=True) - def test__init__sets_project_dir(self, mocker): - """ - Test Project constructor - """ + def test__init__sets_project_dir(self, mocker): + """ + Test Project constructor + """ - mocker.patch('gradle.project.Gradle') + mocker.patch("gradle.project.Gradle") - project = Project('.') + project = Project(".") - assert project.project_dir == '.' + assert project.project_dir == "." diff --git a/tests/java/functional/test__init__.py b/tests/java/functional/test__init__.py index 82416e7..aedfac1 100644 --- a/tests/java/functional/test__init__.py +++ b/tests/java/functional/test__init__.py @@ -1,222 +1,224 @@ from javalang.tree import CompilationUnit, ClassDeclaration, Literal, MethodDeclaration -from java import ast_nodes_equal, filter_ast_node, get_renamed_variables, undo_variable_renames +from java import ( + ast_nodes_equal, + filter_ast_node, + get_renamed_variables, + undo_variable_renames, +) def wrap_in_class(code: str) -> str: - return """package foo; + return ( + """package foo; class Foo { public static void main(String[] args) { %s } } - """ % code + """ + % code + ) def test_ast_nodes_equal_with_equal_ints_returns_true(): - assert ast_nodes_equal(1, 1, recursive=False) + assert ast_nodes_equal(1, 1, recursive=False) def test_ast_nodes_equal_recursive_with_equal_ints_returns_true(): - assert ast_nodes_equal(1, 1) + assert ast_nodes_equal(1, 1) def test_ast_nodes_equal_with_different_ints_returns_true(): - assert not ast_nodes_equal(1, 2, recursive=False) + assert not ast_nodes_equal(1, 2, recursive=False) def test_ast_nodes_equal_recursive_with_different_ints_returns_true(): - assert not ast_nodes_equal(1, 2) + assert not ast_nodes_equal(1, 2) def test_ast_nodes_equal_with_equal_nodes_returns_true(): - node1 = Literal(value='hello') - node2 = Literal(value='hello') - assert ast_nodes_equal(node1, node2, recursive=False) + node1 = Literal(value="hello") + node2 = Literal(value="hello") + assert ast_nodes_equal(node1, node2, recursive=False) def test_ast_nodes_equal_recursive_with_equal_nodes_returns_true(): - node1 = Literal(value='hello') - node2 = Literal(value='hello') - assert ast_nodes_equal(node1, node2) + node1 = Literal(value="hello") + node2 = Literal(value="hello") + assert ast_nodes_equal(node1, node2) def test_ast_nodes_equal_with_inequal_nodes_returns_false(): - node1 = Literal(value='hello') - node2 = Literal(value='bye') - assert not ast_nodes_equal(node1, node2, recursive=True) + node1 = Literal(value="hello") + node2 = Literal(value="bye") + assert not ast_nodes_equal(node1, node2, recursive=True) def test_ast_nodes_equal_recursive_with_inequal_nodes_returns_false(): - node1 = Literal(value='hello') - node2 = Literal(value='bye') - assert not ast_nodes_equal(node1, node2) + node1 = Literal(value="hello") + node2 = Literal(value="bye") + assert not ast_nodes_equal(node1, node2) def test_ast_nodes_equal_recursive_with_equal_nodes_wrapped_in_lists_returns_true(): - node1 = Literal(value='hello') - node2 = Literal(value='hello') - assert ast_nodes_equal([node1], [node2]) + node1 = Literal(value="hello") + node2 = Literal(value="hello") + assert ast_nodes_equal([node1], [node2]) def test_ast_nodes_equal_recursive_with_inequal_nodes_wrapped_in_lists_returns_false(): - node1 = Literal(value='hello') - node2 = Literal(value='bye') - assert not ast_nodes_equal([node1], [node2]) + node1 = Literal(value="hello") + node2 = Literal(value="bye") + assert not ast_nodes_equal([node1], [node2]) def test_ast_nodes_equal_recursive_with_inequal_nodes_and_the_inequal_part_ignored_returns_true(): - # The following doesn't make any sense, but it's just to test our code, calm - # down - node1 = Literal(value='hello') - node2 = Literal(value='bye') - node3 = Literal(value=node1) - node4 = Literal(value=node2) - assert ast_nodes_equal(node3, node4, matches=[(node1, node2, 'value')]) + # The following doesn't make any sense, but it's just to test our code, calm + # down + node1 = Literal(value="hello") + node2 = Literal(value="bye") + node3 = Literal(value=node1) + node4 = Literal(value=node2) + assert ast_nodes_equal(node3, node4, matches=[(node1, node2, "value")]) def test_filter_ast_node_with_no_matching_children_returns_empty_list(): - node = Literal(value='hello') - assert list(filter_ast_node(node, CompilationUnit)) == [] + node = Literal(value="hello") + assert list(filter_ast_node(node, CompilationUnit)) == [] def test_get_renamed_variables_identical_code_with_single_declaration_with_intialization_returns_empty_list(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x = 0;'), - wrap_in_class('int x = 0;') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x = 0;"), wrap_in_class("int x = 0;") + ) - assert renamed_variables == [] + assert renamed_variables == [] def test_get_renamed_variables_identical_code_with_two_declarations_without_initializations_returns_empty_list(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x; int y;'), - wrap_in_class('int x; int y;') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x; int y;"), wrap_in_class("int x; int y;") + ) - assert renamed_variables == [] + assert renamed_variables == [] def test_get_renamed_variables_identical_code_with_one_declaration_with_two_variables_without_initializations_returns_empty_list(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x, y;'), - wrap_in_class('int x, y;') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x, y;"), wrap_in_class("int x, y;") + ) - assert renamed_variables == [] + assert renamed_variables == [] def test_get_renamed_variables_single_declaration_with_intialization_returns_the_mapping(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x = 0;'), - wrap_in_class('int y = 0;') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x = 0;"), wrap_in_class("int y = 0;") + ) - assert [v for path, v in renamed_variables] == [[('x', 'y')]] + assert [v for path, v in renamed_variables] == [[("x", "y")]] def test_get_renamed_variables_single_declaration_with_intialization_returns_correct_path(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x = 0;'), - wrap_in_class('int y = 0;') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x = 0;"), wrap_in_class("int y = 0;") + ) - actual = [type(node) for path, v in renamed_variables for node in path] - expected = [CompilationUnit, str, ClassDeclaration, str, MethodDeclaration, str] - assert actual == expected + actual = [type(node) for path, v in renamed_variables for node in path] + expected = [CompilationUnit, str, ClassDeclaration, str, MethodDeclaration, str] + assert actual == expected def test_get_renamed_variables_two_declarations_without_intializations_returns_both_mappings(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x; int a;'), - wrap_in_class('int y; int b;') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x; int a;"), wrap_in_class("int y; int b;") + ) - assert [v for path, v in renamed_variables] == [[('x', 'y'), ('a', 'b')]] + assert [v for path, v in renamed_variables] == [[("x", "y"), ("a", "b")]] def test_get_renamed_variables_two_declarations_with_intializations_returns_both_mappings(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x = 0; int a = 1;'), - wrap_in_class('int y = 0; int b = 1;') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x = 0; int a = 1;"), wrap_in_class("int y = 0; int b = 1;") + ) - assert [v for path, v in renamed_variables] == [[('x', 'y'), ('a', 'b')]] + assert [v for path, v in renamed_variables] == [[("x", "y"), ("a", "b")]] def test_get_renamed_variables_one_declaration_with_different_intializations_returns_empty_list(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x = 0;'), - wrap_in_class('int y = 1;') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x = 0;"), wrap_in_class("int y = 1;") + ) - assert renamed_variables == [] + assert renamed_variables == [] def test_get_renamed_variables_single_declaration_without_intialization_returns_the_mapping(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x;'), - wrap_in_class('int y;') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x;"), wrap_in_class("int y;") + ) - assert [v for path, v in renamed_variables] == [[('x', 'y')]] + assert [v for path, v in renamed_variables] == [[("x", "y")]] def test_get_renamed_variables_single_declaration_with_one_identical_reference_returns_the_mapping(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x; System.out.println(x);'), - wrap_in_class('int y; System.out.println(y);') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x; System.out.println(x);"), + wrap_in_class("int y; System.out.println(y);"), + ) - assert [v for path, v in renamed_variables] == [[('x', 'y')]] + assert [v for path, v in renamed_variables] == [[("x", "y")]] def test_get_renamed_variables_single_declaration_with_one_different_reference_returns_empty_list(): - renamed_variables = get_renamed_variables( - wrap_in_class('int x; System.out.println(x);'), - wrap_in_class('int y; System.out.println(y + "...");') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("int x; System.out.println(x);"), + wrap_in_class('int y; System.out.println(y + "...");'), + ) - assert renamed_variables == [] + assert renamed_variables == [] def test_get_renamed_variables_one_declaration_without_initialization_in_each_branch_of_if_statement_returns_the_mapping(): - renamed_variables = get_renamed_variables( - wrap_in_class('if (true) { int x; } else { int x; }'), - wrap_in_class('if (true) { int y; } else { int y; }') - ) + renamed_variables = get_renamed_variables( + wrap_in_class("if (true) { int x; } else { int x; }"), + wrap_in_class("if (true) { int y; } else { int y; }"), + ) - assert [v for path, v in renamed_variables] == [[('x', 'y')], [('x', 'y')]] + assert [v for path, v in renamed_variables] == [[("x", "y")], [("x", "y")]] def test_undo_variable_renames_with_one_declaration_and_no_renames_returns_identical_code(): - code = 'int x;' - renamed_variables = [] + code = "int x;" + renamed_variables = [] - assert undo_variable_renames(code, renamed_variables) == code + assert undo_variable_renames(code, renamed_variables) == code def test_undo_variable_renames_with_one_renamed_declaration_updates_code(): - renamed_variables = [(None, [('x', 'y')])] + renamed_variables = [(None, [("x", "y")])] - assert undo_variable_renames('int y;', renamed_variables) == 'int x;' + assert undo_variable_renames("int y;", renamed_variables) == "int x;" def test_undo_variable_renames_with_one_declaration_with_one_renamed_variable_and_one_unchanged_variable_updates_renamed_variable(): - renamed_variables = [(None, [('x', 'y')])] + renamed_variables = [(None, [("x", "y")])] - assert undo_variable_renames('int a, y;', renamed_variables) == 'int a, x;' + assert undo_variable_renames("int a, y;", renamed_variables) == "int a, x;" def test_undo_variable_renames_with_one_renamed_declaration_with_one_reference_updates_declaration_and_reference(): - renamed_variables = [(None, [('x', 'y')])] + renamed_variables = [(None, [("x", "y")])] - assert undo_variable_renames('int y = 0; int a = y;', renamed_variables) == 'int x = 0; int a = x;' + assert ( + undo_variable_renames("int y = 0; int a = y;", renamed_variables) + == "int x = 0; int a = x;" + ) def test_undo_variable_renames_with_one_renamed_declaration_set_to_string_with_same_text_only_updates_identifier(): - renamed_variables = [(None, [('x', 'y')])] + renamed_variables = [(None, [("x", "y")])] - assert undo_variable_renames('int y = "y";', renamed_variables) == 'int x = "y";' + assert undo_variable_renames('int y = "y";', renamed_variables) == 'int x = "y";' diff --git a/tests/minecraft/unit/conftest.py b/tests/minecraft/unit/conftest.py index 4c01a70..edc7eef 100644 --- a/tests/minecraft/unit/conftest.py +++ b/tests/minecraft/unit/conftest.py @@ -7,82 +7,70 @@ MANIFEST_DATA = { - 'latest': { - 'release': '1.20.5', - 'snapshot': 'abcdef' - }, - 'versions': [ - { - 'type': 'release', - 'id': '1.20.5' - }, - { - 'type': 'snapshot', - 'id': 'abcdef' - }, - { - 'type': 'release', - 'id': '1.20.4' - } - ] + "latest": {"release": "1.20.5", "snapshot": "abcdef"}, + "versions": [ + {"type": "release", "id": "1.20.5"}, + {"type": "snapshot", "id": "abcdef"}, + {"type": "release", "id": "1.20.4"}, + ], } class TestVersions: - def __init__(self, v1_20_4: Version, snapshot: Version, v1_20_5: Version) -> None: - self.v1_20_4 = v1_20_4 - self.snapshot = snapshot - self.v1_20_5 = v1_20_5 - self.release = v1_20_5 + def __init__(self, v1_20_4: Version, snapshot: Version, v1_20_5: Version) -> None: + self.v1_20_4 = v1_20_4 + self.snapshot = snapshot + self.v1_20_5 = v1_20_5 + self.release = v1_20_5 def create_repo(mocker, path: str): - repo = MagicMock() + repo = MagicMock() - # Make the repo directory useful for testing shulkr.minecraft.source - repo.path = os.path.abspath(path) + # Make the repo directory useful for testing shulkr.minecraft.source + repo.path = os.path.abspath(path) - class MockGitCommand: - def add(): - pass + class MockGitCommand: + def add(): + pass - def checkout(): - pass + def checkout(): + pass - def clean(): - pass + def clean(): + pass - def commit(): - pass + def commit(): + pass - def describe(): - pass + def describe(): + pass - def fetch(): - pass + def fetch(): + pass - def reset(): - pass + def reset(): + pass - def tag(): - pass + def tag(): + pass - def rev_parse(): - pass + def rev_parse(): + pass - repo.git = mocker.create_autospec(MockGitCommand()) + repo.git = mocker.create_autospec(MockGitCommand()) - return repo + return repo @pytest.fixture def versions(): - load_manifest(MANIFEST_DATA, earliest_supported_version_id='1.20.4') + load_manifest(MANIFEST_DATA, earliest_supported_version_id="1.20.4") - v1_20_4 = Version.of('1.20.4') - snapshot = Version.of('abcdef') - v1_20_5 = Version.of('1.20.5') + v1_20_4 = Version.of("1.20.4") + snapshot = Version.of("abcdef") + v1_20_5 = Version.of("1.20.5") - yield TestVersions(v1_20_4, snapshot, v1_20_5) + yield TestVersions(v1_20_4, snapshot, v1_20_5) - clear_manifest() + clear_manifest() diff --git a/tests/minecraft/unit/test_source.py b/tests/minecraft/unit/test_source.py index 0009c8f..fe4753f 100644 --- a/tests/minecraft/unit/test_source.py +++ b/tests/minecraft/unit/test_source.py @@ -9,206 +9,198 @@ class GitTree: - def __init__(self, name: str = None) -> None: - self.name = name + def __init__(self, name: str = None) -> None: + self.name = name class SubprocessMock: - def __init__(self, returncode=0, stderr=None): - self.returncode = returncode - self.stderr = stderr + def __init__(self, returncode=0, stderr=None): + self.returncode = returncode + self.stderr = stderr def create_repo(path: str, mocker): - def null_fn(*args, **kwargs): - return None + def null_fn(*args, **kwargs): + return None - repo = mocker.create_autospec(Repo(None, check_path=False)) - repo.path = path + repo = mocker.create_autospec(Repo(None, check_path=False)) + repo.path = path - repo.git.checkout = null_fn - repo.git.clean = null_fn - repo.git.fetch = null_fn - repo.git.reset = null_fn + repo.git.checkout = null_fn + repo.git.clean = null_fn + repo.git.fetch = null_fn + repo.git.reset = null_fn - return repo + return repo def create_yarn_project(mocker): - mocker.patch('gradle.command.os') - # Mock command.shutil.which for creating a Project - mocker.patch('command.shutil.which', return_value='/path/to/gradle') - project = mocker.create_autospec(Project(None)) - project.gradle.decompileCFR = mocker.MagicMock() + mocker.patch("gradle.command.os") + # Mock command.shutil.which for creating a Project + mocker.patch("command.shutil.which", return_value="/path/to/gradle") + project = mocker.create_autospec(Project(None)) + project.gradle.decompileCFR = mocker.MagicMock() - return project + return project @pytest.fixture def yarn_project(mocker): - repo_path = os.path.join('foo', 'yarn') - repo = create_repo(repo_path, mocker) - project = create_yarn_project(mocker) - mocker.patch('minecraft.source.Project', return_value=project) - mocker.patch('minecraft.source.Repo', return_value=repo) - mocker.patch('minecraft.source.Repo.clone', return_value=repo) - yield project + repo_path = os.path.join("foo", "yarn") + repo = create_repo(repo_path, mocker) + project = create_yarn_project(mocker) + mocker.patch("minecraft.source.Project", return_value=project) + mocker.patch("minecraft.source.Repo", return_value=repo) + mocker.patch("minecraft.source.Repo.clone", return_value=repo) + yield project @pytest.fixture def decompiler_mc_repo(mocker): - repo_path = os.path.join('foo', 'DecompilerMC') - repo = create_repo(repo_path, mocker) - mocker.patch('minecraft.source.Repo', return_value=repo) - mocker.patch('minecraft.source.Repo.clone', return_value=repo) - yield repo + repo_path = os.path.join("foo", "DecompilerMC") + repo = create_repo(repo_path, mocker) + mocker.patch("minecraft.source.Repo", return_value=repo) + mocker.patch("minecraft.source.Repo.clone", return_value=repo) + yield repo def test_generate_sources_with_yarn_runs_decompiler(mocker, versions, yarn_project): - root_path = 'foo' - - mocker.patch('minecraft.source.click') - mocker.patch('shutil.rmtree') - mocker.patch('shutil.move') - mocker.patch('os.makedirs') - - generate_sources(versions.snapshot, 'yarn', root_path) - - yarn_project.gradle.decompileCFR.assert_called_once_with() - - -def test_generate_sources_with_yarn_removes_old_decompiler_dir_if_exists(mocker, versions, yarn_project): - root_path = 'foo' - old_decompiler_dir = os.path.join(root_path, '.yarn') - - mocker.patch( - 'subprocess.run', - return_value=SubprocessMock() - ) - mocker.patch('minecraft.source.click') - mocker.patch('shutil.move') - mocker.patch('os.makedirs') - mocker.patch('os.path.exists', return_value=True) - - rmtree = mocker.patch('shutil.rmtree') + root_path = "foo" - generate_sources(versions.snapshot, 'yarn', root_path) + mocker.patch("minecraft.source.click") + mocker.patch("shutil.rmtree") + mocker.patch("shutil.move") + mocker.patch("os.makedirs") - rmtree.assert_any_call(old_decompiler_dir) + generate_sources(versions.snapshot, "yarn", root_path) + yarn_project.gradle.decompileCFR.assert_called_once_with() -def test_generate_sources_with_1_20_4_and_yarn_moves_sources_to_repo(mocker, versions, yarn_project): - root_path = 'foo' - decompiler_dir = os.path.join(root_path, 'DecompilerMC') - - mocker.patch( - 'subprocess.run', - return_value=SubprocessMock() - ) - mocker.patch('minecraft.source.click') - mocker.patch('shutil.rmtree') - mocker.patch('os.makedirs') - move = mocker.patch('shutil.move') - - generate_sources(versions.v1_20_4, 'yarn', root_path) - - decompiler_dir = os.path.join(root_path, 'yarn') - move.assert_called_once_with( - os.path.join(decompiler_dir, 'namedSrc'), - os.path.join(root_path, 'src') - ) - - -def test_generate_sources_with_1_20_5_and_yarn_moves_sources_to_repo(mocker, versions, yarn_project): - root_path = 'foo' - decompiler_dir = os.path.join(root_path, 'DecompilerMC') - - mocker.patch( - 'subprocess.run', - return_value=SubprocessMock() - ) - mocker.patch('minecraft.source.click') - mocker.patch('shutil.rmtree') - mocker.patch('os.makedirs') - move = mocker.patch('shutil.move') - - generate_sources(versions.v1_20_5, 'yarn', root_path) - - decompiler_dir = os.path.join(root_path, 'yarn') - move.assert_called_once_with( - os.path.join(decompiler_dir, 'src'), - os.path.join(root_path, 'src') - ) - - -def test_generate_sources_with_mojang_runs_decompiler(mocker, versions, decompiler_mc_repo): - root_path = 'foo' - decompiler_dir = os.path.join(root_path, 'DecompilerMC') - - subprocess_run = mocker.patch( - 'subprocess.run', - return_value=SubprocessMock() - ) - mocker.patch('minecraft.source.click') - mocker.patch('shutil.rmtree') - mocker.patch('shutil.move') - mocker.patch('os.makedirs') - - generate_sources(versions.snapshot, 'mojang', root_path) - - subprocess_run.assert_called_once_with( - [ - 'python3', - 'main.py', - '--mcv', - str(versions.snapshot), - '-s', - 'client', - '-c', - '-f', - '-q', - ], - stderr=subprocess.PIPE, - cwd=decompiler_dir - ) - - -def test_generate_sources_with_mojang_removes_old_decompiler_dir_if_exists(mocker, versions, decompiler_mc_repo): - root_path = 'foo' - old_decompiler_dir = os.path.join(root_path, '.DecompilerMC') - - mocker.patch( - 'subprocess.run', - return_value=SubprocessMock() - ) - mocker.patch('minecraft.source.click') - mocker.patch('shutil.move') - mocker.patch('os.makedirs') - mocker.patch('os.path.exists', return_value=True) - - rmtree = mocker.patch('shutil.rmtree') - - generate_sources(versions.snapshot, 'mojang', root_path) - - rmtree.assert_any_call(old_decompiler_dir) - - -def test_generate_sources_with_mojang_moves_sources_to_repo(mocker, versions, decompiler_mc_repo): - root_path = 'foo' - decompiler_dir = os.path.join(root_path, 'DecompilerMC') - - mocker.patch( - 'subprocess.run', - return_value=SubprocessMock() - ) - mocker.patch('minecraft.source.click') - mocker.patch('shutil.rmtree') - mocker.patch('os.makedirs') - move = mocker.patch('shutil.move') - - generate_sources(versions.snapshot, 'mojang', root_path) - - move.assert_called_once_with( - os.path.join(decompiler_dir, 'src', str(versions.snapshot), 'client'), - os.path.join(root_path, 'src') - ) + +def test_generate_sources_with_yarn_removes_old_decompiler_dir_if_exists( + mocker, versions, yarn_project +): + root_path = "foo" + old_decompiler_dir = os.path.join(root_path, ".yarn") + + mocker.patch("subprocess.run", return_value=SubprocessMock()) + mocker.patch("minecraft.source.click") + mocker.patch("shutil.move") + mocker.patch("os.makedirs") + mocker.patch("os.path.exists", return_value=True) + + rmtree = mocker.patch("shutil.rmtree") + + generate_sources(versions.snapshot, "yarn", root_path) + + rmtree.assert_any_call(old_decompiler_dir) + + +def test_generate_sources_with_1_20_4_and_yarn_moves_sources_to_repo( + mocker, versions, yarn_project +): + root_path = "foo" + decompiler_dir = os.path.join(root_path, "DecompilerMC") + + mocker.patch("subprocess.run", return_value=SubprocessMock()) + mocker.patch("minecraft.source.click") + mocker.patch("shutil.rmtree") + mocker.patch("os.makedirs") + move = mocker.patch("shutil.move") + + generate_sources(versions.v1_20_4, "yarn", root_path) + + decompiler_dir = os.path.join(root_path, "yarn") + move.assert_called_once_with( + os.path.join(decompiler_dir, "namedSrc"), os.path.join(root_path, "src") + ) + + +def test_generate_sources_with_1_20_5_and_yarn_moves_sources_to_repo( + mocker, versions, yarn_project +): + root_path = "foo" + decompiler_dir = os.path.join(root_path, "DecompilerMC") + + mocker.patch("subprocess.run", return_value=SubprocessMock()) + mocker.patch("minecraft.source.click") + mocker.patch("shutil.rmtree") + mocker.patch("os.makedirs") + move = mocker.patch("shutil.move") + + generate_sources(versions.v1_20_5, "yarn", root_path) + + decompiler_dir = os.path.join(root_path, "yarn") + move.assert_called_once_with( + os.path.join(decompiler_dir, "src"), os.path.join(root_path, "src") + ) + + +def test_generate_sources_with_mojang_runs_decompiler( + mocker, versions, decompiler_mc_repo +): + root_path = "foo" + decompiler_dir = os.path.join(root_path, "DecompilerMC") + + subprocess_run = mocker.patch("subprocess.run", return_value=SubprocessMock()) + mocker.patch("minecraft.source.click") + mocker.patch("shutil.rmtree") + mocker.patch("shutil.move") + mocker.patch("os.makedirs") + + generate_sources(versions.snapshot, "mojang", root_path) + + subprocess_run.assert_called_once_with( + [ + "python3", + "main.py", + "--mcv", + str(versions.snapshot), + "-s", + "client", + "-c", + "-f", + "-q", + ], + stderr=subprocess.PIPE, + cwd=decompiler_dir, + ) + + +def test_generate_sources_with_mojang_removes_old_decompiler_dir_if_exists( + mocker, versions, decompiler_mc_repo +): + root_path = "foo" + old_decompiler_dir = os.path.join(root_path, ".DecompilerMC") + + mocker.patch("subprocess.run", return_value=SubprocessMock()) + mocker.patch("minecraft.source.click") + mocker.patch("shutil.move") + mocker.patch("os.makedirs") + mocker.patch("os.path.exists", return_value=True) + + rmtree = mocker.patch("shutil.rmtree") + + generate_sources(versions.snapshot, "mojang", root_path) + + rmtree.assert_any_call(old_decompiler_dir) + + +def test_generate_sources_with_mojang_moves_sources_to_repo( + mocker, versions, decompiler_mc_repo +): + root_path = "foo" + decompiler_dir = os.path.join(root_path, "DecompilerMC") + + mocker.patch("subprocess.run", return_value=SubprocessMock()) + mocker.patch("minecraft.source.click") + mocker.patch("shutil.rmtree") + mocker.patch("os.makedirs") + move = mocker.patch("shutil.move") + + generate_sources(versions.snapshot, "mojang", root_path) + + move.assert_called_once_with( + os.path.join(decompiler_dir, "src", str(versions.snapshot), "client"), + os.path.join(root_path, "src"), + ) diff --git a/tests/minecraft/unit/test_version.py b/tests/minecraft/unit/test_version.py index 1109b71..c2420a7 100644 --- a/tests/minecraft/unit/test_version.py +++ b/tests/minecraft/unit/test_version.py @@ -4,76 +4,123 @@ class TestVersion: - def test_next_of_snapshot_is_the_release(self, versions): - assert versions.snapshot.next == versions.release - - def test_next_of_release_is_none(self, versions): - assert versions.release.next is None - - def test_release_does_not_equal_snapshot(self, versions): - assert versions.release != versions.snapshot - - def test_snapshot_is_less_than_release(self, versions): - assert versions.snapshot < versions.release - - def test_release_is_greater_than_snapshot(self, versions): - assert versions.release > versions.snapshot - - def test_snapshot_to_release_returns_list_containing_snapshot_and_release(self, versions): - assert versions.snapshot.to(versions.release) == [versions.snapshot, versions.release] - - def test_snapshot_to_snapshot_returns_list_containing_snapshot(self, versions): - assert versions.snapshot.to(versions.snapshot) == [versions.snapshot] - - def test_snapshot_to_none_returns_list_containing_snapshot_and_release(self, versions): - assert versions.snapshot.to(None) == [versions.snapshot, versions.release] - - def test_snapshot_to_release_excluding_snapshots_returns_list_containing_release(self, versions): - assert versions.snapshot.to(versions.release, snapshots=False) == [versions.release] - - def test_release_to_snapshot_raises_version_error(self, versions): - with pytest.raises(ValueError): - versions.release.to(versions.snapshot) - - def test_pattern_with_no_range_operator_returns_list_containing_version(self, versions): - assert Version.pattern('1.20.5') == [versions.release] - - def test_pattern_with_snapshot_two_dots_returns_list_containing_release(self, versions): - assert Version.pattern('abcdef..') == [versions.release] - - def test_pattern_with_snapshot_three_dots_returns_list_containing_snapshot_and_release(self, versions): - assert Version.pattern('abcdef...') == [versions.snapshot, versions.release] - - def test_pattern_with_release_two_dots_returns_list_containing_release(self, versions): - assert Version.pattern('1.20.5..') == [versions.release] - - def test_pattern_with_release_two_dots_release_returns_list_containing_release(self, versions): - assert Version.pattern('1.20.5..1.20.5') == [versions.release] - - def test_pattern_with_snapshot_three_dots_release_returns_list_containing_snapshot_and_release(self, versions): - assert Version.pattern('abcdef...1.20.5') == [versions.snapshot, versions.release] - - def test_pattern_with_three_dots_on_empty_repo_throws_value_error(self, versions): - with pytest.raises(ValueError, match='No commits from which to derive current version'): - Version.pattern('...', latest_in_repo=None) == [versions.snapshot, versions.release] - - def test_pattern_with_two_dots_on_repo_with_snapshot_returns_list_containing_release(self, versions): - assert Version.pattern('..', latest_in_repo=versions.snapshot) == [versions.release] - - def test_pattern_with_three_dots_on_repo_with_snapshot_returns_list_containing_release(self, versions): - assert Version.pattern('...', latest_in_repo=versions.snapshot) == [versions.release] - - def test_patterns_with_empty_list_returns_empty_list(self, versions): - assert Version.patterns([]) == [] - - def test_patterns_with_one_positive_id_returns_corresponding_version(self, versions): - assert Version.patterns(['1.20.5']) == [versions.release] - - def test_patterns_with_two_positive_identical_ids_returns_one_version(self, versions): - assert Version.patterns(['1.20.5', '1.20.5']) == [versions.release] - - def test_patterns_with_one_positive_id_and_the_same_negative_id_returns_empty_list(self, versions): - assert Version.patterns(['1.20.5', '-1.20.5']) == [] - - def test_patterns_with_one_positive_id_and_the_same_negative_id_and_the_same_positive_id_returns_one_version(self, versions): - assert Version.patterns(['1.20.5', '-1.20.5', '1.20.5']) == [versions.release] + def test_next_of_snapshot_is_the_release(self, versions): + assert versions.snapshot.next == versions.release + + def test_next_of_release_is_none(self, versions): + assert versions.release.next is None + + def test_release_does_not_equal_snapshot(self, versions): + assert versions.release != versions.snapshot + + def test_snapshot_is_less_than_release(self, versions): + assert versions.snapshot < versions.release + + def test_release_is_greater_than_snapshot(self, versions): + assert versions.release > versions.snapshot + + def test_snapshot_to_release_returns_list_containing_snapshot_and_release( + self, versions + ): + assert versions.snapshot.to(versions.release) == [ + versions.snapshot, + versions.release, + ] + + def test_snapshot_to_snapshot_returns_list_containing_snapshot(self, versions): + assert versions.snapshot.to(versions.snapshot) == [versions.snapshot] + + def test_snapshot_to_none_returns_list_containing_snapshot_and_release( + self, versions + ): + assert versions.snapshot.to(None) == [versions.snapshot, versions.release] + + def test_snapshot_to_release_excluding_snapshots_returns_list_containing_release( + self, versions + ): + assert versions.snapshot.to(versions.release, snapshots=False) == [ + versions.release + ] + + def test_release_to_snapshot_raises_version_error(self, versions): + with pytest.raises(ValueError): + versions.release.to(versions.snapshot) + + def test_pattern_with_no_range_operator_returns_list_containing_version( + self, versions + ): + assert Version.pattern("1.20.5") == [versions.release] + + def test_pattern_with_snapshot_two_dots_returns_list_containing_release( + self, versions + ): + assert Version.pattern("abcdef..") == [versions.release] + + def test_pattern_with_snapshot_three_dots_returns_list_containing_snapshot_and_release( + self, versions + ): + assert Version.pattern("abcdef...") == [versions.snapshot, versions.release] + + def test_pattern_with_release_two_dots_returns_list_containing_release( + self, versions + ): + assert Version.pattern("1.20.5..") == [versions.release] + + def test_pattern_with_release_two_dots_release_returns_list_containing_release( + self, versions + ): + assert Version.pattern("1.20.5..1.20.5") == [versions.release] + + def test_pattern_with_snapshot_three_dots_release_returns_list_containing_snapshot_and_release( + self, versions + ): + assert Version.pattern("abcdef...1.20.5") == [ + versions.snapshot, + versions.release, + ] + + def test_pattern_with_three_dots_on_empty_repo_throws_value_error(self, versions): + with pytest.raises( + ValueError, match="No commits from which to derive current version" + ): + Version.pattern("...", latest_in_repo=None) == [ + versions.snapshot, + versions.release, + ] + + def test_pattern_with_two_dots_on_repo_with_snapshot_returns_list_containing_release( + self, versions + ): + assert Version.pattern("..", latest_in_repo=versions.snapshot) == [ + versions.release + ] + + def test_pattern_with_three_dots_on_repo_with_snapshot_returns_list_containing_release( + self, versions + ): + assert Version.pattern("...", latest_in_repo=versions.snapshot) == [ + versions.release + ] + + def test_patterns_with_empty_list_returns_empty_list(self, versions): + assert Version.patterns([]) == [] + + def test_patterns_with_one_positive_id_returns_corresponding_version( + self, versions + ): + assert Version.patterns(["1.20.5"]) == [versions.release] + + def test_patterns_with_two_positive_identical_ids_returns_one_version( + self, versions + ): + assert Version.patterns(["1.20.5", "1.20.5"]) == [versions.release] + + def test_patterns_with_one_positive_id_and_the_same_negative_id_returns_empty_list( + self, versions + ): + assert Version.patterns(["1.20.5", "-1.20.5"]) == [] + + def test_patterns_with_one_positive_id_and_the_same_negative_id_and_the_same_positive_id_returns_one_version( + self, versions + ): + assert Version.patterns(["1.20.5", "-1.20.5", "1.20.5"]) == [versions.release] diff --git a/tests/mint/smoke/conftest.py b/tests/mint/smoke/conftest.py index 8b07a45..689609a 100644 --- a/tests/mint/smoke/conftest.py +++ b/tests/mint/smoke/conftest.py @@ -6,15 +6,15 @@ @pytest.fixture def tempdir(): - with TemporaryDirectory() as tempdir: - yield tempdir + with TemporaryDirectory() as tempdir: + yield tempdir @pytest.fixture def repo(tempdir): - yield Repo.init(tempdir) + yield Repo.init(tempdir) @pytest.fixture def shallow_cloned_repo(tempdir): - yield Repo.clone('https://github.com/clabe45/shulkr.git', tempdir, depth=1) + yield Repo.clone("https://github.com/clabe45/shulkr.git", tempdir, depth=1) diff --git a/tests/mint/smoke/test_repo.py b/tests/mint/smoke/test_repo.py index b15c7a3..f3fd01e 100644 --- a/tests/mint/smoke/test_repo.py +++ b/tests/mint/smoke/test_repo.py @@ -2,10 +2,10 @@ class TestRepo: - def test_init_creates_git_directory(self, repo): - git_dir = os.path.join(repo.path, '.git') - assert os.path.exists(git_dir) + def test_init_creates_git_directory(self, repo): + git_dir = os.path.join(repo.path, ".git") + assert os.path.exists(git_dir) - def test_clone_creates_git_directory(self, shallow_cloned_repo): - git_dir = os.path.join(shallow_cloned_repo.path, '.git') - assert os.path.exists(git_dir) + def test_clone_creates_git_directory(self, shallow_cloned_repo): + git_dir = os.path.join(shallow_cloned_repo.path, ".git") + assert os.path.exists(git_dir) diff --git a/tests/mint/unit/test_repo.py b/tests/mint/unit/test_repo.py index 3cda903..85e6a0e 100644 --- a/tests/mint/unit/test_repo.py +++ b/tests/mint/unit/test_repo.py @@ -5,16 +5,16 @@ @pytest.fixture(autouse=True) def fs(mocker) -> None: - mocker.patch('mint.repo.os.path.exists', return_value=True) - mocker.patch('mint.repo.os.path.isfile', return_value=False) + mocker.patch("mint.repo.os.path.exists", return_value=True) + mocker.patch("mint.repo.os.path.isfile", return_value=False) @pytest.fixture(autouse=True) def command(mocker) -> None: - mocker.patch('mint.repo.Command') + mocker.patch("mint.repo.Command") class TestRepo: - def test_clone_returns_repo_with_same_path(self): - repo = Repo.clone('bar.git', 'bar') - assert repo.path == 'bar' + def test_clone_returns_repo_with_same_path(self): + repo = Repo.clone("bar.git", "bar") + assert repo.path == "bar" diff --git a/tests/shulkr/functional/conftest.py b/tests/shulkr/functional/conftest.py index 3beff8a..da6723a 100644 --- a/tests/shulkr/functional/conftest.py +++ b/tests/shulkr/functional/conftest.py @@ -9,84 +9,78 @@ class RunParams: - def __init__( - self, - versions: List[str], - repo_path: str, - undo_renamed_vars: bool - ) -> None: + def __init__( + self, versions: List[str], repo_path: str, undo_renamed_vars: bool + ) -> None: - self.versions = versions - self.repo_path = repo_path - self.undo_renamed_vars = undo_renamed_vars + self.versions = versions + self.repo_path = repo_path + self.undo_renamed_vars = undo_renamed_vars @pytest.fixture(autouse=True) def manifest(): - load_manifest() - yield - clear_manifest() + load_manifest() + yield + clear_manifest() @pytest.fixture def repo(mocker): - tmp_dir = tempfile.TemporaryDirectory(prefix='shulkr-test') - repo = Repo.init(tmp_dir.name) - mocker.patch('shulkr.repo.repo', repo) + tmp_dir = tempfile.TemporaryDirectory(prefix="shulkr-test") + repo = Repo.init(tmp_dir.name) + mocker.patch("shulkr.repo.repo", repo) - yield repo + yield repo - # tmp_dir is removed when it goes out of scope + # tmp_dir is removed when it goes out of scope def create_command( - versions: List[str], - mappings: str, - repo_path: str, - undo_renamed_vars: bool + versions: List[str], mappings: str, repo_path: str, undo_renamed_vars: bool ) -> List[str]: - command = ['pipenv', 'run', 'start', '-p', repo_path, '--mappings', mappings] - if undo_renamed_vars: - command.append('-u') + command = ["pipenv", "run", "start", "-p", repo_path, "--mappings", mappings] + if undo_renamed_vars: + command.append("-u") - command.extend(versions) + command.extend(versions) - return command + return command def _run(versions: List[str], mappings: str, undo_renamed_vars: bool) -> None: - with tempfile.TemporaryDirectory(prefix='shulkr') as repo_path: - command = create_command(versions, mappings, repo_path, undo_renamed_vars) - p = subprocess.run(command, stderr=subprocess.PIPE) + with tempfile.TemporaryDirectory(prefix="shulkr") as repo_path: + command = create_command(versions, mappings, repo_path, undo_renamed_vars) + p = subprocess.run(command, stderr=subprocess.PIPE) - if p.returncode != 0: - raise Exception(p.stderr.decode()) + if p.returncode != 0: + raise Exception(p.stderr.decode()) - yield RunParams(versions, repo_path, undo_renamed_vars) + yield RunParams(versions, repo_path, undo_renamed_vars) @pytest.fixture( - scope='session', - params=[ - # Testing every combination of mappings to undo_variable_renames will - # take too long, so just mix and match - (['1.17.1', '1.18'], 'mojang', False), - ] + scope="session", + params=[ + # Testing every combination of mappings to undo_variable_renames will + # take too long, so just mix and match + (["1.17.1", "1.18"], "mojang", False), + ], ) def run_mojang(request): - versions, mappings, undo_variable_renames = request.param - yield from _run(versions, mappings, undo_variable_renames) + versions, mappings, undo_variable_renames = request.param + yield from _run(versions, mappings, undo_variable_renames) @pytest.fixture( - scope='session', - params=[ - # Testing every combination of mappings to undo_variable_renames will - # take too long, so just mix and match - (['1.17.1', '1.18'], 'yarn', True), - ] + scope="session", + params=[ + # Testing every combination of mappings to undo_variable_renames will + # take too long, so just mix and match + (["1.17.1", "1.18"], "yarn", True), + ], ) def run_yarn(request): - versions, mappings, undo_variable_renames = request.param - yield from _run(versions, mappings, undo_variable_renames) + versions, mappings, undo_variable_renames = request.param + yield from _run(versions, mappings, undo_variable_renames) diff --git a/tests/shulkr/functional/minecraft/test_version.py b/tests/shulkr/functional/minecraft/test_version.py index 42be80e..2cc125f 100644 --- a/tests/shulkr/functional/minecraft/test_version.py +++ b/tests/shulkr/functional/minecraft/test_version.py @@ -4,35 +4,39 @@ from shulkr.version import get_latest_generated_version -def test_get_latest_generated_version_with_repo_with_two_versions_returns_newer_version(repo: Repo): - # Add two tagged commits (Minecraft versions) - repo.git.commit(message='1.17', allow_empty=True) - repo.git.tag('1.17') +def test_get_latest_generated_version_with_repo_with_two_versions_returns_newer_version( + repo: Repo, +): + # Add two tagged commits (Minecraft versions) + repo.git.commit(message="1.17", allow_empty=True) + repo.git.tag("1.17") - repo.git.commit(message='1.18', allow_empty=True) - repo.git.tag('1.18') + repo.git.commit(message="1.18", allow_empty=True) + repo.git.tag("1.18") - # The latest generated version from HEAD should be 1.18 - assert get_latest_generated_version() == Version.of('1.18') + # The latest generated version from HEAD should be 1.18 + assert get_latest_generated_version() == Version.of("1.18") -def test_get_latest_generated_version_with_repo_with_two_versions_after_checking_out_older_version_returns_newer_version(repo: Repo): - # Add two tagged commits (Minecraft versions) - repo.git.commit(message='1.17', allow_empty=True) - repo.git.tag('1.17') +def test_get_latest_generated_version_with_repo_with_two_versions_after_checking_out_older_version_returns_newer_version( + repo: Repo, +): + # Add two tagged commits (Minecraft versions) + repo.git.commit(message="1.17", allow_empty=True) + repo.git.tag("1.17") - repo.git.commit(message='1.18', allow_empty=True) - repo.git.tag('1.18') + repo.git.commit(message="1.18", allow_empty=True) + repo.git.tag("1.18") - # Get the name of the current (only) branch - orig_branch = repo.git.rev_parse('HEAD', abbrev_ref=True) + # Get the name of the current (only) branch + orig_branch = repo.git.rev_parse("HEAD", abbrev_ref=True) - # Now, check out 1.17 (this used to cause get_latest_generated_version to - # return 1.17 instead of 1.18) - repo.git.checkout('1.17') + # Now, check out 1.17 (this used to cause get_latest_generated_version to + # return 1.17 instead of 1.18) + repo.git.checkout("1.17") - # Return to 1.18, without checking it out directly (important) - repo.git.checkout(orig_branch) + # Return to 1.18, without checking it out directly (important) + repo.git.checkout(orig_branch) - # The latest generated version from HEAD should be 1.18 - assert get_latest_generated_version() == Version.of('1.18') + # The latest generated version from HEAD should be 1.18 + assert get_latest_generated_version() == Version.of("1.18") diff --git a/tests/shulkr/functional/test_file_system.py b/tests/shulkr/functional/test_file_system.py index a454bd0..dad45e6 100644 --- a/tests/shulkr/functional/test_file_system.py +++ b/tests/shulkr/functional/test_file_system.py @@ -1,19 +1,27 @@ import os -def test_when_running_with_yarn_repo_only_contains_git_gitignore_yarn_and_src_directories(run_yarn): - assert set(os.listdir(run_yarn.repo_path)) == set(['.git', '.gitignore', '.shulkr', 'yarn', 'src']) +def test_when_running_with_yarn_repo_only_contains_git_gitignore_yarn_and_src_directories( + run_yarn, +): + assert set(os.listdir(run_yarn.repo_path)) == set( + [".git", ".gitignore", ".shulkr", "yarn", "src"] + ) def test_when_running_with_yarn_src_directory_is_not_empty(run_yarn): - src_dir = os.path.join(run_yarn.repo_path, 'src') - assert len(os.listdir(src_dir)) > 0 + src_dir = os.path.join(run_yarn.repo_path, "src") + assert len(os.listdir(src_dir)) > 0 -def test_when_running_with_decompilermc_repo_only_contains_git_gitignore_yarn_client_and_server_directories(run_mojang): - assert set(os.listdir(run_mojang.repo_path)) == set(['.git', '.gitignore', '.shulkr', 'DecompilerMC', 'src']) +def test_when_running_with_decompilermc_repo_only_contains_git_gitignore_yarn_client_and_server_directories( + run_mojang, +): + assert set(os.listdir(run_mojang.repo_path)) == set( + [".git", ".gitignore", ".shulkr", "DecompilerMC", "src"] + ) def test_when_running_with_decompilermc_src_directory_is_not_empty(run_mojang): - src_dir = os.path.join(run_mojang.repo_path, 'src') - assert len(os.listdir(src_dir)) > 0 + src_dir = os.path.join(run_mojang.repo_path, "src") + assert len(os.listdir(src_dir)) > 0 diff --git a/tests/shulkr/functional/test_git.py b/tests/shulkr/functional/test_git.py index ba26fed..2299bbf 100644 --- a/tests/shulkr/functional/test_git.py +++ b/tests/shulkr/functional/test_git.py @@ -2,60 +2,62 @@ def test_git_repo_initiated(run_yarn): - # Make sure this is a git repo. If it's not, an error will be thrown. - Repo(run_yarn.repo_path) + # Make sure this is a git repo. If it's not, an error will be thrown. + Repo(run_yarn.repo_path) def test_commits_created(run_yarn): - repo = Repo(run_yarn.repo_path) - history = repo.git.rev_list('HEAD').splitlines() - actual = [repo.git.log("--format=%B", commit, n=1) for commit in history] - actual.reverse() - - # Calculate expected commit messages from versions - expected = ['add .shulkr', 'add .gitignore'] - if run_yarn.undo_renamed_vars: - expected.append(f'version {run_yarn.versions[0]}') - expected.extend([ - f'version {version}\n\nRenamed variables reverted' - for version in run_yarn.versions[1:] - ]) - else: - expected.extend([f'version {version}' for version in run_yarn.versions]) - - assert actual == expected + repo = Repo(run_yarn.repo_path) + history = repo.git.rev_list("HEAD").splitlines() + actual = [repo.git.log("--format=%B", commit, n=1) for commit in history] + actual.reverse() + + # Calculate expected commit messages from versions + expected = ["add .shulkr", "add .gitignore"] + if run_yarn.undo_renamed_vars: + expected.append(f"version {run_yarn.versions[0]}") + expected.extend( + [ + f"version {version}\n\nRenamed variables reverted" + for version in run_yarn.versions[1:] + ] + ) + else: + expected.extend([f"version {version}" for version in run_yarn.versions]) + + assert actual == expected def test_when_running_with_yarn_gitignore_and_src_are_tracked(run_yarn): - repo = Repo(run_yarn.repo_path) + repo = Repo(run_yarn.repo_path) - # List files and directories that were changed directly under the root - actual = set(repo.git.ls_tree('HEAD', name_only=True).splitlines()) - expected = set(['.gitignore', '.shulkr', 'src']) - assert actual == expected + # List files and directories that were changed directly under the root + actual = set(repo.git.ls_tree("HEAD", name_only=True).splitlines()) + expected = set([".gitignore", ".shulkr", "src"]) + assert actual == expected def test_when_running_with_decompilermc_gitignore_src_are_tracked(run_mojang): - repo = Repo(run_mojang.repo_path) + repo = Repo(run_mojang.repo_path) - # List files and directories that were changed directly under the root - actual = set(repo.git.ls_tree('HEAD', name_only=True).splitlines()) - expected = set(['.gitignore', '.shulkr', 'src']) - assert actual == expected + # List files and directories that were changed directly under the root + actual = set(repo.git.ls_tree("HEAD", name_only=True).splitlines()) + expected = set([".gitignore", ".shulkr", "src"]) + assert actual == expected def test_tags_created(run_yarn): - repo = Repo(run_yarn.repo_path) - actual = set(repo.git.tag(list=True).splitlines()) - expected = set(run_yarn.versions) - assert actual == expected + repo = Repo(run_yarn.repo_path) + actual = set(repo.git.tag(list=True).splitlines()) + expected = set(run_yarn.versions) + assert actual == expected def test_when_running_with_yarn_working_tree_clean(run_yarn): - repo = Repo(run_yarn.repo_path) - assert len(repo.git.status(porcelain=True)) == 0 + repo = Repo(run_yarn.repo_path) + assert len(repo.git.status(porcelain=True)) == 0 def test_when_running_with_decompilermc_working_tree_clean(run_mojang): - repo = Repo(run_mojang.repo_path) - assert len(repo.git.status(porcelain=True)) == 0 + repo = Repo(run_mojang.repo_path) + assert len(repo.git.status(porcelain=True)) == 0 diff --git a/tests/shulkr/profile/__main__.py b/tests/shulkr/profile/__main__.py index e31fdc6..611f355 100644 --- a/tests/shulkr/profile/__main__.py +++ b/tests/shulkr/profile/__main__.py @@ -1,5 +1,5 @@ from . import profile_get_renamed_variables -if __name__ == '__main__': - profile_get_renamed_variables() +if __name__ == "__main__": + profile_get_renamed_variables() diff --git a/tests/shulkr/profile/profile_java.py b/tests/shulkr/profile/profile_java.py index 7e3d629..8c82aed 100644 --- a/tests/shulkr/profile/profile_java.py +++ b/tests/shulkr/profile/profile_java.py @@ -4,50 +4,52 @@ def wrap_in_class(code: str) -> str: - return """package foo; + return ( + """package foo; class Foo { public static void main(String[] args) { %s } } - """ % code + """ + % code + ) def profile_get_renamed_variables(): - SAMPLES = [ - ('int x = 0;', 'int x = 0;'), - ('int x = 0;', 'int y = 0;'), - ('int x, y, z;', 'int a, b, c;'), - ('int x; int y; int z;', 'int a; int b; int c;'), - ( - 'if (true) { int x = 0; } else { int x = 0; }', - 'if (true) { int x = 0; } else { int x = 0; }' - ), - ( - '; '.join([f'int x{i}' for i in range(100)]) + ';', - '; '.join([f'int x{i}' for i in range(100)]) + ';' - ), - ( - '; '.join([f'int x{i}' for i in range(100)]) + ';', - '; '.join([f'int y{i}' for i in range(100)]) + ';' - ) - ] - - total_time = 0.0 - - for before, after in SAMPLES: - print('---------------') - print(before, end='\n') - print(after, end='\n') - before_wrapped = wrap_in_class(before) - after_wrapped = wrap_in_class(after) - duration = timeit( - lambda: get_renamed_variables(before_wrapped, after_wrapped), - number=100 - ) - total_time += duration - print(f'{duration}s', end='\n\n') - - print('---------------') - print(f'{total_time}s') + SAMPLES = [ + ("int x = 0;", "int x = 0;"), + ("int x = 0;", "int y = 0;"), + ("int x, y, z;", "int a, b, c;"), + ("int x; int y; int z;", "int a; int b; int c;"), + ( + "if (true) { int x = 0; } else { int x = 0; }", + "if (true) { int x = 0; } else { int x = 0; }", + ), + ( + "; ".join([f"int x{i}" for i in range(100)]) + ";", + "; ".join([f"int x{i}" for i in range(100)]) + ";", + ), + ( + "; ".join([f"int x{i}" for i in range(100)]) + ";", + "; ".join([f"int y{i}" for i in range(100)]) + ";", + ), + ] + + total_time = 0.0 + + for before, after in SAMPLES: + print("---------------") + print(before, end="\n") + print(after, end="\n") + before_wrapped = wrap_in_class(before) + after_wrapped = wrap_in_class(after) + duration = timeit( + lambda: get_renamed_variables(before_wrapped, after_wrapped), number=100 + ) + total_time += duration + print(f"{duration}s", end="\n\n") + + print("---------------") + print(f"{total_time}s") diff --git a/tests/shulkr/smoke/conftest.py b/tests/shulkr/smoke/conftest.py index ab85f38..37bd8f2 100644 --- a/tests/shulkr/smoke/conftest.py +++ b/tests/shulkr/smoke/conftest.py @@ -9,61 +9,57 @@ class RunResult: - def __init__(self, exit_code: int, output: str, error: str) -> None: - """ - Args: - status_code (int): The exit code of the command. - output (str): Stdout - error (str): Stderr - """ + def __init__(self, exit_code: int, output: str, error: str) -> None: + """ + Args: + status_code (int): The exit code of the command. + output (str): Stdout + error (str): Stderr + """ - self.exit_code = exit_code - self.output = output - self.error = error + self.exit_code = exit_code + self.output = output + self.error = error @pytest.fixture(autouse=True) def manifest(): - load_manifest() - yield - clear_manifest() + load_manifest() + yield + clear_manifest() @pytest.fixture def repo(mocker): - tmp_dir = tempfile.TemporaryDirectory(prefix='shulkr-test') - repo = Repo.init(tmp_dir.name) - mocker.patch('shulkr.repo.repo', repo) + tmp_dir = tempfile.TemporaryDirectory(prefix="shulkr-test") + repo = Repo.init(tmp_dir.name) + mocker.patch("shulkr.repo.repo", repo) - yield repo + yield repo - # tmp_dir is removed when it goes out of scope + # tmp_dir is removed when it goes out of scope def create_command(repo_path: str) -> List[str]: - return ['pipenv', 'run', 'start', '-p', repo_path] + return ["pipenv", "run", "start", "-p", repo_path] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def run() -> Generator[RunResult, None, None]: - """ - Run shulkr with no versions - - It is expected to display a warning message stating that no versions are - selected. - - Yields: - Stderr (if present), otherwise None - """ - - with tempfile.TemporaryDirectory(prefix='shulkr') as repo_path: - command = create_command(repo_path) - p = subprocess.run( - command, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) - - stdout = p.stdout.decode('utf-8') - stderr = p.stderr.decode('utf-8') - yield RunResult(p.returncode, stdout, stderr) + """ + Run shulkr with no versions + + It is expected to display a warning message stating that no versions are + selected. + + Yields: + Stderr (if present), otherwise None + """ + + with tempfile.TemporaryDirectory(prefix="shulkr") as repo_path: + command = create_command(repo_path) + p = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + stdout = p.stdout.decode("utf-8") + stderr = p.stderr.decode("utf-8") + yield RunResult(p.returncode, stdout, stderr) diff --git a/tests/shulkr/smoke/test_cli.py b/tests/shulkr/smoke/test_cli.py index acde48e..01d7f31 100644 --- a/tests/shulkr/smoke/test_cli.py +++ b/tests/shulkr/smoke/test_cli.py @@ -1,8 +1,8 @@ def test_exits_with_status_code_of_zero(run): - print('Exitted with non-zero status code:') - print(run.error) - assert run.exit_code == 0 + print("Exitted with non-zero status code:") + print(run.error) + assert run.exit_code == 0 def test_displays_error_for_no_versions(run): - assert 'No versions selected' in run.output + assert "No versions selected" in run.output diff --git a/tests/shulkr/unit/conftest.py b/tests/shulkr/unit/conftest.py index d129aad..65a5f16 100644 --- a/tests/shulkr/unit/conftest.py +++ b/tests/shulkr/unit/conftest.py @@ -9,163 +9,157 @@ def create_repo(mocker, path: str): - repo = MagicMock() + repo = MagicMock() - # Make the repo directory useful for testing shulkr.minecraft.source - repo.path = os.path.abspath(path) + # Make the repo directory useful for testing shulkr.minecraft.source + repo.path = os.path.abspath(path) - class MockGitCommand: - def add(): - pass + class MockGitCommand: + def add(): + pass - def checkout(): - pass + def checkout(): + pass - def clean(): - pass + def clean(): + pass - def commit(): - pass + def commit(): + pass - def describe(): - pass + def describe(): + pass - def fetch(): - pass + def fetch(): + pass - def reset(): - pass + def reset(): + pass - def tag(): - pass + def tag(): + pass - def rev_parse(): - pass + def rev_parse(): + pass - repo.git = mocker.create_autospec(MockGitCommand()) + repo.git = mocker.create_autospec(MockGitCommand()) - return repo + return repo @pytest.fixture def config(mocker): - """ - Use a fake config + """ + Use a fake config - The advantage of using this fixture over calling init_config is that this - fixture does not use any operating system resources. - """ + The advantage of using this fixture over calling init_config is that this + fixture does not use any operating system resources. + """ - config = Config( - repo_path=os.path.abspath('foo'), - mappings='mojang', - message_template='{}', - tag=True, - undo_renamed_vars=False - ) - mocker.patch('shulkr.config.config', config) + config = Config( + repo_path=os.path.abspath("foo"), + mappings="mojang", + message_template="{}", + tag=True, + undo_renamed_vars=False, + ) + mocker.patch("shulkr.config.config", config) - return config + return config @pytest.fixture def decompiler(mocker): - """ - Mock _setup_decompiler() to return a fake git repo with the current - path - """ + """ + Mock _setup_decompiler() to return a fake git repo with the current + path + """ - def mocked_setup_decompiler(local_dir: str, _remote_url: str) -> Repo: - """Create a fake decompiler subdirectory (yarn or DecompilerMC)""" + def mocked_setup_decompiler(local_dir: str, _remote_url: str) -> Repo: + """Create a fake decompiler subdirectory (yarn or DecompilerMC)""" - # It will be located directly under the shulkr repo directory - repo = get_repo() - decompiler_dir = os.path.join(repo.path, local_dir) + # It will be located directly under the shulkr repo directory + repo = get_repo() + decompiler_dir = os.path.join(repo.path, local_dir) - # Create a fake git repo, and set the path property - return create_repo(mocker, decompiler_dir) + # Create a fake git repo, and set the path property + return create_repo(mocker, decompiler_dir) - # Tell the generate_sources functions to use our fake decompiler creator - mocker.patch( - 'minecraft.source._setup_decompiler', - new=mocked_setup_decompiler - ) + # Tell the generate_sources functions to use our fake decompiler creator + mocker.patch("minecraft.source._setup_decompiler", new=mocked_setup_decompiler) @pytest.fixture def empty_repo(mocker, decompiler): - repo = create_repo(mocker, 'foo') + repo = create_repo(mocker, "foo") - # Throw error when `git rev-parse` is called - def rev_parse(*args, **kwargs): - raise GitError( - 'git rev-parse...', - stderr="fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree." - ) + # Throw error when `git rev-parse` is called + def rev_parse(*args, **kwargs): + raise GitError( + "git rev-parse...", + stderr="fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree.", + ) - mocker.patch.object(repo.git, 'rev_parse', side_effect=rev_parse) + mocker.patch.object(repo.git, "rev_parse", side_effect=rev_parse) - # Throw error when 'git describe' is called (it will only be called with - # --tags) - describe_error = GitError( - 'git describe...', - stderr='fatal: No names found, cannot describe anything.' - ) - mocker.patch.object(repo.git, 'describe', side_effect=describe_error) + # Throw error when 'git describe' is called (it will only be called with + # --tags) + describe_error = GitError( + "git describe...", stderr="fatal: No names found, cannot describe anything." + ) + mocker.patch.object(repo.git, "describe", side_effect=describe_error) - # get_repo() will return this value - mocker.patch('shulkr.repo.repo', repo) + # get_repo() will return this value + mocker.patch("shulkr.repo.repo", repo) - return repo + return repo @pytest.fixture def nonempty_repo(mocker, decompiler): - repo = create_repo(mocker, 'foo') + repo = create_repo(mocker, "foo") - # Add a fake commit - mocker.patch.object( - repo.git, - 'rev_parse', - return_value=iter(['9e71573c6ae5a52195274871a679a23379ad1274']) - ) + # Add a fake commit + mocker.patch.object( + repo.git, + "rev_parse", + return_value=iter(["9e71573c6ae5a52195274871a679a23379ad1274"]), + ) - # Add a fake tag for that commit (it will only be called with --tags) - mocker.patch.object(repo.git, 'describe', return_value='abcdef') + # Add a fake tag for that commit (it will only be called with --tags) + mocker.patch.object(repo.git, "describe", return_value="abcdef") - # get_repo() will return this value - mocker.patch('shulkr.repo.repo', repo) + # get_repo() will return this value + mocker.patch("shulkr.repo.repo", repo) - return repo + return repo @pytest.fixture def yarn_mappings(config): - config = get_config() + config = get_config() - prev_mappings = config.mappings - config.mappings = 'yarn' + prev_mappings = config.mappings + config.mappings = "yarn" - yield + yield - config.mappings = prev_mappings + config.mappings = prev_mappings @pytest.fixture def mojang_mappings(config): - config = get_config() + config = get_config() - prev_mappings = config.mappings - config.mappings = 'mojang' + prev_mappings = config.mappings + config.mappings = "mojang" - yield + yield - config.mappings = prev_mappings + config.mappings = prev_mappings @pytest.fixture def root_dir(): - script_dir = os.path.dirname(__file__) - return os.path.realpath( - os.path.join(script_dir, '..', '..') - ) + script_dir = os.path.dirname(__file__) + return os.path.realpath(os.path.join(script_dir, "..", "..")) diff --git a/tests/shulkr/unit/test_app.py b/tests/shulkr/unit/test_app.py index 367a9d9..6ab72ee 100644 --- a/tests/shulkr/unit/test_app.py +++ b/tests/shulkr/unit/test_app.py @@ -6,163 +6,152 @@ @pytest.fixture def versions(): - return [ - Version(id='1.18', index=0), - Version(id='1.19', index=1) - ] + return [Version(id="1.18", index=0), Version(id="1.19", index=1)] @pytest.fixture(autouse=True) def mock_all(mocker, versions): - mocker.patch('shulkr.app.click') - mocker.patch('shulkr.app.load_manifest') - mocker.patch('shulkr.app.os') - mocker.patch('shulkr.app.init_repo') - mocker.patch('shulkr.app.is_compatible') - mocker.patch('shulkr.app.init_config') - mocker.patch('shulkr.app.ensure_gitignore_exists') - mocker.patch('shulkr.app.Version.patterns', return_value=versions) - mocker.patch('shulkr.app.get_latest_generated_version') - mocker.patch('shulkr.app.create_version') + mocker.patch("shulkr.app.click") + mocker.patch("shulkr.app.load_manifest") + mocker.patch("shulkr.app.os") + mocker.patch("shulkr.app.init_repo") + mocker.patch("shulkr.app.is_compatible") + mocker.patch("shulkr.app.init_config") + mocker.patch("shulkr.app.ensure_gitignore_exists") + mocker.patch("shulkr.app.Version.patterns", return_value=versions) + mocker.patch("shulkr.app.get_latest_generated_version") + mocker.patch("shulkr.app.create_version") def test_run_loads_version_manifest(): - app.run( - versions=[], - mappings='mappings', - repo_path='path/to/repo', - message_template='message', - tags=True, - undo_renamed_vars=True - ) + app.run( + versions=[], + mappings="mappings", + repo_path="path/to/repo", + message_template="message", + tags=True, + undo_renamed_vars=True, + ) - app.load_manifest.assert_called_once_with() + app.load_manifest.assert_called_once_with() def test_run_calls_init_repo(mocker): - app.os.path.join.return_value = 'full/path/to/repo' + app.os.path.join.return_value = "full/path/to/repo" - app.run( - versions=[], - mappings='mappings', - repo_path='path/to/repo', - message_template='message', - tags=True, - undo_renamed_vars=True - ) + app.run( + versions=[], + mappings="mappings", + repo_path="path/to/repo", + message_template="message", + tags=True, + undo_renamed_vars=True, + ) - app.init_repo.assert_called_once_with('full/path/to/repo') + app.init_repo.assert_called_once_with("full/path/to/repo") def test_run_with_unsupported_repo_exits_with_error(): - app.is_compatible.return_value = False + app.is_compatible.return_value = False - with pytest.raises(SystemExit, match='4'): - app.run( - versions=[], - mappings='mappings', - repo_path='path/to/repo', - message_template='message', - tags=True, - undo_renamed_vars=True - ) + with pytest.raises(SystemExit, match="4"): + app.run( + versions=[], + mappings="mappings", + repo_path="path/to/repo", + message_template="message", + tags=True, + undo_renamed_vars=True, + ) def test_run_calls_init_config_when_init_repo_returns_false(): - app.init_repo.return_value = False - app.os.path.join.return_value = 'full/path/to/repo' - - app.run( - versions=[], - mappings='mappings', - repo_path='path/to/repo', - message_template='message', - tags=True, - undo_renamed_vars=True - ) - - app.init_config.assert_called_once_with( - 'full/path/to/repo', - 'mappings', - 'message', - True, - True - ) + app.init_repo.return_value = False + app.os.path.join.return_value = "full/path/to/repo" + + app.run( + versions=[], + mappings="mappings", + repo_path="path/to/repo", + message_template="message", + tags=True, + undo_renamed_vars=True, + ) + + app.init_config.assert_called_once_with( + "full/path/to/repo", "mappings", "message", True, True + ) def test_run_calls_init_config_when_init_repo_returns_true(): - app.init_repo.return_value = True - app.os.path.join.return_value = 'full/path/to/repo' - - app.run( - versions=[], - mappings='mappings', - repo_path='path/to/repo', - message_template='message', - tags=True, - undo_renamed_vars=True - ) - - app.init_config.assert_called_once_with( - 'full/path/to/repo', - 'mappings', - 'message', - True, - True - ) + app.init_repo.return_value = True + app.os.path.join.return_value = "full/path/to/repo" + + app.run( + versions=[], + mappings="mappings", + repo_path="path/to/repo", + message_template="message", + tags=True, + undo_renamed_vars=True, + ) + + app.init_config.assert_called_once_with( + "full/path/to/repo", "mappings", "message", True, True + ) def test_run_calls_ensure_gitignore_exists(): - app.run( - versions=[], - mappings='mappings', - repo_path='path/to/repo', - message_template='message', - tags=True, - undo_renamed_vars=True - ) + app.run( + versions=[], + mappings="mappings", + repo_path="path/to/repo", + message_template="message", + tags=True, + undo_renamed_vars=True, + ) - app.ensure_gitignore_exists.assert_called_once_with() + app.ensure_gitignore_exists.assert_called_once_with() def test_run_with_version_older_than_latest_version_in_repo_exits_with_error(): - app.get_latest_generated_version.return_value = Version(id='1.18', index=1) - - with pytest.raises(SystemExit, match='3'): - app.run( - versions=[Version(id='1.17', index=0)], - mappings='mappings', - repo_path='path/to/repo', - message_template='message', - tags=True, - undo_renamed_vars=True - ) - - -def test_run_with_multiple_versions_calls_create_version_for_each_version(mocker, versions): - app.run( - versions=[], - mappings='mappings', - repo_path='path/to/repo', - message_template='message', - tags=True, - undo_renamed_vars=True - ) - - app.create_version.assert_has_calls([ - mocker.call(version) for version in versions - ]) + app.get_latest_generated_version.return_value = Version(id="1.18", index=1) + + with pytest.raises(SystemExit, match="3"): + app.run( + versions=[Version(id="1.17", index=0)], + mappings="mappings", + repo_path="path/to/repo", + message_template="message", + tags=True, + undo_renamed_vars=True, + ) + + +def test_run_with_multiple_versions_calls_create_version_for_each_version( + mocker, versions +): + app.run( + versions=[], + mappings="mappings", + repo_path="path/to/repo", + message_template="message", + tags=True, + undo_renamed_vars=True, + ) + + app.create_version.assert_has_calls([mocker.call(version) for version in versions]) def test_run_without_any_versions_exits(): - app.Version.patterns.return_value = [] - - with pytest.raises(SystemExit, match='0'): - app.run( - versions=[], - mappings='mappings', - repo_path='path/to/repo', - message_template='message', - tags=True, - undo_renamed_vars=True - ) + app.Version.patterns.return_value = [] + + with pytest.raises(SystemExit, match="0"): + app.run( + versions=[], + mappings="mappings", + repo_path="path/to/repo", + message_template="message", + tags=True, + undo_renamed_vars=True, + ) diff --git a/tests/shulkr/unit/test_compatibility.py b/tests/shulkr/unit/test_compatibility.py index 03bd5a5..822903e 100644 --- a/tests/shulkr/unit/test_compatibility.py +++ b/tests/shulkr/unit/test_compatibility.py @@ -6,19 +6,19 @@ @pytest.fixture(autouse=True) def mock_all(mocker): - mocker.patch('shulkr.compatibility.config_exists') + mocker.patch("shulkr.compatibility.config_exists") def test_is_compatible_with_no_config_and_empty_repo_returns_true(empty_repo): - shulkr.compatibility.config_exists.return_value = False - assert is_compatible() + shulkr.compatibility.config_exists.return_value = False + assert is_compatible() def test_is_compatible_with_no_config_and_nonempty_repo_returns_false(nonempty_repo): - shulkr.compatibility.config_exists.return_value = False - assert not is_compatible() + shulkr.compatibility.config_exists.return_value = False + assert not is_compatible() def test_is_compatible_with_config_and_nonempty_repo_returns_true(nonempty_repo): - shulkr.compatibility.config_exists.return_value = True - assert is_compatible() + shulkr.compatibility.config_exists.return_value = True + assert is_compatible() diff --git a/tests/shulkr/unit/test_config.py b/tests/shulkr/unit/test_config.py index e5bc541..479f7ec 100644 --- a/tests/shulkr/unit/test_config.py +++ b/tests/shulkr/unit/test_config.py @@ -4,155 +4,153 @@ class TestConfig: - def test_save_opens_config_file_for_writing(self, mocker): - # 1. Create a new configuration - config = Config( - repo_path='foo', - mappings='mojang', - message_template='{}', - tag=True, - undo_renamed_vars=False - ) - - # 2. Mock open() - open_ = mocker.patch('shulkr.config.open') - - # 3. Try to save the configuration to the disk - config.save() - - # 4. Make sure open() was called currectly - config_path = os.path.join('foo', '.shulkr') - open_.assert_called_once_with(config_path, 'w+') - - -def test_init_config_creates_new_configuration_with_provided_arguments_if_config_file_is_not_found(mocker, empty_repo): - # 1. Spy on Config constructor - Config_ = mocker.patch('shulkr.config.Config') - - # 2. Patch os.path.exists to return False - mocker.patch('shulkr.config.os.path.exists', return_value=False) - - # 3. Call init_config - init_config( - repo_path='foo', - mappings='mojang', - message_template='{}', - tag=True, - undo_renamed_vars=False - ) - - # 4. The Config constructor should have been called with the specified - # path and mappings - Config_.assert_called_once_with('foo', 'mojang', '{}', True, False) + def test_save_opens_config_file_for_writing(self, mocker): + # 1. Create a new configuration + config = Config( + repo_path="foo", + mappings="mojang", + message_template="{}", + tag=True, + undo_renamed_vars=False, + ) + + # 2. Mock open() + open_ = mocker.patch("shulkr.config.open") + + # 3. Try to save the configuration to the disk + config.save() + + # 4. Make sure open() was called currectly + config_path = os.path.join("foo", ".shulkr") + open_.assert_called_once_with(config_path, "w+") + + +def test_init_config_creates_new_configuration_with_provided_arguments_if_config_file_is_not_found( + mocker, empty_repo +): + # 1. Spy on Config constructor + Config_ = mocker.patch("shulkr.config.Config") + + # 2. Patch os.path.exists to return False + mocker.patch("shulkr.config.os.path.exists", return_value=False) + + # 3. Call init_config + init_config( + repo_path="foo", + mappings="mojang", + message_template="{}", + tag=True, + undo_renamed_vars=False, + ) + + # 4. The Config constructor should have been called with the specified + # path and mappings + Config_.assert_called_once_with("foo", "mojang", "{}", True, False) def test_init_config_commits_config_when_creating_new_one(mocker, empty_repo): - # 1. Stub out Config class - mocker.patch('shulkr.config.Config') + # 1. Stub out Config class + mocker.patch("shulkr.config.Config") - # 2. Mock 'git commit' - mocker.patch.object(empty_repo.git, 'commit') + # 2. Mock 'git commit' + mocker.patch.object(empty_repo.git, "commit") - # 3. No existing config should be found - mocker.patch('shulkr.config.os.path.exists', return_value=False) + # 3. No existing config should be found + mocker.patch("shulkr.config.os.path.exists", return_value=False) - # 4. Call init_config - init_config( - repo_path='foo', - mappings='mojang', - message_template='{}', - tag=True, - undo_renamed_vars=False - ) + # 4. Call init_config + init_config( + repo_path="foo", + mappings="mojang", + message_template="{}", + tag=True, + undo_renamed_vars=False, + ) - # 5. "git commit --message 'add .shulkr' .shulkr" - empty_repo.git.add.assert_called_once_with('.shulkr') - empty_repo.git.commit.assert_called_once_with(message='add .shulkr') + # 5. "git commit --message 'add .shulkr' .shulkr" + empty_repo.git.add.assert_called_once_with(".shulkr") + empty_repo.git.commit.assert_called_once_with(message="add .shulkr") def test_init_config_loads_existing_config_if_config_file_is_found(mocker): - # 1. Stub out the Config constructor - Config_ = mocker.patch('shulkr.config.Config') - - # 2. Add a fake config - # 2a. Patch os.path.exists to return True - mocker.patch('shulkr.config.os.path.exists', return_value=True) - - # 2b. Stub out open() - mocker.patch('shulkr.config.open') - - # 2c. Patch toml.load to return a dummy config file - raw_config = { - 'mappings': 'yarn', - 'message': 'Minecraft {}', - 'tag': False, - 'experimental': { - 'undo_renamed_vars': True - } - } - mocker.patch('shulkr.config.toml.load', return_value=raw_config) - - # 3. Call init_config - init_config( - repo_path='foo', - mappings='mojang', - message_template='{}', - tag=True, - undo_renamed_vars=False - ) - - # 4. The Config constructor should have been called with the path and - # mappings from the existing config - Config_.assert_called_once_with( - repo_path='foo', - mappings='yarn', - message_template='Minecraft {}', - tag=False, - undo_renamed_vars=True - ) + # 1. Stub out the Config constructor + Config_ = mocker.patch("shulkr.config.Config") + + # 2. Add a fake config + # 2a. Patch os.path.exists to return True + mocker.patch("shulkr.config.os.path.exists", return_value=True) + + # 2b. Stub out open() + mocker.patch("shulkr.config.open") + + # 2c. Patch toml.load to return a dummy config file + raw_config = { + "mappings": "yarn", + "message": "Minecraft {}", + "tag": False, + "experimental": {"undo_renamed_vars": True}, + } + mocker.patch("shulkr.config.toml.load", return_value=raw_config) + + # 3. Call init_config + init_config( + repo_path="foo", + mappings="mojang", + message_template="{}", + tag=True, + undo_renamed_vars=False, + ) + + # 4. The Config constructor should have been called with the path and + # mappings from the existing config + Config_.assert_called_once_with( + repo_path="foo", + mappings="yarn", + message_template="Minecraft {}", + tag=False, + undo_renamed_vars=True, + ) def test_init_config_returns_true_if_config_file_is_found(mocker): - # 1. Mock dependencies - mocker.patch('shulkr.config.Config') - mocker.patch('shulkr.config.os.path.exists', return_value=True) - mocker.patch('shulkr.config.open') - raw_config = { - 'mappings': 'yarn', - 'message': 'Minecraft {}', - 'tag': False, - 'experimental': { - 'undo_renamed_vars': True - } - } - mocker.patch('shulkr.config.toml.load', return_value=raw_config) - - # 2. Call init_config - result = init_config( - repo_path='foo', - mappings='mojang', - message_template='{}', - tag=True, - undo_renamed_vars=False - ) - - # 3. The result should be True - assert result + # 1. Mock dependencies + mocker.patch("shulkr.config.Config") + mocker.patch("shulkr.config.os.path.exists", return_value=True) + mocker.patch("shulkr.config.open") + raw_config = { + "mappings": "yarn", + "message": "Minecraft {}", + "tag": False, + "experimental": {"undo_renamed_vars": True}, + } + mocker.patch("shulkr.config.toml.load", return_value=raw_config) + + # 2. Call init_config + result = init_config( + repo_path="foo", + mappings="mojang", + message_template="{}", + tag=True, + undo_renamed_vars=False, + ) + + # 3. The result should be True + assert result def test_init_config_returns_false_if_config_file_is_not_found(mocker, empty_repo): - # 1. Mock dependencies - mocker.patch('shulkr.config.Config') - mocker.patch('shulkr.config.os.path.exists', return_value=False) - - # 2. Call init_config - result = init_config( - repo_path='foo', - mappings='mojang', - message_template='{}', - tag=True, - undo_renamed_vars=False - ) - - # 3. The result should be False - assert not result + # 1. Mock dependencies + mocker.patch("shulkr.config.Config") + mocker.patch("shulkr.config.os.path.exists", return_value=False) + + # 2. Call init_config + result = init_config( + repo_path="foo", + mappings="mojang", + message_template="{}", + tag=True, + undo_renamed_vars=False, + ) + + # 3. The result should be False + assert not result diff --git a/tests/shulkr/unit/test_gitignore.py b/tests/shulkr/unit/test_gitignore.py index 4a70dd9..36915cc 100644 --- a/tests/shulkr/unit/test_gitignore.py +++ b/tests/shulkr/unit/test_gitignore.py @@ -3,38 +3,42 @@ from shulkr.gitignore import ensure_gitignore_exists -def test_ensure_gitignore_exists_opens_gitignore_for_writing_if_it_does_not_exist(mocker, empty_repo): - mocker.patch('shulkr.gitignore.click') - mocker.patch('shulkr.gitignore.os.path.isfile', return_value=False) - open_ = mocker.patch('shulkr.gitignore.open') +def test_ensure_gitignore_exists_opens_gitignore_for_writing_if_it_does_not_exist( + mocker, empty_repo +): + mocker.patch("shulkr.gitignore.click") + mocker.patch("shulkr.gitignore.os.path.isfile", return_value=False) + open_ = mocker.patch("shulkr.gitignore.open") - ensure_gitignore_exists() + ensure_gitignore_exists() - gitignore_path = os.path.join(empty_repo.path, '.gitignore') - open_.assert_called_once_with(gitignore_path, 'w+') + gitignore_path = os.path.join(empty_repo.path, ".gitignore") + open_.assert_called_once_with(gitignore_path, "w+") -def test_ensure_gitignore_exists_does_not_open_gitignore_if_it_does_exist(mocker, empty_repo): - mocker.patch('shulkr.gitignore.click') - mocker.patch('shulkr.gitignore.os.path.isfile', return_value=True) - open_ = mocker.patch('shulkr.gitignore.open') +def test_ensure_gitignore_exists_does_not_open_gitignore_if_it_does_exist( + mocker, empty_repo +): + mocker.patch("shulkr.gitignore.click") + mocker.patch("shulkr.gitignore.os.path.isfile", return_value=True) + open_ = mocker.patch("shulkr.gitignore.open") - ensure_gitignore_exists() + ensure_gitignore_exists() - open_.assert_not_called() + open_.assert_not_called() def test_ensure_gitignore_exists_returns_false_if_it_does_not_exist(mocker, empty_repo): - mocker.patch('shulkr.gitignore.click') - mocker.patch('shulkr.gitignore.os.path.isfile', return_value=False) - mocker.patch('shulkr.gitignore.open') + mocker.patch("shulkr.gitignore.click") + mocker.patch("shulkr.gitignore.os.path.isfile", return_value=False) + mocker.patch("shulkr.gitignore.open") - assert not ensure_gitignore_exists() + assert not ensure_gitignore_exists() def test_ensure_gitignore_exists_returns_true_if_it_does_exist(mocker, empty_repo): - mocker.patch('shulkr.gitignore.click') - mocker.patch('shulkr.gitignore.os.path.isfile', return_value=True) - mocker.patch('shulkr.gitignore.open') + mocker.patch("shulkr.gitignore.click") + mocker.patch("shulkr.gitignore.os.path.isfile", return_value=True) + mocker.patch("shulkr.gitignore.open") - assert ensure_gitignore_exists() + assert ensure_gitignore_exists() diff --git a/tests/shulkr/unit/test_repo.py b/tests/shulkr/unit/test_repo.py index ab539b6..a79047a 100644 --- a/tests/shulkr/unit/test_repo.py +++ b/tests/shulkr/unit/test_repo.py @@ -2,14 +2,14 @@ def test_init_repo_returns_true_if_repo_exists(mocker, empty_repo): - mocker.patch('shulkr.repo.click') - mocker.patch('shulkr.repo.Repo') + mocker.patch("shulkr.repo.click") + mocker.patch("shulkr.repo.Repo") - assert init_repo(empty_repo.path) + assert init_repo(empty_repo.path) def test_init_repo_returns_false_if_repo_directory_does_not_exist(mocker): - mocker.patch('shulkr.repo.click') - mocker.patch('shulkr.repo.Repo', side_effect=FileNotFoundError) + mocker.patch("shulkr.repo.click") + mocker.patch("shulkr.repo.Repo", side_effect=FileNotFoundError) - assert not init_repo('/tmp/does-not-exist') + assert not init_repo("/tmp/does-not-exist") diff --git a/tests/shulkr/unit/test_version.py b/tests/shulkr/unit/test_version.py index e3f487e..c83fa97 100644 --- a/tests/shulkr/unit/test_version.py +++ b/tests/shulkr/unit/test_version.py @@ -5,132 +5,150 @@ from shulkr.version import create_version, get_latest_generated_version -def test_create_version_calls_generate_sources_with_mappings_from_config_and_correct_version(mocker, config: Config, empty_repo): - # Set mappings in config - config.mappings = 'foo' +def test_create_version_calls_generate_sources_with_mappings_from_config_and_correct_version( + mocker, config: Config, empty_repo +): + # Set mappings in config + config.mappings = "foo" - # Mock - mocker.patch('shulkr.version.click') - mocker.patch('shulkr.version.generate_sources') + # Mock + mocker.patch("shulkr.version.click") + mocker.patch("shulkr.version.generate_sources") - # Call create_version - version = Version('1.18.1', 0) - create_version(version) + # Call create_version + version = Version("1.18.1", 0) + create_version(version) - # generate_sources() should have been called with the correct arguments - shulkr.version.generate_sources.assert_called_once_with(version, 'foo', empty_repo.path) + # generate_sources() should have been called with the correct arguments + shulkr.version.generate_sources.assert_called_once_with( + version, "foo", empty_repo.path + ) -def test_create_version_with_undo_renamed_vars_on_repo_with_no_commits_does_not_call_undo_renames(mocker, config, empty_repo): - # Mock - # mocker.patch('shulkr.open', create=True) - mocker.patch('shulkr.version.click') - mocker.patch('shulkr.version.generate_sources') - mocker.patch('shulkr.version.undo_renames') +def test_create_version_with_undo_renamed_vars_on_repo_with_no_commits_does_not_call_undo_renames( + mocker, config, empty_repo +): + # Mock + # mocker.patch('shulkr.open', create=True) + mocker.patch("shulkr.version.click") + mocker.patch("shulkr.version.generate_sources") + mocker.patch("shulkr.version.undo_renames") - # Call create_version - config.undo_renamed_vars = True - version = Version('1.18.1', 0) - create_version(version) + # Call create_version + config.undo_renamed_vars = True + version = Version("1.18.1", 0) + create_version(version) - # Assert that undo_renames was not called - shulkr.version.undo_renames.assert_not_called() + # Assert that undo_renames was not called + shulkr.version.undo_renames.assert_not_called() -def test_create_version_with_undo_renamed_vars_on_repo_with_one_commit_calls_undo_renames(mocker, config, nonempty_repo): - # Mock - # mocker.patch('shulkr.open', create=True) - mocker.patch('shulkr.version.click') - mocker.patch('shulkr.version.generate_sources') - mocker.patch('shulkr.version.undo_renames') +def test_create_version_with_undo_renamed_vars_on_repo_with_one_commit_calls_undo_renames( + mocker, config, nonempty_repo +): + # Mock + # mocker.patch('shulkr.open', create=True) + mocker.patch("shulkr.version.click") + mocker.patch("shulkr.version.generate_sources") + mocker.patch("shulkr.version.undo_renames") - # Call create_version - config.undo_renamed_vars = True - version = Version('1.18.1', 0) - create_version(version) + # Call create_version + config.undo_renamed_vars = True + version = Version("1.18.1", 0) + create_version(version) - # Assert that undo_renames was not called - shulkr.version.undo_renames.assert_called_once() + # Assert that undo_renames was not called + shulkr.version.undo_renames.assert_called_once() -def test_create_version_with_yarn_mappings_stages_the_src_directory(mocker, config, empty_repo, yarn_mappings): - mocker.patch('shulkr.version.click') - mocker.patch('shulkr.version.generate_sources') +def test_create_version_with_yarn_mappings_stages_the_src_directory( + mocker, config, empty_repo, yarn_mappings +): + mocker.patch("shulkr.version.click") + mocker.patch("shulkr.version.generate_sources") - # Call _commit_version - version = Version('1.18.1', 0) - create_version(version) + # Call _commit_version + version = Version("1.18.1", 0) + create_version(version) - # src needs to have been staged - empty_repo.git.add.assert_called_once_with('src') + # src needs to have been staged + empty_repo.git.add.assert_called_once_with("src") -def test_create_version_with_mojang_mappings_stages_the_src_directory(mocker, config, empty_repo, mojang_mappings): - mocker.patch('shulkr.version.click') - mocker.patch('shulkr.version.generate_sources') +def test_create_version_with_mojang_mappings_stages_the_src_directory( + mocker, config, empty_repo, mojang_mappings +): + mocker.patch("shulkr.version.click") + mocker.patch("shulkr.version.generate_sources") - # Call _commit_version - version = Version('1.18.1', 0) - create_version(version) + # Call _commit_version + version = Version("1.18.1", 0) + create_version(version) - # client and server need to have been staged - empty_repo.git.add.assert_called_once_with('src') + # client and server need to have been staged + empty_repo.git.add.assert_called_once_with("src") def test_create_version_creates_a_commit(mocker, config, empty_repo): - mocker.patch('shulkr.version.click') - mocker.patch('shulkr.version.generate_sources') + mocker.patch("shulkr.version.click") + mocker.patch("shulkr.version.generate_sources") - # Call _commit_version - version = Version('1.18.1', 0) - create_version(version) + # Call _commit_version + version = Version("1.18.1", 0) + create_version(version) - # commit must have been called - empty_repo.git.commit.assert_called_once() + # commit must have been called + empty_repo.git.commit.assert_called_once() -def test_create_version_replaces_all_brackets_with_the_version(mocker, config, empty_repo): - mocker.patch('shulkr.version.click') - mocker.patch('shulkr.version.generate_sources') +def test_create_version_replaces_all_brackets_with_the_version( + mocker, config, empty_repo +): + mocker.patch("shulkr.version.click") + mocker.patch("shulkr.version.generate_sources") - # Call _commit_version - config.message_template = '{} {}' - version = Version('1.18.1', 0) - create_version(version) + # Call _commit_version + config.message_template = "{} {}" + version = Version("1.18.1", 0) + create_version(version) - # commit must have been called - expected_message = config.message_template.replace('{}', str(version)) - empty_repo.git.commit.assert_called_once_with(message=expected_message) + # commit must have been called + expected_message = config.message_template.replace("{}", str(version)) + empty_repo.git.commit.assert_called_once_with(message=expected_message) -def test_create_version_with_existing_commits_and_undo_renamed_vars_adds_note_to_commit_message(mocker, config, nonempty_repo): - mocker.patch('shulkr.version.click') - mocker.patch('shulkr.version.generate_sources') +def test_create_version_with_existing_commits_and_undo_renamed_vars_adds_note_to_commit_message( + mocker, config, nonempty_repo +): + mocker.patch("shulkr.version.click") + mocker.patch("shulkr.version.generate_sources") - # Call _commit_version - config.undo_renamed_vars = True - version = Version('1.18.1', 0) - create_version(version) + # Call _commit_version + config.undo_renamed_vars = True + version = Version("1.18.1", 0) + create_version(version) - # commit must have been called - expected_message = f'{version}\n\nRenamed variables reverted' - nonempty_repo.git.commit.assert_called_once_with(message=expected_message) + # commit must have been called + expected_message = f"{version}\n\nRenamed variables reverted" + nonempty_repo.git.commit.assert_called_once_with(message=expected_message) def test_create_version_with_tag_calls_git_tag(mocker, config, nonempty_repo): - mocker.patch('shulkr.version.click') - mocker.patch('shulkr.version.generate_sources') + mocker.patch("shulkr.version.click") + mocker.patch("shulkr.version.generate_sources") - version = Version('1.18.1', 0) - create_version(version) + version = Version("1.18.1", 0) + create_version(version) - nonempty_repo.git.tag.assert_called_once_with(version) + nonempty_repo.git.tag.assert_called_once_with(version) -def test_get_latest_generated_version_with_repo_with_one_version_returns_version(mocker, nonempty_repo): - mocker.patch('shulkr.version.Version.of') +def test_get_latest_generated_version_with_repo_with_one_version_returns_version( + mocker, nonempty_repo +): + mocker.patch("shulkr.version.Version.of") - # nonempty_repo contains one commit (the snapshot) - get_latest_generated_version() + # nonempty_repo contains one commit (the snapshot) + get_latest_generated_version() - shulkr.version.Version.of.assert_called_once_with('abcdef') + shulkr.version.Version.of.assert_called_once_with("abcdef")