From 6f979ff2397fddb34a692f915270777252022f56 Mon Sep 17 00:00:00 2001 From: Krishnan Chandra <1229365+krishnan-chandra@users.noreply.github.com> Date: Tue, 14 May 2024 19:00:05 -0400 Subject: [PATCH 01/18] Update Ruff version to 0.4.4 (#20924) There have been a few more patch releases since we last upgraded a month ago, so bumping the version in the lockfile to latest. --- docs/notes/2.22.x.md | 2 +- .../pants/backend/python/lint/ruff/ruff.lock | 58 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/notes/2.22.x.md b/docs/notes/2.22.x.md index d3681b223cf..290e451da0f 100644 --- a/docs/notes/2.22.x.md +++ b/docs/notes/2.22.x.md @@ -50,7 +50,7 @@ The `openapi_document` target will now bundle itself and its `openapi_source` de #### Python -[The `pants.backend.experimental.python.lint.ruff` backend](https://www.pantsbuild.org/2.22/reference/subsystems/ruff) now uses version 0.4.1 by default. +[The `pants.backend.experimental.python.lint.ruff` backend](https://www.pantsbuild.org/2.22/reference/subsystems/ruff) now uses version 0.4.4 by default. The new `layout="loose"` field for AWS Lambda [function](https://www.pantsbuild.org/2.22/reference/targets/python_aws_lambda_function#layout), [layer](https://www.pantsbuild.org/2.22/reference/targets/python_aws_lambda_layer#layout) and [Google Cloud Function](https://www.pantsbuild.org/2.22/reference/targets/python_google_cloud_function#layout) targets outputs the artefact as a directory, rather than a zip file. diff --git a/src/python/pants/backend/python/lint/ruff/ruff.lock b/src/python/pants/backend/python/lint/ruff/ruff.lock index eb8b7bf13ee..f32f3a7310c 100644 --- a/src/python/pants/backend/python/lint/ruff/ruff.lock +++ b/src/python/pants/backend/python/lint/ruff/ruff.lock @@ -31,79 +31,79 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "6e68d248ed688b9d69fd4d18737edcbb79c98b251bba5a2b031ce2470224bdf9", - "url": "https://files.pythonhosted.org/packages/70/61/ad210ae4b48f15de5630d96eede38a6d98984130fb3b61e7000721848b71/ruff-0.4.1-py3-none-musllinux_1_2_x86_64.whl" + "hash": "958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95", + "url": "https://files.pythonhosted.org/packages/58/f8/30969d9268d7435761431c002d29bd4e742ff47e9ffea56d4c6737c4accf/ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "b92f03b4aa9fa23e1799b40f15f8b95cdc418782a567d6c43def65e1bbb7f1cf", - "url": "https://files.pythonhosted.org/packages/28/fa/70236002bc002edca0a3d4ed2e45f3d3f499a45a3e9fd606c87f2c5822fe/ruff-0.4.1-py3-none-musllinux_1_2_aarch64.whl" + "hash": "29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6", + "url": "https://files.pythonhosted.org/packages/01/6e/d4d59a457b6633b4a2b08b2efb61323299b3ad3889ce604e1e8c2d278027/ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "2c6e37f2e3cd74496a74af9a4fa67b547ab3ca137688c484749189bf3a686ceb", - "url": "https://files.pythonhosted.org/packages/29/1d/d49911b2ad919575b0895043b97db81cce401635eb20cd8ebba949a038b9/ruff-0.4.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl" + "hash": "60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab", + "url": "https://files.pythonhosted.org/packages/18/ee/4b7d6a7ef3ee6f67d60de67172791e8f334abd9f85a319fea9412343d0ec/ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "0926cefb57fc5fced629603fbd1a23d458b25418681d96823992ba975f050c2b", - "url": "https://files.pythonhosted.org/packages/36/8a/de76c13f9e1ce00bb03a70b371a3cd2cec86634263001b7afe8473f9da80/ruff-0.4.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + "hash": "f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af", + "url": "https://files.pythonhosted.org/packages/2f/2f/1e91c17c5223a37992a9302af69056966657d23f945e07039319fe006f82/ruff-0.4.4.tar.gz" }, { "algorithm": "sha256", - "hash": "b34510141e393519a47f2d7b8216fec747ea1f2c81e85f076e9f2910588d4b64", - "url": "https://files.pythonhosted.org/packages/50/19/1d25f4daf6518f615676cab90d205ef1e221500f95e4a79325e4cd2bf937/ruff-0.4.1-py3-none-musllinux_1_2_i686.whl" + "hash": "c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9", + "url": "https://files.pythonhosted.org/packages/30/d5/5e22d68e5f79e63ea23322bca5b04aad94d03e6dcf23f8de5a472707b500/ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "9485f54a7189e6f7433e0058cf8581bee45c31a25cd69009d2a040d1bd4bfaef", - "url": "https://files.pythonhosted.org/packages/52/29/ce2d1aa82f0c8db7b1468fd4adf921c8572dfc2c95d25df2a7980edc4764/ruff-0.4.1-py3-none-macosx_10_12_x86_64.whl" + "hash": "9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e", + "url": "https://files.pythonhosted.org/packages/42/87/835d1bdb908a9c4ce56956cf0b08aa0cabe3a1de94294293f4420abd0ac7/ruff-0.4.4-py3-none-musllinux_1_2_i686.whl" }, { "algorithm": "sha256", - "hash": "2d9ef6231e3fbdc0b8c72404a1a0c46fd0dcea84efca83beb4681c318ea6a953", - "url": "https://files.pythonhosted.org/packages/84/7a/1eea0f76c900b824f50631645dd84a1e4e3bb52b44642c1b82e808375259/ruff-0.4.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl" + "hash": "c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891", + "url": "https://files.pythonhosted.org/packages/5e/04/6388dcb969cc69b365e2d024c0180a1462ce1be385b76ac126fe1bafa680/ruff-0.4.4-py3-none-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "d2921ac03ce1383e360e8a95442ffb0d757a6a7ddd9a5be68561a671e0e5807e", - "url": "https://files.pythonhosted.org/packages/b0/19/a1c7c7b9f15c58195675abad31ac4b4555c8b94d147ba8c7e9f6fcb9043f/ruff-0.4.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595", + "url": "https://files.pythonhosted.org/packages/78/a4/ad40ffae25ed3bbe6d835243383061fe59910fe0b8b969f69bf8adfbfa6e/ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "baa27d9d72a94574d250f42b7640b3bd2edc4c58ac8ac2778a8c82374bb27984", - "url": "https://files.pythonhosted.org/packages/b1/f5/4f81560b8b555fda93ac624d5e534cba8c2362aa29eebd7a1ebb20185bd3/ruff-0.4.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15", + "url": "https://files.pythonhosted.org/packages/ae/b8/34d5e2560d89cb43b3e27b12b11545a7557b7363cb7e8cbfdb0dac916bb3/ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "efd703a5975ac1998c2cc5e9494e13b28f31e66c616b0a76e206de2562e0843c", - "url": "https://files.pythonhosted.org/packages/bd/38/0c172941d736433c494c5f3d3ce476c7a8060c70e2d8a2ab73fabd5e5869/ruff-0.4.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85", + "url": "https://files.pythonhosted.org/packages/b2/fc/c70845aa31a7971b838b5dfa07c5df461927148d84148e9a8e59482b418d/ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl" }, { "algorithm": "sha256", - "hash": "1c859f294f8633889e7d77de228b203eb0e9a03071b72b5989d89a0cf98ee262", - "url": "https://files.pythonhosted.org/packages/c4/3f/11b0a93ae0e50bc323bfe74a70bbd2b84f6e1cd83b71967f9f34d9032d91/ruff-0.4.1-py3-none-musllinux_1_2_armv7l.whl" + "hash": "b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd", + "url": "https://files.pythonhosted.org/packages/da/9f/9203470e3e30a088446a29ccadf81cffaf603cade8f4230f62da5658b292/ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "d592116cdbb65f8b1b7e2a2b48297eb865f6bdc20641879aa9d7b9c11d86db79", - "url": "https://files.pythonhosted.org/packages/cd/76/667f7536232ff6c3769a13f8d67911e038367350f7c3b3e09c4d98648fbc/ruff-0.4.1.tar.gz" + "hash": "1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768", + "url": "https://files.pythonhosted.org/packages/ea/85/e1bdcdc531a965af551b82233e9ceb12af9661144c9e548693e8b0eba902/ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl" }, { "algorithm": "sha256", - "hash": "f1ee41580bff1a651339eb3337c20c12f4037f6110a36ae4a2d864c52e5ef954", - "url": "https://files.pythonhosted.org/packages/e6/1c/66ed2617bfa589ff88490448bb49385bd776c7f39d09359e46cdcc78e537/ruff-0.4.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl" + "hash": "8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef", + "url": "https://files.pythonhosted.org/packages/ef/6d/0d3f7632f86f09dde0429872835d592151e22d4584ea6a5cfb8f0ade1394/ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl" }, { "algorithm": "sha256", - "hash": "eec8d185fe193ad053eda3a6be23069e0c8ba8c5d20bc5ace6e3b9e37d246d3f", - "url": "https://files.pythonhosted.org/packages/fa/b5/d48020b41bd05a47be6c53d59e5fa8cbbe3bda597535286948d27415c1f6/ruff-0.4.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + "hash": "b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36", + "url": "https://files.pythonhosted.org/packages/fa/13/dd0ff7a488ace95676486c9d4d0202fc15d64a6491748e2ce8876df87870/ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" } ], "project_name": "ruff", "requires_dists": [], "requires_python": ">=3.7", - "version": "0.4.1" + "version": "0.4.4" } ], "platform_tag": null From 6284d69160feb8abb813772d7436bd9629d731e7 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 15 May 2024 11:38:02 +1000 Subject: [PATCH 02/18] Update semgrep 1.46.0 -> 1.72.0 (#20501) This updates semgrep to the current latest release: https://github.com/semgrep/semgrep/releases/tag/v1.72.0 There's been dozens of releases since we last updated, which are presumably described in the changelog: https://github.com/semgrep/semgrep/blob/v1.72.0/CHANGELOG.md --- docs/notes/2.22.x.md | 4 + .../tools/semgrep/rules_integration_test.py | 6 +- .../pants/backend/tools/semgrep/semgrep.lock | 1125 ++++++++--------- .../pants/backend/tools/semgrep/subsystem.py | 1 + 4 files changed, 570 insertions(+), 566 deletions(-) diff --git a/docs/notes/2.22.x.md b/docs/notes/2.22.x.md index 290e451da0f..f3bf8311a7b 100644 --- a/docs/notes/2.22.x.md +++ b/docs/notes/2.22.x.md @@ -64,6 +64,10 @@ The deprecation for the `platforms` field for the `pex_binary` and `pex_binaries The option help text for the `install_from_resolve` field for Python tools now contains the default version of the tool, along with instructions on how to override this version using a custom lockfile. +#### Semgrep + +The default version of `semgrep` used by the `pants.backends.experimental.tool.semgrep` backend is now version 1.72.0, upgraded from 1.46.0. This version requires Python 3.8 or greater. + #### Shell [The `pants.backend.shell.lint.shfmt` backend](https://www.pantsbuild.org/2.22/docs/shell#shfmt-autoformatter) now uses shfmt version 3.8.0 by default. diff --git a/src/python/pants/backend/tools/semgrep/rules_integration_test.py b/src/python/pants/backend/tools/semgrep/rules_integration_test.py index dd3e1e3ff9e..b54256b4de6 100644 --- a/src/python/pants/backend/tools/semgrep/rules_integration_test.py +++ b/src/python/pants/backend/tools/semgrep/rules_integration_test.py @@ -110,7 +110,11 @@ def run_semgrep( extra_args = ("--semgrep-args=[]", *extra_args) rule_runner.set_options( - ["--backend-packages=pants.backend.tools.semgrep", *extra_args], + [ + "--backend-packages=pants.backend.tools.semgrep", + f"--python-interpreter-constraints={SemgrepSubsystem.default_interpreter_constraints!r}", + *extra_args, + ], env_inherit={"PATH", "PYENV_ROOT", "HOME"}, ) partitions = rule_runner.request( diff --git a/src/python/pants/backend/tools/semgrep/semgrep.lock b/src/python/pants/backend/tools/semgrep/semgrep.lock index 9771ca67cf4..c89cf882319 100644 --- a/src/python/pants/backend/tools/semgrep/semgrep.lock +++ b/src/python/pants/backend/tools/semgrep/semgrep.lock @@ -6,7 +6,7 @@ // { // "version": 3, // "valid_for_interpreter_constraints": [ -// "CPython<4,>=3.7" +// "CPython<4,>=3.8" // ], // "generated_with_requirements": [ // "semgrep<2,>=1.20.0" @@ -90,19 +90,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "351b7f20d56fb9ea91f9b9e9e7664db466eb234188c175fd943f8f755c807e73", - "url": "https://files.pythonhosted.org/packages/26/f5/7c60fb31c9aea37b3424e4206f9f6ed23c1ee0a717fa31749d10665a4eef/bracex-2.3.post1-py3-none-any.whl" + "hash": "efdc71eff95eaff5e0f8cfebe7d01adf2c8637c8c92edaf63ef348c241a82418", + "url": "https://files.pythonhosted.org/packages/2b/f5/8f99837b32e2badc189382774dbe6227fa01e1e928a7eff857fdc89d8a75/bracex-2.4-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "e7b23fc8b2cd06d3dec0692baabecb249dda94e06a617901ff03a6c56fd71693", - "url": "https://files.pythonhosted.org/packages/b3/96/d53e290ddf6215cfb24f93449a1835eff566f79a1f332cf046a978df0c9e/bracex-2.3.post1.tar.gz" + "hash": "a27eaf1df42cf561fed58b7a8f3fdf129d1ea16a81e1fadd1d17989bc6384beb", + "url": "https://files.pythonhosted.org/packages/90/8b/34d174ce519f859af104c722fa30213103d34896a07a4f27bde6ac780633/bracex-2.4.tar.gz" } ], "project_name": "bracex", "requires_dists": [], - "requires_python": ">=3.7", - "version": "2.3.post1" + "requires_python": ">=3.8", + "version": "2.4" }, { "artifacts": [ @@ -149,11 +149,6 @@ "hash": "6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", "url": "https://files.pythonhosted.org/packages/13/82/83c188028b6f38d39538442dd127dc794c602ae6d45d66c469f4063a4c30/charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl" }, - { - "algorithm": "sha256", - "hash": "bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "url": "https://files.pythonhosted.org/packages/13/f8/eefae0629fa9260f83b826ee3363e311bb03cfdd518dad1bd10d57cb2d84/charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl" - }, { "algorithm": "sha256", "hash": "37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", @@ -194,11 +189,6 @@ "hash": "80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", "url": "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl" }, - { - "algorithm": "sha256", - "hash": "3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "url": "https://files.pythonhosted.org/packages/2e/37/9223632af0872c86d8b851787f0edd3fe66be4a5378f51242b25212f8374/charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl" - }, { "algorithm": "sha256", "hash": "ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", @@ -269,11 +259,6 @@ "hash": "9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", "url": "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl" }, - { - "algorithm": "sha256", - "hash": "95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "url": "https://files.pythonhosted.org/packages/4f/d1/d547cc26acdb0cc458b152f79b2679d7422f29d41581e6fa907861e88af1/charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl" - }, { "algorithm": "sha256", "hash": "9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", @@ -294,11 +279,6 @@ "hash": "2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", "url": "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl" }, - { - "algorithm": "sha256", - "hash": "a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "url": "https://files.pythonhosted.org/packages/58/a2/0c63d5d7ffac3104b86631b7f2690058c97bf72d3145c0a9cd4fb90c58c2/charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" - }, { "algorithm": "sha256", "hash": "efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", @@ -344,21 +324,11 @@ "hash": "eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", "url": "https://files.pythonhosted.org/packages/81/b2/160893421adfa3c45554fb418e321ed342bb10c0a4549e855b2b2a3699cb/charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" }, - { - "algorithm": "sha256", - "hash": "87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "url": "https://files.pythonhosted.org/packages/8d/b7/9e95102e9a8cce6654b85770794b582dda2921ec1fd924c10fbcf215ad31/charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl" - }, { "algorithm": "sha256", "hash": "a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", "url": "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, - { - "algorithm": "sha256", - "hash": "c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "url": "https://files.pythonhosted.org/packages/91/95/e2cfa7ce962e6c4b59a44a6e19e541c3a0317e543f0e0923f844e8d7d21d/charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl" - }, { "algorithm": "sha256", "hash": "b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", @@ -374,11 +344,6 @@ "hash": "2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", "url": "https://files.pythonhosted.org/packages/9e/ef/cd47a63d3200b232792e361cd67530173a09eb011813478b1c0fb8aa7226/charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl" }, - { - "algorithm": "sha256", - "hash": "8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "url": "https://files.pythonhosted.org/packages/a0/b1/4e72ef73d68ebdd4748f2df97130e8428c4625785f2b6ece31f555590c2d/charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl" - }, { "algorithm": "sha256", "hash": "6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", @@ -394,11 +359,6 @@ "hash": "a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", "url": "https://files.pythonhosted.org/packages/a8/6f/4ff299b97da2ed6358154b6eb3a2db67da2ae204e53d205aacb18a7e4f34/charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl" }, - { - "algorithm": "sha256", - "hash": "0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "url": "https://files.pythonhosted.org/packages/b2/62/5a5dcb9a71390a9511a253bde19c9c89e0b20118e41080185ea69fb2c209/charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" - }, { "algorithm": "sha256", "hash": "06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", @@ -424,11 +384,6 @@ "hash": "d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", "url": "https://files.pythonhosted.org/packages/c2/65/52aaf47b3dd616c11a19b1052ce7fa6321250a7a0b975f48d8c366733b9f/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl" }, - { - "algorithm": "sha256", - "hash": "42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "url": "https://files.pythonhosted.org/packages/c9/7a/6d8767fac16f2c80c7fa9f14e0f53d4638271635c306921844dc0b5fd8a6/charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" - }, { "algorithm": "sha256", "hash": "06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", @@ -494,21 +449,11 @@ "hash": "6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", "url": "https://files.pythonhosted.org/packages/ef/d4/a1d72a8f6aa754fdebe91b848912025d30ab7dced61e9ed8aabbf791ed65/charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl" }, - { - "algorithm": "sha256", - "hash": "c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "url": "https://files.pythonhosted.org/packages/f2/0e/e06bc07ef4673e4d24dc461333c254586bb759fdd075031539bab6514d07/charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl" - }, { "algorithm": "sha256", "hash": "cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", "url": "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, - { - "algorithm": "sha256", - "hash": "c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "url": "https://files.pythonhosted.org/packages/f6/d3/bfc699ab2c4f9245867060744e8136d359412ff1e5ad93be38a46d160f9d/charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" - }, { "algorithm": "sha256", "hash": "c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", @@ -605,6 +550,26 @@ "requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7", "version": "0.7.1" }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad", + "url": "https://files.pythonhosted.org/packages/01/90/79fe92dd413a9cab314ef5c591b5aa9b9ba787ae4cadab75055b0ae00b33/exceptiongroup-1.2.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16", + "url": "https://files.pythonhosted.org/packages/a0/65/d66b7fbaef021b3c954b3bbb196d21d8a4b97918ea524f82cfae474215af/exceptiongroup-1.2.1.tar.gz" + } + ], + "project_name": "exceptiongroup", + "requires_dists": [ + "pytest>=6; extra == \"test\"" + ], + "requires_python": ">=3.7", + "version": "1.2.1" + }, { "artifacts": [ { @@ -652,157 +617,140 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", - "url": "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl" + "hash": "82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0", + "url": "https://files.pythonhosted.org/packages/e5/3e/741d8c82801c347547f8a2a06aa57dbb1992be9e948df2ea0eda2c8b79e8/idna-3.7-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "url": "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz" + "hash": "028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", + "url": "https://files.pythonhosted.org/packages/21/ed/f86a79a07470cb07819390452f178b3bef1d375f2ec021ecfc709fc7cf07/idna-3.7.tar.gz" } ], "project_name": "idna", "requires_dists": [], "requires_python": ">=3.5", - "version": "3.6" + "version": "3.7" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5", - "url": "https://files.pythonhosted.org/packages/ff/94/64287b38c7de4c90683630338cf28f129decbba0a44f0c6db35a873c73c4/importlib_metadata-6.7.0-py3-none-any.whl" + "hash": "50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c", + "url": "https://files.pythonhosted.org/packages/75/06/4df55e1b7b112d183f65db9503bff189e97179b256e1ea450a3c365241e0/importlib_resources-6.4.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4", - "url": "https://files.pythonhosted.org/packages/a3/82/f6e29c8d5c098b6be61460371c2c5591f4a335923639edec43b3830650a4/importlib_metadata-6.7.0.tar.gz" - } - ], - "project_name": "importlib-metadata", - "requires_dists": [ - "flufl.flake8; extra == \"testing\"", - "furo; extra == \"docs\"", - "importlib-resources>=1.3; python_version < \"3.9\" and extra == \"testing\"", - "ipython; extra == \"perf\"", - "jaraco.packaging>=9; extra == \"docs\"", - "jaraco.tidelift>=1.4; extra == \"docs\"", - "packaging; extra == \"testing\"", - "pyfakefs; extra == \"testing\"", - "pytest-black>=0.3.7; platform_python_implementation != \"PyPy\" and extra == \"testing\"", - "pytest-checkdocs>=2.4; extra == \"testing\"", - "pytest-cov; extra == \"testing\"", - "pytest-enabler>=1.3; extra == \"testing\"", - "pytest-mypy>=0.9.1; platform_python_implementation != \"PyPy\" and extra == \"testing\"", - "pytest-perf>=0.9.2; extra == \"testing\"", - "pytest-ruff; extra == \"testing\"", - "pytest>=6; extra == \"testing\"", - "rst.linker>=1.9; extra == \"docs\"", - "sphinx-lint; extra == \"docs\"", - "sphinx>=3.5; extra == \"docs\"", - "typing-extensions>=3.6.4; python_version < \"3.8\"", - "zipp>=0.5" - ], - "requires_python": ">=3.7", - "version": "6.7.0" - }, - { - "artifacts": [ - { - "algorithm": "sha256", - "hash": "7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a", - "url": "https://files.pythonhosted.org/packages/38/71/c13ea695a4393639830bf96baea956538ba7a9d06fcce7cef10bfff20f72/importlib_resources-5.12.0-py3-none-any.whl" - }, - { - "algorithm": "sha256", - "hash": "4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6", - "url": "https://files.pythonhosted.org/packages/4e/a2/3cab1de83f95dd15297c15bdc04d50902391d707247cada1f021bbfe2149/importlib_resources-5.12.0.tar.gz" + "hash": "cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145", + "url": "https://files.pythonhosted.org/packages/c8/9d/6ee73859d6be81c6ea7ebac89655e92740296419bd37e5c8abdb5b62fd55/importlib_resources-6.4.0.tar.gz" } ], "project_name": "importlib-resources", "requires_dists": [ - "flake8<5; extra == \"testing\"", "furo; extra == \"docs\"", - "jaraco.packaging>=9; extra == \"docs\"", + "jaraco.packaging>=9.3; extra == \"docs\"", + "jaraco.test>=5.4; extra == \"testing\"", "jaraco.tidelift>=1.4; extra == \"docs\"", - "pytest-black>=0.3.7; platform_python_implementation != \"PyPy\" and extra == \"testing\"", "pytest-checkdocs>=2.4; extra == \"testing\"", "pytest-cov; extra == \"testing\"", - "pytest-enabler>=1.3; extra == \"testing\"", - "pytest-flake8; python_version < \"3.12\" and extra == \"testing\"", - "pytest-mypy>=0.9.1; platform_python_implementation != \"PyPy\" and extra == \"testing\"", + "pytest-enabler>=2.2; extra == \"testing\"", + "pytest-mypy; platform_python_implementation != \"PyPy\" and extra == \"testing\"", + "pytest-ruff>=0.2.1; extra == \"testing\"", "pytest>=6; extra == \"testing\"", "rst.linker>=1.9; extra == \"docs\"", "sphinx-lint; extra == \"docs\"", + "sphinx<7.2.5; extra == \"docs\"", "sphinx>=3.5; extra == \"docs\"", - "zipp>=3.1.0; python_version < \"3.10\"" + "zipp>=3.1.0; python_version < \"3.10\"", + "zipp>=3.17; extra == \"testing\"" ], - "requires_python": ">=3.7", - "version": "5.12.0" + "requires_python": ">=3.8", + "version": "6.4.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6", - "url": "https://files.pythonhosted.org/packages/c1/97/c698bd9350f307daad79dd740806e1a59becd693bd11443a0f531e3229b3/jsonschema-4.17.3-py3-none-any.whl" + "hash": "ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802", + "url": "https://files.pythonhosted.org/packages/c8/2f/324fab4be6fe37fb7b521546e8a557e6cf08c1c1b3d0b4839a00f589d9ef/jsonschema-4.22.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "url": "https://files.pythonhosted.org/packages/36/3d/ca032d5ac064dff543aa13c984737795ac81abc9fb130cd2fcff17cfabc7/jsonschema-4.17.3.tar.gz" + "hash": "5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7", + "url": "https://files.pythonhosted.org/packages/19/f1/1c1dc0f6b3bf9e76f7526562d29c320fa7d6a2f35b37a1392cc0acd58263/jsonschema-4.22.0.tar.gz" } ], "project_name": "jsonschema", "requires_dists": [ - "attrs>=17.4.0", + "attrs>=22.2.0", "fqdn; extra == \"format\"", "fqdn; extra == \"format-nongpl\"", "idna; extra == \"format\"", "idna; extra == \"format-nongpl\"", - "importlib-metadata; python_version < \"3.8\"", "importlib-resources>=1.4.0; python_version < \"3.9\"", "isoduration; extra == \"format\"", "isoduration; extra == \"format-nongpl\"", "jsonpointer>1.13; extra == \"format\"", "jsonpointer>1.13; extra == \"format-nongpl\"", + "jsonschema-specifications>=2023.03.6", "pkgutil-resolve-name>=1.3.10; python_version < \"3.9\"", - "pyrsistent!=0.17.0,!=0.17.1,!=0.17.2,>=0.14.0", + "referencing>=0.28.4", "rfc3339-validator; extra == \"format\"", "rfc3339-validator; extra == \"format-nongpl\"", "rfc3986-validator>0.1.0; extra == \"format-nongpl\"", "rfc3987; extra == \"format\"", - "typing-extensions; python_version < \"3.8\"", + "rpds-py>=0.7.1", "uri-template; extra == \"format\"", "uri-template; extra == \"format-nongpl\"", "webcolors>=1.11; extra == \"format\"", "webcolors>=1.11; extra == \"format-nongpl\"" ], - "requires_python": ">=3.7", - "version": "4.17.3" + "requires_python": ">=3.8", + "version": "4.22.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c", + "url": "https://files.pythonhosted.org/packages/ee/07/44bd408781594c4d0a027666ef27fab1e441b109dc3b76b4f836f8fd04fe/jsonschema_specifications-2023.12.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", + "url": "https://files.pythonhosted.org/packages/f8/b9/cc0cc592e7c195fb8a650c1d5990b10175cf13b4c97465c72ec841de9e4b/jsonschema_specifications-2023.12.1.tar.gz" + } + ], + "project_name": "jsonschema-specifications", + "requires_dists": [ + "importlib-resources>=1.4.0; python_version < \"3.9\"", + "referencing>=0.31.0" + ], + "requires_python": ">=3.8", + "version": "2023.12.1" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30", - "url": "https://files.pythonhosted.org/packages/bf/25/2d88e8feee8e055d015343f9b86e370a1ccbec546f2865c98397aaef24af/markdown_it_py-2.2.0-py3-none-any.whl" + "hash": "355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", + "url": "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1", - "url": "https://files.pythonhosted.org/packages/e4/c0/59bd6d0571986f72899288a95d9d6178d0eebd70b6650f1bb3f0da90f8f7/markdown-it-py-2.2.0.tar.gz" + "hash": "e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", + "url": "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz" } ], "project_name": "markdown-it-py", "requires_dists": [ - "attrs; extra == \"rtd\"", "commonmark~=0.9; extra == \"compare\"", "coverage; extra == \"testing\"", "gprof2dot; extra == \"profiling\"", + "jupyter_sphinx; extra == \"rtd\"", "linkify-it-py<3,>=1; extra == \"linkify\"", "markdown~=3.4; extra == \"compare\"", "mdit-py-plugins; extra == \"plugins\"", + "mdit-py-plugins; extra == \"rtd\"", "mdurl~=0.1", "mistletoe~=1.0; extra == \"compare\"", "mistune~=2.0; extra == \"compare\"", @@ -819,11 +767,10 @@ "sphinx-copybutton; extra == \"rtd\"", "sphinx-design; extra == \"rtd\"", "sphinx; extra == \"rtd\"", - "sphinx_book_theme; extra == \"rtd\"", - "typing_extensions>=3.7.4; python_version < \"3.8\"" + "sphinx_book_theme; extra == \"rtd\"" ], - "requires_python": ">=3.7", - "version": "2.2.0" + "requires_python": ">=3.8", + "version": "3.0.0" }, { "artifacts": [ @@ -847,32 +794,32 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", - "url": "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl" + "hash": "2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "url": "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "url": "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz" + "hash": "eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9", + "url": "https://files.pythonhosted.org/packages/ee/b5/b43a27ac7472e1818c4bafd44430e69605baefe1f34440593e0332ec8b4d/packaging-24.0.tar.gz" } ], "project_name": "packaging", "requires_dists": [], "requires_python": ">=3.7", - "version": "23.2" + "version": "24.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "e009ac4227c4fdc0058a56e822ad5987684f0a1fbb20fed577200785102581c3", - "url": "https://files.pythonhosted.org/packages/8d/a5/89cdbc4a7f6d7a0624c120be102db770ee717aa371066581e3daf2beb96f/peewee-3.17.1.tar.gz" + "hash": "ef15f90b628e41a584be8306cdc3243c51f73ce88b06154d9572f6d0284a0169", + "url": "https://files.pythonhosted.org/packages/7a/b1/2331e6b1fc8c61686dd9276f34142265462640eb0dd431246e30c4461065/peewee-3.17.3.tar.gz" } ], "project_name": "peewee", "requires_dists": [], "requires_python": null, - "version": "3.17.1" + "version": "3.17.3" }, { "artifacts": [ @@ -896,849 +843,897 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", - "url": "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl" + "hash": "b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", + "url": "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", - "url": "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz" + "hash": "786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", + "url": "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz" } ], "project_name": "pygments", "requires_dists": [ - "colorama>=0.4.6; extra == \"windows-terminal\"", - "importlib-metadata; python_version < \"3.8\" and extra == \"plugins\"" + "colorama>=0.4.6; extra == \"windows-terminal\"" + ], + "requires_python": ">=3.8", + "version": "2.18.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de", + "url": "https://files.pythonhosted.org/packages/b7/59/2056f61236782a2c86b33906c025d4f4a0b17be0161b63b70fd9e8775d36/referencing-0.35.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", + "url": "https://files.pythonhosted.org/packages/99/5b/73ca1f8e72fff6fa52119dbd185f73a907b1989428917b24cff660129b6d/referencing-0.35.1.tar.gz" + } + ], + "project_name": "referencing", + "requires_dists": [ + "attrs>=22.2.0", + "rpds-py>=0.7.0" + ], + "requires_python": ">=3.8", + "version": "0.35.1" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "url": "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", + "url": "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz" + } + ], + "project_name": "requests", + "requires_dists": [ + "PySocks!=1.5.7,>=1.5.6; extra == \"socks\"", + "certifi>=2017.4.17", + "chardet<6,>=3.0.2; extra == \"use-chardet-on-py3\"", + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1" ], "requires_python": ">=3.7", - "version": "2.17.2" + "version": "2.31.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64", - "url": "https://files.pythonhosted.org/packages/64/de/375aa14daaee107f987da76ca32f7a907fea00fa8b8afb67dc09bec0de91/pyrsistent-0.19.3-py3-none-any.whl" + "hash": "4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", + "url": "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a", - "url": "https://files.pythonhosted.org/packages/07/d2/0e72806d668c001d13885e8d7c78fefa5a649c34ad9d77b90eb472096ae7/pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", + "url": "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz" + } + ], + "project_name": "rich", + "requires_dists": [ + "ipywidgets<9,>=7.5.1; extra == \"jupyter\"", + "markdown-it-py>=2.2.0", + "pygments<3.0.0,>=2.13.0", + "typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"" + ], + "requires_python": ">=3.7.0", + "version": "13.7.1" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e", + "url": "https://files.pythonhosted.org/packages/bd/a4/b22a925754f53ea26abe2edd1e4dbb2f43973e6aeade84aa1d39d61be426/rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf", - "url": "https://files.pythonhosted.org/packages/0b/c0/5ba658ab88966a5a709e17739d1da02615b95e8210d52041d147f11da5da/pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3", + "url": "https://files.pythonhosted.org/packages/03/04/a39fc930b1ab45943b7f3a8d990a3c731b37541d9eb58c5664374c2ce007/rpds_py-0.18.1-cp312-cp312-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64", - "url": "https://files.pythonhosted.org/packages/40/04/f1d7813d4cdb62ed58e75b53e2ef481b47081ab5ad2a104cd284fa507042/pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6", + "url": "https://files.pythonhosted.org/packages/03/30/067d42b83d2b4e0c918a3e130236423cb3c7da0920cc162c5fce8f944205/rpds_py-0.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8", - "url": "https://files.pythonhosted.org/packages/59/4b/b6ea0f5c564c40f2c9d05ad3dbe3b8db6a6f1e7153e49eee29674c3c3bbe/pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl" + "hash": "51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de", + "url": "https://files.pythonhosted.org/packages/05/48/b578893a32290c9011e93e340264fdefa0df0f074d793a8c419e707fe346/rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393", - "url": "https://files.pythonhosted.org/packages/64/bd/b108e1a288a63871be1cf062176dcd5be922c748f843f316440104a45df3/pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4", + "url": "https://files.pythonhosted.org/packages/07/e9/89e1f70ee6e32fd2c7f0829d9264b28683bcb4ddb54bcfff0fa4506bf629/rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf", - "url": "https://files.pythonhosted.org/packages/73/55/1e300772f5c24921a81fc1c8b3de8a06a199c4ebb523d7c5a85f4e74a32e/pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8", + "url": "https://files.pythonhosted.org/packages/0c/f3/454ef9c66219ea511991e024f3a379fca618acd4cbe12e369b2d02f9d0b6/rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c", - "url": "https://files.pythonhosted.org/packages/82/5e/037a808341e4464c702eb45e741c69292516d0ac00e64080269a2e98d12d/pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d", + "url": "https://files.pythonhosted.org/packages/10/9e/57141c5fadc242f5766e92d3b068cc1a7823a41f07c8da2b3fd112c3da4b/rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9", - "url": "https://files.pythonhosted.org/packages/86/0e/33b4cde936d247024c26772dae0a7c93d650d8ec7ee1824d2752d3d8883c/pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc", + "url": "https://files.pythonhosted.org/packages/11/12/866549e2af13cc70f23ec297d9bbdb9cf97745392bfdf61cbd946a7d7ca6/rpds_py-0.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19", - "url": "https://files.pythonhosted.org/packages/86/f2/fda71652a6baa0147891296a99b4145572538417609c164450beebcf8ebc/pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd", + "url": "https://files.pythonhosted.org/packages/11/4b/7897191652162ae0082258e04edd672bdf7258652c81192bfc360c7c74e3/rpds_py-0.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2", - "url": "https://files.pythonhosted.org/packages/af/3e/7c94e58ade258179c2e13fb254f040830e97654d76dee8288200d30d575d/pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b", + "url": "https://files.pythonhosted.org/packages/19/38/eb7ae2b3ca5001b74536a67555e65202bedd1302f3d5d5000f7b0dc67ba6/rpds_py-0.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9", - "url": "https://files.pythonhosted.org/packages/b1/46/3f9cfa75c46b8a55d3a235456bc129a26431a65e4922fc9af66aa4e2db7e/pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl" + "hash": "06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc", + "url": "https://files.pythonhosted.org/packages/19/4e/e72c314d3a5933aa37cdd045ec21e1b0ad9cf3b38166b61732481d3a4a00/rpds_py-0.18.1-cp38-cp38-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440", - "url": "https://files.pythonhosted.org/packages/bf/90/445a7dbd275c654c268f47fa9452152709134f61f09605cf776407055a89/pyrsistent-0.19.3.tar.gz" + "hash": "2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139", + "url": "https://files.pythonhosted.org/packages/1b/94/7cff0e0c4e0e25650ba45f6c6741dcf9d6f7e3ff90da51bc8a3f41deb8f9/rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc", - "url": "https://files.pythonhosted.org/packages/d5/bf/6ed2d861e3e94c5e92dbb1399eef672fb6add6e824d8c0f4b55d9cd9e733/pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl" + "hash": "3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100", + "url": "https://files.pythonhosted.org/packages/1b/a0/a3702128743ae5bf14175a7333a4741db167f62d42f70e0edc15d9cd45c5/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28", - "url": "https://files.pythonhosted.org/packages/dc/c2/994b3e91f22b040fefbb3058d8622e3b45ab78dd1256599575bf36319b6d/pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl" + "hash": "ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43", + "url": "https://files.pythonhosted.org/packages/20/6a/571d8b2afa73bf750f86eeaad7e132c7cce1b0a22cc0ab2c53545bbac6e1/rpds_py-0.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a", - "url": "https://files.pythonhosted.org/packages/ed/7b/7d032130a6838b179b46dff1ee88909c11d518a10ec9bc70c4b72c7c2f80/pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl" + "hash": "6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338", + "url": "https://files.pythonhosted.org/packages/24/57/599c6a77ceec52607fc2db134c583cad9b89268573ffd1fbcb15b9576f5f/rpds_py-0.18.1-cp39-cp39-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3", - "url": "https://files.pythonhosted.org/packages/f4/43/183384edb4d2788374aa7663b82ace4afe4a0c1fbfee064875eb40ada95b/pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" - } - ], - "project_name": "pyrsistent", - "requires_dists": [], - "requires_python": ">=3.7", - "version": "0.19.3" - }, - { - "artifacts": [ + "hash": "6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8", + "url": "https://files.pythonhosted.org/packages/28/1c/2e208636275eab9636981fee10cb5cdaf3d5a202c3c16bf31fdd52ee08d0/rpds_py-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, { "algorithm": "sha256", - "hash": "079b143be64b0a378bdb21dff5e28a8c1393fe7e8a654ef068322d754e545fc7", - "url": "https://files.pythonhosted.org/packages/06/ee/754bfd5f6bfe7162c10d3ecb0aeef6f882f91d3231596c83f761a75efd0b/python_lsp_jsonrpc-1.0.0-py3-none-any.whl" + "hash": "dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f", + "url": "https://files.pythonhosted.org/packages/2d/aa/e7c404bdee1db7be09860dff423d022ffdce9269ec8e6532cce09ee7beea/rpds_py-0.18.1.tar.gz" }, { "algorithm": "sha256", - "hash": "7bec170733db628d3506ea3a5288ff76aa33c70215ed223abdb0d95e957660bd", - "url": "https://files.pythonhosted.org/packages/99/45/1c2a272950679af529f7360af6ee567ef266f282e451be926329e8d50d84/python-lsp-jsonrpc-1.0.0.tar.gz" - } - ], - "project_name": "python-lsp-jsonrpc", - "requires_dists": [ - "coverage; extra == \"test\"", - "pycodestyle; extra == \"test\"", - "pyflakes; extra == \"test\"", - "pylint; extra == \"test\"", - "pytest-cov; extra == \"test\"", - "pytest; extra == \"test\"", - "ujson>=3.0.0" - ], - "requires_python": null, - "version": "1.0.0" - }, - { - "artifacts": [ + "hash": "6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4", + "url": "https://files.pythonhosted.org/packages/2f/d8/04b736aede0317b08c4cc08a8d32b6ff9487e0e00758c02b93d3caa1a7f4/rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, { "algorithm": "sha256", - "hash": "58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "url": "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl" + "hash": "d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f", + "url": "https://files.pythonhosted.org/packages/30/ad/665552fe1f461c0fff496859c98d9404cea3da64bf7bfcbabe1f6c7659a5/rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", - "url": "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz" - } - ], - "project_name": "requests", - "requires_dists": [ - "PySocks!=1.5.7,>=1.5.6; extra == \"socks\"", - "certifi>=2017.4.17", - "chardet<6,>=3.0.2; extra == \"use-chardet-on-py3\"", - "charset-normalizer<4,>=2", - "idna<4,>=2.5", - "urllib3<3,>=1.21.1" - ], - "requires_python": ">=3.7", - "version": "2.31.0" - }, - { - "artifacts": [ + "hash": "b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184", + "url": "https://files.pythonhosted.org/packages/3c/b1/af0a13cdcd9183ec6f4ea9f2ae8cb48a697db0961a3f80e11a937af91f28/rpds_py-0.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + }, { "algorithm": "sha256", - "hash": "6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235", - "url": "https://files.pythonhosted.org/packages/be/be/1520178fa01eabe014b16e72a952b9f900631142ccd03dc36cf93e30c1ce/rich-13.7.0-py3-none-any.whl" + "hash": "740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644", + "url": "https://files.pythonhosted.org/packages/3e/5d/e3ac575c5d46ecb5defe5fd64ccf44fb3969bb916a87905e7eee2d34e215/rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa", - "url": "https://files.pythonhosted.org/packages/a7/ec/4a7d80728bd429f7c0d4d51245287158a1516315cadbb146012439403a9d/rich-13.7.0.tar.gz" - } - ], - "project_name": "rich", - "requires_dists": [ - "ipywidgets<9,>=7.5.1; extra == \"jupyter\"", - "markdown-it-py>=2.2.0", - "pygments<3.0.0,>=2.13.0", - "typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"" - ], - "requires_python": ">=3.7.0", - "version": "13.7.0" - }, - { - "artifacts": [ + "hash": "b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a", + "url": "https://files.pythonhosted.org/packages/42/b3/b0f4a8860d5786fcea4c591432841d9d89b7b1d96d3b22b3db20b47b7c03/rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl" + }, { "algorithm": "sha256", - "hash": "b16b6c3816dff0a93dca12acf5e70afd089fa5acb80604afd1ffa8b465b7722c", - "url": "https://files.pythonhosted.org/packages/35/79/5e2cffa1c77432f11cd93a5351f30732c997a239d3a3090856a72d6d8ba7/ruamel.yaml-0.17.40-py3-none-any.whl" + "hash": "d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac", + "url": "https://files.pythonhosted.org/packages/45/04/8fd5e166e8bb6d386491f41da51ae07118deb5461114f9eb160a7c10cedc/rpds_py-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "6024b986f06765d482b5b07e086cc4b4cd05dd22ddcbc758fa23d54873cf313d", - "url": "https://files.pythonhosted.org/packages/d1/d6/eb2833ccba5ea36f8f4de4bcfa0d1a91eb618f832d430b70e3086821f251/ruamel.yaml-0.17.40.tar.gz" - } - ], - "project_name": "ruamel-yaml", - "requires_dists": [ - "mercurial>5.7; extra == \"docs\"", - "ruamel.yaml.clib>=0.2.7; platform_python_implementation == \"CPython\" and python_version < \"3.13\"", - "ruamel.yaml.jinja2>=0.2; extra == \"jinja2\"", - "ryd; extra == \"docs\"" - ], - "requires_python": ">=3", - "version": "0.17.40" - }, - { - "artifacts": [ + "hash": "7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163", + "url": "https://files.pythonhosted.org/packages/47/c2/c711866156543ada46d5977383235d4c7821bb27db108014f4895d18fc9c/rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl" + }, { "algorithm": "sha256", - "hash": "a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880", - "url": "https://files.pythonhosted.org/packages/54/61/c18d378caadac66fa97da5d28758c751730dac7510b6a8b8b096da3fff9a/ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl" + "hash": "8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07", + "url": "https://files.pythonhosted.org/packages/4e/6c/c658183fc2d2a6ed97b0816ab4fef59d609e596bf6f5f0898ae955c14885/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248", - "url": "https://files.pythonhosted.org/packages/01/b0/4ddef56e9f703d7909febc3a421d709a3482cda25826816ec595b73e3847/ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl" + "hash": "d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1", + "url": "https://files.pythonhosted.org/packages/4f/3c/2807bb396f1d940813d1ec39efb8984ec01e84e2064db9a06bf314f3658d/rpds_py-0.18.1-cp310-cp310-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337", - "url": "https://files.pythonhosted.org/packages/08/4c/5770b8f318fe404a455141a7a33a5568c27a1f944724e82354c8f3554db2/ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl" + "hash": "f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e", + "url": "https://files.pythonhosted.org/packages/52/fc/1eb8dcf82ec8d1252c060644fa44478660e94637ddd91dc8d452b467f359/rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl" }, { "algorithm": "sha256", - "hash": "3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9", - "url": "https://files.pythonhosted.org/packages/0d/aa/06db7ca0995b513538402e11280282c615b5ae5f09eb820460d35fb69715/ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl" + "hash": "f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da", + "url": "https://files.pythonhosted.org/packages/55/5c/f59ed857a85d6713d936d70e3235a7c9bc51bc83ab7c1b4e9b4f9371abbc/rpds_py-0.18.1-cp310-cp310-musllinux_1_2_i686.whl" }, { "algorithm": "sha256", - "hash": "1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615", - "url": "https://files.pythonhosted.org/packages/18/52/8dc27bbd9ef1d4695975b8dc132c27c431d0186037ad3c731a6dd1c154b9/ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl" + "hash": "08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944", + "url": "https://files.pythonhosted.org/packages/57/65/b9769f891d0f2f915151f6d172f82ce182cedf950bcc65af4e853d794421/rpds_py-0.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7", - "url": "https://files.pythonhosted.org/packages/19/96/8d13a3f959c339ec86b01dafb2da3c34addbbed3a965bc4f3d08db44f297/ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl" + "hash": "8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d", + "url": "https://files.pythonhosted.org/packages/58/e3/b5eb611e2d51688726533bb97b420d36a55d4560c53d016e977ff6d48116/rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6", - "url": "https://files.pythonhosted.org/packages/1f/36/1626cfc675e6b254040147e4871adc00aa263a4a19cfe6216b55fbf35b9f/ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl" + "hash": "0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20", + "url": "https://files.pythonhosted.org/packages/59/21/26b0b40d3945e2a83d3626dff45ca65d4d719f914bbcbc23e43162b5cd8d/rpds_py-0.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91", - "url": "https://files.pythonhosted.org/packages/22/fa/b2a8fd49c92693e9b9b6b11eef4c2a8aedaca2b521ab3e020aa4778efc23/ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl" + "hash": "6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922", + "url": "https://files.pythonhosted.org/packages/62/9a/a0dbf17f197d7d754ad694952691ca8f386c5f8b56c76c68c44848eb65eb/rpds_py-0.18.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d", - "url": "https://files.pythonhosted.org/packages/27/38/4cf4d482b84ecdf51efae6635cc5483a83cf5ca9d9c13e205a750e251696/ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl" + "hash": "2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a", + "url": "https://files.pythonhosted.org/packages/63/5e/20602b2cfdf0cafa8b1668cf64ccdb32d43dcb996d3ba456fcc02e791a88/rpds_py-0.18.1-cp312-cp312-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3", - "url": "https://files.pythonhosted.org/packages/2d/91/b1e3eb36dbe2e338545893f3a05196549baa96208540f9866b2e46c0ee40/ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl" + "hash": "aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8", + "url": "https://files.pythonhosted.org/packages/65/42/e8ea20e5ecbd3ed2228500db56024fc09f130a75bf9d530c88aca4f57aa6/rpds_py-0.18.1-cp38-cp38-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92", - "url": "https://files.pythonhosted.org/packages/30/d3/5fe978cd01a61c12efd24d65fa68c6f28f28c8073a06cf11db3a854390ca/ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl" + "hash": "d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611", + "url": "https://files.pythonhosted.org/packages/6a/cb/86f59a827bdd446759cffeb10242814da0652b28e57362c41dfabab71ca0/rpds_py-0.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3", - "url": "https://files.pythonhosted.org/packages/38/bc/2399ac38b7badf795e1d8fe2a4ad32881401edf2d4a18372a5adecf67ce5/ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl" + "hash": "607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5", + "url": "https://files.pythonhosted.org/packages/6b/2d/7ce6b2fe57d288b0c3e735e79612e79cddb52b6042b68e9c329e25c42ff5/rpds_py-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512", - "url": "https://files.pythonhosted.org/packages/46/ab/bab9eb1566cd16f060b54055dd39cf6a34bfa0240c53a7218c43e974295b/ruamel.yaml.clib-0.2.8.tar.gz" + "hash": "e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c", + "url": "https://files.pythonhosted.org/packages/6b/b8/5da92178ecd95f6193cb4144f5c2a21bd6d743200c0739c9290c6240bfc1/rpds_py-0.18.1-cp312-cp312-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62", - "url": "https://files.pythonhosted.org/packages/55/b3/e2531a050758b717c969cbf76c103b75d8a01e11af931b94ba656117fbe9/ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl" + "hash": "5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e", + "url": "https://files.pythonhosted.org/packages/6f/3e/efa774354adc00b3d3dbae80daf3fbd0155d68c246d0b41f626d53f329d1/rpds_py-0.18.1-cp38-cp38-musllinux_1_2_i686.whl" }, { "algorithm": "sha256", - "hash": "03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001", - "url": "https://files.pythonhosted.org/packages/56/a9/e3be88fcebe04016c57207260f2b07c5ecacab86e9f585d10daaa2a4074f/ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl" + "hash": "5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8", + "url": "https://files.pythonhosted.org/packages/71/d3/03f88ed04220c5228eee01568b7dc8da23fd0727e5a24a7fba926627e466/rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c", - "url": "https://files.pythonhosted.org/packages/57/e4/f572d7e2502854f15291dfa94eebdc687e04db387559f026995c7697af34/ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl" + "hash": "ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d", + "url": "https://files.pythonhosted.org/packages/73/5b/bf77d1fe5025eeec85d62e389edacf073b93553b4837f8221093acc3ed7e/rpds_py-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1", - "url": "https://files.pythonhosted.org/packages/5a/45/644d839c09c0717c2d7f26b705560ad74b3056085b3bc7f9c2ac2081317b/ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl" + "hash": "4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b", + "url": "https://files.pythonhosted.org/packages/74/42/d05e5e023aa3b410408881960664e7e3a526d2e6e9c8620a3fa926574451/rpds_py-0.18.1-cp39-cp39-musllinux_1_2_i686.whl" }, { "algorithm": "sha256", - "hash": "e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28", - "url": "https://files.pythonhosted.org/packages/5c/f0/702e56e12497da7960ed8a6972e5edc50545757c40f1a86a41a5217da7e9/ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl" + "hash": "a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65", + "url": "https://files.pythonhosted.org/packages/75/e6/3a04f482d8c6d602d6d848ce18e6195510504fed6ff32928052321fcca72/rpds_py-0.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462", - "url": "https://files.pythonhosted.org/packages/61/ee/4874c9fc96010fce85abefdcbe770650c5324288e988d7a48b527a423815/ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl" + "hash": "e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5", + "url": "https://files.pythonhosted.org/packages/75/ea/b1fa07df655cc906ce90a575c4294a59e5b3441e713b4b2bcbf4600a02cc/rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2", - "url": "https://files.pythonhosted.org/packages/66/98/8de4f22bbfd9135deb3422e96d450c4bc0a57d38c25976119307d2efe0aa/ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl" + "hash": "07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc", + "url": "https://files.pythonhosted.org/packages/77/66/905aa687ea072d8980f7b14eb9e3c3023f5f3892eb8b88be024094956014/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1", - "url": "https://files.pythonhosted.org/packages/7a/a2/eb5e9d088cb9d15c24d956944c09dca0a89108ad6e2e913c099ef36e3f0d/ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl" + "hash": "f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843", + "url": "https://files.pythonhosted.org/packages/79/f4/e91e3d9c462387c08b833687c7095967461b785ac52e95eaa4d928a459d8/rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5", - "url": "https://files.pythonhosted.org/packages/7c/b2/389b345a60131593028b0263fddaa580edb4081697a3f3aa1f168f67519f/ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl" + "hash": "a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0", + "url": "https://files.pythonhosted.org/packages/7e/f6/89c0302279feb2c7dcce7064326cb588299470f0c172979ef26888b6aa53/rpds_py-0.18.1-cp39-cp39-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe", - "url": "https://files.pythonhosted.org/packages/7c/e4/0d19d65e340f93df1c47f323d95fa4b256bb28320290f5fddef90837853a/ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl" + "hash": "673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e", + "url": "https://files.pythonhosted.org/packages/89/4b/633f22dcfc96b33e0bba801d3022359d0cec6f02f0001790cea84df636ad/rpds_py-0.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d", - "url": "https://files.pythonhosted.org/packages/87/a6/efb1add3bac06c25aa4c8ff8c6d3e5e91c539f6600832dd63ff98e2b44cc/ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl" + "hash": "352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72", + "url": "https://files.pythonhosted.org/packages/8c/14/fa947a9cf31b7d08b26feea4fcc3d57de96924fd1a6c87b46b430fbb11b7/rpds_py-0.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334", - "url": "https://files.pythonhosted.org/packages/88/30/fc45b45d5eaf2ff36cffd215a2f85e9b90ac04e70b97fd4097017abfb567/ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl" + "hash": "489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261", + "url": "https://files.pythonhosted.org/packages/91/33/b680feac0159b5b66ebb9e6d635d78e94486b0b7ed58bea273be2cc80817/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b", - "url": "https://files.pythonhosted.org/packages/8d/c0/fd7196ca7a1c3867e7068ad1c4ff9230291af3f8adab2f9c2c202ecaf9cb/ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl" + "hash": "618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c", + "url": "https://files.pythonhosted.org/packages/92/00/426001ad8c36f1a9a76cc414489f3eab6750f34cf1fee5ec054dba8af07f/rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl" }, { "algorithm": "sha256", - "hash": "aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f", - "url": "https://files.pythonhosted.org/packages/90/8c/6cdb44f548b29eb6328b9e7e175696336bc856de2ff82e5776f860f03822/ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl" + "hash": "154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7", + "url": "https://files.pythonhosted.org/packages/92/48/32bed868dd4e7d16e26457cc0b8634d6b16cb0e082a93f044ef5f7c77361/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b", - "url": "https://files.pythonhosted.org/packages/a4/f7/22d6b620ed895a05d40802d8281eff924dc6190f682d933d4efff60db3b5/ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl" + "hash": "32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26", + "url": "https://files.pythonhosted.org/packages/96/57/bb354853ee6d867c900d4149b277ad027df7ad530084caa7e3b29c2ee6d3/rpds_py-0.18.1-cp39-cp39-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d", - "url": "https://files.pythonhosted.org/packages/af/dc/133547f90f744a0c827bac5411d84d4e81da640deb3af1459e38c5f3b6a0/ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl" + "hash": "cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e", + "url": "https://files.pythonhosted.org/packages/97/04/966a1b2286d6af7ab00bf66ccd18cac38c59b0c2973be18975edb19c639f/rpds_py-0.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069", - "url": "https://files.pythonhosted.org/packages/b1/15/971b385c098e8d0d170893f5ba558452bb7b776a0c90658b8f4dd0e3382b/ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl" + "hash": "05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee", + "url": "https://files.pythonhosted.org/packages/97/2a/e96015e3e6c0b1d5c06a009ab058f30776a4a6e1b14773ad7beefac82b99/rpds_py-0.18.1-cp312-cp312-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf", - "url": "https://files.pythonhosted.org/packages/b2/ed/f221e60a4cdc7996aae23643da44b12ef33f457c2a52d590236a6950ac8e/ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl" + "hash": "e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab", + "url": "https://files.pythonhosted.org/packages/97/b1/12238bd8cdf3cef71e85188af133399bfde1bddf319007361cc869d6f6a7/rpds_py-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875", - "url": "https://files.pythonhosted.org/packages/b9/68/0f84292041b15d1b89a6dabf5d54844d35948c4f6ce3757dcd130c001cbc/ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl" + "hash": "d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53", + "url": "https://files.pythonhosted.org/packages/a1/eb/5b7591bb8d9f710df243a3b6304a2b70db5a426a2bd478c2912f8b81b806/rpds_py-0.18.1-cp310-cp310-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899", - "url": "https://files.pythonhosted.org/packages/c9/ff/f781eb5e2ae011e586d5426e2086a011cf1e0f59704a6cad1387975c5a62/ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl" + "hash": "207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0", + "url": "https://files.pythonhosted.org/packages/a5/2d/2447c5a8872097a7b6bb7d28305fc85d27d797bf9715d5ea44f36eacabc7/rpds_py-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d", - "url": "https://files.pythonhosted.org/packages/ca/01/37ac131614f71b98e9b148b2d7790662dcee92217d2fb4bac1aa377def33/ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl" + "hash": "942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f", + "url": "https://files.pythonhosted.org/packages/a6/71/f4e8ac7a833ff6f70e18f6d2496b1a1d3a08272c777624359d2aa785de45/rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412", - "url": "https://files.pythonhosted.org/packages/d3/62/c60b034d9a008bbd566eeecf53a5a4c73d191c8de261290db6761802b72d/ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl" + "hash": "6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff", + "url": "https://files.pythonhosted.org/packages/a7/37/2fb6028fb5a31912d1f407a161b79a833ff2d9870c8f65553b4c1afc81cb/rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942", - "url": "https://files.pythonhosted.org/packages/d6/cf/7a97045dd4ece8b4ab692e9b4393edf811174ecd39dcaa0231f42099849b/ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl" + "hash": "9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88", + "url": "https://files.pythonhosted.org/packages/a9/3f/0b8e2ac89076fede032aae3fc9cf9390c6fd99a470b238fb62bbc64f4cbf/rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9", - "url": "https://files.pythonhosted.org/packages/e3/41/f62e67ac651358b8f0d60cfb12ab2daf99b1b69eeaa188d0cec809d943a6/ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl" - } - ], - "project_name": "ruamel-yaml-clib", - "requires_dists": [], - "requires_python": ">=3.6", - "version": "0.2.8" - }, - { - "artifacts": [ + "hash": "bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346", + "url": "https://files.pythonhosted.org/packages/a9/60/cc3d345d125998ecbccb9ab394193243c66903d53b02beade693810563fa/rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl" + }, { "algorithm": "sha256", - "hash": "91984fb9959d29325885afad6db8149baf30a811dd0ebe6aa80507801828008d", - "url": "https://files.pythonhosted.org/packages/f6/64/0341055915e7128d04d997176aa628bdbcf9e1352fc57e5d05404557d094/semgrep-1.46.0-cp37.cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-musllinux_1_0_aarch64.manylinux2014_aarch64.whl" + "hash": "fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89", + "url": "https://files.pythonhosted.org/packages/ad/2a/061a5755e11f41e69e80dfa89bfcf6692305aa8d404bc4122aa432c8e134/rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "ab4662b7ee663910da71d5808178f856d94a000c74736d4a7b46520e26daa4c4", - "url": "https://files.pythonhosted.org/packages/18/e0/ec6ed31d84ae8cae9959f7a3376fe8c4fb0558d10b35bae9fe07f01824ea/semgrep-1.46.0-cp37.cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-any.whl" + "hash": "910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10", + "url": "https://files.pythonhosted.org/packages/b2/95/09863640d095960a76ea753fcb551a1bcf1e5159162fb855695261f7110e/rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "5311e077501fe9c5345718451097939276bce483f3c918907b6151f6ec88c930", - "url": "https://files.pythonhosted.org/packages/26/77/898d49f7231fb55d0cc9c2527d7bf43eea605ada8dcff4707997f2c31051/semgrep-1.46.0-cp37.cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-macosx_10_14_x86_64.whl" + "hash": "732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80", + "url": "https://files.pythonhosted.org/packages/b9/9a/f1cce2481968d0ff1301d6da02bb977d7c7dc2ad9d218281ead38cc7f1ae/rpds_py-0.18.1-cp310-cp310-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "e7c40073533282378545e3467a5c74e06ddcefe7d0c9e12cec39f0a31db3710d", - "url": "https://files.pythonhosted.org/packages/45/92/ffaa5ad519dd6597323b2b1ecf4153f71924aab3a20f4b24832f77136ef2/semgrep-1.46.0-cp37.cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-macosx_11_0_arm64.whl" + "hash": "7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104", + "url": "https://files.pythonhosted.org/packages/ba/47/7995225572d53b14bafb9d406f52145b6a082009cf10143646d3f22913f2/rpds_py-0.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "f354e7e6ab3ccc8c0932126f30f218a12f5f254ef8ff187ec8e708887d31a215", - "url": "https://files.pythonhosted.org/packages/f7/1d/9ef0a73599edfed3fd4d4c5ac1b37304c622ac2d95d2a44239492111d2fb/semgrep-1.46.0.tar.gz" - } - ], - "project_name": "semgrep", - "requires_dists": [ - "attrs>=21.3", - "boltons~=21.0", - "click-option-group~=0.5", - "click~=8.1", - "colorama~=0.4.0", - "defusedxml~=0.7.1", - "glom~=22.1", - "jsonnet~=0.18; extra == \"experiments\"", - "jsonschema~=4.6", - "packaging>=21.0", - "peewee~=3.14", - "python-lsp-jsonrpc~=1.0.0", - "requests~=2.22", - "rich>=12.6.0", - "ruamel.yaml<0.18,>=0.16.0", - "tomli~=2.0.1", - "typing-extensions~=4.2", - "urllib3~=1.26", - "wcmatch~=8.3" - ], - "requires_python": ">=3.7", - "version": "1.46.0" - }, - { - "artifacts": [ + "hash": "7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae", + "url": "https://files.pythonhosted.org/packages/c0/96/edfe0d2cb019aab199344d19a2c0e2e3514ffeb67a04236933630c8a4090/rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, { "algorithm": "sha256", - "hash": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl" + "hash": "27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8", + "url": "https://files.pythonhosted.org/packages/c0/a4/b212a9e6944398b9e3630e908df80ab576aeda65c449e6489445fb0fb781/rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", - "url": "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz" - } - ], - "project_name": "tomli", - "requires_dists": [], - "requires_python": ">=3.7", - "version": "2.0.1" - }, - { - "artifacts": [ + "hash": "fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64", + "url": "https://files.pythonhosted.org/packages/c0/c3/5ce5bfcd2f2c0306b5ed68a5b237e51d62f8285378a392edfc0ee003afc3/rpds_py-0.18.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl" + }, { "algorithm": "sha256", - "hash": "440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", - "url": "https://files.pythonhosted.org/packages/ec/6b/63cc3df74987c36fe26157ee12e09e8f9db4de771e0f3404263117e75b95/typing_extensions-4.7.1-py3-none-any.whl" + "hash": "967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8", + "url": "https://files.pythonhosted.org/packages/c1/3b/a4ed8b067a8f55df92dc1bc4d20858f02ddfdba3057f96759f4dd1bff7af/rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2", - "url": "https://files.pythonhosted.org/packages/3c/8b/0111dd7d6c1478bf83baa1cab85c686426c7a6274119aceb2bd9d35395ad/typing_extensions-4.7.1.tar.gz" - } - ], - "project_name": "typing-extensions", - "requires_dists": [], - "requires_python": ">=3.7", - "version": "4.7.1" - }, - { - "artifacts": [ + "hash": "81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60", + "url": "https://files.pythonhosted.org/packages/c2/b9/dcb20646cda07b4e9db3b346c19a4685623c9a9aa8ad8a566776def0da33/rpds_py-0.18.1-cp310-cp310-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49", + "url": "https://files.pythonhosted.org/packages/c4/79/029d75bfdc65d9c127b513e68ad555ab5d0150ade6b7e405448d56db220c/rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3", + "url": "https://files.pythonhosted.org/packages/c6/d8/d801361ec7c726f44016b57624a2193d09f25ab06fc25553ed58fbb4fee2/rpds_py-0.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl" + }, { "algorithm": "sha256", - "hash": "ea7423d8a2f9e160c5e011119741682414c5b8dce4ae56590a966316a07a4618", - "url": "https://files.pythonhosted.org/packages/b0/9b/7ae752c8f1e2e7bf261c4d5ded14a7e8dd6878350d130c78a74a833f37ac/ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3", + "url": "https://files.pythonhosted.org/packages/c8/3b/cc231994f64baaff05ac2a6f847f9760d362263765b904c98782e377a317/rpds_py-0.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "581c945b811a3d67c27566539bfcb9705ea09cb27c4be0002f7a553c8886b817", - "url": "https://files.pythonhosted.org/packages/00/8c/ef2884d41cdeb0324c69be5acf4367282e34c0c80b7c255ac6955203b4a8/ujson-5.7.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl" + "hash": "d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa", + "url": "https://files.pythonhosted.org/packages/c9/2d/b09e882bb7aba2b46db986a1969e4baed131fb6463b1e74993582731559f/rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "5eba5e69e4361ac3a311cf44fa71bc619361b6e0626768a494771aacd1c2f09b", - "url": "https://files.pythonhosted.org/packages/01/ac/d06d6361ffb641cda6ffd16c763a76eed07abb073c49a41fbf007c764242/ujson-5.7.0-cp310-cp310-macosx_10_9_x86_64.whl" + "hash": "b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397", + "url": "https://files.pythonhosted.org/packages/cb/61/90bb60a78c7c5da7155fed66b6cc875b9b402108565a00057f45391f3dcc/rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "75204a1dd7ec6158c8db85a2f14a68d2143503f4bafb9a00b63fe09d35762a5e", - "url": "https://files.pythonhosted.org/packages/02/5f/bef7d57cd7dba6c3124ce2c42c215e2194f51835c2e9176e2833ea04e15c/ujson-5.7.0-cp38-cp38-macosx_10_9_x86_64.whl" + "hash": "f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac", + "url": "https://files.pythonhosted.org/packages/d1/28/e63095852b338b4c476f34fca47d87befb19f219a326fd3f3e3749e1a49b/rpds_py-0.18.1-cp312-cp312-musllinux_1_2_i686.whl" }, { "algorithm": "sha256", - "hash": "a5d2f44331cf04689eafac7a6596c71d6657967c07ac700b0ae1c921178645da", - "url": "https://files.pythonhosted.org/packages/08/47/41f40896aad1a098b4fea2e0bfe66a3fed8305d2457945f7082b7f493307/ujson-5.7.0-cp37-cp37m-musllinux_1_1_i686.whl" + "hash": "19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93", + "url": "https://files.pythonhosted.org/packages/d7/a0/15114fa1e4427b966e686f10fbad8c23a464d226d34a6712dcaa366707d7/rpds_py-0.18.1-cp39-cp39-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "b01a9af52a0d5c46b2c68e3f258fdef2eacaa0ce6ae3e9eb97983f5b1166edb6", - "url": "https://files.pythonhosted.org/packages/18/19/2754b8d50affbf4456f31af5a75a1904d40499e89facdb742496b0a9c8c7/ujson-5.7.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09", + "url": "https://files.pythonhosted.org/packages/d7/a9/b25013071a61f008a8266a208e701cf8ec2c2946feb6005b3ec454f63a66/rpds_py-0.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "800bf998e78dae655008dd10b22ca8dc93bdcfcc82f620d754a411592da4bbf2", - "url": "https://files.pythonhosted.org/packages/21/0b/9fd1a3dc94175d8cf141c3356776346e1b5fca10571441fc370fbf560e1c/ujson-5.7.0-cp39-cp39-musllinux_1_1_i686.whl" + "hash": "aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c", + "url": "https://files.pythonhosted.org/packages/d9/d6/cb9edd4910b6f253b4f64b0d8871ddcdc9ff02241307f33cccdb61f903cd/rpds_py-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "e87cec407ec004cf1b04c0ed7219a68c12860123dfb8902ef880d3d87a71c172", - "url": "https://files.pythonhosted.org/packages/22/27/81b6b0537fbc6ff0baaeb175738ee7464d643ad5ff30105e03a9e744682d/ujson-5.7.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl" + "hash": "c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74", + "url": "https://files.pythonhosted.org/packages/db/89/c0e8e04be0d4ebb390d9b0941288a0268b5d1a98a6eec22723e009c9334f/rpds_py-0.18.1-cp38-cp38-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "54384ce4920a6d35fa9ea8e580bc6d359e3eb961fa7e43f46c78e3ed162d56ff", - "url": "https://files.pythonhosted.org/packages/23/46/874217a97b822d0d9c072fe49db362ce1ece8e912ea6422a3f12fa5e56e1/ujson-5.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909", + "url": "https://files.pythonhosted.org/packages/e1/a9/32827105924c7d0982b2d3d41e36e03e48dedc5c5b835a9f6c1d363d3011/rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl" }, { "algorithm": "sha256", - "hash": "6e80f0d03e7e8646fc3d79ed2d875cebd4c83846e129737fdc4c2532dbd43d9e", - "url": "https://files.pythonhosted.org/packages/29/5f/52a99a8ae0ae74213f1994a8180afd32b4d0cde427e2610f6e1ffb0fa15c/ujson-5.7.0-cp310-cp310-musllinux_1_1_i686.whl" + "hash": "998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7", + "url": "https://files.pythonhosted.org/packages/e1/dd/3beaa8ee9254e8ac03fb1237d614ec7c6976766013ab9803831e93182967/rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "bab10165db6a7994e67001733f7f2caf3400b3e11538409d8756bc9b1c64f7e8", - "url": "https://files.pythonhosted.org/packages/2c/fe/855ee750936e9d065e6e49f7340571bd2db756fbcaf338c00456d39dd217/ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0", + "url": "https://files.pythonhosted.org/packages/e5/20/10c12b1acb102c4981a7e1dc86b60e36c1d5c940a7bda48643542f80dbff/rpds_py-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c0d1f7c3908357ee100aa64c4d1cf91edf99c40ac0069422a4fd5fd23b263263", - "url": "https://files.pythonhosted.org/packages/2e/4a/e802a5f22e0fffdeaceb3d139c79ab7995f118c2fadb8cdb129a7fd83c8d/ujson-5.7.0-cp37-cp37m-musllinux_1_1_aarch64.whl" + "hash": "6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d", + "url": "https://files.pythonhosted.org/packages/e5/5a/2e0ac1c38021da0055f08a0f49be29f64df6dcffcdd63b2d38c402e94489/rpds_py-0.18.1-cp38-cp38-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "341f891d45dd3814d31764626c55d7ab3fd21af61fbc99d070e9c10c1190680b", - "url": "https://files.pythonhosted.org/packages/30/c3/adb327b07e554f9c14f05df79bbad914532054f31303bb0716744354fe51/ujson-5.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724", + "url": "https://files.pythonhosted.org/packages/e8/81/d47f29b80a9cd9ea81bd03561c7e0515f2bbe823767864f91745a3906cb0/rpds_py-0.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "14f9082669f90e18e64792b3fd0bf19f2b15e7fe467534a35ea4b53f3bf4b755", - "url": "https://files.pythonhosted.org/packages/31/5c/c8b9e14583aeaf473596c3861edf20c0c3d850e00873858f8279c6e884f5/ujson-5.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9", + "url": "https://files.pythonhosted.org/packages/f3/16/7ddc46210ec4b52614c5d5ed72ca0afd12b6c6af1d7cb3859b2e7faa8ffe/rpds_py-0.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "4a3d794afbf134df3056a813e5c8a935208cddeae975bd4bc0ef7e89c52f0ce0", - "url": "https://files.pythonhosted.org/packages/34/ad/98c4bd2cfe2d04330bc7d6b7e3dee5b52b7358430b1cf4973ca25b7413c3/ujson-5.7.0-cp39-cp39-musllinux_1_1_aarch64.whl" + "hash": "1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633", + "url": "https://files.pythonhosted.org/packages/f4/9e/2b5f3e1123ce9bd055ec4003274ce2955040679958936d98a5c2a360ffa4/rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "18679484e3bf9926342b1c43a3bd640f93a9eeeba19ef3d21993af7b0c44785d", - "url": "https://files.pythonhosted.org/packages/37/34/017f0904417617d2af2a30021f0b494535e63cb4a343dc32b05d9f0e96dd/ujson-5.7.0-cp37-cp37m-macosx_10_9_x86_64.whl" + "hash": "2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7", + "url": "https://files.pythonhosted.org/packages/f4/ba/adb81247a2fe211ff74cd4c2790454bbf37eb7430ee898e4aa7ce5cb6507/rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "64772a53f3c4b6122ed930ae145184ebaed38534c60f3d859d8c3f00911eb122", - "url": "https://files.pythonhosted.org/packages/3b/bd/a7ad5d56a4a9491487bd658cda12c2a7a0d5a41c9943086471e6cfa73854/ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d", + "url": "https://files.pythonhosted.org/packages/fd/6a/e67b83791863db607a297b822fe95813c6cbff979608496f47d81ad45fea/rpds_py-0.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + } + ], + "project_name": "rpds-py", + "requires_dists": [], + "requires_python": ">=3.8", + "version": "0.18.1" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "b16b6c3816dff0a93dca12acf5e70afd089fa5acb80604afd1ffa8b465b7722c", + "url": "https://files.pythonhosted.org/packages/35/79/5e2cffa1c77432f11cd93a5351f30732c997a239d3a3090856a72d6d8ba7/ruamel.yaml-0.17.40-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "6024b986f06765d482b5b07e086cc4b4cd05dd22ddcbc758fa23d54873cf313d", + "url": "https://files.pythonhosted.org/packages/d1/d6/eb2833ccba5ea36f8f4de4bcfa0d1a91eb618f832d430b70e3086821f251/ruamel.yaml-0.17.40.tar.gz" + } + ], + "project_name": "ruamel-yaml", + "requires_dists": [ + "mercurial>5.7; extra == \"docs\"", + "ruamel.yaml.clib>=0.2.7; platform_python_implementation == \"CPython\" and python_version < \"3.13\"", + "ruamel.yaml.jinja2>=0.2; extra == \"jinja2\"", + "ryd; extra == \"docs\"" + ], + "requires_python": ">=3", + "version": "0.17.40" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880", + "url": "https://files.pythonhosted.org/packages/54/61/c18d378caadac66fa97da5d28758c751730dac7510b6a8b8b096da3fff9a/ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "e788e5d5dcae8f6118ac9b45d0b891a0d55f7ac480eddcb7f07263f2bcf37b23", - "url": "https://files.pythonhosted.org/packages/43/1a/b0a027144aa5c8f4ea654f4afdd634578b450807bb70b9f8bad00d6f6d3c/ujson-5.7.0.tar.gz" + "hash": "b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248", + "url": "https://files.pythonhosted.org/packages/01/b0/4ddef56e9f703d7909febc3a421d709a3482cda25826816ec595b73e3847/ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "7312731c7826e6c99cdd3ac503cd9acd300598e7a80bcf41f604fee5f49f566c", - "url": "https://files.pythonhosted.org/packages/47/f8/8e5668e80f7389281954e283222bfaf7f3936809ecf9b9293b9d8b4b40e2/ujson-5.7.0-cp38-cp38-macosx_11_0_arm64.whl" + "hash": "a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337", + "url": "https://files.pythonhosted.org/packages/08/4c/5770b8f318fe404a455141a7a33a5568c27a1f944724e82354c8f3554db2/ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "f7f241488879d91a136b299e0c4ce091996c684a53775e63bb442d1a8e9ae22a", - "url": "https://files.pythonhosted.org/packages/4a/c4/cabfd64d4b0021285803703af67042aa01e1b1bc646fdf8847cd7e814b15/ujson-5.7.0-cp311-cp311-musllinux_1_1_i686.whl" + "hash": "3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9", + "url": "https://files.pythonhosted.org/packages/0d/aa/06db7ca0995b513538402e11280282c615b5ae5f09eb820460d35fb69715/ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl" }, { "algorithm": "sha256", - "hash": "adf445a49d9a97a5a4c9bb1d652a1528de09dd1c48b29f79f3d66cea9f826bf6", - "url": "https://files.pythonhosted.org/packages/4d/f2/035e82d3baacc9c225ca3bae95bed5963bcdd796dd66ffa3fd0a5a087da7/ujson-5.7.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl" + "hash": "1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615", + "url": "https://files.pythonhosted.org/packages/18/52/8dc27bbd9ef1d4695975b8dc132c27c431d0186037ad3c731a6dd1c154b9/ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "3d3b3499c55911f70d4e074c626acdb79a56f54262c3c83325ffb210fb03e44d", - "url": "https://files.pythonhosted.org/packages/50/bf/1893d4f5dc6a2acb9a6db7ff018aa1cb7df367c35d491ebef6e30cdcc8ce/ujson-5.7.0-cp39-cp39-macosx_11_0_arm64.whl" + "hash": "700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91", + "url": "https://files.pythonhosted.org/packages/22/fa/b2a8fd49c92693e9b9b6b11eef4c2a8aedaca2b521ab3e020aa4778efc23/ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "9b0f2680ce8a70f77f5d70aaf3f013d53e6af6d7058727a35d8ceb4a71cdd4e9", - "url": "https://files.pythonhosted.org/packages/58/57/bbc3e7efa9967247fba684b60a392174b7d18222931edcef2d52565bc0ac/ujson-5.7.0-cp311-cp311-macosx_10_9_x86_64.whl" + "hash": "aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d", + "url": "https://files.pythonhosted.org/packages/27/38/4cf4d482b84ecdf51efae6635cc5483a83cf5ca9d9c13e205a750e251696/ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c3af9f9f22a67a8c9466a32115d9073c72a33ae627b11de6f592df0ee09b98b6", - "url": "https://files.pythonhosted.org/packages/59/9e/447bce1a6f29ff1bfd726ea5aa9b934bc02fef9f2b41689a00e17538f436/ujson-5.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92", + "url": "https://files.pythonhosted.org/packages/30/d3/5fe978cd01a61c12efd24d65fa68c6f28f28c8073a06cf11db3a854390ca/ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "b5ac3d5c5825e30b438ea92845380e812a476d6c2a1872b76026f2e9d8060fc2", - "url": "https://files.pythonhosted.org/packages/5a/b1/7edca18e74a218d39fd8d00efc489cfd07c94271959103c647b794ce7bd5/ujson-5.7.0-cp39-cp39-musllinux_1_1_x86_64.whl" + "hash": "beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512", + "url": "https://files.pythonhosted.org/packages/46/ab/bab9eb1566cd16f060b54055dd39cf6a34bfa0240c53a7218c43e974295b/ruamel.yaml.clib-0.2.8.tar.gz" }, { "algorithm": "sha256", - "hash": "b7316d3edeba8a403686cdcad4af737b8415493101e7462a70ff73dd0609eafc", - "url": "https://files.pythonhosted.org/packages/61/dd/38fc61ee050bd7cd24126721fae6cd7044b34cd8821e9d12a02c04757b7d/ujson-5.7.0-cp38-cp38-musllinux_1_1_aarch64.whl" + "hash": "1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62", + "url": "https://files.pythonhosted.org/packages/55/b3/e2531a050758b717c969cbf76c103b75d8a01e11af931b94ba656117fbe9/ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl" }, { "algorithm": "sha256", - "hash": "8b4257307e3662aa65e2644a277ca68783c5d51190ed9c49efebdd3cbfd5fa44", - "url": "https://files.pythonhosted.org/packages/65/89/398648bb869af5fce3d246ba61fb154528d5e71c24d6bcb008676d15fc2c/ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001", + "url": "https://files.pythonhosted.org/packages/56/a9/e3be88fcebe04016c57207260f2b07c5ecacab86e9f585d10daaa2a4074f/ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "2f242eec917bafdc3f73a1021617db85f9958df80f267db69c76d766058f7b19", - "url": "https://files.pythonhosted.org/packages/69/24/a7df580e9981c4f8a28eb96eb897ab7363b96fca7f8a398ddc735bf190ea/ujson-5.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c", + "url": "https://files.pythonhosted.org/packages/57/e4/f572d7e2502854f15291dfa94eebdc687e04db387559f026995c7697af34/ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl" }, { "algorithm": "sha256", - "hash": "afff311e9f065a8f03c3753db7011bae7beb73a66189c7ea5fcb0456b7041ea4", - "url": "https://files.pythonhosted.org/packages/71/bc/c8851ac5cf2ec8b0a69ef1e220fc27a88f8ff72fe1751fda0d7b28b58604/ujson-5.7.0-cp310-cp310-musllinux_1_1_aarch64.whl" + "hash": "305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1", + "url": "https://files.pythonhosted.org/packages/5a/45/644d839c09c0717c2d7f26b705560ad74b3056085b3bc7f9c2ac2081317b/ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl" }, { "algorithm": "sha256", - "hash": "dda9aa4c33435147262cd2ea87c6b7a1ca83ba9b3933ff7df34e69fee9fced0c", - "url": "https://files.pythonhosted.org/packages/73/34/8821ac107019227a5ba3a544208cff444fee14bf779e08ec4e3706c91d00/ujson-5.7.0-cp38-cp38-musllinux_1_1_x86_64.whl" + "hash": "e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28", + "url": "https://files.pythonhosted.org/packages/5c/f0/702e56e12497da7960ed8a6972e5edc50545757c40f1a86a41a5217da7e9/ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl" }, { "algorithm": "sha256", - "hash": "4ee997799a23227e2319a3f8817ce0b058923dbd31904761b788dc8f53bd3e30", - "url": "https://files.pythonhosted.org/packages/75/82/b08227424871ac0cd739d142a7fd071d2934755dfcf8460e6e13d649f1b1/ujson-5.7.0-cp38-cp38-musllinux_1_1_i686.whl" + "hash": "07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462", + "url": "https://files.pythonhosted.org/packages/61/ee/4874c9fc96010fce85abefdcbe770650c5324288e988d7a48b527a423815/ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "7592f40175c723c032cdbe9fe5165b3b5903604f774ab0849363386e99e1f253", - "url": "https://files.pythonhosted.org/packages/76/23/86820eb933c7d626380881a2d88bf9e395771ce349e5261df1e6760d209c/ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2", + "url": "https://files.pythonhosted.org/packages/66/98/8de4f22bbfd9135deb3422e96d450c4bc0a57d38c25976119307d2efe0aa/ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "5593263a7fcfb934107444bcfba9dde8145b282de0ee9f61e285e59a916dda0f", - "url": "https://files.pythonhosted.org/packages/7b/77/14bef9f13f68f93643d1e8f3652bd40e7bdf54fc8968f20976c3c2a1dbe1/ujson-5.7.0-cp311-cp311-musllinux_1_1_x86_64.whl" + "hash": "ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1", + "url": "https://files.pythonhosted.org/packages/7a/a2/eb5e9d088cb9d15c24d956944c09dca0a89108ad6e2e913c099ef36e3f0d/ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl" }, { "algorithm": "sha256", - "hash": "d7ff6ebb43bc81b057724e89550b13c9a30eda0f29c2f506f8b009895438f5a6", - "url": "https://files.pythonhosted.org/packages/7b/f6/f363b991aae592a77fe80f2882753211b59ed6a5174fddbb1ed6f5deccad/ujson-5.7.0-cp311-cp311-musllinux_1_1_aarch64.whl" + "hash": "da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5", + "url": "https://files.pythonhosted.org/packages/7c/b2/389b345a60131593028b0263fddaa580edb4081697a3f3aa1f168f67519f/ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "6411aea4c94a8e93c2baac096fbf697af35ba2b2ed410b8b360b3c0957a952d3", - "url": "https://files.pythonhosted.org/packages/85/4a/1db9cc0d4d78d4485a6527cf5ed2602729d87d8c35a4f11ec6890708ac75/ujson-5.7.0-cp39-cp39-macosx_10_9_x86_64.whl" + "hash": "1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe", + "url": "https://files.pythonhosted.org/packages/7c/e4/0d19d65e340f93df1c47f323d95fa4b256bb28320290f5fddef90837853a/ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl" }, { "algorithm": "sha256", - "hash": "67a19fd8e7d8cc58a169bea99fed5666023adf707a536d8f7b0a3c51dd498abf", - "url": "https://files.pythonhosted.org/packages/87/f1/d5ee0307d455aab6fe90fde167a00feeb8f71eaf66292d2926221d1de2fe/ujson-5.7.0-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d", + "url": "https://files.pythonhosted.org/packages/87/a6/efb1add3bac06c25aa4c8ff8c6d3e5e91c539f6600832dd63ff98e2b44cc/ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "35209cb2c13fcb9d76d249286105b4897b75a5e7f0efb0c0f4b90f222ce48910", - "url": "https://files.pythonhosted.org/packages/95/fb/fcd8f947f773ea55f650d64acd15240592c5637b3bfea164b4cd83da84c1/ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334", + "url": "https://files.pythonhosted.org/packages/88/30/fc45b45d5eaf2ff36cffd215a2f85e9b90ac04e70b97fd4097017abfb567/ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl" }, { "algorithm": "sha256", - "hash": "aae4d9e1b4c7b61780f0a006c897a4a1904f862fdab1abb3ea8f45bd11aa58f3", - "url": "https://files.pythonhosted.org/packages/a8/0d/51c70250116700017a3dc84ca910ff7c480c8d22afa76366920e8b1fdbfc/ujson-5.7.0-cp310-cp310-macosx_11_0_arm64.whl" + "hash": "184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b", + "url": "https://files.pythonhosted.org/packages/8d/c0/fd7196ca7a1c3867e7068ad1c4ff9230291af3f8adab2f9c2c202ecaf9cb/ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl" }, { "algorithm": "sha256", - "hash": "00343501dbaa5172e78ef0e37f9ebd08040110e11c12420ff7c1f9f0332d939e", - "url": "https://files.pythonhosted.org/packages/aa/e5/7655459351a1ce26202bbe971a6e6959d366925babe716f3751e1de96920/ujson-5.7.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f", + "url": "https://files.pythonhosted.org/packages/90/8c/6cdb44f548b29eb6328b9e7e175696336bc856de2ff82e5776f860f03822/ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl" }, { "algorithm": "sha256", - "hash": "b6a6961fc48821d84b1198a09516e396d56551e910d489692126e90bf4887d29", - "url": "https://files.pythonhosted.org/packages/b4/50/5146b9464506718a9372e12d15f2cff330575ee7cf5faf3c51aa83d82e4a/ujson-5.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b", + "url": "https://files.pythonhosted.org/packages/a4/f7/22d6b620ed895a05d40802d8281eff924dc6190f682d933d4efff60db3b5/ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl" }, { "algorithm": "sha256", - "hash": "d8cd622c069368d5074bd93817b31bdb02f8d818e57c29e206f10a1f9c6337dd", - "url": "https://files.pythonhosted.org/packages/b5/27/b8d3830cb60adc08505321b21cd2ae3930fe9d140728026dee17b2f795ef/ujson-5.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d", + "url": "https://files.pythonhosted.org/packages/af/dc/133547f90f744a0c827bac5411d84d4e81da640deb3af1459e38c5f3b6a0/ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "d36a807a24c7d44f71686685ae6fbc8793d784bca1adf4c89f5f780b835b6243", - "url": "https://files.pythonhosted.org/packages/c1/39/a4e45a0b9f1be517d0236a52292adb21ffdf6531bd36310488ed1ee07071/ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069", + "url": "https://files.pythonhosted.org/packages/b1/15/971b385c098e8d0d170893f5ba558452bb7b776a0c90658b8f4dd0e3382b/ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl" }, { "algorithm": "sha256", - "hash": "b738282e12a05f400b291966630a98d622da0938caa4bc93cf65adb5f4281c60", - "url": "https://files.pythonhosted.org/packages/d1/7d/ec4dace4c686be92845e3d593f01828465546c5b8254ca296324cbcda8f8/ujson-5.7.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf", + "url": "https://files.pythonhosted.org/packages/b2/ed/f221e60a4cdc7996aae23643da44b12ef33f457c2a52d590236a6950ac8e/ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "24ad1aa7fc4e4caa41d3d343512ce68e41411fb92adf7f434a4d4b3749dc8f58", - "url": "https://files.pythonhosted.org/packages/d2/5b/876d7ca50f6be9c72a806a74d55a585043faae36d9a160ca4351f5d64b4d/ujson-5.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899", + "url": "https://files.pythonhosted.org/packages/c9/ff/f781eb5e2ae011e586d5426e2086a011cf1e0f59704a6cad1387975c5a62/ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl" }, { "algorithm": "sha256", - "hash": "d2e43ccdba1cb5c6d3448eadf6fc0dae7be6c77e357a3abc968d1b44e265866d", - "url": "https://files.pythonhosted.org/packages/d4/13/4c59d1dd29f7ec9b80cffb8ac393e735c5171e9430eb9a9af10e8fbc7b66/ujson-5.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d", + "url": "https://files.pythonhosted.org/packages/ca/01/37ac131614f71b98e9b148b2d7790662dcee92217d2fb4bac1aa377def33/ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl" }, { "algorithm": "sha256", - "hash": "137831d8a0db302fb6828ee21c67ad63ac537bddc4376e1aab1c8573756ee21c", - "url": "https://files.pythonhosted.org/packages/d9/3e/507663d97fb574b56b35df2fb3d059516f9d11c270ab0ff170ef9cca2853/ujson-5.7.0-cp310-cp310-musllinux_1_1_x86_64.whl" + "hash": "fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412", + "url": "https://files.pythonhosted.org/packages/d3/62/c60b034d9a008bbd566eeecf53a5a4c73d191c8de261290db6761802b72d/ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl" }, { "algorithm": "sha256", - "hash": "7b9dc5a90e2149643df7f23634fe202fed5ebc787a2a1be95cf23632b4d90651", - "url": "https://files.pythonhosted.org/packages/da/bc/d8b84c6e1156a7cdc4b3269994aff52e90101ddbfc0a8dabebbd8f484f54/ujson-5.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9", + "url": "https://files.pythonhosted.org/packages/e3/41/f62e67ac651358b8f0d60cfb12ab2daf99b1b69eeaa188d0cec809d943a6/ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl" + } + ], + "project_name": "ruamel-yaml-clib", + "requires_dists": [], + "requires_python": ">=3.6", + "version": "0.2.8" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "ef30b2d886f1b0d3b5265fde7eef5dc12cafbfa6b41d8bea101cf15694f3a803", + "url": "https://files.pythonhosted.org/packages/19/1c/d03ad313bfcc2956c06ae00a295d48703d6ed322f8b503e852bdb58f7806/semgrep-1.72.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-musllinux_1_0_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "6abb8e6d8f1ae72f0ed18287245f5b6d40094e2656d1eab6d99d666361514074", - "url": "https://files.pythonhosted.org/packages/e3/c1/2e7163fdad47acb63ac2231b70b637cd8dada78c2ad985a438930ef0ac8c/ujson-5.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "8f2905397382b44b654f137acf5ee6f04da0d96a5beea757c7ac382fac3e0db6", + "url": "https://files.pythonhosted.org/packages/03/37/2dc2456d16953a90859fb6cac43ed3953258d72a1bb56ee31a64ffa48fee/semgrep-1.72.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-macosx_10_14_x86_64.whl" }, { "algorithm": "sha256", - "hash": "90712dfc775b2c7a07d4d8e059dd58636bd6ff1776d79857776152e693bddea6", - "url": "https://files.pythonhosted.org/packages/ea/f8/e547383551149f23a9cb40a717d75d2a72c6df50416c68538c64b79cd5bb/ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "fd7673cc36ec5baa70f0b0bc0e0a1f6751abede790c87ef5f4dcc90c2a178699", + "url": "https://files.pythonhosted.org/packages/19/d8/2b701067cca4a01c27cf0193b139ac0d056464a59ab1c576fb818333b782/semgrep-1.72.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-any.whl" }, { "algorithm": "sha256", - "hash": "b522be14a28e6ac1cf818599aeff1004a28b42df4ed4d7bc819887b9dac915fc", - "url": "https://files.pythonhosted.org/packages/ef/f5/76dfa7e2e8135213ece8cd18478338bc9a3b4820152ecec5632dce598f66/ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "65cd19df8b8cafafdfd420303cc1bc81412a27e4b6895e01852d5f92d7aef347", + "url": "https://files.pythonhosted.org/packages/9c/82/072fc36a736948fd126c5220668d97b1fe2efb3f9ace2fa1a52f6f504654/semgrep-1.72.0.tar.gz" }, { "algorithm": "sha256", - "hash": "16b2254a77b310f118717715259a196662baa6b1f63b1a642d12ab1ff998c3d7", - "url": "https://files.pythonhosted.org/packages/f8/d1/369fceb26e8eb69f9f8792323d123351c187c7866a0457c3ffe90ee9793c/ujson-5.7.0-cp37-cp37m-musllinux_1_1_x86_64.whl" + "hash": "d1d0648b7cbdb4c7634517d86f1b65d1f5530d73bcb4ba71efa10f212c6d0b2e", + "url": "https://files.pythonhosted.org/packages/e1/c7/9b2da1653d4e59b2f940ea1f688158999db191028fb425a76e2c090d05d8/semgrep-1.72.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-macosx_11_0_arm64.whl" + } + ], + "project_name": "semgrep", + "requires_dists": [ + "attrs>=21.3", + "boltons~=21.0", + "click-option-group~=0.5", + "click~=8.1", + "colorama~=0.4.0", + "defusedxml~=0.7.1", + "exceptiongroup~=1.2.0", + "glom~=22.1", + "jsonschema~=4.6", + "packaging>=21.0", + "peewee~=3.14", + "requests~=2.22", + "rich>=12.6.0", + "ruamel.yaml<0.18,>=0.16.0", + "tomli~=2.0.1", + "typing-extensions~=4.2", + "urllib3~=2.0", + "wcmatch~=8.3" + ], + "requires_python": ">=3.8", + "version": "1.72.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "0ee295761e1c6c30400641f0a20d381633d7622633cdf83a194f3c876a0e4b7e", - "url": "https://files.pythonhosted.org/packages/fa/d6/01756485dd9c42f12f9b74c6b4b3f3008917e091597390a970cc85486631/ujson-5.7.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", + "url": "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz" } ], - "project_name": "ujson", + "project_name": "tomli", "requires_dists": [], "requires_python": ">=3.7", - "version": "5.7.0" + "version": "2.0.1" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07", - "url": "https://files.pythonhosted.org/packages/b0/53/aa91e163dcfd1e5b82d8a890ecf13314e3e149c05270cc644581f77f17fd/urllib3-1.26.18-py2.py3-none-any.whl" + "hash": "c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a", + "url": "https://files.pythonhosted.org/packages/01/f3/936e209267d6ef7510322191003885de524fc48d1b43269810cd589ceaf5/typing_extensions-4.11.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0", - "url": "https://files.pythonhosted.org/packages/0c/39/64487bf07df2ed854cc06078c27c0d0abc59bd27b32232876e403c333a08/urllib3-1.26.18.tar.gz" + "hash": "83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0", + "url": "https://files.pythonhosted.org/packages/f6/f3/b827b3ab53b4e3d8513914586dcca61c355fa2ce8252dea4da56e67bf8f2/typing_extensions-4.11.0.tar.gz" + } + ], + "project_name": "typing-extensions", + "requires_dists": [], + "requires_python": ">=3.8", + "version": "4.11.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", + "url": "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19", + "url": "https://files.pythonhosted.org/packages/7a/50/7fd50a27caa0652cd4caf224aa87741ea41d3265ad13f010886167cfcc79/urllib3-2.2.1.tar.gz" } ], "project_name": "urllib3", "requires_dists": [ - "PySocks!=1.5.7,<2.0,>=1.5.6; extra == \"socks\"", - "brotli==1.0.9; (os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\") and extra == \"brotli\"", - "brotli>=1.0.9; (python_version >= \"3\" and platform_python_implementation == \"CPython\") and extra == \"brotli\"", - "brotlicffi>=0.8.0; ((os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\") and extra == \"brotli\"", - "brotlipy>=0.6.0; (os_name == \"nt\" and python_version < \"3\") and extra == \"brotli\"", - "certifi; extra == \"secure\"", - "cryptography>=1.3.4; extra == \"secure\"", - "idna>=2.0.0; extra == \"secure\"", - "ipaddress; python_version == \"2.7\" and extra == \"secure\"", - "pyOpenSSL>=0.14; extra == \"secure\"", - "urllib3-secure-extra; extra == \"secure\"" + "brotli>=1.0.9; platform_python_implementation == \"CPython\" and extra == \"brotli\"", + "brotlicffi>=0.8.0; platform_python_implementation != \"CPython\" and extra == \"brotli\"", + "h2<5,>=4; extra == \"h2\"", + "pysocks!=1.5.7,<2.0,>=1.5.6; extra == \"socks\"", + "zstandard>=0.18.0; extra == \"zstd\"" ], - "requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7", - "version": "1.26.18" + "requires_python": ">=3.8", + "version": "2.2.1" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "3476cd107aba7b25ba1d59406938a47dc7eec6cfd0ad09ff77193f21a964dee7", - "url": "https://files.pythonhosted.org/packages/b4/a7/1875f68c4e39f9c68f5ba3aaf80ed1853903e5119941d514789fbc51a80d/wcmatch-8.4.1-py3-none-any.whl" + "hash": "24c19cedc92bc9c9e27f39db4e1824d72f95bd2cea32b254a47a45b1a1b227ed", + "url": "https://files.pythonhosted.org/packages/17/06/73f17a8c322de938809f570e7bb09f1345fc9821b8fbe37f0f785d403475/wcmatch-8.5.1-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "b1f042a899ea4c458b7321da1b5e3331e3e0ec781583434de1301946ceadb943", - "url": "https://files.pythonhosted.org/packages/b7/94/5dd083fc972655f6689587c3af705aabc8b8e781bacdf22d6d2282fe6142/wcmatch-8.4.1.tar.gz" + "hash": "c0088c7f6426cf6bf27e530e2b7b734031905f7e490475fd83c7c5008ab581b3", + "url": "https://files.pythonhosted.org/packages/38/c6/0c5f324561c9396868d6badf571590c1a7802a81180c3097e4dfdc2f35c0/wcmatch-8.5.1.tar.gz" } ], "project_name": "wcmatch", "requires_dists": [ "bracex>=2.1.1" ], - "requires_python": ">=3.7", - "version": "8.4.1" + "requires_python": ">=3.8", + "version": "8.5.1" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556", - "url": "https://files.pythonhosted.org/packages/5b/fa/c9e82bbe1af6266adf08afb563905eb87cab83fde00a0a08963510621047/zipp-3.15.0-py3-none-any.whl" + "hash": "206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b", + "url": "https://files.pythonhosted.org/packages/c2/0a/ba9d0ee9536d3ef73a3448e931776e658b36f128d344e175bc32b092a8bf/zipp-3.18.1-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", - "url": "https://files.pythonhosted.org/packages/00/27/f0ac6b846684cecce1ee93d32450c45ab607f65c2e0255f0092032d91f07/zipp-3.15.0.tar.gz" + "hash": "2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715", + "url": "https://files.pythonhosted.org/packages/3e/ef/65da662da6f9991e87f058bc90b91a935ae655a16ae5514660d6460d1298/zipp-3.18.1.tar.gz" } ], "project_name": "zipp", "requires_dists": [ "big-O; extra == \"testing\"", - "flake8<5; extra == \"testing\"", "furo; extra == \"docs\"", "jaraco.functools; extra == \"testing\"", "jaraco.itertools; extra == \"testing\"", - "jaraco.packaging>=9; extra == \"docs\"", + "jaraco.packaging>=9.3; extra == \"docs\"", "jaraco.tidelift>=1.4; extra == \"docs\"", "more-itertools; extra == \"testing\"", - "pytest-black>=0.3.7; platform_python_implementation != \"PyPy\" and extra == \"testing\"", "pytest-checkdocs>=2.4; extra == \"testing\"", "pytest-cov; extra == \"testing\"", - "pytest-enabler>=1.3; extra == \"testing\"", - "pytest-flake8; python_version < \"3.12\" and extra == \"testing\"", - "pytest-mypy>=0.9.1; platform_python_implementation != \"PyPy\" and extra == \"testing\"", + "pytest-enabler>=2.2; extra == \"testing\"", + "pytest-ignore-flaky; extra == \"testing\"", + "pytest-mypy; platform_python_implementation != \"PyPy\" and extra == \"testing\"", + "pytest-ruff>=0.2.1; extra == \"testing\"", "pytest>=6; extra == \"testing\"", "rst.linker>=1.9; extra == \"docs\"", "sphinx-lint; extra == \"docs\"", "sphinx>=3.5; extra == \"docs\"" ], - "requires_python": ">=3.7", - "version": "3.15.0" + "requires_python": ">=3.8", + "version": "3.18.1" } ], "platform_tag": null @@ -1747,14 +1742,14 @@ "only_builds": [], "only_wheels": [], "path_mappings": {}, - "pex_version": "2.2.1", + "pex_version": "2.3.1", "pip_version": "24.0", "prefer_older_binary": false, "requirements": [ "semgrep<2,>=1.20.0" ], "requires_python": [ - "<4,>=3.7" + "<4,>=3.8" ], "resolver_version": "pip-2020-resolver", "style": "universal", diff --git a/src/python/pants/backend/tools/semgrep/subsystem.py b/src/python/pants/backend/tools/semgrep/subsystem.py index 51c7a578ea5..fca5fc677ce 100644 --- a/src/python/pants/backend/tools/semgrep/subsystem.py +++ b/src/python/pants/backend/tools/semgrep/subsystem.py @@ -45,6 +45,7 @@ class SemgrepSubsystem(PythonToolBase): default_requirements = ["semgrep>=1.20.0,<2"] register_interpreter_constraints = True + default_interpreter_constraints = ["CPython>=3.8,<4"] register_lockfile = True default_lockfile_resource = ("pants.backend.tools.semgrep", "semgrep.lock") From fa60552d2620457e249e93a64b73f1e42834da2f Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 15 May 2024 11:44:15 +1000 Subject: [PATCH 03/18] Add release notes for 2.21.x (#20894) This writes out release notes for the 2.21.x branch, up to and including 54a36ea05358eddd13a841f2c00dc1f4a1519a0c #20846 --------- Co-authored-by: SJ --- docs/notes/2.21.x.md | 155 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 docs/notes/2.21.x.md diff --git a/docs/notes/2.21.x.md b/docs/notes/2.21.x.md new file mode 100644 index 00000000000..b26965fdc8f --- /dev/null +++ b/docs/notes/2.21.x.md @@ -0,0 +1,155 @@ +# 2.21.x Release Series + +Pants 2 is a fast, scalable, user-friendly build system for codebases of all sizes. It's currently focused on Python, Go, Java, Scala, Kotlin, Shell, and Docker, with support for other languages and frameworks coming soon. + +Individuals and companies can now [sponsor Pants financially](https://www.pantsbuild.org/sponsorship). + +Pants is an open-source project that is not owned or controlled by any one company or organization, and does incur some expenses. These expenses are managed by Pants Build, a non-profit that was established for this purpose. This non-profit's only source of revenue is sponsorship by individuals and companies that use Pants. + +We offer [formal sponsorship tiers for companies](https://www.pantsbuild.org/sponsorship), as well as individual sponsorships via [GitHub](https://github.com/sponsors/pantsbuild). + +## What's New + +### Highlights + +- All built-in backends now support the `[test].attempts_default` option to automatically retry tests. +- Significant speed-ups when manipulating Python dependencies, especially huge ones like PyTorch. +- When a `pants test ...` run in CI fails, the summary now includes suggested invocation to copy-paste and re-run locally. +- The `__defaults__` symbol now applies to targets generated by a target generator, in addition to targets written literally in BUILD files. + +Keep reading to see the details and what's also included. + +### Overall + +All built-in targets that support testing with the `pants test` goal now support automatic retries, using [the `[test].attempts_default` option](https://www.pantsbuild.org/2.21/reference/goals/test#attempts_default). + +When running in CI, the printed summary at the end of a `pants test ...` run will now include single invocation to rerun all failing tests, if any. This can be controlled by [the new `[test].show_rerun_command` option](https://www.pantsbuild.org/2.21/reference/goals/test#show_rerun_command). + +Option values can now be optionally [read from files using `@?...`](https://www.pantsbuild.org/2.21/docs/using-pants/key-concepts/options#reading-individual-option-values-from-files). For instance, `opt = "@some_file.txt"` will give an error if `some_file.txt` does not exist, but the new syntax `opt = "@?some_file.txt"` will just leave the option unset. + +The `stats` subsystem can now output structured data, using [the new `[stats].format = "jsonlines"` option](https://www.pantsbuild.org/2.21/reference/subsystems/stats#format). The structured output can make it easier to ingest and analyze Pants stats data, for example, tracking information about CI builds. + +Introspection goals have had a few improvements: + +* [`pants peek ...`](https://www.pantsbuild.org/2.21/docs/using-pants/project-introspection#peek---programmatically-inspect-a-target) now includes a `goals` field which will return a curated list of goals that can be run on the target + +* Using the `json` format for [`pants dependencies ...`](https://www.pantsbuild.org/2.21/reference/goals/dependencies#format) or [`pants dependencies ...`](format#format) now correctly obeys the `output_file` setting. + +### BUILD files + +[The `__defaults__` symbol](https://www.pantsbuild.org/2.21/docs/using-pants/key-concepts/targets-and-build-files#field-default-values) will now set default values for generated targets too. For instance, `__defaults__({python_source: dict(skip_black=True)})` will now apply to the `python_source` targets generated by a `python_sources` target, without having to write `__defaults__({(python_source, python_sources): ...})`. + +The [visibility rules](https://www.pantsbuild.org/2.21/docs/using-pants/validating-dependencies) now supports using `dict`s for rules, in addition to strings, the same as a selectors. This means, for instance, `{"path": "foo/bar.py", "action": "deny"}` is equivalent to `"!foo/bar.py"`. + +Targets that [use parametrize groups](https://www.pantsbuild.org/2.21/docs/using-pants/key-concepts/targets-and-build-files#parametrizing-targets) depending on other parametrized targets now match by the name passed as the positional argument to `parametrize`. + +### Backends + +#### Docker + +The [`cache_from` field](https://www.pantsbuild.org/2.21/reference/targets/docker_image#cache_from) on `docker_image` now supports multiple values. + +The [new `extra_run_args` field](https://www.pantsbuild.org/2.21/reference/targets/docker_image#extra_run_args) on `docker_image` allows adding extra arguments that are passed into the `docker run ...` invocation when running the image with `pants run`. + +Pants now correctly extracts the image ID when using `docker buildx` version 0.13.1 or greater. + +Note: existing code that sets `__defaults__(all=dict(extra_build_args=...))` to apply to `docker_image` targets will start applying to `pex_binary` targets too, since that target now has a field of the same name. Use `__defaults__({docker_image: dict(extra_build_args=...)})` instead. + +#### Go + +Failing `go_package` tests can now be automatically retried by setting [the `[test].attempts_default` option](https://www.pantsbuild.org/2.21/docs/go#retries). + +Go mod files referring to Go versions with patch (like `go 1.21.0`) no longer cause Pants to crash. + +#### Helm + +Failing `helm_unittest_test` tests can now be automatically retried by setting [the `[test].attempts_default` option](https://www.pantsbuild.org/2.21/docs/helm#retries). + +[The `sources` field](https://www.pantsbuild.org/2.21/reference/targets/helm_chart#sources) of `helm_chart` targets now includes documentation files like `README.md` and `values.schema.json` so that they are available once published. + +Versions 3.13.3 and 3.14.3 of the Helm tool are now [known](https://www.pantsbuild.org/2.21/reference/subsystems/helm#known_versions). Version 3.14.3 is now [the default version](https://www.pantsbuild.org/2.21/reference/subsystems/helm#version). + +The `experimental-deploy` goal now has built-in support for [the `--dry-run` option](https://www.pantsbuild.org/2.21/reference/goals/experimental-deploy#dry_run), which passes that to the underlying Helm invocation. + +[The `helm-k8s-parser` subsystem](https://www.pantsbuild.org/2.21/reference/subsystems/helm-k8s-parser) now uses `hikaru` version 0.16.0b0 by default. [The `helm-post-renderer` subsystem](https://www.pantsbuild.org/2.21/reference/subsystems/helm-post-renderer) now uses `yamlpath` version 3.8.1 by default. + +#### JavaScript + +Failing `javascript_test` tests can now be automatically retried by setting [the `[test].attempts_default` option](https://www.pantsbuild.org/2.21/reference/goals/test#attempts_default). + +`package_json` targets using yarn v2 or greater as the package manager are now supported. + +#### JVM + +Failing `kotlin_junit_test`, `junit_test`, and `scala_junit_test` tests can now be automatically retried by setting [the `[test].attempts_default` option](https://www.pantsbuild.org/2.21/docs/jvm/java-and-scala#retries). + +##### Scala + +Scalafix can now be run by Pants using the [new `pants.backend.experimental.scala.lint.scalafix` backend](https://www.pantsbuild.org/2.21/docs/jvm/java-and-scala#fix-scala-code). + +#### Python + +All built-in tool lockfiles are now configured to support Python 3.12, if the underlying tool version does. + +Any PEX files Pants builds internally now take less work to build, meaning running tools and tests can be faster. These PEX files are now built with the `--no-pre-install-wheels` flag and thus directly use the wheels for third-party dependencies, without repackaging them. This is particularly valuable for requirements with huge wheels like PyTorch. [One benchmark](https://github.com/pex-tool/pex/issues/2292#issuecomment-1854582647) indicates key parts of build a pex file containing PyTorch are several minutes faster. This speed up applies for all tools and is visible even with smaller wheels. The author of these release notes observed the time to run mypy from scratch dropped from 150s to 100s in a production codebase. + +The [new `extra_build_args` field](https://www.pantsbuild.org/2.21/reference/targets/pex_binary#extra_build_args) on `pex_binary` allows passing arbitrary additional arguments to the underlying pex invocation when packaging those targets. (Note: existing code that sets `__defaults__(all=dict(extra_build_args=...))` to apply to `docker_image` targets will thus start applying this to `pex_binary` targets too. Use `__defaults__({docker_image: dict(extra_build_args=...)})` instead.) + +Pants now uses pex [version 2.3.1 by default](https://www.pantsbuild.org/2.21/reference/subsystems/pex-cli#version), and requires version 2.3.0 or greater. + +[The Ruff backend](https://www.pantsbuild.org/2.21/reference/subsystems/ruff) now uses version 0.3.0 by default. + +The new [`skip_ruff_check`](https://www.pantsbuild.org/2.21/reference/targets/python_source#skip_ruff_check) and [`skip_ruff_format`](https://www.pantsbuild.org/2.21/reference/targets/python_source#skip_ruff_format) fields on targets like `python_source` and `python_test` allow stopping either the linter or formatter specifically from running on that target. The [existing `skip_ruff` field](https://www.pantsbuild.org/2.21/reference/targets/python_source#skip_ruff) continues to stop both the linter and formatter from running. + +[The `runtime` field](https://www.pantsbuild.org/2.21/reference/targets/python_google_cloud_function#runtime) of the `python_google_cloud_function` target now supports `python312`. + +Virtualenvs exported with `pants export ...` have improved in two ways (Note: these apply only when using [`py_resolve_format = "mutable_virtualenv"`](https://www.pantsbuild.org/2.21/reference/goals/export#py_resolve_format), which is the default): + +* They include Pants' resolve name in the shell prompt, when activated. + +* The [new `py_hermetic_scripts` option](https://www.pantsbuild.org/2.21/reference/goals/export#py_hermetic_scripts) allows disabling the default behaviour of rewriting scripts to be hermetic (ignore system libraries and configuration), to, for example, allow IDEs like PyCharm to inject its debugger, coverage, or other IDE-specific libs when running a script. + +Default module mappings were added for more modules: `antlr4-python3-runtime`, `auth0-python`, `biopython`, `cloud-sql-python-connector`, `coolprop`, `databricks-sdk`, `databricks-sql-connetor`, `delta-spark`, `django-activity-stream`, `django-notifications-hq`, `django-oauth-toolkit`, `django-two-factor-auth`, `djangorestframework-jwt`, `dnspython`, `drf-api-tracking`, `faiss-cpu`, `faiss-gpu`, `fonttools`, `grpcio-status`, `grpcio-testing`, `matplotlib`, `matrix-nio`, `netcdf4`, `o365`, `py-healthcheck`, `pycryptodome`, `perfa`, `pynacl`, `pyshp`, `python-sat`, `pywavelets`, `robotraconteur`, `sisl`. The mappings for `opentelemetry` have been restored after a regression in 2.20.0 (NB. this fix was released in 2.20.1 too). + +The default version for many subsystems/backends has been updated: + +- [`coverage-py`](https://www.pantsbuild.org/2.21/reference/subsystems/coverage-py): 7.2.7 +- [`debugpy`](https://www.pantsbuild.org/2.21/reference/subsystems/debugpy): 1.6.7.post1 +- [`pants.backend.python.lint.autoflake`](https://www.pantsbuild.org/2.21/reference/subsystems/autoflake): 2.1.1 +- [`pants.backend.python.lint.bandit`](https://www.pantsbuild.org/2.21/reference/subsystems/bandit): 1.7.5 +- [`pants.backend.python.lint.black`](https://www.pantsbuild.org/2.21/reference/subsystems/black): 23.3.0 +- [`pants.backend.python.typecheck.mypy`](https://www.pantsbuild.org/2.21/reference/subsystems/mypy): 1.4.1 +- [`pants.backend.python.lint.pyupgrade`](https://www.pantsbuild.org/2.21/reference/subsystems/pyupgrade): 3.3.2 +- [`pants.backend.python.lint.yapf`](https://www.pantsbuild.org/2.21/reference/subsystems/yapf): 0.40.2 + +The deprecation has expired for the `python_awslambda` target alias: use the [new `python_aws_lambda_function` target](https://www.pantsbuild.org/2.21/reference/targets/python_aws_lambda_function) instead. + +#### Shell + +[The `pants.backend.shell.lint.shellcheck` backend](https://www.pantsbuild.org/2.21/reference/subsystems/shellcheck) now uses [version 0.10.0 by default](https://www.pantsbuild.org/2.21/reference/subsystems/shellcheck#version). + +Failing `shunit2_test` and `experimental_test_shell_command` tests can now be automatically retried by setting [the `[test].attempts_default` option](https://www.pantsbuild.org/2.21/docs/shell#retries). + +#### Terraform + +The `experimental-deploy` goal now has built-in support for [the `--dry-run` option](https://www.pantsbuild.org/2.21/reference/goals/experimental-deploy#dry_run), which runs `terraform plan` instead of `terraform apply`. + +[The `terraform-hcl2-parser` subsystem](https://www.pantsbuild.org/2.21/reference/subsystems/terraform-hcl2-parser) now uses `python-hcl2` version 4.3.2 by default. + +#### Yaml + +[The `pants.backend.experimental.tools.yamllint` backend](https://www.pantsbuild.org/2.21/reference/subsystems/yamllint) now uses version 1.32.0 by default. + +### Plugin API changes + +Plugins that define target generators should set the `generated_target_cls` class var to the targets that they generate. This ensures that `__defaults__` set for that `generated_target_cls` class are applied to the targets generated by the plugin. ([#20649](https://github.com/pantsbuild/pants/pull/20649)) + +[The new `pants.backend.project_info.peek.HasAdditionalTargetDataFieldSet` union](https://www.pantsbuild.org/2.21/docs/writing-plugins/the-target-api/concepts#adding-information-to-pants-peek-output) allows attaching arbitrary information to targets. This is shown in the `peek` output for these targets by using `pants peek --include-additional-info ...`. + +[The new `pants.core.goals.resolves.ExportableTool` union](https://www.pantsbuild.org/2.21/docs/writing-plugins/common-subsystem-tasks#making-subsystems-exportable-with-their-default-lockfile) allows subsystems and tools to be exported with `pants export ...`, similar to a user resolve. ([#20730](https://github.com/pantsbuild/pants/pull/20730)) + +The `implicit_value` keyword argument for option registration is no longer supported. ([#20762](https://github.com/pantsbuild/pants/pull/20762)) + +## Full Changelog + +For the full changelog, see the individual GitHub Releases for this series: https://github.com/pantsbuild/pants/releases From bef63e96042d6f79975a662172b4cd85e8ecd0a3 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 15 May 2024 15:03:42 +1000 Subject: [PATCH 04/18] Fix markdown syntax in "Making subsystems exportable ..." docs (#20902) This makes two minor adjustments to the "Making subsystems exportable ..." docs from #20730: - close the `:::note` admonitions with `:::` - indent some items to be nested within the relevant list items Fixes #20891 --- .../common-subsystem-tasks.mdx | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/docs/writing-plugins/common-subsystem-tasks.mdx b/docs/docs/writing-plugins/common-subsystem-tasks.mdx index d5bafb9fd73..70490d9ae3d 100644 --- a/docs/docs/writing-plugins/common-subsystem-tasks.mdx +++ b/docs/docs/writing-plugins/common-subsystem-tasks.mdx @@ -54,25 +54,27 @@ class FortranLintFieldSet(FieldSet): :::note Support depends on language backend of the subsystem Only some language backends support `pants export`. These include the Python and JVM backends. Only tools which are themselves written to use a backend with this feature can be exported. For example, a Python-based tool which operates on a different language is exportable. - +::: 1. Make the subsystem a subclass of `ExportableTool` -:::note Language backends may have done this in their Tool base class. For example, the Python backend with `PythonToolRequirementsBase` and JVM with `JvmToolBase` are already subclasses. + :::note Language backends may have done this in their Tool base class. + For example, the Python backend with `PythonToolRequirementsBase` and JVM with `JvmToolBase` are already subclasses. + ::: -```python -from pants.backend.python.subsystems.python_tool_base import PythonToolBase -from pants.core.goals.resolves import ExportableTool + ```python + from pants.backend.python.subsystems.python_tool_base import PythonToolBase + from pants.core.goals.resolves import ExportableTool -class FortranLint(PythonToolBase, ExportableTool): - ... -``` + class FortranLint(PythonToolBase, ExportableTool): + ... + ``` 2. Register your class with a `UnionRule` with `ExportableTool` -```python -def rules(): - return [ - UnionRule(ExportableTool, FortranLint) - ] -``` + ```python + def rules(): + return [ + UnionRule(ExportableTool, FortranLint) + ] + ``` From 1c1ae0d4af2a1048fd4d099023f0bd52fdcc3c9a Mon Sep 17 00:00:00 2001 From: Benjy Weinberger Date: Wed, 15 May 2024 18:33:43 -0400 Subject: [PATCH 05/18] Create FUNDING.yml (#20927) This will be displayed when people click the "Sponsor" button on github. --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..840fec3a377 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +Please visit [our sponsorship page](https://www.pantsbuild.org/sponsorship) for information on how to support the Pants Build project. From 8ba6ea25bdf0deeb21a3d71787081d4a8a1c0724 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 16 May 2024 08:38:08 +1000 Subject: [PATCH 06/18] Include extra_build_args on pex_binaries too (#20929) This moves the `extra_build_args` from being a field on `pex_binary` only to being a common field, so it appears on both `pex_binary` and the `pex_binaries` target generator. I'd missed this in #20737. --- docs/notes/2.22.x.md | 2 ++ src/python/pants/backend/python/target_types.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/notes/2.22.x.md b/docs/notes/2.22.x.md index f3bf8311a7b..c32d0063284 100644 --- a/docs/notes/2.22.x.md +++ b/docs/notes/2.22.x.md @@ -58,6 +58,8 @@ Initial support for Python 3.13 (pre-release) has been added. NB. built-in tool The [PyOxizider tool is effectively stagnant](https://github.com/indygreg/PyOxidizer/issues/7410). [The `pants.backend.experimental.python.packaging.pyoxidizer` backend](https://www.pantsbuild.org/2.22/docs/python/integrations/pyoxidizer) docs now have a note reflecting this. +[The `extra_build_args` field](https://www.pantsbuild.org/2.22/reference/targets/pex_binaries#extra_build_args) is now available on the `pex_binaries` target generator, in addition to `pex_binary`. + Default module mappings were added for more modules: The deprecation for the `platforms` field for the `pex_binary` and `pex_binaries` targets has expired, and so has been removed. The `resolve_local_platforms` field is now meaningless and is thus deprecated. diff --git a/src/python/pants/backend/python/target_types.py b/src/python/pants/backend/python/target_types.py index ea9a1b05409..4b7f5f86fbe 100644 --- a/src/python/pants/backend/python/target_types.py +++ b/src/python/pants/backend/python/target_types.py @@ -755,6 +755,7 @@ class PexVenvHermeticScripts(BoolField): PexIncludeToolsField, PexVenvSitePackagesCopies, PexVenvHermeticScripts, + PexExtraBuildArgsField, RestartableField, ) @@ -768,7 +769,6 @@ class PexBinary(Target): PexScriptField, PexExecutableField, PexArgsField, - PexExtraBuildArgsField, PexEnvField, OutputPathField, ) From 9e02d428b2ba08f8051e6219fb80b49e90c4fdc7 Mon Sep 17 00:00:00 2001 From: Nicholas Gustafson Date: Thu, 16 May 2024 08:24:40 +0900 Subject: [PATCH 07/18] Use Wildcard When jvm_exclude Artifact is None (#20802) Fixes #20794 Update `to_coord_str` method to append `*` to the artifact coordinate string when a `jvm_exclude` artifact is `None`. --------- Co-authored-by: Nicholas Gustafson --- docs/notes/2.22.x.md | 2 ++ .../coursier_fetch_integration_test.py | 25 ++++++++++++++++++- src/python/pants/jvm/target_types.py | 2 ++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/docs/notes/2.22.x.md b/docs/notes/2.22.x.md index c32d0063284..5f4313026dd 100644 --- a/docs/notes/2.22.x.md +++ b/docs/notes/2.22.x.md @@ -40,6 +40,8 @@ The default version of the [hikaru](https://github.com/haxsaw/hikaru) packages u for [`jvm_artifacts`](https://www.pantsbuild.org/2.22/reference/targets/jvm_artifacts) targets generator from `pom.xml`. +Exclusions for `jvm_artifact` and `scala_artifact` now correctly handle a `jvm_exclude` with only the group defined. + ##### Scala Setting the `orphan_files_behaviour = "ignore"` option for [`pants.backend.experimental.scala.lint.scalafix`](https://www.pantsbuild.org/2.22/reference/subsystems/scalafix#orphan_files_behavior) or [`pants.backend.experimental.scala.lint.scalafmt`](https://www.pantsbuild.org/2.22/reference/subsystems/scalafmt#orphan_files_behavior) backend is now properly silent. It previously showed spurious warnings. diff --git a/src/python/pants/jvm/resolve/coursier_fetch_integration_test.py b/src/python/pants/jvm/resolve/coursier_fetch_integration_test.py index 8167db55952..7e8efb87db5 100644 --- a/src/python/pants/jvm/resolve/coursier_fetch_integration_test.py +++ b/src/python/pants/jvm/resolve/coursier_fetch_integration_test.py @@ -18,7 +18,11 @@ from pants.jvm.resolve.coordinate import Coordinate, Coordinates from pants.jvm.resolve.coursier_fetch import CoursierLockfileEntry, CoursierResolvedLockfile from pants.jvm.resolve.coursier_fetch import rules as coursier_fetch_rules -from pants.jvm.target_types import JvmArtifactJarSourceField, JvmArtifactTarget +from pants.jvm.target_types import ( + JvmArtifactExclusion, + JvmArtifactJarSourceField, + JvmArtifactTarget, +) from pants.jvm.testutil import maybe_skip_jdk_test from pants.jvm.util_rules import ExtractFileDigest from pants.jvm.util_rules import rules as util_rules @@ -662,6 +666,25 @@ def test_transitive_excludes(rule_runner: RuleRunner) -> None: assert not any(i for i in entries if i.coord.artifact == "jackson-core") +@maybe_skip_jdk_test +def test_transitive_group_only_excludes(rule_runner: RuleRunner) -> None: + group_only_excludes = JvmArtifactExclusion(group="com.fasterxml.jackson.core", artifact=None) + + requirement = ArtifactRequirement( + coordinate=Coordinate( + group="com.fasterxml.jackson.module", + artifact="jackson-module-jaxb-annotations", + version="2.17.1", + ), + excludes=frozenset([group_only_excludes.to_coord_str()]), + ) + + resolve = rule_runner.request(CoursierResolvedLockfile, [ArtifactRequirements([requirement])]) + + entries = resolve.entries + assert not any(i for i in entries if i.coord.group == "com.fasterxml.jackson.core") + + @maybe_skip_jdk_test def test_missing_entry_for_transitive_dependency(rule_runner: RuleRunner) -> None: requirement = ArtifactRequirement( diff --git a/src/python/pants/jvm/target_types.py b/src/python/pants/jvm/target_types.py index ea7e3e6a5fc..c696eacbf65 100644 --- a/src/python/pants/jvm/target_types.py +++ b/src/python/pants/jvm/target_types.py @@ -309,6 +309,8 @@ def to_coord_str(self) -> str: result = self.group if self.artifact: result += f":{self.artifact}" + else: + result += ":*" return result From 40e8bc94803a70f42b2c2889f73573c914c153dd Mon Sep 17 00:00:00 2001 From: Krishnan Chandra <1229365+krishnan-chandra@users.noreply.github.com> Date: Wed, 15 May 2024 21:43:11 -0400 Subject: [PATCH 08/18] Add `help_short` field for Python tools and use it to generate the help text (#20921) Closes #20912. This actually wasn't too bad! I didn't realize the trick with `classproperty` would work as I intended, but it did. Looking at the individual commits should help separate out the repetitive work (changing `help` to `help_short`) from the more substantive changes. --- docs/notes/2.22.x.md | 4 +++- .../backend/cc/lint/clangformat/subsystem.py | 2 +- .../python/python_protobuf_subsystem.py | 4 ++-- .../docker/subsystems/dockerfile_parser.py | 2 +- .../backend/helm/subsystems/k8s_parser.py | 2 +- .../backend/helm/subsystems/post_renderer.py | 2 +- .../pants/backend/python/goals/coverage_py.py | 2 +- .../lint/add_trailing_comma/subsystem.py | 2 +- .../python/lint/autoflake/subsystem.py | 2 +- .../backend/python/lint/bandit/subsystem.py | 4 +++- .../backend/python/lint/black/subsystem.py | 2 +- .../python/lint/docformatter/subsystem.py | 2 +- .../backend/python/lint/flake8/subsystem.py | 2 +- .../backend/python/lint/isort/subsystem.py | 2 +- .../python/lint/pydocstyle/subsystem.py | 2 +- .../backend/python/lint/pylint/subsystem.py | 2 +- .../python/lint/pyupgrade/subsystem.py | 2 +- .../backend/python/lint/ruff/subsystem.py | 2 +- .../backend/python/lint/yapf/subsystem.py | 2 +- .../python/packaging/pyoxidizer/subsystem.py | 2 +- .../backend/python/subsystems/debugpy.py | 2 +- .../backend/python/subsystems/ipython.py | 2 +- .../pants/backend/python/subsystems/pytest.py | 2 +- .../python/subsystems/python_tool_base.py | 19 +++++++++++++++++-- .../backend/python/subsystems/setuptools.py | 2 +- .../python/subsystems/setuptools_scm.py | 2 +- .../pants/backend/python/subsystems/twine.py | 4 +++- .../python/typecheck/mypy/subsystem.py | 2 +- .../python/typecheck/pytype/subsystem.py | 2 +- .../backend/sql/lint/sqlfluff/subsystem.py | 2 +- .../backend/terraform/dependency_inference.py | 2 +- .../pants/backend/tools/semgrep/subsystem.py | 2 +- .../pants/backend/tools/yamllint/subsystem.py | 2 +- 33 files changed, 56 insertions(+), 35 deletions(-) diff --git a/docs/notes/2.22.x.md b/docs/notes/2.22.x.md index 5f4313026dd..fd37a0331a1 100644 --- a/docs/notes/2.22.x.md +++ b/docs/notes/2.22.x.md @@ -66,7 +66,7 @@ Default module mappings were added for more modules: The deprecation for the `platforms` field for the `pex_binary` and `pex_binaries` targets has expired, and so has been removed. The `resolve_local_platforms` field is now meaningless and is thus deprecated. -The option help text for the `install_from_resolve` field for Python tools now contains the default version of the tool, along with instructions on how to override this version using a custom lockfile. +Python tool subsystem docs and help text now include the default version of the tool, along with instructions on how to override this version using a custom lockfile. Additionally, the help text for the `install_from_resolve` option for Python tools now includes this same information. #### Semgrep @@ -82,6 +82,8 @@ Setting [the `orphan_files_behaviour = "ignore"` option](https://www.pantsbuild. ### Plugin API changes +The `PythonToolRequirementsBase` and `PythonToolBase` classes now have a new `help_short` field. Subclasses should now use `help_short` instead of the `help` field. The `help` field will be automatically generated using `help_short`, and will include the tool's default package version and provide instructions on how to override this version using a custom lockfile. + ## Full Changelog For the full changelog, see the individual GitHub Releases for this series: https://github.com/pantsbuild/pants/releases diff --git a/src/python/pants/backend/cc/lint/clangformat/subsystem.py b/src/python/pants/backend/cc/lint/clangformat/subsystem.py index bf3a73d1b4b..aa1a8ff72bc 100644 --- a/src/python/pants/backend/cc/lint/clangformat/subsystem.py +++ b/src/python/pants/backend/cc/lint/clangformat/subsystem.py @@ -18,7 +18,7 @@ class ClangFormat(PythonToolBase): options_scope = "clang-format" name = "ClangFormat" - help = help_text( + help_short = help_text( """ The clang-format utility for formatting C/C++ (and others) code (https://clang.llvm.org/docs/ClangFormat.html). The clang-format binaries diff --git a/src/python/pants/backend/codegen/protobuf/python/python_protobuf_subsystem.py b/src/python/pants/backend/codegen/protobuf/python/python_protobuf_subsystem.py index ca4246e7e2a..4a900415d28 100644 --- a/src/python/pants/backend/codegen/protobuf/python/python_protobuf_subsystem.py +++ b/src/python/pants/backend/codegen/protobuf/python/python_protobuf_subsystem.py @@ -93,7 +93,7 @@ class PythonProtobufSubsystem(Subsystem): class PythonProtobufMypyPlugin(PythonToolRequirementsBase): options_scope = "mypy-protobuf" - help = "Configuration of the mypy-protobuf type stub generation plugin." + help_short = "Configuration of the mypy-protobuf type stub generation plugin." default_requirements = ["mypy-protobuf>=3.4.0,<4"] @@ -104,7 +104,7 @@ class PythonProtobufMypyPlugin(PythonToolRequirementsBase): class PythonProtobufGrpclibPlugin(PythonToolRequirementsBase): options_scope = "python-grpclib-protobuf" - help = "Configuration of the grpclib plugin." + help_short = "Configuration of the grpclib plugin." default_requirements = ["grpclib[protobuf]>=0.4,<1"] diff --git a/src/python/pants/backend/docker/subsystems/dockerfile_parser.py b/src/python/pants/backend/docker/subsystems/dockerfile_parser.py index 0d7615e7e71..e06b602e9f0 100644 --- a/src/python/pants/backend/docker/subsystems/dockerfile_parser.py +++ b/src/python/pants/backend/docker/subsystems/dockerfile_parser.py @@ -33,7 +33,7 @@ class DockerfileParser(PythonToolRequirementsBase): options_scope = "dockerfile-parser" - help = "Used to parse Dockerfile build specs to infer their dependencies." + help_short = "Used to parse Dockerfile build specs to infer their dependencies." default_requirements = ["dockerfile>=3.2.0,<4"] diff --git a/src/python/pants/backend/helm/subsystems/k8s_parser.py b/src/python/pants/backend/helm/subsystems/k8s_parser.py index ccf0d22a717..067d5be431e 100644 --- a/src/python/pants/backend/helm/subsystems/k8s_parser.py +++ b/src/python/pants/backend/helm/subsystems/k8s_parser.py @@ -29,7 +29,7 @@ class HelmKubeParserSubsystem(PythonToolRequirementsBase): options_scope = "helm-k8s-parser" - help = "Analyses K8S manifests rendered by Helm." + help_short = "Analyses K8S manifests rendered by Helm." default_requirements = [ "hikaru>=1.1.0", diff --git a/src/python/pants/backend/helm/subsystems/post_renderer.py b/src/python/pants/backend/helm/subsystems/post_renderer.py index b34bdbe7d21..6a395444d6f 100644 --- a/src/python/pants/backend/helm/subsystems/post_renderer.py +++ b/src/python/pants/backend/helm/subsystems/post_renderer.py @@ -40,7 +40,7 @@ class HelmPostRendererSubsystem(PythonToolRequirementsBase): options_scope = "helm-post-renderer" - help = "Used perform modifications to the final output produced by Helm charts when they've been fully rendered." + help_short = "Used perform modifications to the final output produced by Helm charts when they've been fully rendered." default_requirements = [ "yamlpath>=3.6.0,<4", diff --git a/src/python/pants/backend/python/goals/coverage_py.py b/src/python/pants/backend/python/goals/coverage_py.py index d4fa9378903..9b705b94b08 100644 --- a/src/python/pants/backend/python/goals/coverage_py.py +++ b/src/python/pants/backend/python/goals/coverage_py.py @@ -106,7 +106,7 @@ def value(self) -> str: class CoverageSubsystem(PythonToolBase): options_scope = "coverage-py" - help = "Configuration for Python test coverage measurement." + help_short = "Configuration for Python test coverage measurement." default_main = ConsoleScript("coverage") default_requirements = ["coverage[toml]>=6.5,<8"] diff --git a/src/python/pants/backend/python/lint/add_trailing_comma/subsystem.py b/src/python/pants/backend/python/lint/add_trailing_comma/subsystem.py index 858b643232a..d44b8ba99b7 100644 --- a/src/python/pants/backend/python/lint/add_trailing_comma/subsystem.py +++ b/src/python/pants/backend/python/lint/add_trailing_comma/subsystem.py @@ -12,7 +12,7 @@ class AddTrailingComma(PythonToolBase): options_scope = "add-trailing-comma" name = "add-trailing-comma" - help = "The add-trailing-comma Python code formatter (https://github.com/asottile/add-trailing-comma)." + help_short = "The add-trailing-comma Python code formatter (https://github.com/asottile/add-trailing-comma)." default_main = ConsoleScript("add-trailing-comma") default_requirements = ["add-trailing-comma>=2.2.3,<3"] diff --git a/src/python/pants/backend/python/lint/autoflake/subsystem.py b/src/python/pants/backend/python/lint/autoflake/subsystem.py index dfc16fc3f73..0d583e96fc1 100644 --- a/src/python/pants/backend/python/lint/autoflake/subsystem.py +++ b/src/python/pants/backend/python/lint/autoflake/subsystem.py @@ -12,7 +12,7 @@ class Autoflake(PythonToolBase): options_scope = "autoflake" name = "Autoflake" - help = "The Autoflake Python code formatter (https://github.com/myint/autoflake)." + help_short = "The Autoflake Python code formatter (https://github.com/myint/autoflake)." default_main = ConsoleScript("autoflake") default_requirements = ["autoflake>=1.4,<3"] diff --git a/src/python/pants/backend/python/lint/bandit/subsystem.py b/src/python/pants/backend/python/lint/bandit/subsystem.py index 9dd9dcb9aee..0274f1f69d1 100644 --- a/src/python/pants/backend/python/lint/bandit/subsystem.py +++ b/src/python/pants/backend/python/lint/bandit/subsystem.py @@ -34,7 +34,9 @@ def opt_out(cls, tgt: Target) -> bool: class Bandit(PythonToolBase): options_scope = "bandit" name = "Bandit" - help = "A tool for finding security issues in Python code (https://bandit.readthedocs.io)." + help_short = ( + "A tool for finding security issues in Python code (https://bandit.readthedocs.io)." + ) default_main = ConsoleScript("bandit") default_requirements = [ diff --git a/src/python/pants/backend/python/lint/black/subsystem.py b/src/python/pants/backend/python/lint/black/subsystem.py index 50911d6ef6d..5d9244eabce 100644 --- a/src/python/pants/backend/python/lint/black/subsystem.py +++ b/src/python/pants/backend/python/lint/black/subsystem.py @@ -36,7 +36,7 @@ def opt_out(cls, tgt: Target) -> bool: class Black(PythonToolBase): options_scope = "black" name = "Black" - help = "The Black Python code formatter (https://black.readthedocs.io/)." + help_short = "The Black Python code formatter (https://black.readthedocs.io/)." default_main = ConsoleScript("black") default_requirements = [ diff --git a/src/python/pants/backend/python/lint/docformatter/subsystem.py b/src/python/pants/backend/python/lint/docformatter/subsystem.py index a7b6d3af61c..2b7896398f6 100644 --- a/src/python/pants/backend/python/lint/docformatter/subsystem.py +++ b/src/python/pants/backend/python/lint/docformatter/subsystem.py @@ -11,7 +11,7 @@ class Docformatter(PythonToolBase): options_scope = "docformatter" name = "docformatter" - help = "The Python docformatter tool (https://github.com/myint/docformatter)." + help_short = "The Python docformatter tool (https://github.com/myint/docformatter)." default_main = ConsoleScript("docformatter") default_requirements = ["docformatter>=1.4,<1.5"] diff --git a/src/python/pants/backend/python/lint/flake8/subsystem.py b/src/python/pants/backend/python/lint/flake8/subsystem.py index 400590e8189..1edd7587a2a 100644 --- a/src/python/pants/backend/python/lint/flake8/subsystem.py +++ b/src/python/pants/backend/python/lint/flake8/subsystem.py @@ -55,7 +55,7 @@ def opt_out(cls, tgt: Target) -> bool: class Flake8(PythonToolBase): options_scope = "flake8" name = "Flake8" - help = "The Flake8 Python linter (https://flake8.pycqa.org/)." + help_short = "The Flake8 Python linter (https://flake8.pycqa.org/)." default_main = ConsoleScript("flake8") default_requirements = ["flake8>=5.0.4,<7"] diff --git a/src/python/pants/backend/python/lint/isort/subsystem.py b/src/python/pants/backend/python/lint/isort/subsystem.py index b7ead3cc3d1..b0970fcf010 100644 --- a/src/python/pants/backend/python/lint/isort/subsystem.py +++ b/src/python/pants/backend/python/lint/isort/subsystem.py @@ -17,7 +17,7 @@ class Isort(PythonToolBase): options_scope = "isort" name = "isort" - help = "The Python import sorter tool (https://pycqa.github.io/isort/)." + help_short = "The Python import sorter tool (https://pycqa.github.io/isort/)." default_main = ConsoleScript("isort") default_requirements = ["isort[pyproject,colors]>=5.9.3,<6.0"] diff --git a/src/python/pants/backend/python/lint/pydocstyle/subsystem.py b/src/python/pants/backend/python/lint/pydocstyle/subsystem.py index 5875ccb83e9..15721d2f231 100644 --- a/src/python/pants/backend/python/lint/pydocstyle/subsystem.py +++ b/src/python/pants/backend/python/lint/pydocstyle/subsystem.py @@ -31,7 +31,7 @@ def opt_out(cls, tgt: Target) -> bool: class Pydocstyle(PythonToolBase): options_scope = "pydocstyle" name = "Pydocstyle" - help = "A tool for checking compliance with Python docstring conventions (http://www.pydocstyle.org/en/stable/)." + help_short = "A tool for checking compliance with Python docstring conventions (http://www.pydocstyle.org/en/stable/)." default_main = ConsoleScript("pydocstyle") default_requirements = ["pydocstyle[toml]>=6.1.1,<7.0"] diff --git a/src/python/pants/backend/python/lint/pylint/subsystem.py b/src/python/pants/backend/python/lint/pylint/subsystem.py index 00d4289b7de..a98a3eee1db 100644 --- a/src/python/pants/backend/python/lint/pylint/subsystem.py +++ b/src/python/pants/backend/python/lint/pylint/subsystem.py @@ -61,7 +61,7 @@ def opt_out(cls, tgt: Target) -> bool: class Pylint(PythonToolBase): options_scope = "pylint" name = "Pylint" - help = "The Pylint linter for Python code (https://www.pylint.org/)." + help_short = "The Pylint linter for Python code (https://www.pylint.org/)." default_main = ConsoleScript("pylint") default_requirements = ["pylint>=2.13.0,<3"] diff --git a/src/python/pants/backend/python/lint/pyupgrade/subsystem.py b/src/python/pants/backend/python/lint/pyupgrade/subsystem.py index 29e759f4295..21ce87c473a 100644 --- a/src/python/pants/backend/python/lint/pyupgrade/subsystem.py +++ b/src/python/pants/backend/python/lint/pyupgrade/subsystem.py @@ -12,7 +12,7 @@ class PyUpgrade(PythonToolBase): options_scope = "pyupgrade" name = "pyupgrade" - help = ( + help_short = ( "Upgrade syntax for newer versions of the language (https://github.com/asottile/pyupgrade)." ) diff --git a/src/python/pants/backend/python/lint/ruff/subsystem.py b/src/python/pants/backend/python/lint/ruff/subsystem.py index dbfc344cf60..cbe2cbe4337 100644 --- a/src/python/pants/backend/python/lint/ruff/subsystem.py +++ b/src/python/pants/backend/python/lint/ruff/subsystem.py @@ -31,7 +31,7 @@ class RuffMode(str, Enum): class Ruff(PythonToolBase): options_scope = "ruff" name = "Ruff" - help = "The Ruff Python formatter (https://github.com/astral-sh/ruff)." + help_short = "The Ruff Python formatter (https://github.com/astral-sh/ruff)." default_main = ConsoleScript("ruff") default_requirements = ["ruff>=0.1.2,<1"] diff --git a/src/python/pants/backend/python/lint/yapf/subsystem.py b/src/python/pants/backend/python/lint/yapf/subsystem.py index 4deace4320a..7dcce2bed7a 100644 --- a/src/python/pants/backend/python/lint/yapf/subsystem.py +++ b/src/python/pants/backend/python/lint/yapf/subsystem.py @@ -17,7 +17,7 @@ class Yapf(PythonToolBase): options_scope = "yapf" name = "yapf" - help = "A formatter for Python files (https://github.com/google/yapf)." + help_short = "A formatter for Python files (https://github.com/google/yapf)." default_main = ConsoleScript("yapf") default_requirements = ["yapf>=0.32.0,<1", "toml"] diff --git a/src/python/pants/backend/python/packaging/pyoxidizer/subsystem.py b/src/python/pants/backend/python/packaging/pyoxidizer/subsystem.py index fae74b5ff12..88201d6ed84 100644 --- a/src/python/pants/backend/python/packaging/pyoxidizer/subsystem.py +++ b/src/python/pants/backend/python/packaging/pyoxidizer/subsystem.py @@ -11,7 +11,7 @@ class PyOxidizer(PythonToolBase): options_scope = "pyoxidizer" name = "PyOxidizer" - help = help_text( + help_short = help_text( """ The PyOxidizer utility for packaging Python code in a Rust binary (https://pyoxidizer.readthedocs.io/en/stable/pyoxidizer.html). diff --git a/src/python/pants/backend/python/subsystems/debugpy.py b/src/python/pants/backend/python/subsystems/debugpy.py index 0d6ce8d6153..92155e5b312 100644 --- a/src/python/pants/backend/python/subsystems/debugpy.py +++ b/src/python/pants/backend/python/subsystems/debugpy.py @@ -13,7 +13,7 @@ class DebugPy(PythonToolBase): options_scope = "debugpy" name = options_scope - help = "An implementation of the Debug Adapter Protocol for Python (https://github.com/microsoft/debugpy)." + help_short = "An implementation of the Debug Adapter Protocol for Python (https://github.com/microsoft/debugpy)." default_main = EntryPoint("debugpy") default_requirements = ["debugpy>=1.6.5,<1.7"] diff --git a/src/python/pants/backend/python/subsystems/ipython.py b/src/python/pants/backend/python/subsystems/ipython.py index 86659b577ae..bd37a833410 100644 --- a/src/python/pants/backend/python/subsystems/ipython.py +++ b/src/python/pants/backend/python/subsystems/ipython.py @@ -13,7 +13,7 @@ class IPython(PythonToolBase): options_scope = "ipython" - help = "The IPython enhanced REPL (https://ipython.org/)." + help_short = "The IPython enhanced REPL (https://ipython.org/)." default_main = ConsoleScript("ipython") default_requirements = ["ipython>=7.34,<9"] diff --git a/src/python/pants/backend/python/subsystems/pytest.py b/src/python/pants/backend/python/subsystems/pytest.py index 7a8bf99f094..ba8e54ba31d 100644 --- a/src/python/pants/backend/python/subsystems/pytest.py +++ b/src/python/pants/backend/python/subsystems/pytest.py @@ -51,7 +51,7 @@ def opt_out(cls, tgt: Target) -> bool: class PyTest(PythonToolBase): options_scope = "pytest" name = "Pytest" - help = "The pytest Python test framework (https://docs.pytest.org/)." + help_short = "The pytest Python test framework (https://docs.pytest.org/)." # Pytest 7.1.0 introduced a significant bug that is apparently not fixed as of 7.1.1 (the most # recent release at the time of writing). see https://github.com/pantsbuild/pants/issues/14990. diff --git a/src/python/pants/backend/python/subsystems/python_tool_base.py b/src/python/pants/backend/python/subsystems/python_tool_base.py index 6eb7c3ccd17..4eacb4e8349 100644 --- a/src/python/pants/backend/python/subsystems/python_tool_base.py +++ b/src/python/pants/backend/python/subsystems/python_tool_base.py @@ -9,7 +9,7 @@ import os from dataclasses import dataclass from functools import cache -from typing import ClassVar, Iterable, Optional, Sequence +from typing import Callable, ClassVar, Iterable, Optional, Sequence from urllib.parse import urlparse from pants.backend.python.target_types import ConsoleScript, EntryPoint, MainSpecification @@ -34,7 +34,7 @@ from pants.util.docutil import doc_url, git_url from pants.util.meta import classproperty from pants.util.pip_requirement import PipRequirement -from pants.util.strutil import softwrap +from pants.util.strutil import softwrap, strval logger = logging.getLogger(__name__) @@ -50,6 +50,8 @@ class PythonToolRequirementsBase(Subsystem, ExportableTool): # Subclasses must set. default_version: ClassVar[str] + # Must be set by subclasses - will be used to set the help text in this class. + help_short: ClassVar[str | Callable[[], str]] # Subclasses do not need to override. default_extra_requirements: ClassVar[Sequence[str]] = [] @@ -65,6 +67,19 @@ class PythonToolRequirementsBase(Subsystem, ExportableTool): default_lockfile_resource: ClassVar[tuple[str, str] | None] = None + @classmethod + def _help_extended(cls) -> str: + base_help = strval(cls.help_short) + help_paragraphs = [base_help] + package_and_version = cls._default_package_name_and_version() + if package_and_version: + new_paragraph = f"This version of Pants uses `{package_and_version.name}` version {package_and_version.version} by default. Use a dedicated lockfile and the `install_from_resolve` option to control this." + help_paragraphs.append(new_paragraph) + + return "\n\n".join(help_paragraphs) + + help = classproperty(_help_extended) + @classmethod def _install_from_resolve_help(cls) -> str: package_and_version = cls._default_package_name_and_version() diff --git a/src/python/pants/backend/python/subsystems/setuptools.py b/src/python/pants/backend/python/subsystems/setuptools.py index fb1e45b4560..cbb2a6f5422 100644 --- a/src/python/pants/backend/python/subsystems/setuptools.py +++ b/src/python/pants/backend/python/subsystems/setuptools.py @@ -19,7 +19,7 @@ class PythonDistributionFieldSet(PackageFieldSet): class Setuptools(PythonToolRequirementsBase): options_scope = "setuptools" - help = "Python setuptools, used to package `python_distribution` targets." + help_short = "Python setuptools, used to package `python_distribution` targets." default_requirements = ["setuptools>=63.1.0,<64.0", "wheel>=0.35.1,<0.38"] diff --git a/src/python/pants/backend/python/subsystems/setuptools_scm.py b/src/python/pants/backend/python/subsystems/setuptools_scm.py index 2db06e2beb9..2d80d9fb571 100644 --- a/src/python/pants/backend/python/subsystems/setuptools_scm.py +++ b/src/python/pants/backend/python/subsystems/setuptools_scm.py @@ -8,7 +8,7 @@ class SetuptoolsSCM(PythonToolBase): options_scope = "setuptools-scm" - help = ( + help_short = ( "A tool for generating versions from VCS metadata (https://github.com/pypa/setuptools_scm)." ) diff --git a/src/python/pants/backend/python/subsystems/twine.py b/src/python/pants/backend/python/subsystems/twine.py index a1474e7fd14..04a670012d2 100644 --- a/src/python/pants/backend/python/subsystems/twine.py +++ b/src/python/pants/backend/python/subsystems/twine.py @@ -17,7 +17,9 @@ class TwineSubsystem(PythonToolBase): options_scope = "twine" name = "Twine" - help = "The utility for publishing Python distributions to PyPI and other Python repositories." + help_short = ( + "The utility for publishing Python distributions to PyPI and other Python repositories." + ) default_version = "twine>=4,<5" default_main = ConsoleScript("twine") diff --git a/src/python/pants/backend/python/typecheck/mypy/subsystem.py b/src/python/pants/backend/python/typecheck/mypy/subsystem.py index d7f968021b5..56e4ab2db27 100644 --- a/src/python/pants/backend/python/typecheck/mypy/subsystem.py +++ b/src/python/pants/backend/python/typecheck/mypy/subsystem.py @@ -66,7 +66,7 @@ def opt_out(cls, tgt: Target) -> bool: class MyPy(PythonToolBase): options_scope = "mypy" name = "MyPy" - help = "The MyPy Python type checker (http://mypy-lang.org/)." + help_short = "The MyPy Python type checker (http://mypy-lang.org/)." default_main = ConsoleScript("mypy") default_requirements = ["mypy>=0.961,<2"] diff --git a/src/python/pants/backend/python/typecheck/pytype/subsystem.py b/src/python/pants/backend/python/typecheck/pytype/subsystem.py index 2e81ffc481d..3f4f7ab6359 100644 --- a/src/python/pants/backend/python/typecheck/pytype/subsystem.py +++ b/src/python/pants/backend/python/typecheck/pytype/subsystem.py @@ -13,7 +13,7 @@ class Pytype(PythonToolBase): options_scope = "pytype" name = "Pytype" - help = help_text( + help_short = help_text( """ The Pytype utility for typechecking Python code (https://github.com/google/pytype). diff --git a/src/python/pants/backend/sql/lint/sqlfluff/subsystem.py b/src/python/pants/backend/sql/lint/sqlfluff/subsystem.py index cfbfbd53b6b..767550a2e4d 100644 --- a/src/python/pants/backend/sql/lint/sqlfluff/subsystem.py +++ b/src/python/pants/backend/sql/lint/sqlfluff/subsystem.py @@ -43,7 +43,7 @@ class SqlfluffMode(str, Enum): class Sqlfluff(PythonToolBase): options_scope = "sqlfluff" name = "Sqlfluff" - help = "The Sqlfluff SQL linter (https://github.com/sqlfluff/sqlfluff)." + help_short = "The Sqlfluff SQL linter (https://github.com/sqlfluff/sqlfluff)." default_main = ConsoleScript("sqlfluff") default_requirements = ["sqlfluff>=2.3.5,<3"] diff --git a/src/python/pants/backend/terraform/dependency_inference.py b/src/python/pants/backend/terraform/dependency_inference.py index 06de426b659..d7df948bd0b 100644 --- a/src/python/pants/backend/terraform/dependency_inference.py +++ b/src/python/pants/backend/terraform/dependency_inference.py @@ -45,7 +45,7 @@ class TerraformHcl2Parser(PythonToolRequirementsBase): options_scope = "terraform-hcl2-parser" - help = "Used to parse Terraform modules to infer their dependencies." + help_short = "Used to parse Terraform modules to infer their dependencies." default_requirements = ["python-hcl2>=3.0.5,<5"] diff --git a/src/python/pants/backend/tools/semgrep/subsystem.py b/src/python/pants/backend/tools/semgrep/subsystem.py index fca5fc677ce..f114f0537a3 100644 --- a/src/python/pants/backend/tools/semgrep/subsystem.py +++ b/src/python/pants/backend/tools/semgrep/subsystem.py @@ -30,7 +30,7 @@ def opt_out(cls, tgt: Target) -> bool: class SemgrepSubsystem(PythonToolBase): name = "Semgrep" options_scope = "semgrep" - help = softwrap( + help_short = softwrap( """ Lightweight static analysis for many languages. Find bug variants with patterns that look like source code. (https://semgrep.dev/) diff --git a/src/python/pants/backend/tools/yamllint/subsystem.py b/src/python/pants/backend/tools/yamllint/subsystem.py index 5e65e37960c..ada40a9991a 100644 --- a/src/python/pants/backend/tools/yamllint/subsystem.py +++ b/src/python/pants/backend/tools/yamllint/subsystem.py @@ -23,7 +23,7 @@ class Yamllint(PythonToolBase): name = "Yamllint" options_scope = "yamllint" - help = "A linter for YAML files (https://yamllint.readthedocs.io)" + help_short = "A linter for YAML files (https://yamllint.readthedocs.io)" default_main = ConsoleScript("yamllint") default_requirements = ["yamllint>=1.28.0,<2"] From 86d8729a2e9df2558d5bc6358983c308ab395e78 Mon Sep 17 00:00:00 2001 From: Andreas Stenius Date: Mon, 20 May 2024 17:17:54 +0200 Subject: [PATCH 09/18] internal: sort more tuples to potentially improve cache hit rates. (#20767) I looked through most `tuple` uses in the Python backend, and added `sorted(..)` where I think the order wasn't already stable and the result ends up as a rule output. --- .../python/dependency_inference/module_mapper.py | 3 ++- .../framework/stevedore/python_target_dependencies.py | 4 +++- src/python/pants/backend/python/goals/coverage_py.py | 2 +- .../pants/backend/python/util_rules/local_dists.py | 4 +++- .../backend/python/util_rules/local_dists_pep660.py | 6 +++--- .../pants/backend/python/util_rules/package_dists.py | 2 +- src/python/pants/engine/target.py | 10 ++++++++++ 7 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/python/pants/backend/python/dependency_inference/module_mapper.py b/src/python/pants/backend/python/dependency_inference/module_mapper.py index c7c1a81d6cd..1369285f88f 100644 --- a/src/python/pants/backend/python/dependency_inference/module_mapper.py +++ b/src/python/pants/backend/python/dependency_inference/module_mapper.py @@ -93,7 +93,8 @@ def find_all_python_projects(all_targets: AllTargets) -> AllPythonTargets: first_party.append(tgt) if tgt.has_field(PythonRequirementsField): third_party.append(tgt) - return AllPythonTargets(tuple(first_party), tuple(third_party)) + + return AllPythonTargets(tuple(sorted(first_party)), tuple(sorted(third_party))) # ----------------------------------------------------------------------------------------------- diff --git a/src/python/pants/backend/python/framework/stevedore/python_target_dependencies.py b/src/python/pants/backend/python/framework/stevedore/python_target_dependencies.py index 4945b4a2bef..0d64ea18025 100644 --- a/src/python/pants/backend/python/framework/stevedore/python_target_dependencies.py +++ b/src/python/pants/backend/python/framework/stevedore/python_target_dependencies.py @@ -96,7 +96,9 @@ async def map_stevedore_extensions( for namespace in (tgt[PythonDistributionEntryPointsField].value or {}).keys(): if isinstance(namespace, StevedoreNamespace): mapping[namespace].append(tgt) - return StevedoreExtensions(FrozenDict((k, tuple(v)) for k, v in sorted(mapping.items()))) + return StevedoreExtensions( + FrozenDict((k, tuple(sorted(v))) for k, v in sorted(mapping.items())) + ) @rule( diff --git a/src/python/pants/backend/python/goals/coverage_py.py b/src/python/pants/backend/python/goals/coverage_py.py index 9b705b94b08..e48e0a354ad 100644 --- a/src/python/pants/backend/python/goals/coverage_py.py +++ b/src/python/pants/backend/python/goals/coverage_py.py @@ -481,7 +481,7 @@ async def merge_coverage_data( ) return MergedCoverageData( await Get(Digest, MergeDigests((result.output_digest, extra_sources_digest))), - tuple(addresses), + tuple(sorted(addresses)), ) diff --git a/src/python/pants/backend/python/util_rules/local_dists.py b/src/python/pants/backend/python/util_rules/local_dists.py index d1eb00292e8..4b015477a77 100644 --- a/src/python/pants/backend/python/util_rules/local_dists.py +++ b/src/python/pants/backend/python/util_rules/local_dists.py @@ -98,7 +98,9 @@ async def isolate_local_dist_wheels( ) provided_files = set(wheels_listing_result.stdout.decode().splitlines()) - return LocalDistWheels(tuple(wheels), wheels_snapshot.digest, frozenset(provided_files)) + return LocalDistWheels( + tuple(sorted(wheels)), wheels_snapshot.digest, frozenset(sorted(provided_files)) + ) @dataclass(frozen=True) diff --git a/src/python/pants/backend/python/util_rules/local_dists_pep660.py b/src/python/pants/backend/python/util_rules/local_dists_pep660.py index 73184f84a4f..59f1a2ebccd 100644 --- a/src/python/pants/backend/python/util_rules/local_dists_pep660.py +++ b/src/python/pants/backend/python/util_rules/local_dists_pep660.py @@ -266,7 +266,7 @@ async def isolate_local_dist_pep660_wheels( Snapshot, DigestSubset(pep660_result.output, PathGlobs(["**/*.whl"])) ) - wheels = tuple(wheels_snapshot.files) + wheels = tuple(sorted(wheels_snapshot.files)) if not wheels: tgt = await Get( @@ -306,7 +306,7 @@ async def isolate_local_dist_pep660_wheels( ) provided_files = set(wheels_listing_result.stdout.decode().splitlines()) - return LocalDistPEP660Wheels(wheels, wheels_snapshot.digest, frozenset(provided_files)) + return LocalDistPEP660Wheels(wheels, wheels_snapshot.digest, frozenset(sorted(provided_files))) @dataclass(frozen=True) @@ -357,7 +357,7 @@ async def sort_all_python_distributions_by_resolve( break dists[resolve].append(dist) return ResolveSortedPythonDistributionTargets( - FrozenDict({resolve: tuple(targets) for resolve, targets in dists.items()}) + FrozenDict({resolve: tuple(sorted(targets)) for resolve, targets in dists.items()}) ) diff --git a/src/python/pants/backend/python/util_rules/package_dists.py b/src/python/pants/backend/python/util_rules/package_dists.py index 21f8bb9cac5..76b1e44f073 100644 --- a/src/python/pants/backend/python/util_rules/package_dists.py +++ b/src/python/pants/backend/python/util_rules/package_dists.py @@ -760,7 +760,7 @@ async def get_sources( file_targets = targets_with_sources_types( [FileSourceField], transitive_targets.closure, union_membership ) - targets = Targets(itertools.chain((od.target for od in owned_deps), file_targets)) + targets = Targets(sorted(itertools.chain((od.target for od in owned_deps), file_targets))) python_sources_request = PythonSourceFilesRequest( targets=targets, include_resources=False, include_files=False diff --git a/src/python/pants/engine/target.py b/src/python/pants/engine/target.py index 1f64b88be6d..f25af7b1de6 100644 --- a/src/python/pants/engine/target.py +++ b/src/python/pants/engine/target.py @@ -438,6 +438,16 @@ def __eq__(self, other: Union[Target, Any]) -> bool: other.field_values, ) + def __lt__(self, other: Any) -> bool: + if not isinstance(other, Target): + return NotImplemented + return self.address < other.address + + def __gt__(self, other: Any) -> bool: + if not isinstance(other, Target): + return NotImplemented + return self.address > other.address + @classmethod @memoized_method def _find_plugin_fields(cls, union_membership: UnionMembership) -> tuple[type[Field], ...]: From aa0bb0fd1b4b5c38e0322595a939e026e9451a25 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 21 May 2024 15:53:20 +1000 Subject: [PATCH 10/18] Update release notes links in Plugin Upgrade Guide docs after movement (#20936) This makes three changes to the releases notes links in the "Plugin Upgrade Guide" docs: - adjust the path for the movement in https://github.com/pantsbuild/pants/pull/20847 - fix `.rst` -> `.md` file extension for 2.0.x and 2.1.x - fix `master` -> `main` branch name for 2.0.x and 2.1.x --- .../plugin-upgrade-guide.mdx | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/docs/writing-plugins/common-plugin-tasks/plugin-upgrade-guide.mdx b/docs/docs/writing-plugins/common-plugin-tasks/plugin-upgrade-guide.mdx index 74323e404e9..ef7db260c27 100644 --- a/docs/docs/writing-plugins/common-plugin-tasks/plugin-upgrade-guide.mdx +++ b/docs/docs/writing-plugins/common-plugin-tasks/plugin-upgrade-guide.mdx @@ -309,7 +309,7 @@ The `convert_dir_literal_to_address_literal` keyword argument for `RawSpecs.crea ## 2.14 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.14.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.14.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.14.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.14.x.md) for the changelog. ### Removed second type parameter from `Get` @@ -365,7 +365,7 @@ The functionality is the same, but can have better performance because we don't ## 2.13 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.13.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.13.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.13.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.13.x.md) for the changelog. ### `AddressInput` and `UnparsedAddressInputs` require `description_of_origin` @@ -448,7 +448,7 @@ You must now use a long option name when [defining options](../the-rules-api/opt ## 2.12 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.12.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.12.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.12.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.12.x.md) for the changelog. ### Unified formatters @@ -458,7 +458,7 @@ See [Add a formatter](./add-a-formatter.mdx) for more information. ## 2.11 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.11.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.11.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.11.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.11.x.md) for the changelog. ### Deprecated `Subsystem.register_options()` @@ -516,7 +516,7 @@ See [https://github.com/pantsbuild/pants/pull/14313](https://github.com/pantsbui ## 2.10 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.10.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.10.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.10.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.10.x.md) for the changelog. ### Rename `LintRequest` to `LintTargetsRequest` @@ -580,7 +580,7 @@ This type was used to determine the owners of a Python module. The new name make ## 2.9 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.9.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.9.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.9.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.9.x.md) for the changelog. ### Deprecated `RuleRunner.create_files()`, `.create_file()` and `.add_to_build_file()` @@ -588,7 +588,7 @@ Instead, for your `RuleRunner` tests, use `.write_files()`. See [https://github. ## 2.8 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.8.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.8.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.8.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.8.x.md) for the changelog. ### Target modeling changes @@ -618,7 +618,7 @@ The method `OutputPathField.value_or_default()` no longer takes `Address` as an ## 2.7 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.7.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.7.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.7.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.7.x.md) for the changelog. ### Type hints work properly @@ -630,7 +630,7 @@ For example, use `my-subsystem` instead of `my_subsystem`. This is to avoid ambi ## 2.6 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.6.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.6.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.6.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.6.x.md) for the changelog. ### `ProcessCacheScope` @@ -644,7 +644,7 @@ Now called `InterpreterConstraints` and defined in `pants.backend.python.util_ru ## 2.5 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.5.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.5.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.5.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.5.x.md) for the changelog. ### `TriBoolField` @@ -658,7 +658,7 @@ This is a more declarative way to set up files than the older API of `RuleRunner ## 2.4 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.4.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.4.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.4.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.4.x.md) for the changelog. ### `PexRequest` changes how entry point is set @@ -674,11 +674,11 @@ For `RuleRunner` tests, you must now either set `env` or the new `env_inherit` a ## 2.3 -There were no substantial changes to the Plugin API in 2.3. See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.3.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.3.x.md) for the changelog. +There were no substantial changes to the Plugin API in 2.3. See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.3.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.3.x.md) for the changelog. ## 2.2 -See [https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.2.x.md](https://github.com/pantsbuild/pants/blob/main/src/python/pants/notes/2.2.x.md) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.2.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.2.x.md) for the changelog. ### `PrimitiveField` and `AsyncField` are removed (2.2.0.dev0) @@ -710,7 +710,7 @@ Pants will now properly wrap strings and preserve newlines. You may want to run ## 2.1 -See [https://github.com/pantsbuild/pants/blob/master/src/python/pants/notes/2.1.x.rst](https://github.com/pantsbuild/pants/blob/master/src/python/pants/notes/2.1.x.rst) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.1.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.1.x.md) for the changelog. ### `SourcesSnapshot` is now `SpecsSnapshot` (2.1.0rc0) @@ -718,7 +718,7 @@ The type was renamed for clarity. Still import it from `pants.engine.fs`. ## 2.0 -See [https://github.com/pantsbuild/pants/blob/master/src/python/pants/notes/2.0.x.rst](https://github.com/pantsbuild/pants/blob/master/src/python/pants/notes/2.0.x.rst) for the changelog. +See [https://github.com/pantsbuild/pants/blob/main/docs/notes/2.0.x.md](https://github.com/pantsbuild/pants/blob/main/docs/notes/2.0.x.md) for the changelog. ### Use `TransitiveTargetsRequest` as input for resolving `TransitiveTargets` (2.0.0rc0) From d81fd330043822a252e8cd08c0acf1822d69a418 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 21 May 2024 15:53:38 +1000 Subject: [PATCH 11/18] Restore stub release notes files, with links to new location (#20937) This is a fix to paper over the move of release notes in https://github.com/pantsbuild/pants/pull/20847 from `src/python/pants/notes` to `docs/notes`. This is to ensure that links to the old `src/python/pants/notes` locations are not dead, so people land on them and get directed to the right place. These links appear in various blog posts and old doc snapshots (https://github.com/pantsbuild/pantsbuild.org/issues/209) and potentially places that cannot be updated too (e.g. slack, and other people linking them privately). We should still update the ones we can, do the new location, but these stubs provide a fallback for ones that we miss. These were generated by running this script in the `src/python/pants/notes` location: ```shell #!/bin/bash for p in ../../../../docs/notes/*; do filename=$(basename $p) release=$(echo $filename | sed 's/.md//; s/.rst//') if [[ $filename == *.md ]]; then # Markdown: echo -e "# $release Stable releases\n\nThese notes have moved to [\`docs/notes/$filename\`]($p)" else # Restructured text: echo -e "$release Stable releases\n=====================\n\nThese notes have moved to \`docs/notes/$filename <$p>\`_" fi > $filename done # there's a few links to 2.{0,1}.x.rst, but they're now .md echo -e "2.0.x Stable releases\n=====================\n\nThese notes have moved to \`docs/notes/2.0.x.md <../../../../docs/notes/2.0.x.md>\`_" > 2.0.x.rst echo -e "2.1.x Stable releases\n=====================\n\nThese notes have moved to \`docs/notes/2.1.x.md <../../../../docs/notes/2.1.x.md>\`_" > 2.1.x.rst ``` We don't need to adding new ones in future, since future releases won't ever have files in the old place. --- src/python/pants/notes/1.0.x.rst | 4 ++++ src/python/pants/notes/1.1.x.rst | 4 ++++ src/python/pants/notes/1.10.x.rst | 4 ++++ src/python/pants/notes/1.11.x.rst | 4 ++++ src/python/pants/notes/1.12.x.rst | 4 ++++ src/python/pants/notes/1.13.x.rst | 4 ++++ src/python/pants/notes/1.14.x.rst | 4 ++++ src/python/pants/notes/1.15.x.rst | 4 ++++ src/python/pants/notes/1.16.x.rst | 4 ++++ src/python/pants/notes/1.17.x.rst | 4 ++++ src/python/pants/notes/1.18.x.rst | 4 ++++ src/python/pants/notes/1.19.x.rst | 4 ++++ src/python/pants/notes/1.2.x.rst | 4 ++++ src/python/pants/notes/1.20.x.rst | 4 ++++ src/python/pants/notes/1.21.x.rst | 4 ++++ src/python/pants/notes/1.22.x.rst | 4 ++++ src/python/pants/notes/1.23.x.rst | 4 ++++ src/python/pants/notes/1.24.x.rst | 4 ++++ src/python/pants/notes/1.25.x.rst | 4 ++++ src/python/pants/notes/1.26.x.rst | 4 ++++ src/python/pants/notes/1.27.x.rst | 4 ++++ src/python/pants/notes/1.28.x.rst | 4 ++++ src/python/pants/notes/1.29.x.rst | 4 ++++ src/python/pants/notes/1.3.x.rst | 4 ++++ src/python/pants/notes/1.30.x.rst | 4 ++++ src/python/pants/notes/1.4.x.rst | 4 ++++ src/python/pants/notes/1.5.x.rst | 4 ++++ src/python/pants/notes/1.6.x.rst | 4 ++++ src/python/pants/notes/1.7.x.rst | 4 ++++ src/python/pants/notes/1.8.x.rst | 4 ++++ src/python/pants/notes/1.9.x.rst | 4 ++++ src/python/pants/notes/2.0.x.md | 3 +++ src/python/pants/notes/2.0.x.rst | 4 ++++ src/python/pants/notes/2.1.x.md | 3 +++ src/python/pants/notes/2.1.x.rst | 4 ++++ src/python/pants/notes/2.10.x.md | 3 +++ src/python/pants/notes/2.11.x.md | 3 +++ src/python/pants/notes/2.12.x.md | 3 +++ src/python/pants/notes/2.13.x.md | 3 +++ src/python/pants/notes/2.14.x.md | 3 +++ src/python/pants/notes/2.15.x.md | 3 +++ src/python/pants/notes/2.16.x.md | 3 +++ src/python/pants/notes/2.17.x.md | 3 +++ src/python/pants/notes/2.18.x.md | 3 +++ src/python/pants/notes/2.19.x.md | 3 +++ src/python/pants/notes/2.2.x.md | 3 +++ src/python/pants/notes/2.20.x.md | 3 +++ src/python/pants/notes/2.21.x.md | 3 +++ src/python/pants/notes/2.22.x.md | 3 +++ src/python/pants/notes/2.3.x.md | 3 +++ src/python/pants/notes/2.4.x.md | 3 +++ src/python/pants/notes/2.5.x.md | 3 +++ src/python/pants/notes/2.6.x.md | 3 +++ src/python/pants/notes/2.7.x.md | 3 +++ src/python/pants/notes/2.8.x.md | 3 +++ src/python/pants/notes/2.9.x.md | 3 +++ src/python/pants/notes/master.rst | 4 ++++ 57 files changed, 205 insertions(+) create mode 100644 src/python/pants/notes/1.0.x.rst create mode 100644 src/python/pants/notes/1.1.x.rst create mode 100644 src/python/pants/notes/1.10.x.rst create mode 100644 src/python/pants/notes/1.11.x.rst create mode 100644 src/python/pants/notes/1.12.x.rst create mode 100644 src/python/pants/notes/1.13.x.rst create mode 100644 src/python/pants/notes/1.14.x.rst create mode 100644 src/python/pants/notes/1.15.x.rst create mode 100644 src/python/pants/notes/1.16.x.rst create mode 100644 src/python/pants/notes/1.17.x.rst create mode 100644 src/python/pants/notes/1.18.x.rst create mode 100644 src/python/pants/notes/1.19.x.rst create mode 100644 src/python/pants/notes/1.2.x.rst create mode 100644 src/python/pants/notes/1.20.x.rst create mode 100644 src/python/pants/notes/1.21.x.rst create mode 100644 src/python/pants/notes/1.22.x.rst create mode 100644 src/python/pants/notes/1.23.x.rst create mode 100644 src/python/pants/notes/1.24.x.rst create mode 100644 src/python/pants/notes/1.25.x.rst create mode 100644 src/python/pants/notes/1.26.x.rst create mode 100644 src/python/pants/notes/1.27.x.rst create mode 100644 src/python/pants/notes/1.28.x.rst create mode 100644 src/python/pants/notes/1.29.x.rst create mode 100644 src/python/pants/notes/1.3.x.rst create mode 100644 src/python/pants/notes/1.30.x.rst create mode 100644 src/python/pants/notes/1.4.x.rst create mode 100644 src/python/pants/notes/1.5.x.rst create mode 100644 src/python/pants/notes/1.6.x.rst create mode 100644 src/python/pants/notes/1.7.x.rst create mode 100644 src/python/pants/notes/1.8.x.rst create mode 100644 src/python/pants/notes/1.9.x.rst create mode 100644 src/python/pants/notes/2.0.x.md create mode 100644 src/python/pants/notes/2.0.x.rst create mode 100644 src/python/pants/notes/2.1.x.md create mode 100644 src/python/pants/notes/2.1.x.rst create mode 100644 src/python/pants/notes/2.10.x.md create mode 100644 src/python/pants/notes/2.11.x.md create mode 100644 src/python/pants/notes/2.12.x.md create mode 100644 src/python/pants/notes/2.13.x.md create mode 100644 src/python/pants/notes/2.14.x.md create mode 100644 src/python/pants/notes/2.15.x.md create mode 100644 src/python/pants/notes/2.16.x.md create mode 100644 src/python/pants/notes/2.17.x.md create mode 100644 src/python/pants/notes/2.18.x.md create mode 100644 src/python/pants/notes/2.19.x.md create mode 100644 src/python/pants/notes/2.2.x.md create mode 100644 src/python/pants/notes/2.20.x.md create mode 100644 src/python/pants/notes/2.21.x.md create mode 100644 src/python/pants/notes/2.22.x.md create mode 100644 src/python/pants/notes/2.3.x.md create mode 100644 src/python/pants/notes/2.4.x.md create mode 100644 src/python/pants/notes/2.5.x.md create mode 100644 src/python/pants/notes/2.6.x.md create mode 100644 src/python/pants/notes/2.7.x.md create mode 100644 src/python/pants/notes/2.8.x.md create mode 100644 src/python/pants/notes/2.9.x.md create mode 100644 src/python/pants/notes/master.rst diff --git a/src/python/pants/notes/1.0.x.rst b/src/python/pants/notes/1.0.x.rst new file mode 100644 index 00000000000..c9842c93bb0 --- /dev/null +++ b/src/python/pants/notes/1.0.x.rst @@ -0,0 +1,4 @@ +1.0.x Stable releases +===================== + +These notes have moved to `docs/notes/1.0.x.rst <../../../../docs/notes/1.0.x.rst>`_ diff --git a/src/python/pants/notes/1.1.x.rst b/src/python/pants/notes/1.1.x.rst new file mode 100644 index 00000000000..71b0faecbf2 --- /dev/null +++ b/src/python/pants/notes/1.1.x.rst @@ -0,0 +1,4 @@ +1.1.x Stable releases +===================== + +These notes have moved to `docs/notes/1.1.x.rst <../../../../docs/notes/1.1.x.rst>`_ diff --git a/src/python/pants/notes/1.10.x.rst b/src/python/pants/notes/1.10.x.rst new file mode 100644 index 00000000000..448c0abf8af --- /dev/null +++ b/src/python/pants/notes/1.10.x.rst @@ -0,0 +1,4 @@ +1.10.x Stable releases +===================== + +These notes have moved to `docs/notes/1.10.x.rst <../../../../docs/notes/1.10.x.rst>`_ diff --git a/src/python/pants/notes/1.11.x.rst b/src/python/pants/notes/1.11.x.rst new file mode 100644 index 00000000000..5e83de2c4b8 --- /dev/null +++ b/src/python/pants/notes/1.11.x.rst @@ -0,0 +1,4 @@ +1.11.x Stable releases +===================== + +These notes have moved to `docs/notes/1.11.x.rst <../../../../docs/notes/1.11.x.rst>`_ diff --git a/src/python/pants/notes/1.12.x.rst b/src/python/pants/notes/1.12.x.rst new file mode 100644 index 00000000000..57fd5fb2c3d --- /dev/null +++ b/src/python/pants/notes/1.12.x.rst @@ -0,0 +1,4 @@ +1.12.x Stable releases +===================== + +These notes have moved to `docs/notes/1.12.x.rst <../../../../docs/notes/1.12.x.rst>`_ diff --git a/src/python/pants/notes/1.13.x.rst b/src/python/pants/notes/1.13.x.rst new file mode 100644 index 00000000000..009f1cdc143 --- /dev/null +++ b/src/python/pants/notes/1.13.x.rst @@ -0,0 +1,4 @@ +1.13.x Stable releases +===================== + +These notes have moved to `docs/notes/1.13.x.rst <../../../../docs/notes/1.13.x.rst>`_ diff --git a/src/python/pants/notes/1.14.x.rst b/src/python/pants/notes/1.14.x.rst new file mode 100644 index 00000000000..bb7d5d1b6e4 --- /dev/null +++ b/src/python/pants/notes/1.14.x.rst @@ -0,0 +1,4 @@ +1.14.x Stable releases +===================== + +These notes have moved to `docs/notes/1.14.x.rst <../../../../docs/notes/1.14.x.rst>`_ diff --git a/src/python/pants/notes/1.15.x.rst b/src/python/pants/notes/1.15.x.rst new file mode 100644 index 00000000000..13ac826060f --- /dev/null +++ b/src/python/pants/notes/1.15.x.rst @@ -0,0 +1,4 @@ +1.15.x Stable releases +===================== + +These notes have moved to `docs/notes/1.15.x.rst <../../../../docs/notes/1.15.x.rst>`_ diff --git a/src/python/pants/notes/1.16.x.rst b/src/python/pants/notes/1.16.x.rst new file mode 100644 index 00000000000..c8aeedca75d --- /dev/null +++ b/src/python/pants/notes/1.16.x.rst @@ -0,0 +1,4 @@ +1.16.x Stable releases +===================== + +These notes have moved to `docs/notes/1.16.x.rst <../../../../docs/notes/1.16.x.rst>`_ diff --git a/src/python/pants/notes/1.17.x.rst b/src/python/pants/notes/1.17.x.rst new file mode 100644 index 00000000000..685a518b5c8 --- /dev/null +++ b/src/python/pants/notes/1.17.x.rst @@ -0,0 +1,4 @@ +1.17.x Stable releases +===================== + +These notes have moved to `docs/notes/1.17.x.rst <../../../../docs/notes/1.17.x.rst>`_ diff --git a/src/python/pants/notes/1.18.x.rst b/src/python/pants/notes/1.18.x.rst new file mode 100644 index 00000000000..8b05ec58f8c --- /dev/null +++ b/src/python/pants/notes/1.18.x.rst @@ -0,0 +1,4 @@ +1.18.x Stable releases +===================== + +These notes have moved to `docs/notes/1.18.x.rst <../../../../docs/notes/1.18.x.rst>`_ diff --git a/src/python/pants/notes/1.19.x.rst b/src/python/pants/notes/1.19.x.rst new file mode 100644 index 00000000000..612ddac337c --- /dev/null +++ b/src/python/pants/notes/1.19.x.rst @@ -0,0 +1,4 @@ +1.19.x Stable releases +===================== + +These notes have moved to `docs/notes/1.19.x.rst <../../../../docs/notes/1.19.x.rst>`_ diff --git a/src/python/pants/notes/1.2.x.rst b/src/python/pants/notes/1.2.x.rst new file mode 100644 index 00000000000..6c337e84350 --- /dev/null +++ b/src/python/pants/notes/1.2.x.rst @@ -0,0 +1,4 @@ +1.2.x Stable releases +===================== + +These notes have moved to `docs/notes/1.2.x.rst <../../../../docs/notes/1.2.x.rst>`_ diff --git a/src/python/pants/notes/1.20.x.rst b/src/python/pants/notes/1.20.x.rst new file mode 100644 index 00000000000..a1116600391 --- /dev/null +++ b/src/python/pants/notes/1.20.x.rst @@ -0,0 +1,4 @@ +1.20.x Stable releases +===================== + +These notes have moved to `docs/notes/1.20.x.rst <../../../../docs/notes/1.20.x.rst>`_ diff --git a/src/python/pants/notes/1.21.x.rst b/src/python/pants/notes/1.21.x.rst new file mode 100644 index 00000000000..779e55f32d2 --- /dev/null +++ b/src/python/pants/notes/1.21.x.rst @@ -0,0 +1,4 @@ +1.21.x Stable releases +===================== + +These notes have moved to `docs/notes/1.21.x.rst <../../../../docs/notes/1.21.x.rst>`_ diff --git a/src/python/pants/notes/1.22.x.rst b/src/python/pants/notes/1.22.x.rst new file mode 100644 index 00000000000..60cb1c3e74b --- /dev/null +++ b/src/python/pants/notes/1.22.x.rst @@ -0,0 +1,4 @@ +1.22.x Stable releases +===================== + +These notes have moved to `docs/notes/1.22.x.rst <../../../../docs/notes/1.22.x.rst>`_ diff --git a/src/python/pants/notes/1.23.x.rst b/src/python/pants/notes/1.23.x.rst new file mode 100644 index 00000000000..0355d8f16ae --- /dev/null +++ b/src/python/pants/notes/1.23.x.rst @@ -0,0 +1,4 @@ +1.23.x Stable releases +===================== + +These notes have moved to `docs/notes/1.23.x.rst <../../../../docs/notes/1.23.x.rst>`_ diff --git a/src/python/pants/notes/1.24.x.rst b/src/python/pants/notes/1.24.x.rst new file mode 100644 index 00000000000..41905f7d188 --- /dev/null +++ b/src/python/pants/notes/1.24.x.rst @@ -0,0 +1,4 @@ +1.24.x Stable releases +===================== + +These notes have moved to `docs/notes/1.24.x.rst <../../../../docs/notes/1.24.x.rst>`_ diff --git a/src/python/pants/notes/1.25.x.rst b/src/python/pants/notes/1.25.x.rst new file mode 100644 index 00000000000..512792096c5 --- /dev/null +++ b/src/python/pants/notes/1.25.x.rst @@ -0,0 +1,4 @@ +1.25.x Stable releases +===================== + +These notes have moved to `docs/notes/1.25.x.rst <../../../../docs/notes/1.25.x.rst>`_ diff --git a/src/python/pants/notes/1.26.x.rst b/src/python/pants/notes/1.26.x.rst new file mode 100644 index 00000000000..71775243a4a --- /dev/null +++ b/src/python/pants/notes/1.26.x.rst @@ -0,0 +1,4 @@ +1.26.x Stable releases +===================== + +These notes have moved to `docs/notes/1.26.x.rst <../../../../docs/notes/1.26.x.rst>`_ diff --git a/src/python/pants/notes/1.27.x.rst b/src/python/pants/notes/1.27.x.rst new file mode 100644 index 00000000000..804e2e65054 --- /dev/null +++ b/src/python/pants/notes/1.27.x.rst @@ -0,0 +1,4 @@ +1.27.x Stable releases +===================== + +These notes have moved to `docs/notes/1.27.x.rst <../../../../docs/notes/1.27.x.rst>`_ diff --git a/src/python/pants/notes/1.28.x.rst b/src/python/pants/notes/1.28.x.rst new file mode 100644 index 00000000000..38f2e9823ff --- /dev/null +++ b/src/python/pants/notes/1.28.x.rst @@ -0,0 +1,4 @@ +1.28.x Stable releases +===================== + +These notes have moved to `docs/notes/1.28.x.rst <../../../../docs/notes/1.28.x.rst>`_ diff --git a/src/python/pants/notes/1.29.x.rst b/src/python/pants/notes/1.29.x.rst new file mode 100644 index 00000000000..e41a0f973ce --- /dev/null +++ b/src/python/pants/notes/1.29.x.rst @@ -0,0 +1,4 @@ +1.29.x Stable releases +===================== + +These notes have moved to `docs/notes/1.29.x.rst <../../../../docs/notes/1.29.x.rst>`_ diff --git a/src/python/pants/notes/1.3.x.rst b/src/python/pants/notes/1.3.x.rst new file mode 100644 index 00000000000..9a4c1f2a080 --- /dev/null +++ b/src/python/pants/notes/1.3.x.rst @@ -0,0 +1,4 @@ +1.3.x Stable releases +===================== + +These notes have moved to `docs/notes/1.3.x.rst <../../../../docs/notes/1.3.x.rst>`_ diff --git a/src/python/pants/notes/1.30.x.rst b/src/python/pants/notes/1.30.x.rst new file mode 100644 index 00000000000..c13f761b5b1 --- /dev/null +++ b/src/python/pants/notes/1.30.x.rst @@ -0,0 +1,4 @@ +1.30.x Stable releases +===================== + +These notes have moved to `docs/notes/1.30.x.rst <../../../../docs/notes/1.30.x.rst>`_ diff --git a/src/python/pants/notes/1.4.x.rst b/src/python/pants/notes/1.4.x.rst new file mode 100644 index 00000000000..e95c24e6098 --- /dev/null +++ b/src/python/pants/notes/1.4.x.rst @@ -0,0 +1,4 @@ +1.4.x Stable releases +===================== + +These notes have moved to `docs/notes/1.4.x.rst <../../../../docs/notes/1.4.x.rst>`_ diff --git a/src/python/pants/notes/1.5.x.rst b/src/python/pants/notes/1.5.x.rst new file mode 100644 index 00000000000..6fed6e4886d --- /dev/null +++ b/src/python/pants/notes/1.5.x.rst @@ -0,0 +1,4 @@ +1.5.x Stable releases +===================== + +These notes have moved to `docs/notes/1.5.x.rst <../../../../docs/notes/1.5.x.rst>`_ diff --git a/src/python/pants/notes/1.6.x.rst b/src/python/pants/notes/1.6.x.rst new file mode 100644 index 00000000000..e1095c78e1c --- /dev/null +++ b/src/python/pants/notes/1.6.x.rst @@ -0,0 +1,4 @@ +1.6.x Stable releases +===================== + +These notes have moved to `docs/notes/1.6.x.rst <../../../../docs/notes/1.6.x.rst>`_ diff --git a/src/python/pants/notes/1.7.x.rst b/src/python/pants/notes/1.7.x.rst new file mode 100644 index 00000000000..11fe7bef742 --- /dev/null +++ b/src/python/pants/notes/1.7.x.rst @@ -0,0 +1,4 @@ +1.7.x Stable releases +===================== + +These notes have moved to `docs/notes/1.7.x.rst <../../../../docs/notes/1.7.x.rst>`_ diff --git a/src/python/pants/notes/1.8.x.rst b/src/python/pants/notes/1.8.x.rst new file mode 100644 index 00000000000..39dc6f968c4 --- /dev/null +++ b/src/python/pants/notes/1.8.x.rst @@ -0,0 +1,4 @@ +1.8.x Stable releases +===================== + +These notes have moved to `docs/notes/1.8.x.rst <../../../../docs/notes/1.8.x.rst>`_ diff --git a/src/python/pants/notes/1.9.x.rst b/src/python/pants/notes/1.9.x.rst new file mode 100644 index 00000000000..126208f6288 --- /dev/null +++ b/src/python/pants/notes/1.9.x.rst @@ -0,0 +1,4 @@ +1.9.x Stable releases +===================== + +These notes have moved to `docs/notes/1.9.x.rst <../../../../docs/notes/1.9.x.rst>`_ diff --git a/src/python/pants/notes/2.0.x.md b/src/python/pants/notes/2.0.x.md new file mode 100644 index 00000000000..e4ca5a10a08 --- /dev/null +++ b/src/python/pants/notes/2.0.x.md @@ -0,0 +1,3 @@ +# 2.0.x Stable releases + +These notes have moved to [`docs/notes/2.0.x.md`](../../../../docs/notes/2.0.x.md) diff --git a/src/python/pants/notes/2.0.x.rst b/src/python/pants/notes/2.0.x.rst new file mode 100644 index 00000000000..1a4f7792867 --- /dev/null +++ b/src/python/pants/notes/2.0.x.rst @@ -0,0 +1,4 @@ +2.0.x Stable releases +===================== + +These notes have moved to `docs/notes/2.0.x.md <../../../../docs/notes/2.0.x.md>`_ diff --git a/src/python/pants/notes/2.1.x.md b/src/python/pants/notes/2.1.x.md new file mode 100644 index 00000000000..d8b00c103bb --- /dev/null +++ b/src/python/pants/notes/2.1.x.md @@ -0,0 +1,3 @@ +# 2.1.x Stable releases + +These notes have moved to [`docs/notes/2.1.x.md`](../../../../docs/notes/2.1.x.md) diff --git a/src/python/pants/notes/2.1.x.rst b/src/python/pants/notes/2.1.x.rst new file mode 100644 index 00000000000..469f98857e4 --- /dev/null +++ b/src/python/pants/notes/2.1.x.rst @@ -0,0 +1,4 @@ +2.1.x Stable releases +===================== + +These notes have moved to `docs/notes/2.1.x.md <../../../../docs/notes/2.1.x.md>`_ diff --git a/src/python/pants/notes/2.10.x.md b/src/python/pants/notes/2.10.x.md new file mode 100644 index 00000000000..1d6d61be919 --- /dev/null +++ b/src/python/pants/notes/2.10.x.md @@ -0,0 +1,3 @@ +# 2.10.x Stable releases + +These notes have moved to [`docs/notes/2.10.x.md`](../../../../docs/notes/2.10.x.md) diff --git a/src/python/pants/notes/2.11.x.md b/src/python/pants/notes/2.11.x.md new file mode 100644 index 00000000000..617a81e2969 --- /dev/null +++ b/src/python/pants/notes/2.11.x.md @@ -0,0 +1,3 @@ +# 2.11.x Stable releases + +These notes have moved to [`docs/notes/2.11.x.md`](../../../../docs/notes/2.11.x.md) diff --git a/src/python/pants/notes/2.12.x.md b/src/python/pants/notes/2.12.x.md new file mode 100644 index 00000000000..8577c105443 --- /dev/null +++ b/src/python/pants/notes/2.12.x.md @@ -0,0 +1,3 @@ +# 2.12.x Stable releases + +These notes have moved to [`docs/notes/2.12.x.md`](../../../../docs/notes/2.12.x.md) diff --git a/src/python/pants/notes/2.13.x.md b/src/python/pants/notes/2.13.x.md new file mode 100644 index 00000000000..668dfac3471 --- /dev/null +++ b/src/python/pants/notes/2.13.x.md @@ -0,0 +1,3 @@ +# 2.13.x Stable releases + +These notes have moved to [`docs/notes/2.13.x.md`](../../../../docs/notes/2.13.x.md) diff --git a/src/python/pants/notes/2.14.x.md b/src/python/pants/notes/2.14.x.md new file mode 100644 index 00000000000..b6fda1872fa --- /dev/null +++ b/src/python/pants/notes/2.14.x.md @@ -0,0 +1,3 @@ +# 2.14.x Stable releases + +These notes have moved to [`docs/notes/2.14.x.md`](../../../../docs/notes/2.14.x.md) diff --git a/src/python/pants/notes/2.15.x.md b/src/python/pants/notes/2.15.x.md new file mode 100644 index 00000000000..85c0435060b --- /dev/null +++ b/src/python/pants/notes/2.15.x.md @@ -0,0 +1,3 @@ +# 2.15.x Stable releases + +These notes have moved to [`docs/notes/2.15.x.md`](../../../../docs/notes/2.15.x.md) diff --git a/src/python/pants/notes/2.16.x.md b/src/python/pants/notes/2.16.x.md new file mode 100644 index 00000000000..d4128a3aa0a --- /dev/null +++ b/src/python/pants/notes/2.16.x.md @@ -0,0 +1,3 @@ +# 2.16.x Stable releases + +These notes have moved to [`docs/notes/2.16.x.md`](../../../../docs/notes/2.16.x.md) diff --git a/src/python/pants/notes/2.17.x.md b/src/python/pants/notes/2.17.x.md new file mode 100644 index 00000000000..4ca8c7d8a0d --- /dev/null +++ b/src/python/pants/notes/2.17.x.md @@ -0,0 +1,3 @@ +# 2.17.x Stable releases + +These notes have moved to [`docs/notes/2.17.x.md`](../../../../docs/notes/2.17.x.md) diff --git a/src/python/pants/notes/2.18.x.md b/src/python/pants/notes/2.18.x.md new file mode 100644 index 00000000000..6da34e3d454 --- /dev/null +++ b/src/python/pants/notes/2.18.x.md @@ -0,0 +1,3 @@ +# 2.18.x Stable releases + +These notes have moved to [`docs/notes/2.18.x.md`](../../../../docs/notes/2.18.x.md) diff --git a/src/python/pants/notes/2.19.x.md b/src/python/pants/notes/2.19.x.md new file mode 100644 index 00000000000..5153fc62ced --- /dev/null +++ b/src/python/pants/notes/2.19.x.md @@ -0,0 +1,3 @@ +# 2.19.x Stable releases + +These notes have moved to [`docs/notes/2.19.x.md`](../../../../docs/notes/2.19.x.md) diff --git a/src/python/pants/notes/2.2.x.md b/src/python/pants/notes/2.2.x.md new file mode 100644 index 00000000000..f87c3ba7437 --- /dev/null +++ b/src/python/pants/notes/2.2.x.md @@ -0,0 +1,3 @@ +# 2.2.x Stable releases + +These notes have moved to [`docs/notes/2.2.x.md`](../../../../docs/notes/2.2.x.md) diff --git a/src/python/pants/notes/2.20.x.md b/src/python/pants/notes/2.20.x.md new file mode 100644 index 00000000000..8638b66c592 --- /dev/null +++ b/src/python/pants/notes/2.20.x.md @@ -0,0 +1,3 @@ +# 2.20.x Stable releases + +These notes have moved to [`docs/notes/2.20.x.md`](../../../../docs/notes/2.20.x.md) diff --git a/src/python/pants/notes/2.21.x.md b/src/python/pants/notes/2.21.x.md new file mode 100644 index 00000000000..c1a73ce7d28 --- /dev/null +++ b/src/python/pants/notes/2.21.x.md @@ -0,0 +1,3 @@ +# 2.21.x Stable releases + +These notes have moved to [`docs/notes/2.21.x.md`](../../../../docs/notes/2.21.x.md) diff --git a/src/python/pants/notes/2.22.x.md b/src/python/pants/notes/2.22.x.md new file mode 100644 index 00000000000..6e7e594bca3 --- /dev/null +++ b/src/python/pants/notes/2.22.x.md @@ -0,0 +1,3 @@ +# 2.22.x Stable releases + +These notes have moved to [`docs/notes/2.22.x.md`](../../../../docs/notes/2.22.x.md) diff --git a/src/python/pants/notes/2.3.x.md b/src/python/pants/notes/2.3.x.md new file mode 100644 index 00000000000..fa8d3288db8 --- /dev/null +++ b/src/python/pants/notes/2.3.x.md @@ -0,0 +1,3 @@ +# 2.3.x Stable releases + +These notes have moved to [`docs/notes/2.3.x.md`](../../../../docs/notes/2.3.x.md) diff --git a/src/python/pants/notes/2.4.x.md b/src/python/pants/notes/2.4.x.md new file mode 100644 index 00000000000..54503066e17 --- /dev/null +++ b/src/python/pants/notes/2.4.x.md @@ -0,0 +1,3 @@ +# 2.4.x Stable releases + +These notes have moved to [`docs/notes/2.4.x.md`](../../../../docs/notes/2.4.x.md) diff --git a/src/python/pants/notes/2.5.x.md b/src/python/pants/notes/2.5.x.md new file mode 100644 index 00000000000..3db91dabeb1 --- /dev/null +++ b/src/python/pants/notes/2.5.x.md @@ -0,0 +1,3 @@ +# 2.5.x Stable releases + +These notes have moved to [`docs/notes/2.5.x.md`](../../../../docs/notes/2.5.x.md) diff --git a/src/python/pants/notes/2.6.x.md b/src/python/pants/notes/2.6.x.md new file mode 100644 index 00000000000..d2632a296fd --- /dev/null +++ b/src/python/pants/notes/2.6.x.md @@ -0,0 +1,3 @@ +# 2.6.x Stable releases + +These notes have moved to [`docs/notes/2.6.x.md`](../../../../docs/notes/2.6.x.md) diff --git a/src/python/pants/notes/2.7.x.md b/src/python/pants/notes/2.7.x.md new file mode 100644 index 00000000000..bb907561112 --- /dev/null +++ b/src/python/pants/notes/2.7.x.md @@ -0,0 +1,3 @@ +# 2.7.x Stable releases + +These notes have moved to [`docs/notes/2.7.x.md`](../../../../docs/notes/2.7.x.md) diff --git a/src/python/pants/notes/2.8.x.md b/src/python/pants/notes/2.8.x.md new file mode 100644 index 00000000000..996bf8de588 --- /dev/null +++ b/src/python/pants/notes/2.8.x.md @@ -0,0 +1,3 @@ +# 2.8.x Stable releases + +These notes have moved to [`docs/notes/2.8.x.md`](../../../../docs/notes/2.8.x.md) diff --git a/src/python/pants/notes/2.9.x.md b/src/python/pants/notes/2.9.x.md new file mode 100644 index 00000000000..de83f6b49bd --- /dev/null +++ b/src/python/pants/notes/2.9.x.md @@ -0,0 +1,3 @@ +# 2.9.x Stable releases + +These notes have moved to [`docs/notes/2.9.x.md`](../../../../docs/notes/2.9.x.md) diff --git a/src/python/pants/notes/master.rst b/src/python/pants/notes/master.rst new file mode 100644 index 00000000000..2a29cf0d02e --- /dev/null +++ b/src/python/pants/notes/master.rst @@ -0,0 +1,4 @@ +master Stable releases +===================== + +These notes have moved to `docs/notes/master.rst <../../../../docs/notes/master.rst>`_ From aa06beaad85e9dacf79efeffe3078803dc679bec Mon Sep 17 00:00:00 2001 From: Benjy Weinberger Date: Tue, 21 May 2024 17:35:00 -0400 Subject: [PATCH 12/18] An option for excluding certain files from all deploy jars (#20943) The default value will strip signature files from deploy jars, which is necessary since the deploy jar will naturally not match the signature of some random jar that was splatted in to it. The alternative was to set this as the default on `deploy_jar`'s `exclude_files` field. But then anyone setting that field would overrwrite the default, and be surprised when everything broke due to their being *more* files in their jar than before. The previous workaround was to set these patterns manually on each deploy_jar, but that is an unnecessary gotcha. --- docs/notes/2.22.x.md | 5 +++++ src/python/pants/jvm/package/deploy_jar.py | 2 +- src/python/pants/jvm/subsystems.py | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/notes/2.22.x.md b/docs/notes/2.22.x.md index fd37a0331a1..46fa44d43d6 100644 --- a/docs/notes/2.22.x.md +++ b/docs/notes/2.22.x.md @@ -42,6 +42,11 @@ targets generator from `pom.xml`. Exclusions for `jvm_artifact` and `scala_artifact` now correctly handle a `jvm_exclude` with only the group defined. +Added a `deploy_jar_exclude_files` option to the `[jvm]` subsystem, containing file patterns to exclude from all +deploy jars, in addition to those specified on a per-jar basis in the `deploy_jar` target's `exclude_files` field. +This option's default value excludes signature files from constituent jars, which are known to cause the deploy jar +to fail to execute (since naturally it doesn't match those signatures). + ##### Scala Setting the `orphan_files_behaviour = "ignore"` option for [`pants.backend.experimental.scala.lint.scalafix`](https://www.pantsbuild.org/2.22/reference/subsystems/scalafix#orphan_files_behavior) or [`pants.backend.experimental.scala.lint.scalafmt`](https://www.pantsbuild.org/2.22/reference/subsystems/scalafmt#orphan_files_behavior) backend is now properly silent. It previously showed spurious warnings. diff --git a/src/python/pants/jvm/package/deploy_jar.py b/src/python/pants/jvm/package/deploy_jar.py index 4dfbe6ef56e..e3bba83f5ad 100644 --- a/src/python/pants/jvm/package/deploy_jar.py +++ b/src/python/pants/jvm/package/deploy_jar.py @@ -138,7 +138,7 @@ async def package_deploy_jar( (rule.pattern, rule.action) for rule in field_set.duplicate_policy.value_or_default() ], - skip=field_set.exclude_files.value, + skip=[*(jvm.deploy_jar_exclude_files or []), *(field_set.exclude_files.value or [])], compress=True, ), ) diff --git a/src/python/pants/jvm/subsystems.py b/src/python/pants/jvm/subsystems.py index 3500d5521ff..1b1256de99a 100644 --- a/src/python/pants/jvm/subsystems.py +++ b/src/python/pants/jvm/subsystems.py @@ -100,6 +100,24 @@ class EnvironmentAware: ), advanced=True, ) + deploy_jar_exclude_files = StrListOption( + default=[ + # Signature files. + r"^META-INF/[^/]+\.SF$", + r"^META-INF/[^/]+\.DSA$", + r"^META-INF/[^/]+\.RSA$", + # interferes with Class-Path: see man jar for i option. + "META-INF/INDEX.LIST$", + ], + help=softwrap( + """ + A list of patterns to exclude from all deploy jars. + An individual deploy_jar target can also exclude other files, in addition to these, + by setting its `exclude_files` field. + """ + ), + ) + # See https://github.com/pantsbuild/pants/issues/14937 for discussion of one way to improve # our behavior around cancellation with nailgun. nailgun_remote_cache_speculation_delay = IntOption( From c8cdca04e0cbe232109c69454ea4d33e666eb5ac Mon Sep 17 00:00:00 2001 From: Tom Dyas Date: Tue, 21 May 2024 19:57:35 -0400 Subject: [PATCH 13/18] refactor fork/exec logic for the command runners (#20944) Move the exclusive spawn logic out of `process_execution::local::CommandRunner` into a helper module in advance of using that logic in the forthcoming "workspace" command runner to be introduced by https://github.com/pantsbuild/pants/pull/20772. (This PR was extracted from https://github.com/pantsbuild/pants/pull/20772.) --- .../process_execution/src/cache_tests.rs | 3 + .../engine/process_execution/src/fork_exec.rs | 75 +++++++++++++++++++ src/rust/engine/process_execution/src/lib.rs | 2 + .../engine/process_execution/src/local.rs | 72 +++--------------- .../process_execution/src/local_tests.rs | 3 + src/rust/engine/process_executor/src/main.rs | 2 + src/rust/engine/src/context.rs | 6 ++ 7 files changed, 100 insertions(+), 63 deletions(-) create mode 100644 src/rust/engine/process_execution/src/fork_exec.rs diff --git a/src/rust/engine/process_execution/src/cache_tests.rs b/src/rust/engine/process_execution/src/cache_tests.rs index dd0cabbb44c..7d839ab52a0 100644 --- a/src/rust/engine/process_execution/src/cache_tests.rs +++ b/src/rust/engine/process_execution/src/cache_tests.rs @@ -3,6 +3,7 @@ use std::convert::TryInto; use std::io::Write; use std::path::PathBuf; +use std::sync::Arc; use cache::PersistentCache; use sharded_lmdb::DEFAULT_LEASE_TIME; @@ -10,6 +11,7 @@ use store::{ImmutableInputs, Store}; use tempfile::TempDir; use testutil::data::TestData; use testutil::relative_paths; +use tokio::sync::RwLock; use workunit_store::{RunningWorkunit, WorkunitStore}; use crate::{ @@ -35,6 +37,7 @@ fn create_local_runner() -> (Box, Store, TempDir) { NamedCaches::new_local(named_cache_dir), ImmutableInputs::new(store.clone(), base_dir.path()).unwrap(), KeepSandboxes::Never, + Arc::new(RwLock::new(())), )); (runner, store, base_dir) } diff --git a/src/rust/engine/process_execution/src/fork_exec.rs b/src/rust/engine/process_execution/src/fork_exec.rs new file mode 100644 index 00000000000..3c356d5cc10 --- /dev/null +++ b/src/rust/engine/process_execution/src/fork_exec.rs @@ -0,0 +1,75 @@ +// Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +// Licensed under the Apache License, Version 2.0 (see LICENSE). + +use std::io; +use std::sync::Arc; +use std::time::Duration; + +use tokio::sync::RwLock; + +use crate::ManagedChild; + +/// Spawn a subprocess safely given that binaries may be written by this process. +pub async fn spawn_process( + spawn_lock: Arc>, + exclusive: bool, + mut fork_exec: impl FnMut() -> io::Result, +) -> Result { + // See the documentation of the `CapturedWorkdir::run_in_workdir` method, but `exclusive_spawn` + // indicates the binary we're spawning was written out by the current thread, and, as such, + // there may be open file handles against it. This will occur whenever a concurrent call of this + // method proceeds through its fork point + // (https://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html) while the current + // thread is in the middle of writing the binary and thus captures a clone of the open file + // handle, but that concurrent call has not yet gotten to its exec point + // (https://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html) where the operating + // system will close the cloned file handle (via O_CLOEXEC being set on all files opened by + // Rust). To prevent a race like this holding this thread's binary open leading to an ETXTBSY + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html) error, we + // maintain RwLock that allows non-`exclusive_spawn` binaries to spawn concurrently but ensures + // all such concurrent spawns have completed (and thus closed any cloned file handles) before + // proceeding to spawn the `exclusive_spawn` binary this thread has written. + // + // See: https://github.com/golang/go/issues/22315 for an excellent description of this generic + // unix problem. + + if exclusive { + let _write_locked = spawn_lock.write().await; + + // Despite the mitigations taken against racing our own forks, forks can happen in our + // process but outside of our control (in libraries). As such, we back-stop by sleeping and + // trying again for a while if we do hit one of these fork races we do not control. + const MAX_ETXTBSY_WAIT: Duration = Duration::from_millis(100); + let mut retries: u32 = 0; + let mut sleep_millis = 1; + + let start_time = std::time::Instant::now(); + loop { + match fork_exec() { + Err(e) => { + if e.raw_os_error() == Some(libc::ETXTBSY) + && start_time.elapsed() < MAX_ETXTBSY_WAIT + { + tokio::time::sleep(std::time::Duration::from_millis(sleep_millis)).await; + retries += 1; + sleep_millis *= 2; + continue; + } else if retries > 0 { + break Err(format!( + "Error launching process after {} {} for ETXTBSY. Final error was: {:?}", + retries, + if retries == 1 { "retry" } else { "retries" }, + e + )); + } else { + break Err(format!("Error launching process: {e:?}")); + } + } + Ok(child) => break Ok(child), + } + } + } else { + let _read_locked = spawn_lock.read().await; + fork_exec().map_err(|e| format!("Error launching process: {e:?}")) + } +} diff --git a/src/rust/engine/process_execution/src/lib.rs b/src/rust/engine/process_execution/src/lib.rs index efbdb47af38..c7e05fee7bb 100644 --- a/src/rust/engine/process_execution/src/lib.rs +++ b/src/rust/engine/process_execution/src/lib.rs @@ -59,6 +59,8 @@ pub mod named_caches; #[cfg(test)] pub mod named_caches_tests; +pub(crate) mod fork_exec; + extern crate uname; pub use crate::children::ManagedChild; diff --git a/src/rust/engine/process_execution/src/local.rs b/src/rust/engine/process_execution/src/local.rs index 92ab5533b6e..075e4d5dc4f 100644 --- a/src/rust/engine/process_execution/src/local.rs +++ b/src/rust/engine/process_execution/src/local.rs @@ -31,10 +31,11 @@ use task_executor::Executor; use tempfile::TempDir; use tokio::process::Command; use tokio::sync::RwLock; -use tokio::time::{timeout, Duration}; +use tokio::time::timeout; use tokio_util::codec::{BytesCodec, FramedRead}; use workunit_store::{in_workunit, Level, Metric, RunningWorkunit}; +use crate::fork_exec::spawn_process; use crate::{ Context, FallibleProcessResultWithPlatform, ManagedChild, NamedCaches, Process, ProcessError, ProcessResultMetadata, ProcessResultSource, @@ -57,7 +58,7 @@ pub struct CommandRunner { named_caches: NamedCaches, immutable_inputs: ImmutableInputs, keep_sandboxes: KeepSandboxes, - spawn_lock: RwLock<()>, + spawn_lock: Arc>, } impl CommandRunner { @@ -68,6 +69,7 @@ impl CommandRunner { named_caches: NamedCaches, immutable_inputs: ImmutableInputs, keep_sandboxes: KeepSandboxes, + spawn_lock: Arc>, ) -> CommandRunner { CommandRunner { store, @@ -76,7 +78,7 @@ impl CommandRunner { named_caches, immutable_inputs, keep_sandboxes, - spawn_lock: RwLock::new(()), + spawn_lock, } } @@ -307,66 +309,10 @@ impl CapturedWorkdir for CommandRunner { .stdout(Stdio::piped()) .stderr(Stdio::piped()); - // See the documentation of the `CapturedWorkdir::run_in_workdir` method, but `exclusive_spawn` - // indicates the binary we're spawning was written out by the current thread, and, as such, - // there may be open file handles against it. This will occur whenever a concurrent call of this - // method proceeds through its fork point - // (https://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html) while the current - // thread is in the middle of writing the binary and thus captures a clone of the open file - // handle, but that concurrent call has not yet gotten to its exec point - // (https://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html) where the operating - // system will close the cloned file handle (via O_CLOEXEC being set on all files opened by - // Rust). To prevent a race like this holding this thread's binary open leading to an ETXTBSY - // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html) error, we - // maintain RwLock that allows non-`exclusive_spawn` binaries to spawn concurrently but ensures - // all such concurrent spawns have completed (and thus closed any cloned file handles) before - // proceeding to spawn the `exclusive_spawn` binary this thread has written. - // - // See: https://github.com/golang/go/issues/22315 for an excellent description of this generic - // unix problem. - let mut fork_exec = move || ManagedChild::spawn(&mut command, None); - let mut child = { - if exclusive_spawn { - let _write_locked = self.spawn_lock.write().await; - - // Despite the mitigations taken against racing our own forks, forks can happen in our - // process but outside of our control (in libraries). As such, we back-stop by sleeping and - // trying again for a while if we do hit one of these fork races we do not control. - const MAX_ETXTBSY_WAIT: Duration = Duration::from_millis(100); - let mut retries: u32 = 0; - let mut sleep_millis = 1; - - let start_time = std::time::Instant::now(); - loop { - match fork_exec() { - Err(e) => { - if e.raw_os_error() == Some(libc::ETXTBSY) - && start_time.elapsed() < MAX_ETXTBSY_WAIT - { - tokio::time::sleep(std::time::Duration::from_millis(sleep_millis)) - .await; - retries += 1; - sleep_millis *= 2; - continue; - } else if retries > 0 { - break Err(format!( - "Error launching process after {} {} for ETXTBSY. Final error was: {:?}", - retries, - if retries == 1 { "retry" } else { "retries" }, - e - )); - } else { - break Err(format!("Error launching process: {e:?}")); - } - } - Ok(child) => break Ok(child), - } - } - } else { - let _read_locked = self.spawn_lock.read().await; - fork_exec().map_err(|e| format!("Error launching process: {e:?}")) - } - }?; + let mut child = spawn_process(self.spawn_lock.clone(), exclusive_spawn, move || { + ManagedChild::spawn(&mut command, None) + }) + .await?; debug!("spawned local process as {:?} for {:?}", child.id(), req); let stdout_stream = FramedRead::new(child.stdout.take().unwrap(), BytesCodec::new()) diff --git a/src/rust/engine/process_execution/src/local_tests.rs b/src/rust/engine/process_execution/src/local_tests.rs index a05c63803e1..38bf4cd8f8e 100644 --- a/src/rust/engine/process_execution/src/local_tests.rs +++ b/src/rust/engine/process_execution/src/local_tests.rs @@ -3,6 +3,7 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::path::PathBuf; use std::str; +use std::sync::Arc; use std::time::Duration; use maplit::hashset; @@ -14,6 +15,7 @@ use store::{ImmutableInputs, Store}; use testutil::data::{TestData, TestDirectory}; use testutil::path::{find_bash, which}; use testutil::{owned_string_vec, relative_paths}; +use tokio::sync::RwLock; use workunit_store::{RunningWorkunit, WorkunitStore}; use crate::{ @@ -794,6 +796,7 @@ async fn run_command_locally_in_dir( named_caches, immutable_inputs, cleanup, + Arc::new(RwLock::new(())), ); let original = runner.run(Context::default(), workunit, req).await?; let stdout_bytes = store diff --git a/src/rust/engine/process_executor/src/main.rs b/src/rust/engine/process_executor/src/main.rs index 24967b0c8ed..148344e1e0d 100644 --- a/src/rust/engine/process_executor/src/main.rs +++ b/src/rust/engine/process_executor/src/main.rs @@ -23,6 +23,7 @@ use protos::gen::buildbarn::cas::UncachedActionResult; use protos::require_digest; use remote::remote_cache::RemoteCacheRunnerOptions; use store::{ImmutableInputs, RemoteProvider, RemoteStoreOptions, Store}; +use tokio::sync::RwLock; use workunit_store::{in_workunit, Level, WorkunitStore}; #[derive(Clone, Debug, Default)] @@ -393,6 +394,7 @@ async fn main() { ), ImmutableInputs::new(store.clone(), &workdir).unwrap(), KeepSandboxes::Never, + Arc::new(RwLock::new(())), )) as Box, }; diff --git a/src/rust/engine/src/context.rs b/src/rust/engine/src/context.rs index 7fbddac08b4..3a7423211ba 100644 --- a/src/rust/engine/src/context.rs +++ b/src/rust/engine/src/context.rs @@ -37,6 +37,7 @@ use remote::{self, remote_cache}; use rule_graph::RuleGraph; use store::{self, ImmutableInputs, RemoteProvider, RemoteStoreOptions, Store}; use task_executor::Executor; +use tokio::sync::RwLock; use watch::{Invalidatable, InvalidateCaller, InvalidationWatcher}; use workunit_store::{Metric, RunningWorkunit}; @@ -210,6 +211,10 @@ impl Core { exec_strategy_opts: &ExecutionStrategyOptions, remoting_opts: &RemotingOptions, ) -> Result, String> { + // Lock shared between local command runner (and in future work) other "local" command runners + // for spawning processes. + let spawn_lock = Arc::new(RwLock::new(())); + let local_command_runner = local::CommandRunner::new( local_runner_store.clone(), executor.clone(), @@ -217,6 +222,7 @@ impl Core { named_caches.clone(), immutable_inputs.clone(), exec_strategy_opts.local_keep_sandboxes, + spawn_lock.clone(), ); let runner: Box = if exec_strategy_opts.local_enable_nailgun { From 6dd925a7a02f5fd43de008b4a16968de9802a702 Mon Sep 17 00:00:00 2001 From: Daniel Goldman Date: Wed, 22 May 2024 18:37:28 -0400 Subject: [PATCH 14/18] Mark most JVM-based subsystems as exportable (#20788) Converts all JVM tools which are subsystems to use ExportableTool for lockfile generation. Some internal tools still use `GenerateJvmToolLockfileSentinel` subclasses. They aren't subsystems, so they'd need some more work. We could either convert them into subsystems (as is done in Python-based backends) although if we don't plan on exposing them (which is the current case) we could also remove the subclass and rule and replace it with a function. --- docs/notes/2.22.x.md | 2 ++ .../pants/backend/codegen/avro/java/rules.py | 19 +++----------- .../backend/codegen/protobuf/java/rules.py | 25 ++++++------------- .../backend/codegen/protobuf/scala/rules.py | 25 ++++++------------- .../pants/backend/codegen/soap/java/rules.py | 19 +++----------- .../backend/codegen/thrift/scrooge/rules.py | 19 +++----------- .../java/lint/google_java_format/rules.py | 21 +++------------- .../pants/backend/kotlin/lint/ktlint/rules.py | 19 +++----------- .../backend/scala/lint/scalafix/rules.py | 19 +++----------- .../backend/scala/lint/scalafmt/rules.py | 19 +++----------- .../pants/backend/scala/test/scalatest.py | 19 +++----------- src/python/pants/jvm/test/junit.py | 19 +++----------- 12 files changed, 53 insertions(+), 172 deletions(-) diff --git a/docs/notes/2.22.x.md b/docs/notes/2.22.x.md index 46fa44d43d6..28488cb6de3 100644 --- a/docs/notes/2.22.x.md +++ b/docs/notes/2.22.x.md @@ -47,6 +47,8 @@ deploy jars, in addition to those specified on a per-jar basis in the `deploy_ja This option's default value excludes signature files from constituent jars, which are known to cause the deploy jar to fail to execute (since naturally it doesn't match those signatures). +The internal code for exporting JVM tools was refactored. + ##### Scala Setting the `orphan_files_behaviour = "ignore"` option for [`pants.backend.experimental.scala.lint.scalafix`](https://www.pantsbuild.org/2.22/reference/subsystems/scalafix#orphan_files_behavior) or [`pants.backend.experimental.scala.lint.scalafmt`](https://www.pantsbuild.org/2.22/reference/subsystems/scalafmt#orphan_files_behavior) backend is now properly silent. It previously showed spurious warnings. diff --git a/src/python/pants/backend/codegen/avro/java/rules.py b/src/python/pants/backend/codegen/avro/java/rules.py index 54310ebad0b..d62a52b7b49 100644 --- a/src/python/pants/backend/codegen/avro/java/rules.py +++ b/src/python/pants/backend/codegen/avro/java/rules.py @@ -17,7 +17,7 @@ from pants.backend.java.target_types import JavaSourceField from pants.base.glob_match_error_behavior import GlobMatchErrorBehavior from pants.build_graph.address import Address -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel +from pants.core.goals.resolves import ExportableTool from pants.engine.fs import ( AddPrefix, CreateDigest, @@ -53,7 +53,7 @@ from pants.jvm.jdk_rules import InternalJdk, JvmProcess from pants.jvm.resolve import jvm_tool from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.jvm.subsystems import JvmSubsystem from pants.jvm.target_types import JvmResolveField, PrefixedJvmJdkField, PrefixedJvmResolveField from pants.source.source_root import SourceRoot, SourceRootRequest @@ -66,10 +66,6 @@ class GenerateJavaFromAvroRequest(GenerateSourcesRequest): output = JavaSourceField -class AvroToolLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = AvroSubsystem.options_scope - - @dataclass(frozen=True) class CompileAvroSourceRequest: digest: Digest @@ -116,7 +112,7 @@ async def compile_avro_source( output_dir = "_generated_files" toolcp_relpath = "__toolcp" - lockfile_request = await Get(GenerateJvmLockfileFromTool, AvroToolLockfileSentinel()) + lockfile_request = GenerateJvmLockfileFromTool.create(avro_tools) tool_classpath, subsetted_input_digest, empty_output_dir = await MultiGet( Get(ToolClasspath, ToolClasspathRequest(lockfile=lockfile_request)), Get( @@ -222,13 +218,6 @@ def make_avro_process( return CompiledAvroSource(normalized_digest) -@rule -def generate_avro_tools_lockfile_request( - _: AvroToolLockfileSentinel, tool: AvroSubsystem -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(tool) - - @dataclass(frozen=True) class AvroRuntimeDependencyInferenceFieldSet(FieldSet): required_fields = ( @@ -326,7 +315,7 @@ def rules(): *jvm_tool.rules(), *jdk_rules.rules(), UnionRule(GenerateSourcesRequest, GenerateJavaFromAvroRequest), - UnionRule(GenerateToolLockfileSentinel, AvroToolLockfileSentinel), + UnionRule(ExportableTool, AvroSubsystem), UnionRule(InferDependenciesRequest, InferAvroRuntimeDependencyRequest), AvroSourceTarget.register_plugin_field(PrefixedJvmJdkField), AvroSourcesGeneratorTarget.register_plugin_field(PrefixedJvmJdkField), diff --git a/src/python/pants/backend/codegen/protobuf/java/rules.py b/src/python/pants/backend/codegen/protobuf/java/rules.py index ec8d589baa9..142e2a006de 100644 --- a/src/python/pants/backend/codegen/protobuf/java/rules.py +++ b/src/python/pants/backend/codegen/protobuf/java/rules.py @@ -16,7 +16,7 @@ ) from pants.backend.experimental.java.register import rules as java_backend_rules from pants.backend.java.target_types import JavaSourceField -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.external_tool import DownloadedExternalTool, ExternalToolRequest from pants.core.util_rules.source_files import SourceFilesRequest from pants.core.util_rules.stripped_source_files import StrippedSourceFiles @@ -42,7 +42,7 @@ ) from pants.engine.unions import UnionRule from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.jvm.target_types import PrefixedJvmJdkField, PrefixedJvmResolveField from pants.source.source_root import SourceRoot, SourceRootRequest from pants.util.logging import LogLevel @@ -53,10 +53,6 @@ class GenerateJavaFromProtobufRequest(GenerateSourcesRequest): output = JavaSourceField -class GrpcJavaToolLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = JavaProtobufGrpcSubsystem.options_scope - - @dataclass(frozen=True) class ProtobufJavaGrpcPlugin: digest: Digest @@ -64,8 +60,11 @@ class ProtobufJavaGrpcPlugin: @rule -async def resolve_protobuf_java_grpc_plugin(platform: Platform) -> ProtobufJavaGrpcPlugin: - lockfile_request = await Get(GenerateJvmLockfileFromTool, GrpcJavaToolLockfileSentinel()) +async def resolve_protobuf_java_grpc_plugin( + platform: Platform, + tool: JavaProtobufGrpcSubsystem, +) -> ProtobufJavaGrpcPlugin: + lockfile_request = GenerateJvmLockfileFromTool.create(tool) classpath = await Get( ToolClasspath, ToolClasspathRequest(lockfile=lockfile_request), @@ -200,21 +199,13 @@ async def generate_java_from_protobuf( return GeneratedSources(source_root_restored) -@rule -async def generate_grpc_java_lockfile_request( - _: GrpcJavaToolLockfileSentinel, - tool: JavaProtobufGrpcSubsystem, -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(tool) - - def rules(): return [ *collect_rules(), *dependency_inference.rules(), *symbol_mapper.rules(), UnionRule(GenerateSourcesRequest, GenerateJavaFromProtobufRequest), - UnionRule(GenerateToolLockfileSentinel, GrpcJavaToolLockfileSentinel), + UnionRule(ExportableTool, JavaProtobufGrpcSubsystem), ProtobufSourceTarget.register_plugin_field(PrefixedJvmJdkField), ProtobufSourcesGeneratorTarget.register_plugin_field(PrefixedJvmJdkField), ProtobufSourceTarget.register_plugin_field(PrefixedJvmResolveField), diff --git a/src/python/pants/backend/codegen/protobuf/scala/rules.py b/src/python/pants/backend/codegen/protobuf/scala/rules.py index 5ae4b529e41..5deb2616c11 100644 --- a/src/python/pants/backend/codegen/protobuf/scala/rules.py +++ b/src/python/pants/backend/codegen/protobuf/scala/rules.py @@ -19,7 +19,7 @@ ScalaArtifactsForVersionResult, ScalaVersion, ) -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules import distdir from pants.core.util_rules.external_tool import DownloadedExternalTool, ExternalToolRequest from pants.core.util_rules.source_files import SourceFilesRequest @@ -53,7 +53,7 @@ from pants.jvm.jdk_rules import InternalJdk, JvmProcess from pants.jvm.resolve.common import ArtifactRequirements, GatherJvmCoordinatesRequest from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.jvm.target_types import PrefixedJvmJdkField, PrefixedJvmResolveField from pants.source.source_root import SourceRoot, SourceRootRequest from pants.util.logging import LogLevel @@ -66,10 +66,6 @@ class GenerateScalaFromProtobufRequest(GenerateSourcesRequest): output = ScalaSourceField -class ScalapbcToolLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = ScalaPBSubsystem.options_scope - - class ScalaPBShimCompiledClassfiles(ClasspathEntry): pass @@ -118,7 +114,7 @@ async def generate_scala_from_protobuf( plugins_relpath = "__plugins" protoc_relpath = "__protoc" - lockfile_request = await Get(GenerateJvmLockfileFromTool, ScalapbcToolLockfileSentinel()) + lockfile_request = GenerateJvmLockfileFromTool.create(scalapb) ( downloaded_protoc_binary, tool_classpath, @@ -258,9 +254,9 @@ async def setup_scalapb_shim_classfiles( scalapb_shim_source = FileContent("ScalaPBShim.scala", scalapb_shim_content) - lockfile_request, scala_artifacts = await MultiGet( - Get(GenerateJvmLockfileFromTool, ScalapbcToolLockfileSentinel()), - Get(ScalaArtifactsForVersionResult, ScalaArtifactsForVersionRequest(SHIM_SCALA_VERSION)), + lockfile_request = GenerateJvmLockfileFromTool.create(scalapb) + scala_artifacts = await Get( + ScalaArtifactsForVersionResult, ScalaArtifactsForVersionRequest(SHIM_SCALA_VERSION) ) tool_classpath, shim_classpath, source_digest = await MultiGet( Get( @@ -310,13 +306,6 @@ async def setup_scalapb_shim_classfiles( return ScalaPBShimCompiledClassfiles(digest=stripped_classfiles_digest) -@rule -def generate_scalapbc_lockfile_request( - _: ScalapbcToolLockfileSentinel, tool: ScalaPBSubsystem -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(tool) - - def rules(): return [ *collect_rules(), @@ -324,7 +313,7 @@ def rules(): *dependency_inference.rules(), *symbol_mapper.rules(), UnionRule(GenerateSourcesRequest, GenerateScalaFromProtobufRequest), - UnionRule(GenerateToolLockfileSentinel, ScalapbcToolLockfileSentinel), + UnionRule(ExportableTool, ScalaPBSubsystem), ProtobufSourceTarget.register_plugin_field(PrefixedJvmJdkField), ProtobufSourcesGeneratorTarget.register_plugin_field(PrefixedJvmJdkField), ProtobufSourceTarget.register_plugin_field(PrefixedJvmResolveField), diff --git a/src/python/pants/backend/codegen/soap/java/rules.py b/src/python/pants/backend/codegen/soap/java/rules.py index d10a94f720c..0a800b37c9a 100644 --- a/src/python/pants/backend/codegen/soap/java/rules.py +++ b/src/python/pants/backend/codegen/soap/java/rules.py @@ -15,7 +15,7 @@ ) from pants.backend.java.target_types import JavaSourceField from pants.base.glob_match_error_behavior import GlobMatchErrorBehavior -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel +from pants.core.goals.resolves import ExportableTool from pants.engine.fs import ( AddPrefix, CreateDigest, @@ -41,7 +41,7 @@ from pants.jvm.jdk_rules import InternalJdk, JvmProcess from pants.jvm.resolve import jvm_tool from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.jvm.target_types import PrefixedJvmJdkField, PrefixedJvmResolveField from pants.source.source_root import SourceRoot, SourceRootRequest from pants.util.logging import LogLevel @@ -52,10 +52,6 @@ class GenerateJavaFromWsdlRequest(GenerateSourcesRequest): output = JavaSourceField -class JaxWsToolsLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = JaxWsTools.options_scope - - @dataclass(frozen=True) class CompileWsdlSourceRequest: digest: Digest @@ -111,7 +107,7 @@ async def compile_wsdl_source( output_dir = "_generated_files" toolcp_relpath = "__toolcp" - lockfile_request = await Get(GenerateJvmLockfileFromTool, JaxWsToolsLockfileSentinel()) + lockfile_request = GenerateJvmLockfileFromTool.create(jaxws) tool_classpath, subsetted_input_digest, empty_output_dir = await MultiGet( Get( ToolClasspath, @@ -174,13 +170,6 @@ async def compile_wsdl_source( return CompiledWsdlSource(normalized_digest) -@rule -async def generate_jaxws_lockfile_request( - _: JaxWsToolsLockfileSentinel, jaxws: JaxWsTools -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(jaxws) - - def rules(): return [ *collect_rules(), @@ -190,7 +179,7 @@ def rules(): *jvm_tool.rules(), *jdk_rules.rules(), UnionRule(GenerateSourcesRequest, GenerateJavaFromWsdlRequest), - UnionRule(GenerateToolLockfileSentinel, JaxWsToolsLockfileSentinel), + UnionRule(ExportableTool, JaxWsTools), WsdlSourceTarget.register_plugin_field(PrefixedJvmJdkField), WsdlSourcesGeneratorTarget.register_plugin_field(PrefixedJvmJdkField), WsdlSourceTarget.register_plugin_field(PrefixedJvmResolveField), diff --git a/src/python/pants/backend/codegen/thrift/scrooge/rules.py b/src/python/pants/backend/codegen/thrift/scrooge/rules.py index 6ecd393d586..ef9ba32275e 100644 --- a/src/python/pants/backend/codegen/thrift/scrooge/rules.py +++ b/src/python/pants/backend/codegen/thrift/scrooge/rules.py @@ -10,7 +10,7 @@ ThriftSourcesGeneratorTarget, ThriftSourceTarget, ) -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest from pants.engine.fs import CreateDigest, Digest, Directory, MergeDigests, RemovePrefix, Snapshot from pants.engine.internals.selectors import Get, MultiGet @@ -26,7 +26,7 @@ from pants.jvm.goals import lockfile from pants.jvm.jdk_rules import InternalJdk, JvmProcess from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.jvm.target_types import PrefixedJvmJdkField, PrefixedJvmResolveField from pants.source.source_root import SourceRootsRequest, SourceRootsResult from pants.util.logging import LogLevel @@ -44,10 +44,6 @@ class GeneratedScroogeThriftSources: snapshot: Snapshot -class ScroogeToolLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = ScroogeSubsystem.options_scope - - @rule async def generate_scrooge_thrift_sources( request: GenerateScroogeThriftSourcesRequest, @@ -57,7 +53,7 @@ async def generate_scrooge_thrift_sources( output_dir = "_generated_files" toolcp_relpath = "__toolcp" - lockfile_request = await Get(GenerateJvmLockfileFromTool, ScroogeToolLockfileSentinel()) + lockfile_request = GenerateJvmLockfileFromTool.create(scrooge) tool_classpath, transitive_targets, empty_output_dir_digest, wrapped_target = await MultiGet( Get(ToolClasspath, ToolClasspathRequest(lockfile=lockfile_request)), Get(TransitiveTargets, TransitiveTargetsRequest([request.thrift_source_field.address])), @@ -141,19 +137,12 @@ async def generate_scrooge_thrift_sources( return GeneratedScroogeThriftSources(output_snapshot) -@rule -def generate_scrooge_lockfile_request( - _: ScroogeToolLockfileSentinel, scrooge: ScroogeSubsystem -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(scrooge) - - def rules(): return [ *collect_rules(), *additional_fields.rules(), *lockfile.rules(), - UnionRule(GenerateToolLockfileSentinel, ScroogeToolLockfileSentinel), + UnionRule(ExportableTool, ScroogeSubsystem), ThriftSourceTarget.register_plugin_field(PrefixedJvmJdkField), ThriftSourcesGeneratorTarget.register_plugin_field(PrefixedJvmJdkField), ThriftSourceTarget.register_plugin_field(PrefixedJvmResolveField), diff --git a/src/python/pants/backend/java/lint/google_java_format/rules.py b/src/python/pants/backend/java/lint/google_java_format/rules.py index 71f7ee329af..021721f7a17 100644 --- a/src/python/pants/backend/java/lint/google_java_format/rules.py +++ b/src/python/pants/backend/java/lint/google_java_format/rules.py @@ -7,7 +7,7 @@ from pants.backend.java.lint.google_java_format.subsystem import GoogleJavaFormatSubsystem from pants.backend.java.target_types import JavaSourceField from pants.core.goals.fmt import FmtResult, FmtTargetsRequest -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.partitions import PartitionerType from pants.engine.internals.selectors import Get from pants.engine.process import ProcessResult @@ -17,7 +17,7 @@ from pants.jvm.jdk_rules import InternalJdk, JvmProcess from pants.jvm.resolve import jvm_tool from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.util.logging import LogLevel from pants.util.strutil import pluralize @@ -41,19 +41,13 @@ class GoogleJavaFormatRequest(FmtTargetsRequest): partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION -class GoogleJavaFormatToolLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = GoogleJavaFormatSubsystem.options_scope - - @rule(desc="Format with Google Java Format", level=LogLevel.DEBUG) async def google_java_format_fmt( request: GoogleJavaFormatRequest.Batch, tool: GoogleJavaFormatSubsystem, jdk: InternalJdk, ) -> FmtResult: - lockfile_request = await Get( - GenerateJvmLockfileFromTool, GoogleJavaFormatToolLockfileSentinel() - ) + lockfile_request = GenerateJvmLockfileFromTool.create(tool) tool_classpath = await Get(ToolClasspath, ToolClasspathRequest(lockfile=lockfile_request)) toolcp_relpath = "__toolcp" @@ -97,17 +91,10 @@ async def google_java_format_fmt( return await FmtResult.create(request, result) -@rule -def generate_google_java_format_lockfile_request( - _: GoogleJavaFormatToolLockfileSentinel, tool: GoogleJavaFormatSubsystem -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(tool) - - def rules(): return [ *collect_rules(), *jvm_tool.rules(), *GoogleJavaFormatRequest.rules(), - UnionRule(GenerateToolLockfileSentinel, GoogleJavaFormatToolLockfileSentinel), + UnionRule(ExportableTool, GoogleJavaFormatSubsystem), ] diff --git a/src/python/pants/backend/kotlin/lint/ktlint/rules.py b/src/python/pants/backend/kotlin/lint/ktlint/rules.py index 8b001a84736..ecda147b49b 100644 --- a/src/python/pants/backend/kotlin/lint/ktlint/rules.py +++ b/src/python/pants/backend/kotlin/lint/ktlint/rules.py @@ -7,7 +7,7 @@ from pants.backend.kotlin.lint.ktlint.subsystem import KtlintSubsystem from pants.backend.kotlin.target_types import KotlinSourceField from pants.core.goals.fmt import FmtResult, FmtTargetsRequest -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.partitions import PartitionerType from pants.engine.internals.selectors import Get from pants.engine.process import ProcessResult @@ -17,7 +17,7 @@ from pants.jvm.jdk_rules import InternalJdk, JvmProcess from pants.jvm.resolve import jvm_tool from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.util.logging import LogLevel from pants.util.strutil import pluralize @@ -41,15 +41,11 @@ class KtlintRequest(FmtTargetsRequest): partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION -class KtlintToolLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = KtlintSubsystem.options_scope - - @rule(desc="Format with Ktlint", level=LogLevel.DEBUG) async def ktlint_fmt( request: KtlintRequest.Batch, tool: KtlintSubsystem, jdk: InternalJdk ) -> FmtResult: - lockfile_request = await Get(GenerateJvmLockfileFromTool, KtlintToolLockfileSentinel()) + lockfile_request = GenerateJvmLockfileFromTool.create(tool) tool_classpath = await Get(ToolClasspath, ToolClasspathRequest(lockfile=lockfile_request)) toolcp_relpath = "__toolcp" @@ -82,17 +78,10 @@ async def ktlint_fmt( return await FmtResult.create(request, result) -@rule -def generate_ktlint_lockfile_request( - _: KtlintToolLockfileSentinel, tool: KtlintSubsystem -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(tool) - - def rules(): return [ *collect_rules(), *jvm_tool.rules(), *KtlintRequest.rules(), - UnionRule(GenerateToolLockfileSentinel, KtlintToolLockfileSentinel), + UnionRule(ExportableTool, KtlintSubsystem), ] diff --git a/src/python/pants/backend/scala/lint/scalafix/rules.py b/src/python/pants/backend/scala/lint/scalafix/rules.py index 99fae3d4d7a..7d46e0dfc82 100644 --- a/src/python/pants/backend/scala/lint/scalafix/rules.py +++ b/src/python/pants/backend/scala/lint/scalafix/rules.py @@ -15,8 +15,8 @@ from pants.backend.scala.target_types import ScalaSourceField from pants.backend.scala.util_rules.versions import ScalaVersion from pants.core.goals.fix import FixResult, FixTargetsRequest, Partitions -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel from pants.core.goals.lint import LintResult, LintTargetsRequest +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ( GatherConfigFilesByDirectoriesRequest, GatheredConfigFilesByDirectories, @@ -35,7 +35,7 @@ from pants.jvm.goals import lockfile from pants.jvm.jdk_rules import InternalJdk, JvmProcess from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.jvm.resolve.key import CoursierResolveKey from pants.jvm.subsystems import JvmSubsystem from pants.jvm.target_types import JvmResolveField @@ -90,10 +90,6 @@ def description(self) -> str: return self.config_snapshot.files[0] -class ScalafixToolLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = ScalafixSubsystem.options_scope - - @dataclass(frozen=True) class _MaybeClasspath: classpath: Classpath | None @@ -149,7 +145,7 @@ async def _partition_scalafix( filepaths = tuple(field_set.source.file_path for field_set in request.field_sets) classpath_by_filepath = dict(zip(filepaths, classpaths)) - lockfile_request = await Get(GenerateJvmLockfileFromTool, ScalafixToolLockfileSentinel()) + lockfile_request = GenerateJvmLockfileFromTool.create(scalafix) tool_runtime_classpath, config_files = await MultiGet( Get(ToolClasspath, ToolClasspathRequest(lockfile=lockfile_request)), Get( @@ -378,18 +374,11 @@ async def scalafix_lint(request: ScalafixLintRequest.Batch) -> LintResult: return LintResult.create(request, process_result) -@rule -async def generate_scalafix_lockfile_request( - _: ScalafixToolLockfileSentinel, tool: ScalafixSubsystem -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(tool) - - def rules(): return [ *collect_rules(), *lockfile.rules(), *ScalafixFixRequest.rules(), *ScalafixLintRequest.rules(), - UnionRule(GenerateToolLockfileSentinel, ScalafixToolLockfileSentinel), + UnionRule(ExportableTool, ScalafixSubsystem), ] diff --git a/src/python/pants/backend/scala/lint/scalafmt/rules.py b/src/python/pants/backend/scala/lint/scalafmt/rules.py index 76d0d75a8b8..bd7f3ab2f32 100644 --- a/src/python/pants/backend/scala/lint/scalafmt/rules.py +++ b/src/python/pants/backend/scala/lint/scalafmt/rules.py @@ -11,7 +11,7 @@ from pants.backend.scala.lint.scalafmt.subsystem import ScalafmtSubsystem from pants.backend.scala.target_types import ScalaSourceField from pants.core.goals.fmt import FmtResult, FmtTargetsRequest, Partitions -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ( GatherConfigFilesByDirectoriesRequest, GatheredConfigFilesByDirectories, @@ -26,7 +26,7 @@ from pants.jvm.goals import lockfile from pants.jvm.jdk_rules import InternalJdk, JvmProcess from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.util.dirutil import group_by_dir from pants.util.frozendict import FrozenDict from pants.util.logging import LogLevel @@ -49,10 +49,6 @@ class ScalafmtRequest(FmtTargetsRequest): tool_subsystem = ScalafmtSubsystem -class ScalafmtToolLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = ScalafmtSubsystem.options_scope - - @dataclass(frozen=True) class GatherScalafmtConfigFilesRequest: filepaths: tuple[str, ...] @@ -85,7 +81,7 @@ async def partition_scalafmt( toolcp_relpath = "__toolcp" filepaths = tuple(field_set.source.file_path for field_set in request.field_sets) - lockfile_request = await Get(GenerateJvmLockfileFromTool, ScalafmtToolLockfileSentinel()) + lockfile_request = GenerateJvmLockfileFromTool.create(tool) tool_classpath, config_files = await MultiGet( Get(ToolClasspath, ToolClasspathRequest(lockfile=lockfile_request)), Get( @@ -166,17 +162,10 @@ async def scalafmt_fmt( return await FmtResult.create(request, result) -@rule -def generate_scalafmt_lockfile_request( - _: ScalafmtToolLockfileSentinel, tool: ScalafmtSubsystem -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(tool) - - def rules(): return [ *collect_rules(), *lockfile.rules(), *ScalafmtRequest.rules(), - UnionRule(GenerateToolLockfileSentinel, ScalafmtToolLockfileSentinel), + UnionRule(ExportableTool, ScalafmtSubsystem), ] diff --git a/src/python/pants/backend/scala/test/scalatest.py b/src/python/pants/backend/scala/test/scalatest.py index 3f06663376b..2893d512850 100644 --- a/src/python/pants/backend/scala/test/scalatest.py +++ b/src/python/pants/backend/scala/test/scalatest.py @@ -12,7 +12,7 @@ ScalatestTestSourceField, ScalatestTestTimeoutField, ) -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel +from pants.core.goals.resolves import ExportableTool from pants.core.goals.test import ( TestDebugRequest, TestExtraEnv, @@ -40,7 +40,7 @@ from pants.jvm.goals import lockfile from pants.jvm.jdk_rules import JdkEnvironment, JdkRequest, JvmProcess from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.jvm.subsystems import JvmSubsystem from pants.jvm.target_types import JvmDependenciesField, JvmJdkField from pants.util.logging import LogLevel @@ -68,10 +68,6 @@ class ScalatestTestRequest(TestRequest): supports_debug = True -class ScalatestToolLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = Scalatest.options_scope - - @dataclass(frozen=True) class TestSetupRequest: field_set: ScalatestTestFieldSet @@ -97,7 +93,7 @@ async def setup_scalatest_for_target( Get(TransitiveTargets, TransitiveTargetsRequest([request.field_set.address])), ) - lockfile_request = await Get(GenerateJvmLockfileFromTool, ScalatestToolLockfileSentinel()) + lockfile_request = GenerateJvmLockfileFromTool.create(scalatest) classpath, scalatest_classpath, files = await MultiGet( Get(Classpath, Addresses([request.field_set.address])), Get(ToolClasspath, ToolClasspathRequest(lockfile=lockfile_request)), @@ -208,17 +204,10 @@ async def setup_scalatest_debug_request( ) -@rule -def generate_scalatest_lockfile_request( - _: ScalatestToolLockfileSentinel, scalatest: Scalatest -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(scalatest) - - def rules(): return [ *collect_rules(), *lockfile.rules(), - UnionRule(GenerateToolLockfileSentinel, ScalatestToolLockfileSentinel), + UnionRule(ExportableTool, Scalatest), *ScalatestTestRequest.rules(), ] diff --git a/src/python/pants/jvm/test/junit.py b/src/python/pants/jvm/test/junit.py index 09d67d39c16..a305dd8d1b6 100644 --- a/src/python/pants/jvm/test/junit.py +++ b/src/python/pants/jvm/test/junit.py @@ -8,7 +8,7 @@ from typing import Any from pants.backend.java.subsystems.junit import JUnit -from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel +from pants.core.goals.resolves import ExportableTool from pants.core.goals.test import ( TestDebugRequest, TestExtraEnv, @@ -37,7 +37,7 @@ from pants.jvm.goals import lockfile from pants.jvm.jdk_rules import JdkEnvironment, JdkRequest, JvmProcess from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest -from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, GenerateJvmToolLockfileSentinel +from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool from pants.jvm.subsystems import JvmSubsystem from pants.jvm.target_types import ( JunitTestSourceField, @@ -70,10 +70,6 @@ class JunitTestRequest(TestRequest): supports_debug = True -class JunitToolLockfileSentinel(GenerateJvmToolLockfileSentinel): - resolve_name = JUnit.options_scope - - @dataclass(frozen=True) class TestSetupRequest: field_set: JunitTestFieldSet @@ -99,7 +95,7 @@ async def setup_junit_for_target( Get(TransitiveTargets, TransitiveTargetsRequest([request.field_set.address])), ) - lockfile_request = await Get(GenerateJvmLockfileFromTool, JunitToolLockfileSentinel()) + lockfile_request = GenerateJvmLockfileFromTool.create(junit) classpath, junit_classpath, files = await MultiGet( Get(Classpath, Addresses([request.field_set.address])), Get(ToolClasspath, ToolClasspathRequest(lockfile=lockfile_request)), @@ -207,17 +203,10 @@ async def setup_junit_debug_request( ) -@rule -def generate_junit_lockfile_request( - _: JunitToolLockfileSentinel, junit: JUnit -) -> GenerateJvmLockfileFromTool: - return GenerateJvmLockfileFromTool.create(junit) - - def rules(): return [ *collect_rules(), *lockfile.rules(), - UnionRule(GenerateToolLockfileSentinel, JunitToolLockfileSentinel), + UnionRule(ExportableTool, JUnit), *JunitTestRequest.rules(), ] From 20f39242575e561a3b7a3be96e5d5268846b3055 Mon Sep 17 00:00:00 2001 From: Daniel Goldman Date: Wed, 22 May 2024 18:37:48 -0400 Subject: [PATCH 15/18] Mark all Python tools as exportable (#20787) Following to #20730 , this MR flags all Python tools as exportable. It omits some internal tools (for example, parsers for dependency inference). I think there's a risk that someone tries to export a Python-based tool without activating the Python backend. I also want to look into a few instances where some lockfile rules are registered and see if those registrations are still necessary. --------- Co-authored-by: Huon Wilson --- docs/docs/python/overview/lockfiles.mdx | 5 +---- docs/docs/using-pants/setting-up-an-ide.mdx | 2 +- docs/notes/2.22.x.md | 2 ++ src/python/pants/backend/cc/lint/clangformat/subsystem.py | 6 +++++- src/python/pants/backend/python/goals/coverage_py.py | 2 ++ src/python/pants/backend/python/goals/export_test.py | 5 ----- .../backend/python/lint/add_trailing_comma/subsystem.py | 7 ++++++- .../pants/backend/python/lint/autoflake/subsystem.py | 7 ++++++- src/python/pants/backend/python/lint/bandit/subsystem.py | 3 +++ src/python/pants/backend/python/lint/black/subsystem.py | 7 ++++++- .../pants/backend/python/lint/docformatter/subsystem.py | 7 ++++++- src/python/pants/backend/python/lint/flake8/subsystem.py | 5 +++-- src/python/pants/backend/python/lint/isort/subsystem.py | 3 +++ .../pants/backend/python/lint/pydocstyle/subsystem.py | 3 +++ src/python/pants/backend/python/lint/pylint/subsystem.py | 5 +++-- .../pants/backend/python/lint/pyupgrade/subsystem.py | 7 ++++++- src/python/pants/backend/python/lint/ruff/subsystem.py | 3 +++ src/python/pants/backend/python/lint/yapf/subsystem.py | 7 ++++++- .../pants/backend/python/packaging/pyoxidizer/subsystem.py | 7 ++++++- src/python/pants/backend/python/subsystems/ipython.py | 5 +++-- src/python/pants/backend/python/subsystems/pytest.py | 5 +++-- .../pants/backend/python/subsystems/setuptools_scm.py | 7 ++++++- src/python/pants/backend/python/subsystems/twine.py | 7 ++++++- .../pants/backend/python/typecheck/mypy/subsystem.py | 5 +++-- src/python/pants/backend/python/typecheck/pytype/rules.py | 4 ++-- src/python/pants/backend/tools/semgrep/subsystem.py | 6 +++++- src/python/pants/backend/tools/yamllint/subsystem.py | 6 +++++- 27 files changed, 104 insertions(+), 34 deletions(-) diff --git a/docs/docs/python/overview/lockfiles.mdx b/docs/docs/python/overview/lockfiles.mdx index 231b3354245..81d642fa2c1 100644 --- a/docs/docs/python/overview/lockfiles.mdx +++ b/docs/docs/python/overview/lockfiles.mdx @@ -184,10 +184,7 @@ It is strongly recommended that these tools be installed from a hermetic lockfil The only time you need to think about this is if you want to customize the tool requirements that Pants uses. This might be the case if you want to modify the version of a tool or add extra requirements (for example, tool plugins). -:::caution Exporting tools requires a custom lockfile -::: - -If you want a tool to be installed from some resolve, instead of from the built-in lockfile, you set `install_from_resolve` and `requirements` on the tool's config section: +Tools can also be installed from a specific resolve instead of from the built-in lockfile. This is useful for specifying a version of the tool and including extra packages. To do this, set `install_from_resolve` and `requirements` on the tool's config section: ```toml title="pants.toml" [python.resolves] diff --git a/docs/docs/using-pants/setting-up-an-ide.mdx b/docs/docs/using-pants/setting-up-an-ide.mdx index 24bf05afeb6..3239835db09 100644 --- a/docs/docs/using-pants/setting-up-an-ide.mdx +++ b/docs/docs/using-pants/setting-up-an-ide.mdx @@ -49,7 +49,7 @@ The `--py-resolve-format=symlinked_immutable_virtualenv` option symlinks to an i ### Tool virtualenvs -`pants export` can also create a virtualenv for each of the Python tools you use via Pants, such as `black`, `isort`, `pytest`, `mypy`, `flake8` and so on. This allows you to configure your editor to use the same version of the tool as Pants does for workflows like formatting on save. Follow [the instructions for creating a tool lockfile](../python/overview/lockfiles#lockfiles-for-tools). +`pants export` can also create a virtualenv for each of the Python tools you use via Pants, such as `black`, `isort`, `pytest`, `mypy`, `flake8` and so on. This allows you to configure your editor to use the same version of the tool as Pants does for workflows like formatting on save. To use a custom version of these tools, follow [the instructions for creating a tool lockfile](../python/overview/lockfiles#lockfiles-for-tools). ## Generated code diff --git a/docs/notes/2.22.x.md b/docs/notes/2.22.x.md index 28488cb6de3..7d0295e7467 100644 --- a/docs/notes/2.22.x.md +++ b/docs/notes/2.22.x.md @@ -75,6 +75,8 @@ The deprecation for the `platforms` field for the `pex_binary` and `pex_binaries Python tool subsystem docs and help text now include the default version of the tool, along with instructions on how to override this version using a custom lockfile. Additionally, the help text for the `install_from_resolve` option for Python tools now includes this same information. +Python tools can be [exported from their default bundled lockfiles](https://www.pantsbuild.org/2.22/docs/using-pants/setting-up-an-ide#tool-virtualenvs). For instance, when using the default `black` subsystem, `pants export --resolve=black` will export a venv containing the version of black that Pants runs. + #### Semgrep The default version of `semgrep` used by the `pants.backends.experimental.tool.semgrep` backend is now version 1.72.0, upgraded from 1.46.0. This version requires Python 3.8 or greater. diff --git a/src/python/pants/backend/cc/lint/clangformat/subsystem.py b/src/python/pants/backend/cc/lint/clangformat/subsystem.py index aa1a8ff72bc..a27ee89e260 100644 --- a/src/python/pants/backend/cc/lint/clangformat/subsystem.py +++ b/src/python/pants/backend/cc/lint/clangformat/subsystem.py @@ -8,6 +8,7 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFilesRequest from pants.engine.rules import Rule, collect_rules from pants.engine.unions import UnionRule @@ -51,4 +52,7 @@ def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: def rules() -> Iterable[Rule | UnionRule]: - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, ClangFormat), + ] diff --git a/src/python/pants/backend/python/goals/coverage_py.py b/src/python/pants/backend/python/goals/coverage_py.py index e48e0a354ad..587c5bdd264 100644 --- a/src/python/pants/backend/python/goals/coverage_py.py +++ b/src/python/pants/backend/python/goals/coverage_py.py @@ -19,6 +19,7 @@ PythonSourceFiles, PythonSourceFilesRequest, ) +from pants.core.goals.resolves import ExportableTool from pants.core.goals.test import ( ConsoleCoverageReport, CoverageData, @@ -621,4 +622,5 @@ def rules(): return [ *collect_rules(), UnionRule(CoverageDataCollection, PytestCoverageDataCollection), + UnionRule(ExportableTool, CoverageSubsystem), ] diff --git a/src/python/pants/backend/python/goals/export_test.py b/src/python/pants/backend/python/goals/export_test.py index 43a425ffc7c..0c1d8d6b0c6 100644 --- a/src/python/pants/backend/python/goals/export_test.py +++ b/src/python/pants/backend/python/goals/export_test.py @@ -20,12 +20,10 @@ from pants.backend.python.util_rules import local_dists_pep660, pex_from_targets from pants.base.specs import RawSpecs from pants.core.goals.export import ExportResults -from pants.core.goals.resolves import ExportableTool from pants.core.util_rules import distdir from pants.engine.internals.parametrize import Parametrize from pants.engine.rules import QueryRule from pants.engine.target import Targets -from pants.engine.unions import UnionRule from pants.testutil.rule_runner import RuleRunner from pants.util.frozendict import FrozenDict @@ -48,9 +46,6 @@ def rule_runner() -> RuleRunner: *distdir.rules(), *local_dists_pep660.rules(), *isort_subsystem.rules(), # add a tool that we can try exporting - UnionRule( - ExportableTool, isort_subsystem.Isort - ), # TODO: remove this manual export when we add ExportableTool to tools QueryRule(Targets, [RawSpecs]), QueryRule(ExportResults, [ExportVenvsRequest]), ], diff --git a/src/python/pants/backend/python/lint/add_trailing_comma/subsystem.py b/src/python/pants/backend/python/lint/add_trailing_comma/subsystem.py index d44b8ba99b7..befcbbf4c2c 100644 --- a/src/python/pants/backend/python/lint/add_trailing_comma/subsystem.py +++ b/src/python/pants/backend/python/lint/add_trailing_comma/subsystem.py @@ -5,7 +5,9 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, SkipOption @@ -29,4 +31,7 @@ class AddTrailingComma(PythonToolBase): def rules(): - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, AddTrailingComma), + ] diff --git a/src/python/pants/backend/python/lint/autoflake/subsystem.py b/src/python/pants/backend/python/lint/autoflake/subsystem.py index 0d583e96fc1..df05c2732c9 100644 --- a/src/python/pants/backend/python/lint/autoflake/subsystem.py +++ b/src/python/pants/backend/python/lint/autoflake/subsystem.py @@ -5,7 +5,9 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, SkipOption @@ -32,4 +34,7 @@ class Autoflake(PythonToolBase): def rules(): - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, Autoflake), + ] diff --git a/src/python/pants/backend/python/lint/bandit/subsystem.py b/src/python/pants/backend/python/lint/bandit/subsystem.py index 0274f1f69d1..303e77e56cf 100644 --- a/src/python/pants/backend/python/lint/bandit/subsystem.py +++ b/src/python/pants/backend/python/lint/bandit/subsystem.py @@ -13,9 +13,11 @@ InterpreterConstraintsField, PythonSourceField, ) +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFilesRequest from pants.engine.rules import collect_rules from pants.engine.target import FieldSet, Target +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, FileOption, SkipOption @@ -72,4 +74,5 @@ def rules(): return ( *collect_rules(), *lockfile.rules(), + UnionRule(ExportableTool, Bandit), ) diff --git a/src/python/pants/backend/python/lint/black/subsystem.py b/src/python/pants/backend/python/lint/black/subsystem.py index 5d9244eabce..5560c5eb994 100644 --- a/src/python/pants/backend/python/lint/black/subsystem.py +++ b/src/python/pants/backend/python/lint/black/subsystem.py @@ -14,9 +14,11 @@ InterpreterConstraintsField, PythonSourceField, ) +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFilesRequest from pants.engine.rules import collect_rules from pants.engine.target import FieldSet, Target +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, BoolOption, FileOption, SkipOption from pants.util.strutil import softwrap @@ -89,4 +91,7 @@ def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: def rules(): - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, Black), + ] diff --git a/src/python/pants/backend/python/lint/docformatter/subsystem.py b/src/python/pants/backend/python/lint/docformatter/subsystem.py index 2b7896398f6..bbdb1cbf44a 100644 --- a/src/python/pants/backend/python/lint/docformatter/subsystem.py +++ b/src/python/pants/backend/python/lint/docformatter/subsystem.py @@ -4,7 +4,9 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, SkipOption @@ -25,4 +27,7 @@ class Docformatter(PythonToolBase): def rules(): - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, Docformatter), + ] diff --git a/src/python/pants/backend/python/lint/flake8/subsystem.py b/src/python/pants/backend/python/lint/flake8/subsystem.py index 1edd7587a2a..84207264ec0 100644 --- a/src/python/pants/backend/python/lint/flake8/subsystem.py +++ b/src/python/pants/backend/python/lint/flake8/subsystem.py @@ -5,7 +5,6 @@ from dataclasses import dataclass -from pants.backend.python.goals import lockfile from pants.backend.python.lint.flake8.skip_field import SkipFlake8Field from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ( @@ -20,12 +19,14 @@ PythonSourceFilesRequest, StrippedPythonSourceFiles, ) +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFilesRequest from pants.engine.addresses import Addresses, UnparsedAddressInputs from pants.engine.fs import AddPrefix, Digest from pants.engine.internals.native_engine import EMPTY_DIGEST from pants.engine.rules import Get, collect_rules, rule from pants.engine.target import FieldSet, Target, TransitiveTargets, TransitiveTargetsRequest +from pants.engine.unions import UnionRule from pants.option.option_types import ( ArgsListOption, BoolOption, @@ -211,6 +212,6 @@ async def flake8_first_party_plugins(flake8: Flake8) -> Flake8FirstPartyPlugins: def rules(): return ( *collect_rules(), - *lockfile.rules(), *python_sources.rules(), + UnionRule(ExportableTool, Flake8), ) diff --git a/src/python/pants/backend/python/lint/isort/subsystem.py b/src/python/pants/backend/python/lint/isort/subsystem.py index b0970fcf010..412e410a43e 100644 --- a/src/python/pants/backend/python/lint/isort/subsystem.py +++ b/src/python/pants/backend/python/lint/isort/subsystem.py @@ -8,8 +8,10 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFilesRequest from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, BoolOption, FileListOption, SkipOption from pants.util.strutil import softwrap @@ -93,4 +95,5 @@ def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: def rules(): return [ *collect_rules(), + UnionRule(ExportableTool, Isort), ] diff --git a/src/python/pants/backend/python/lint/pydocstyle/subsystem.py b/src/python/pants/backend/python/lint/pydocstyle/subsystem.py index 15721d2f231..f63cd5eca94 100644 --- a/src/python/pants/backend/python/lint/pydocstyle/subsystem.py +++ b/src/python/pants/backend/python/lint/pydocstyle/subsystem.py @@ -9,9 +9,11 @@ from pants.backend.python.lint.pydocstyle.skip_field import SkipPydocstyleField from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript, PythonSourceField +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFilesRequest from pants.engine.rules import collect_rules from pants.engine.target import FieldSet, Target +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, BoolOption, FileOption, SkipOption from pants.util.docutil import bin_name from pants.util.strutil import softwrap @@ -91,4 +93,5 @@ def rules(): return ( *collect_rules(), *lockfile.rules(), + UnionRule(ExportableTool, Pydocstyle), ) diff --git a/src/python/pants/backend/python/lint/pylint/subsystem.py b/src/python/pants/backend/python/lint/pylint/subsystem.py index a98a3eee1db..37add5b21ac 100644 --- a/src/python/pants/backend/python/lint/pylint/subsystem.py +++ b/src/python/pants/backend/python/lint/pylint/subsystem.py @@ -7,7 +7,6 @@ from dataclasses import dataclass from typing import Iterable -from pants.backend.python.goals import lockfile from pants.backend.python.lint.pylint.skip_field import SkipPylintField from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ( @@ -22,11 +21,13 @@ PythonSourceFilesRequest, StrippedPythonSourceFiles, ) +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFilesRequest from pants.engine.addresses import Addresses, UnparsedAddressInputs from pants.engine.fs import EMPTY_DIGEST, AddPrefix, Digest from pants.engine.rules import Get, collect_rules, rule from pants.engine.target import FieldSet, Target, TransitiveTargets, TransitiveTargetsRequest +from pants.engine.unions import UnionRule from pants.option.option_types import ( ArgsListOption, BoolOption, @@ -202,5 +203,5 @@ async def pylint_first_party_plugins(pylint: Pylint) -> PylintFirstPartyPlugins: def rules(): return ( *collect_rules(), - *lockfile.rules(), + UnionRule(ExportableTool, Pylint), ) diff --git a/src/python/pants/backend/python/lint/pyupgrade/subsystem.py b/src/python/pants/backend/python/lint/pyupgrade/subsystem.py index 21ce87c473a..8e2188b185a 100644 --- a/src/python/pants/backend/python/lint/pyupgrade/subsystem.py +++ b/src/python/pants/backend/python/lint/pyupgrade/subsystem.py @@ -5,7 +5,9 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, SkipOption @@ -28,4 +30,7 @@ class PyUpgrade(PythonToolBase): def rules(): - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, PyUpgrade), + ] diff --git a/src/python/pants/backend/python/lint/ruff/subsystem.py b/src/python/pants/backend/python/lint/ruff/subsystem.py index cbe2cbe4337..fe769fbebce 100644 --- a/src/python/pants/backend/python/lint/ruff/subsystem.py +++ b/src/python/pants/backend/python/lint/ruff/subsystem.py @@ -10,8 +10,10 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript from pants.backend.python.util_rules import python_sources +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFilesRequest from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, BoolOption, FileOption, SkipOption from pants.util.strutil import softwrap @@ -86,4 +88,5 @@ def rules(): return ( *collect_rules(), *python_sources.rules(), + UnionRule(ExportableTool, Ruff), ) diff --git a/src/python/pants/backend/python/lint/yapf/subsystem.py b/src/python/pants/backend/python/lint/yapf/subsystem.py index 7dcce2bed7a..949f42b3a89 100644 --- a/src/python/pants/backend/python/lint/yapf/subsystem.py +++ b/src/python/pants/backend/python/lint/yapf/subsystem.py @@ -8,8 +8,10 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFilesRequest from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, BoolOption, FileOption, SkipOption from pants.util.strutil import softwrap @@ -88,4 +90,7 @@ def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: def rules(): - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, Yapf), + ] diff --git a/src/python/pants/backend/python/packaging/pyoxidizer/subsystem.py b/src/python/pants/backend/python/packaging/pyoxidizer/subsystem.py index 88201d6ed84..9f0c60af41f 100644 --- a/src/python/pants/backend/python/packaging/pyoxidizer/subsystem.py +++ b/src/python/pants/backend/python/packaging/pyoxidizer/subsystem.py @@ -3,7 +3,9 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption from pants.util.strutil import help_text @@ -32,4 +34,7 @@ class PyOxidizer(PythonToolBase): def rules(): - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, PyOxidizer), + ] diff --git a/src/python/pants/backend/python/subsystems/ipython.py b/src/python/pants/backend/python/subsystems/ipython.py index bd37a833410..a39f7ce5b98 100644 --- a/src/python/pants/backend/python/subsystems/ipython.py +++ b/src/python/pants/backend/python/subsystems/ipython.py @@ -3,10 +3,11 @@ from __future__ import annotations -from pants.backend.python.goals import lockfile from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule from pants.option.option_types import BoolOption from pants.util.strutil import softwrap @@ -41,5 +42,5 @@ class IPython(PythonToolBase): def rules(): return ( *collect_rules(), - *lockfile.rules(), + UnionRule(ExportableTool, IPython), ) diff --git a/src/python/pants/backend/python/subsystems/pytest.py b/src/python/pants/backend/python/subsystems/pytest.py index ba8e54ba31d..60f8ecf8df4 100644 --- a/src/python/pants/backend/python/subsystems/pytest.py +++ b/src/python/pants/backend/python/subsystems/pytest.py @@ -7,7 +7,6 @@ from dataclasses import dataclass from typing import Iterable -from pants.backend.python.goals import lockfile from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ( ConsoleScript, @@ -20,11 +19,13 @@ PythonTestsXdistConcurrencyField, SkipPythonTestsField, ) +from pants.core.goals.resolves import ExportableTool from pants.core.goals.test import RuntimePackageDependenciesField, TestFieldSet from pants.core.util_rules.config_files import ConfigFilesRequest from pants.core.util_rules.environments import EnvironmentField from pants.engine.rules import collect_rules from pants.engine.target import Target +from pants.engine.unions import UnionRule from pants.option.option_types import ArgsListOption, BoolOption, FileOption, SkipOption, StrOption from pants.util.strutil import softwrap @@ -155,5 +156,5 @@ def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: def rules(): return ( *collect_rules(), - *lockfile.rules(), + UnionRule(ExportableTool, PyTest), ) diff --git a/src/python/pants/backend/python/subsystems/setuptools_scm.py b/src/python/pants/backend/python/subsystems/setuptools_scm.py index 2d80d9fb571..68a2cdf3129 100644 --- a/src/python/pants/backend/python/subsystems/setuptools_scm.py +++ b/src/python/pants/backend/python/subsystems/setuptools_scm.py @@ -3,7 +3,9 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import EntryPoint +from pants.core.goals.resolves import ExportableTool from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule class SetuptoolsSCM(PythonToolBase): @@ -21,4 +23,7 @@ class SetuptoolsSCM(PythonToolBase): def rules(): - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, SetuptoolsSCM), + ] diff --git a/src/python/pants/backend/python/subsystems/twine.py b/src/python/pants/backend/python/subsystems/twine.py index 04a670012d2..44db1b42fcd 100644 --- a/src/python/pants/backend/python/subsystems/twine.py +++ b/src/python/pants/backend/python/subsystems/twine.py @@ -5,9 +5,11 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFilesRequest from pants.engine.fs import CreateDigest from pants.engine.rules import collect_rules +from pants.engine.unions import UnionRule from pants.option.global_options import ca_certs_path_to_file_content from pants.option.option_types import ArgsListOption, BoolOption, FileOption, SkipOption, StrOption from pants.util.docutil import doc_url @@ -103,4 +105,7 @@ def ca_certs_digest_request(self, default_ca_certs_path: str | None) -> CreateDi def rules(): - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, TwineSubsystem), + ] diff --git a/src/python/pants/backend/python/typecheck/mypy/subsystem.py b/src/python/pants/backend/python/typecheck/mypy/subsystem.py index 56e4ab2db27..dc9a96627a5 100644 --- a/src/python/pants/backend/python/typecheck/mypy/subsystem.py +++ b/src/python/pants/backend/python/typecheck/mypy/subsystem.py @@ -7,7 +7,6 @@ from dataclasses import dataclass from typing import Iterable -from pants.backend.python.goals import lockfile from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.subsystems.setup import PythonSetup from pants.backend.python.target_types import ( @@ -25,11 +24,13 @@ PythonSourceFiles, PythonSourceFilesRequest, ) +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import ConfigFiles, ConfigFilesRequest from pants.engine.addresses import Addresses, UnparsedAddressInputs from pants.engine.fs import EMPTY_DIGEST, Digest, DigestContents, FileContent from pants.engine.rules import Get, collect_rules, rule from pants.engine.target import FieldSet, Target, TransitiveTargets, TransitiveTargetsRequest +from pants.engine.unions import UnionRule from pants.option.option_types import ( ArgsListOption, BoolOption, @@ -266,5 +267,5 @@ async def _mypy_interpreter_constraints( def rules(): return ( *collect_rules(), - *lockfile.rules(), + UnionRule(ExportableTool, MyPy), ) diff --git a/src/python/pants/backend/python/typecheck/pytype/rules.py b/src/python/pants/backend/python/typecheck/pytype/rules.py index 5a27a22770c..55b64313114 100644 --- a/src/python/pants/backend/python/typecheck/pytype/rules.py +++ b/src/python/pants/backend/python/typecheck/pytype/rules.py @@ -7,7 +7,6 @@ from dataclasses import dataclass from typing import Iterable -from pants.backend.python.goals import lockfile from pants.backend.python.subsystems.setup import PythonSetup from pants.backend.python.target_types import ( InterpreterConstraintsField, @@ -31,6 +30,7 @@ from pants.backend.python.util_rules.pex_environment import PexEnvironment from pants.backend.python.util_rules.pex_from_targets import RequirementsPexRequest from pants.core.goals.check import CheckRequest, CheckResult, CheckResults +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules import config_files from pants.core.util_rules.config_files import ConfigFiles, ConfigFilesRequest from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest @@ -220,7 +220,7 @@ def rules() -> Iterable[Rule | UnionRule]: return ( *collect_rules(), *config_files.rules(), - *lockfile.rules(), *pex_from_targets.rules(), UnionRule(CheckRequest, PytypeRequest), + UnionRule(ExportableTool, Pytype), ) diff --git a/src/python/pants/backend/tools/semgrep/subsystem.py b/src/python/pants/backend/tools/semgrep/subsystem.py index f114f0537a3..af73b460304 100644 --- a/src/python/pants/backend/tools/semgrep/subsystem.py +++ b/src/python/pants/backend/tools/semgrep/subsystem.py @@ -8,6 +8,7 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.engine.rules import Rule, collect_rules from pants.engine.target import Dependencies, FieldSet, SingleSourceField, Target from pants.engine.unions import UnionRule @@ -74,4 +75,7 @@ class SemgrepSubsystem(PythonToolBase): def rules() -> Iterable[Rule | UnionRule]: - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, SemgrepSubsystem), + ] diff --git a/src/python/pants/backend/tools/yamllint/subsystem.py b/src/python/pants/backend/tools/yamllint/subsystem.py index ada40a9991a..4bbb6b8f925 100644 --- a/src/python/pants/backend/tools/yamllint/subsystem.py +++ b/src/python/pants/backend/tools/yamllint/subsystem.py @@ -7,6 +7,7 @@ from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.core.goals.resolves import ExportableTool from pants.core.util_rules.config_files import OrphanFilepathConfigBehavior from pants.engine.rules import Rule, collect_rules from pants.engine.unions import UnionRule @@ -73,4 +74,7 @@ class Yamllint(PythonToolBase): def rules() -> Iterable[Rule | UnionRule]: - return collect_rules() + return [ + *collect_rules(), + UnionRule(ExportableTool, Yamllint), + ] From 770e8259ec90a57a33b3403cd906e1b1251945a0 Mon Sep 17 00:00:00 2001 From: Daniel Goldman Date: Wed, 22 May 2024 18:38:16 -0400 Subject: [PATCH 16/18] make `pants help-all` more deterministic (#20764) `pants help-all` is nondeterministic (see #20621 ) . This fixes all sources of nondeterminism I could find: - [x] unsorted name_to_api_type_info - [x] `__name__` used instead of `__qualname__`, resulting in collisions for classes generated by `distinct_union_per_subclass` - [x] generated `OptionField.__qualname__` not mangled - [x] generated `__qualname__.GenerateWrapSourceSourcesRequest` not mangled - [x] EnvironmentAware classes not uniquified - [x] classes provided by multiple backends now list all providers I've also checked: - [x] changing to `__qualname__` hasn't broken docs - [x] using multiple values in `name_to_api_type_info.[].provider` hasn't broken docs - [x] `name_to_api_type_info` uses `__qualname__` consistently --- .../pants/core/util_rules/environments.py | 2 + .../pants/core/util_rules/wrap_source.py | 6 ++ src/python/pants/engine/target.py | 3 +- src/python/pants/help/help_info_extracter.py | 81 ++++++++++++++----- .../pants/help/help_info_extracter_test.py | 46 +++++------ src/python/pants/help/help_printer.py | 2 +- 6 files changed, 95 insertions(+), 45 deletions(-) diff --git a/src/python/pants/core/util_rules/environments.py b/src/python/pants/core/util_rules/environments.py index 796b2870a64..35c2b83e3d0 100644 --- a/src/python/pants/core/util_rules/environments.py +++ b/src/python/pants/core/util_rules/environments.py @@ -1044,6 +1044,8 @@ class OptionField(field_type, _EnvironmentSensitiveOptionFieldMixin): # type: i subsystem = env_aware_t option_name = option.flag_names[0] + setattr(OptionField, "__qualname__", f"{option_type.__qualname__}.{OptionField.__name__}") + return [ LocalEnvironmentTarget.register_plugin_field(OptionField), DockerEnvironmentTarget.register_plugin_field(OptionField), diff --git a/src/python/pants/core/util_rules/wrap_source.py b/src/python/pants/core/util_rules/wrap_source.py index d742754eed6..6b3694789c2 100644 --- a/src/python/pants/core/util_rules/wrap_source.py +++ b/src/python/pants/core/util_rules/wrap_source.py @@ -117,6 +117,12 @@ class GenerateWrapSourceSourcesRequest(GenerateSourcesRequest): input = ActivateWrapSourceTargetField output = source_field_type + setattr( + GenerateWrapSourceSourcesRequest, + "__qualname__", + f"{source_field_type.__qualname__}.{GenerateWrapSourceSourcesRequest.__name__}", + ) + class WrapSourceTarget(Target): alias = f"experimental_wrap_as_{target_name_suffix}" core_fields = ( diff --git a/src/python/pants/engine/target.py b/src/python/pants/engine/target.py index f25af7b1de6..028ddfa5ce1 100644 --- a/src/python/pants/engine/target.py +++ b/src/python/pants/engine/target.py @@ -16,6 +16,7 @@ from collections import deque from dataclasses import dataclass from enum import Enum +from operator import attrgetter from pathlib import PurePath from typing import ( AbstractSet, @@ -459,7 +460,7 @@ def _find_plugin_fields(cls, union_membership: UnionMembership) -> tuple[type[Fi if issubclass(cls, Target): result.update(cast("set[type[Field]]", union_membership.get(cls.PluginField))) - return tuple(result) + return tuple(sorted(result, key=attrgetter("alias"))) @final @classmethod diff --git a/src/python/pants/help/help_info_extracter.py b/src/python/pants/help/help_info_extracter.py index c6bba3737ff..026b6e62503 100644 --- a/src/python/pants/help/help_info_extracter.py +++ b/src/python/pants/help/help_info_extracter.py @@ -6,12 +6,13 @@ import ast import dataclasses import inspect +import itertools import json from collections import defaultdict, namedtuple from dataclasses import dataclass from enum import Enum +from functools import reduce from itertools import chain -from operator import attrgetter from pathlib import Path from typing import ( Any, @@ -322,7 +323,7 @@ class PluginAPITypeInfo: name: str module: str documentation: str | None - provider: str + provider: tuple[str, ...] is_union: bool union_type: str | None union_members: tuple[str, ...] @@ -332,6 +333,14 @@ class PluginAPITypeInfo: consumed_by_rules: tuple[str, ...] used_in_rules: tuple[str, ...] + @property + def fully_qualified_name(self) -> str: + return f"{self.module}.{self.name}" + + @staticmethod + def fully_qualified_name_from_type(t: type): + return f"{t.__module__}.{t.__qualname__}" + @classmethod def create( cls, api_type: type, rules: Sequence[Rule | UnionRule], **kwargs @@ -345,7 +354,7 @@ def create( task_rules = [rule for rule in rules if isinstance(rule, TaskRule)] return cls( - name=api_type.__name__, + name=api_type.__qualname__, module=api_type.__module__, documentation=maybe_cleandoc(api_type.__doc__), is_union=is_union(api_type), @@ -393,6 +402,25 @@ def satisfies(rule: Rule | UnionRule) -> bool: return satisfies + def merged_with(self, that: PluginAPITypeInfo) -> PluginAPITypeInfo: + def merge_tuples(l, r): + return tuple(sorted({*l, *r})) + + return PluginAPITypeInfo( + self.name, + self.module, + self.documentation, + merge_tuples(self.provider, that.provider), + self.is_union, + self.union_type, + merge_tuples(self.union_members, that.union_members), + merge_tuples(self.dependencies, that.dependencies), + merge_tuples(self.dependents, that.dependents), + merge_tuples(self.returned_by_rules, that.returned_by_rules), + merge_tuples(self.consumed_by_rules, that.consumed_by_rules), + merge_tuples(self.used_in_rules, that.used_in_rules), + ) + @dataclass(frozen=True) class BackendHelpInfo: @@ -810,27 +838,40 @@ def _extract_api_types() -> Iterator[tuple[type, str, tuple[type, ...]]]: ), ) - def get_api_type_info_loader(api_type: type) -> Callable[[], PluginAPITypeInfo]: + def get_api_type_info(api_types: tuple[type, ...]): + """Gather the info from each of the types and aggregate it. + + The gathering is the expensive operation, and we can only aggregate once we've gathered. + """ + def load() -> PluginAPITypeInfo: - return PluginAPITypeInfo.create( - api_type, - rules, - provider=", ".join(type_graph[api_type]["providers"]), - dependencies=type_graph[api_type]["dependencies"], - dependents=type_graph[api_type].get("dependents", ()), - union_members=tuple( - sorted(member.__name__ for member in union_membership.get(api_type)) - ), - ) + gatherered_infos = [ + PluginAPITypeInfo.create( + api_type, + rules, + provider=type_graph[api_type]["providers"], + dependencies=type_graph[api_type]["dependencies"], + dependents=type_graph[api_type].get("dependents", ()), + union_members=tuple( + sorted(member.__qualname__ for member in union_membership.get(api_type)) + ), + ) + for api_type in api_types + ] + return reduce(lambda x, y: x.merged_with(y), gatherered_infos) return load - return LazyFrozenDict( - { - f"{api_type.__module__}.{api_type.__name__}": get_api_type_info_loader(api_type) - for api_type in sorted(all_types, key=attrgetter("__name__")) - } - ) + # We want to provide a lazy dict so we don't spend so long doing the info gathering. + # We provide a list of the types here, and the lookup function performs the gather and the aggregation + api_type_name = PluginAPITypeInfo.fully_qualified_name_from_type + all_types_sorted = sorted(all_types, key=api_type_name) + infos: dict[str, Callable[[], PluginAPITypeInfo]] = { + k: get_api_type_info(tuple(v)) + for k, v in itertools.groupby(all_types_sorted, key=api_type_name) + } + + return LazyFrozenDict(infos) @classmethod def get_backend_help_info(cls, options: Options) -> LazyFrozenDict[str, BackendHelpInfo]: diff --git a/src/python/pants/help/help_info_extracter_test.py b/src/python/pants/help/help_info_extracter_test.py index cc6d523b098..ff248b44a8c 100644 --- a/src/python/pants/help/help_info_extracter_test.py +++ b/src/python/pants/help/help_info_extracter_test.py @@ -531,21 +531,19 @@ def fake_consumed_scopes_mapper(scope: str) -> Tuple[str, ...]: }, }, "name_to_api_type_info": { - "pants.help.help_info_extracter_test.Foo": { - "consumed_by_rules": ( - "pants.help.help_info_extracter_test.test_get_all_help_info.rule_info_test", - ), - "dependents": ("help_info_extracter_test",), - "dependencies": ("pants.option.scope",), - "documentation": None, + "pants.option.scope.Scope": { + "consumed_by_rules": (), + "dependents": (), + "dependencies": (), + "documentation": "An options scope.", "is_union": False, - "module": "pants.help.help_info_extracter_test", - "name": "Foo", - "provider": "help_info_extracter_test", - "returned_by_rules": ("construct_scope_foo",), + "module": "pants.option.scope", + "name": "Scope", + "provider": ("pants.option.scope",), + "returned_by_rules": (), "union_members": (), "union_type": None, - "used_in_rules": (), + "used_in_rules": ("construct_scope_foo",), }, "pants.engine.target.Target": { "consumed_by_rules": (), @@ -561,7 +559,7 @@ def fake_consumed_scopes_mapper(scope: str) -> Tuple[str, ...]: "is_union": False, "module": "pants.engine.target", "name": "Target", - "provider": "help_info_extracter_test", + "provider": ("help_info_extracter_test",), "returned_by_rules": ( "pants.help.help_info_extracter_test.test_get_all_help_info.rule_info_test", ), @@ -569,19 +567,21 @@ def fake_consumed_scopes_mapper(scope: str) -> Tuple[str, ...]: "union_type": None, "used_in_rules": (), }, - "pants.option.scope.Scope": { - "consumed_by_rules": (), - "dependents": (), - "dependencies": (), - "documentation": "An options scope.", + "pants.help.help_info_extracter_test.test_get_all_help_info..Foo": { + "consumed_by_rules": ( + "pants.help.help_info_extracter_test.test_get_all_help_info.rule_info_test", + ), + "dependents": ("help_info_extracter_test",), + "dependencies": ("pants.option.scope",), + "documentation": None, "is_union": False, - "module": "pants.option.scope", - "name": "Scope", - "provider": "pants.option.scope", - "returned_by_rules": (), + "module": "pants.help.help_info_extracter_test", + "name": "test_get_all_help_info..Foo", + "provider": ("help_info_extracter_test",), + "returned_by_rules": ("construct_scope_foo",), "union_members": (), "union_type": None, - "used_in_rules": ("construct_scope_foo",), + "used_in_rules": (), }, }, "name_to_backend_help_info": { diff --git a/src/python/pants/help/help_printer.py b/src/python/pants/help/help_printer.py index 4fb59894c74..c2ac85eafd6 100644 --- a/src/python/pants/help/help_printer.py +++ b/src/python/pants/help/help_printer.py @@ -527,7 +527,7 @@ def _print_api_type_help(self, name: str, show_advanced: bool) -> None: print() self._print_table( { - "activated by": type_info.provider, + "activated by": "\n".join(type_info.provider), "union type": type_info.union_type, "union members": "\n".join(type_info.union_members) if type_info.is_union else None, "dependencies": "\n".join(type_info.dependencies) if show_advanced else None, From ce59932643396f01d939728a51045f0ed0b6b3c6 Mon Sep 17 00:00:00 2001 From: Gregory Borodin Date: Thu, 23 May 2024 00:55:21 +0200 Subject: [PATCH 17/18] Add `makeself` backend (#20360) [Makeself](https://github.com/megastep/makeself) is a small shell script that generates a self-extractable compressed tar archive from a directory. This backend provides a `makeself_archive` target which can package a makeself archive. Here is an example: **src/shell/run.sh:** ```bash #!/bin/bash src/shell/hello.sh src/shell/world.sh ``` **src/shell/hello.sh:** ```bash #!/bin/bash printf hello ``` **src/shell/world.sh:** ```bash #!/bin/bash printf world ``` **src/shell/BUILD:** ```python shell_sources() ``` **BUILD:** ```python makeself_archive( name="hello", startup_script="src/shell/run.sh", files=["src/shell/hello.sh", "src/shell/world.sh"], ) ``` Then you can run `pants package :hello` and it will produce an executable `dist/hello.run` (the file extension is recommended by [makeself maintainer](https://github.com/megastep/makeself)). Here is an [example repo](https://github.com/grihabor/pants-playground/tree/9c6411ea7d313de6fe42063b5444991c18a9ee13/makeself). This is a minimal implementation and does not yet support all makeself options. --------- Co-authored-by: Huon Wilson --- docs/docs/shell/self-extractable-archives.mdx | 265 ++++++++ docs/notes/2.22.x.md | 9 + .../pants/backend/experimental/makeself/BUILD | 8 + .../backend/experimental/makeself/__init__.py | 0 .../backend/experimental/makeself/register.py | 25 + src/python/pants/backend/makeself/BUILD | 3 + src/python/pants/backend/makeself/__init__.py | 0 src/python/pants/backend/makeself/goals/BUILD | 7 + .../pants/backend/makeself/goals/__init__.py | 0 .../pants/backend/makeself/goals/package.py | 189 ++++++ .../goals/package_run_integration_test.py | 294 +++++++++ .../pants/backend/makeself/goals/run.py | 105 ++++ .../pants/backend/makeself/subsystem.py | 102 ++++ .../pants/backend/makeself/system_binaries.py | 137 +++++ .../pants/backend/makeself/target_types.py | 123 ++++ src/python/pants/bin/BUILD | 7 +- .../pants/core/util_rules/system_binaries.py | 574 +++++++++++++++--- 17 files changed, 1772 insertions(+), 76 deletions(-) create mode 100644 docs/docs/shell/self-extractable-archives.mdx create mode 100644 src/python/pants/backend/experimental/makeself/BUILD create mode 100644 src/python/pants/backend/experimental/makeself/__init__.py create mode 100644 src/python/pants/backend/experimental/makeself/register.py create mode 100644 src/python/pants/backend/makeself/BUILD create mode 100644 src/python/pants/backend/makeself/__init__.py create mode 100644 src/python/pants/backend/makeself/goals/BUILD create mode 100644 src/python/pants/backend/makeself/goals/__init__.py create mode 100644 src/python/pants/backend/makeself/goals/package.py create mode 100644 src/python/pants/backend/makeself/goals/package_run_integration_test.py create mode 100644 src/python/pants/backend/makeself/goals/run.py create mode 100644 src/python/pants/backend/makeself/subsystem.py create mode 100644 src/python/pants/backend/makeself/system_binaries.py create mode 100644 src/python/pants/backend/makeself/target_types.py diff --git a/docs/docs/shell/self-extractable-archives.mdx b/docs/docs/shell/self-extractable-archives.mdx new file mode 100644 index 00000000000..3bd592a8c1b --- /dev/null +++ b/docs/docs/shell/self-extractable-archives.mdx @@ -0,0 +1,265 @@ +--- + title: Self-extractable archives + sidebar_position: 2 +--- + +Self-extractable archives with [`makeself`](https://github.com/megastep/makeself) + +--- + +Pants integrates with [`makeself`](https://github.com/megastep/makeself) tool +to allow you to easily build self-extractable archives. To enable the +integration activate the `makeself` backend in `pants.toml`: + +```toml title="pants.toml" +[GLOBAL] +backend_packages = [ + ... + "pants.backend.experimental.makeself", +] +``` + +## Minimal example + +The [`makeself_archive`](../../reference/targets/makeself_archive.mdx) target +allows you to bundle files and packages into a single executable archive. + +Here is a minimal example: + +```python title="BUILD" +makeself_archive( + name="arc", + startup_script=["echo", "hello pants"], +) +``` + +To build the archive use the `package` goal: + +```bash +pants package :arc +``` +``` +[INFO] Wrote dist/arc.run +Built Makeself binary: arc.run +``` + +Now run the archive just like a regular executable: + +```bash +dist/arc.run +``` +``` +Verifying archive integrity... 100% MD5 checksums are OK. All good. +Uncompressing arc.run 100% +hello pants +``` + +The built archive supports a bunch of parameters, you can inspect them manually: + +```bash +dist/arc.run --help +``` + +Or refer to the [`makeself`](https://github.com/megastep/makeself) documentation. + +## Bundling multiple files + +You can bundle multiple shell scripts using +[`files`](../../reference/targets/makeself_archive.mdx#files) field: + +```python tab={"label":"BUILD"} +shell_sources(name="src") + +makeself_archive( + name="arc", + files=["lib.sh:src", "entrypoint.sh:src"], + startup_script=["./entrypoint.sh"], +) +``` + +```bash tab={"label":"entrypoint.sh"} +#!/bin/bash + +. lib.sh +echo $@ "one two three" | first_column +``` + +```bash tab={"label":"lib.sh"} +#!/bin/bash + +function first_column { + awk '{print $1}' +} +``` + +Notice that we need to use a relative path to the `./entrypoint.sh`. + +```bash +pants package :arc && dist/arc.run +``` +``` +[INFO] Wrote dist/arc.run +Built Makeself binary: arc.run +Verifying archive integrity... 100% MD5 checksums are OK. All good. +Uncompressing arc.run 100% +one +``` + +To pass the arguments to the `startup_script` use `--`: + +```bash +pants package :arc && dist/arc.run -- zero +``` +``` +[INFO] Wrote dist/arc.run +Built Makeself binary: arc.run +Verifying archive integrity... 100% MD5 checksums are OK. All good. +Uncompressing arc.run 100% +zero +``` + +## `pants run` + +Instead of packaging and running `makeself_archive` manually, you can use the `run` goal instead: + +```bash +pants run :arc +``` +``` +Verifying archive integrity... 100% MD5 checksums are OK. All good. +Uncompressing arc.run 100% +one +``` + +To pass the arguments through the `pants run` goal you need `--`, then you need +another `--` to pass arguments to the archive's `startup_script`, so you end up with +two `--`: + +```bash +pants run :arc -- -- zero +``` +``` +Verifying archive integrity... 100% MD5 checksums are OK. All good. +Uncompressing arc.run 100% +zero +``` + +Similarly you can pass flags to the archive, for example, `quiet` flag to suppress progress messages: +```bash +pants run :arc -- -q -- zero +``` +``` +zero +``` + +## Bundling packages like `pex_binary` + +You can put other packages like `pex_binary` into a makeself archive. + +To configure `pex_binary`, first, update your `pants.toml`: + +```toml title="pants.toml" +backend_packages = [ + ... + "pants.backend.shell", + "pants.backend.experimental.makeself", + "pants.backend.python", +] + +[python] +interpreter_constraints = ["CPython==3.12.*"] +``` + +Now define the `pex_binary` and add it to the makeself archive via the +`packages` field: + +```python tab={"label":"BUILD"} +python_sources(name="py") +pex_binary( + name="upper", + entry_point="upper.py", +) +shell_sources(name="sh") +makeself_archive( + name="arc", + files=["lib.sh:sh", "entrypoint.sh:sh"], + packages=[":upper"], + startup_script=["./entrypoint.sh"], +) +``` + +```python tab={"label":"upper.py"} +print(input().upper()) +``` + +```bash tab={"label":"entrypoint.sh"} +#!/bin/bash + +. lib.sh +echo $@ "one two three" | first_column | ./upper.pex +``` + +```bash tab={"label":"lib.sh"} +#!/bin/bash + +function first_column { + awk '{print $1}' +} +``` + +```bash +pants run :arc -- -q -- zero +``` +``` +/usr/bin/env: ‘python3.12’: No such file or directory +``` + +Oops! This happened because `pants run` is running in isolated environment, so +we have to explicitly tell pants that we want to access the python interpreter +and a couple of binaries used by pex: + +```python title="BUILD" +... +makeself_archive( + name="arc", + files=["lib.sh:sh", "entrypoint.sh:sh"], + packages=[":upper"], + startup_script=["./entrypoint.sh"], + tools=["python3.12", "grep", "sort"], +) +``` + +Now it should work: + +```bash +pants run :arc -- -q -- zero +``` +``` +ZERO +``` + +Yay! + +## Using `makeself` build args + +To control the `makeself` archive creation you can provide `args` field: + +```python title="BUILD" +makeself_archive( + name="arc", + startup_script=["echo", "Done"], + args=["--xz", "--nomd5"], + tools=["xz"], +) +``` +```bash +pants run :arc +``` +``` +Verifying archive integrity... 100% CRC checksums are OK. All good. +Uncompressing arc.run 100% +Done +``` + +See full list of available options in the +[docs](https://github.com/megastep/makeself#usage). diff --git a/docs/notes/2.22.x.md b/docs/notes/2.22.x.md index 7d0295e7467..7701fa222e3 100644 --- a/docs/notes/2.22.x.md +++ b/docs/notes/2.22.x.md @@ -30,6 +30,15 @@ A new experimental `SQL` backend was added along with the [sqlfluff linter](https://www.pantsbuild.org/2.22/reference/subsystems/sqlfluff). See docs [here](https://www.pantsbuild.org/2.22/docs/sql). +#### NEW: Makeself + +A new experimental `pants.backend.experimental.makeself` backend was added to support +[`makeself`](https://github.com/megastep/makeself) tool. A new +[`makeself_archive`](https://www.pantsbuild.org/2.22/reference/targets/makeself_archive) +target allows you to create self-extractable archives that bundle files, bash +scripts or even pex binaries into a single executable archive! See examples in the +[docs](https://www.pantsbuild.org/2.22/docs/shell/self-extractable-archives). + #### Helm The default version of the [hikaru](https://github.com/haxsaw/hikaru) packages used to parse Kubernetes resources has been upgraded. If you were previously installing `hikaru` from a custom resolve you should be aware that the upstream packaging strategy for supporting multiple Kubernetes versions [has changed](https://github.com/haxsaw/hikaru/blob/main/release_notes.rst#v110). diff --git a/src/python/pants/backend/experimental/makeself/BUILD b/src/python/pants/backend/experimental/makeself/BUILD new file mode 100644 index 00000000000..72cc3184d8f --- /dev/null +++ b/src/python/pants/backend/experimental/makeself/BUILD @@ -0,0 +1,8 @@ +# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +python_sources( + sources=[ + "register.py", + ], +) diff --git a/src/python/pants/backend/experimental/makeself/__init__.py b/src/python/pants/backend/experimental/makeself/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/pants/backend/experimental/makeself/register.py b/src/python/pants/backend/experimental/makeself/register.py new file mode 100644 index 00000000000..013104f40ab --- /dev/null +++ b/src/python/pants/backend/experimental/makeself/register.py @@ -0,0 +1,25 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +"""Make self-extractable archives on Unix. + +See https://github.com/megastep/makeself. +""" +from pants.backend.makeself import subsystem +from pants.backend.makeself import system_binaries as makeself_system_binaries +from pants.backend.makeself.goals import package, run +from pants.backend.makeself.target_types import MakeselfArchiveTarget +from pants.core.util_rules import system_binaries + + +def target_types(): + return [MakeselfArchiveTarget] + + +def rules(): + return [ + *subsystem.rules(), + *package.rules(), + *run.rules(), + *system_binaries.rules(), + *makeself_system_binaries.rules(), + ] diff --git a/src/python/pants/backend/makeself/BUILD b/src/python/pants/backend/makeself/BUILD new file mode 100644 index 00000000000..aa06283b0f4 --- /dev/null +++ b/src/python/pants/backend/makeself/BUILD @@ -0,0 +1,3 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +python_sources() diff --git a/src/python/pants/backend/makeself/__init__.py b/src/python/pants/backend/makeself/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/pants/backend/makeself/goals/BUILD b/src/python/pants/backend/makeself/goals/BUILD new file mode 100644 index 00000000000..ae9940b0f92 --- /dev/null +++ b/src/python/pants/backend/makeself/goals/BUILD @@ -0,0 +1,7 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +python_sources() + +python_tests( + name="tests", +) diff --git a/src/python/pants/backend/makeself/goals/__init__.py b/src/python/pants/backend/makeself/goals/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/pants/backend/makeself/goals/package.py b/src/python/pants/backend/makeself/goals/package.py new file mode 100644 index 00000000000..671906667e8 --- /dev/null +++ b/src/python/pants/backend/makeself/goals/package.py @@ -0,0 +1,189 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +import dataclasses +import logging +import os +from dataclasses import dataclass +from pathlib import PurePath +from typing import Optional, Tuple + +from pants.backend.makeself.goals.run import MakeselfArchiveFieldSet +from pants.backend.makeself.subsystem import MakeselfTool +from pants.backend.makeself.system_binaries import MakeselfBinaryShimsRequest +from pants.backend.shell.target_types import ShellSourceField +from pants.core.goals import package +from pants.core.goals.package import ( + BuiltPackage, + BuiltPackageArtifact, + EnvironmentAwarePackageRequest, + PackageFieldSet, +) +from pants.core.target_types import FileSourceField +from pants.core.util_rules import source_files +from pants.core.util_rules.system_binaries import BinaryShims +from pants.engine.addresses import UnparsedAddressInputs +from pants.engine.fs import Digest, MergeDigests +from pants.engine.internals.native_engine import AddPrefix, Snapshot +from pants.engine.process import Process, ProcessCacheScope, ProcessResult +from pants.engine.rules import Get, MultiGet, collect_rules, rule +from pants.engine.target import ( + FieldSetsPerTarget, + FieldSetsPerTargetRequest, + HydratedSources, + HydrateSourcesRequest, + SourcesField, + Targets, +) +from pants.engine.unions import UnionRule +from pants.util.logging import LogLevel + +logger = logging.getLogger(__name__) + + +@dataclass(frozen=True) +class CreateMakeselfArchive: + """Create Makeself archive with `await Get(ProcessResult, CreateMakeselfArchive(...))`. + + See docs for the options [here](https://github.com/megastep/makeself/tree/release-2.5.0#usage). + """ + + args: tuple[str, ...] + archive_dir: str + file_name: str + label: str + startup_script: tuple[str, ...] + input_digest: Digest + description: str = dataclasses.field(compare=False) + output_filename: str + extra_tools: Optional[Tuple[str, ...]] = None + level: LogLevel = LogLevel.INFO + cache_scope: Optional[ProcessCacheScope] = None + timeout_seconds: Optional[int] = None + + +@rule +async def create_makeself_archive( + request: CreateMakeselfArchive, + makeself: MakeselfTool, +) -> Process: + shims = await Get( + BinaryShims, + MakeselfBinaryShimsRequest( + extra_tools=request.extra_tools or (), + rationale="create makeself archive", + ), + ) + + tooldir = "__makeself" + argv = ( + os.path.join(tooldir, makeself.exe), + *request.args, + request.archive_dir, + request.file_name, + request.label, + *request.startup_script, + ) + + process = Process( + argv, + input_digest=request.input_digest, + immutable_input_digests={ + tooldir: makeself.digest, + **shims.immutable_input_digests, + }, + env={"PATH": shims.path_component}, + description=request.description, + level=request.level, + append_only_caches={}, + output_files=(request.output_filename,), + cache_scope=request.cache_scope or ProcessCacheScope.SUCCESSFUL, + timeout_seconds=request.timeout_seconds, + ) + return process + + +@dataclass(frozen=True) +class BuiltMakeselfArchiveArtifact(BuiltPackageArtifact): + @classmethod + def create(cls, relpath: str) -> "BuiltMakeselfArchiveArtifact": + return cls( + relpath=relpath, + extra_log_lines=(f"Built Makeself binary: {relpath}",), + ) + + +@rule +async def package_makeself_binary(field_set: MakeselfArchiveFieldSet) -> BuiltPackage: + archive_dir = "__archive" + + package_targets, file_targets = await MultiGet( + Get(Targets, UnparsedAddressInputs, field_set.packages.to_unparsed_address_inputs()), + Get(Targets, UnparsedAddressInputs, field_set.files.to_unparsed_address_inputs()), + ) + + package_field_sets_per_target = await Get( + FieldSetsPerTarget, FieldSetsPerTargetRequest(PackageFieldSet, package_targets) + ) + packages = await MultiGet( + Get(BuiltPackage, EnvironmentAwarePackageRequest(field_set)) + for field_set in package_field_sets_per_target.field_sets + ) + + file_sources = await MultiGet( + Get( + HydratedSources, + HydrateSourcesRequest( + tgt.get(SourcesField), + for_sources_types=(FileSourceField, ShellSourceField), + enable_codegen=True, + ), + ) + for tgt in file_targets + ) + + input_digest = await Get( + Digest, + MergeDigests( + ( + *(package.digest for package in packages), + *(sources.snapshot.digest for sources in file_sources), + ) + ), + ) + input_digest = await Get(Digest, AddPrefix(input_digest, archive_dir)) + + output_path = PurePath(field_set.output_path.value_or_default(file_ending="run")) + output_filename = output_path.name + result = await Get( + ProcessResult, + CreateMakeselfArchive( + archive_dir=archive_dir, + file_name=output_filename, + label=field_set.label.value or output_filename, + startup_script=field_set.startup_script.value or (), + args=field_set.args.value or (), + input_digest=input_digest, + output_filename=output_filename, + extra_tools=field_set.tools.value or (), + description=f"Packaging makeself archive: {field_set.address}", + level=LogLevel.DEBUG, + ), + ) + digest = await Get(Digest, AddPrefix(result.output_digest, str(output_path.parent))) + snapshot = await Get(Snapshot, Digest, digest) + assert len(snapshot.files) == 1, snapshot + + return BuiltPackage( + snapshot.digest, + artifacts=tuple(BuiltMakeselfArchiveArtifact.create(file) for file in snapshot.files), + ) + + +def rules(): + return [ + *collect_rules(), + *package.rules(), + *source_files.rules(), + *MakeselfArchiveFieldSet.rules(), + UnionRule(PackageFieldSet, MakeselfArchiveFieldSet), + ] diff --git a/src/python/pants/backend/makeself/goals/package_run_integration_test.py b/src/python/pants/backend/makeself/goals/package_run_integration_test.py new file mode 100644 index 00000000000..5f002e26e35 --- /dev/null +++ b/src/python/pants/backend/makeself/goals/package_run_integration_test.py @@ -0,0 +1,294 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +from textwrap import dedent + +import pytest + +from pants.backend.makeself import subsystem +from pants.backend.makeself import system_binaries as makeself_system_binaries +from pants.backend.makeself.goals import package, run +from pants.backend.makeself.goals.package import BuiltMakeselfArchiveArtifact +from pants.backend.makeself.goals.run import MakeselfArchiveFieldSet, RunMakeselfArchive +from pants.backend.makeself.target_types import MakeselfArchiveTarget +from pants.backend.shell import register +from pants.core.goals.package import BuiltPackage +from pants.core.target_types import FilesGeneratorTarget, FileTarget +from pants.core.target_types import rules as core_target_types_rules +from pants.core.util_rules import system_binaries +from pants.engine.addresses import Address +from pants.engine.fs import DigestContents, FileContent +from pants.engine.process import ProcessResult +from pants.testutil.rule_runner import PYTHON_BOOTSTRAP_ENV, QueryRule, RuleRunner + + +@pytest.fixture +def rule_runner() -> RuleRunner: + rule_runner = RuleRunner( + target_types=[ + MakeselfArchiveTarget, + FileTarget, + FilesGeneratorTarget, + *register.target_types(), + ], + rules=[ + *subsystem.rules(), + *package.rules(), + *run.rules(), + *system_binaries.rules(), + *register.rules(), + *makeself_system_binaries.rules(), + *core_target_types_rules(), + QueryRule(BuiltPackage, [MakeselfArchiveFieldSet]), + QueryRule(ProcessResult, [RunMakeselfArchive]), + ], + ) + rule_runner.set_options(args=[], env_inherit=PYTHON_BOOTSTRAP_ENV) + return rule_runner + + +def test_simple_archive(rule_runner: RuleRunner) -> None: + binary_name = "archive" + + rule_runner.write_files( + { + "files/BUILD": dedent( + f""" + files(sources=["*.txt"]) + makeself_archive( + name="{binary_name}", + args=["--notemp"], + files=["files/README.txt"], + ) + """ + ), + "files/README.txt": "TEST", + } + ) + + target = rule_runner.get_target(Address("files", target_name=binary_name)) + field_set = MakeselfArchiveFieldSet.create(target) + + package = rule_runner.request(BuiltPackage, [field_set]) + + assert len(package.artifacts) == 1, field_set + assert isinstance(package.artifacts[0], BuiltMakeselfArchiveArtifact) + relpath = f"files/{binary_name}.run" + assert package.artifacts[0].relpath == relpath + + result = rule_runner.request( + ProcessResult, + [ + RunMakeselfArchive( + exe=relpath, + extra_args=("--quiet",), + description="Run built subsystem archive", + input_digest=package.digest, + output_directory="_out", + ) + ], + ) + assert result.stdout == b"" + contents = rule_runner.request(DigestContents, [result.output_digest]) + assert contents == DigestContents([FileContent(path="_out/files/README.txt", content=b"TEST")]) + + +def test_inline_script(rule_runner: RuleRunner) -> None: + binary_name = "archive" + + rule_runner.write_files( + { + "src/shell/BUILD": dedent( + f""" + makeself_archive( + name="{binary_name}", + startup_script=["echo", "test"], + ) + """ + ), + } + ) + + target = rule_runner.get_target(Address("src/shell", target_name=binary_name)) + field_set = MakeselfArchiveFieldSet.create(target) + + package = rule_runner.request(BuiltPackage, [field_set]) + + assert len(package.artifacts) == 1, field_set + assert isinstance(package.artifacts[0], BuiltMakeselfArchiveArtifact) + relpath = f"src.shell/{binary_name}.run" + assert package.artifacts[0].relpath == relpath + + result = rule_runner.request( + ProcessResult, + [ + RunMakeselfArchive( + exe=relpath, + extra_args=("--quiet",), + description="Run built subsystem archive", + input_digest=package.digest, + ) + ], + ) + assert result.stdout == b"test\n" + + +def test_same_directory(rule_runner: RuleRunner) -> None: + binary_name = "archive" + + rule_runner.write_files( + { + "src/shell/BUILD": dedent( + f""" + shell_sources(name="src") + makeself_archive( + name="{binary_name}", + startup_script=["src/shell/run.sh"], + files=[":src"], + ) + """ + ), + "src/shell/run.sh": dedent( + """ + #!/bin/bash + echo test + """ + ), + } + ) + rule_runner.chmod("src/shell/run.sh", 0o777) + + target = rule_runner.get_target(Address("src/shell", target_name=binary_name)) + field_set = MakeselfArchiveFieldSet.create(target) + + package = rule_runner.request(BuiltPackage, [field_set]) + + assert len(package.artifacts) == 1, field_set + assert isinstance(package.artifacts[0], BuiltMakeselfArchiveArtifact) + relpath = f"src.shell/{binary_name}.run" + assert package.artifacts[0].relpath == relpath + + result = rule_runner.request( + ProcessResult, + [ + RunMakeselfArchive( + exe=relpath, + extra_args=("--quiet",), + description="Run built subsystem archive", + input_digest=package.digest, + ) + ], + ) + assert result.stdout == b"test\n" + + +def test_different_directory(rule_runner: RuleRunner) -> None: + binary_name = "archive" + + rule_runner.write_files( + { + "src/shell/BUILD": "shell_sources()", + "src/shell/run.sh": dedent( + """ + #!/bin/bash + echo test + """ + ), + "project/BUILD": dedent( + f""" + makeself_archive( + name="{binary_name}", + startup_script=["src/shell/run.sh"], + files=["src/shell/run.sh"], + ) + """ + ), + } + ) + rule_runner.chmod("src/shell/run.sh", 0o777) + + target = rule_runner.get_target(Address("project", target_name=binary_name)) + field_set = MakeselfArchiveFieldSet.create(target) + + package = rule_runner.request(BuiltPackage, [field_set]) + + assert len(package.artifacts) == 1, field_set + assert isinstance(package.artifacts[0], BuiltMakeselfArchiveArtifact) + relpath = f"project/{binary_name}.run" + assert package.artifacts[0].relpath == relpath + + result = rule_runner.request( + ProcessResult, + [ + RunMakeselfArchive( + exe=relpath, + extra_args=("--quiet",), + description="Run built subsystem archive", + input_digest=package.digest, + ) + ], + ) + assert result.stdout == b"test\n" + + +def test_multiple_scripts(rule_runner: RuleRunner) -> None: + binary_name = "archive" + + rule_runner.write_files( + { + "src/shell/BUILD": dedent( + f""" + shell_sources(name="src") + makeself_archive( + name="{binary_name}", + startup_script=["src/shell/run.sh"], + files=[":src"], + ) + """ + ), + "src/shell/hello.sh": dedent( + """ + #!/bin/bash + printf hello + """ + ), + "src/shell/world.sh": dedent( + """ + #!/bin/bash + printf world + """ + ), + "src/shell/run.sh": dedent( + """ + #!/bin/bash + src/shell/hello.sh + src/shell/world.sh + """ + ), + } + ) + rule_runner.chmod("src/shell/run.sh", 0o777) + rule_runner.chmod("src/shell/hello.sh", 0o777) + rule_runner.chmod("src/shell/world.sh", 0o777) + + target = rule_runner.get_target(Address("src/shell", target_name=binary_name)) + field_set = MakeselfArchiveFieldSet.create(target) + + package = rule_runner.request(BuiltPackage, [field_set]) + + assert len(package.artifacts) == 1, field_set + assert isinstance(package.artifacts[0], BuiltMakeselfArchiveArtifact) + relpath = f"src.shell/{binary_name}.run" + assert package.artifacts[0].relpath == relpath + + result = rule_runner.request( + ProcessResult, + [ + RunMakeselfArchive( + exe=relpath, + extra_args=("--quiet",), + description="Run built subsystem archive", + input_digest=package.digest, + ) + ], + ) + assert result.stdout == b"helloworld" diff --git a/src/python/pants/backend/makeself/goals/run.py b/src/python/pants/backend/makeself/goals/run.py new file mode 100644 index 00000000000..fbb84923780 --- /dev/null +++ b/src/python/pants/backend/makeself/goals/run.py @@ -0,0 +1,105 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +import os +from dataclasses import dataclass +from typing import Optional, Tuple + +from pants.backend.makeself.system_binaries import MakeselfBinaryShimsRequest +from pants.backend.makeself.target_types import ( + MakeselfArchiveArgsField, + MakeselfArchiveFilesField, + MakeselfArchiveOutputPathField, + MakeselfArchivePackagesField, + MakeselfArchiveStartupScriptField, + MakeselfArchiveToolsField, + MakeselfArthiveLabelField, +) +from pants.core.goals.package import BuiltPackage, PackageFieldSet +from pants.core.goals.run import RunFieldSet, RunInSandboxBehavior, RunRequest +from pants.core.util_rules.system_binaries import BinaryShims +from pants.engine.fs import Digest +from pants.engine.process import Process +from pants.engine.rules import Get, collect_rules, rule +from pants.util.logging import LogLevel + + +@dataclass(frozen=True) +class RunMakeselfArchive: + exe: str + input_digest: Digest + description: str + level: LogLevel = LogLevel.INFO + output_directory: Optional[str] = None + extra_args: Optional[Tuple[str, ...]] = None + extra_tools: Tuple[str, ...] = () + + +@rule(desc="Run makeself archive", level=LogLevel.DEBUG) +async def run_makeself_archive(request: RunMakeselfArchive) -> Process: + shims = await Get( + BinaryShims, + MakeselfBinaryShimsRequest( + extra_tools=request.extra_tools or (), + rationale="run makeself archive", + ), + ) + output_directories = [] + argv: Tuple[str, ...] = (request.exe,) + + if output_directory := request.output_directory: + output_directories = [output_directory] + argv += ("--target", request.output_directory) + + return Process( + argv=argv + (request.extra_args or ()), + input_digest=request.input_digest, + immutable_input_digests=shims.immutable_input_digests, + output_directories=output_directories, + description=request.description, + level=request.level, + env={"PATH": shims.path_component}, + ) + + +@dataclass(frozen=True) +class MakeselfArchiveFieldSet(PackageFieldSet, RunFieldSet): + required_fields = (MakeselfArchiveStartupScriptField,) + run_in_sandbox_behavior = RunInSandboxBehavior.RUN_REQUEST_HERMETIC + + startup_script: MakeselfArchiveStartupScriptField + label: MakeselfArthiveLabelField + files: MakeselfArchiveFilesField + packages: MakeselfArchivePackagesField + output_path: MakeselfArchiveOutputPathField + args: MakeselfArchiveArgsField + tools: MakeselfArchiveToolsField + + +@rule +async def create_makeself_archive_run_request(field_set: MakeselfArchiveFieldSet) -> RunRequest: + package = await Get(BuiltPackage, PackageFieldSet, field_set) + + exe = package.artifacts[0].relpath + if exe is None: + raise RuntimeError(f"Invalid package artifact: {package}") + + process = await Get( + Process, + RunMakeselfArchive( + exe=exe, + input_digest=package.digest, + description="Run makeself archive", + extra_tools=field_set.tools.value or (), + ), + ) + + return RunRequest( + digest=process.input_digest, + args=(os.path.join("{chroot}", process.argv[0]),) + process.argv[1:], + extra_env=process.env, + immutable_input_digests=process.immutable_input_digests, + ) + + +def rules(): + return collect_rules() diff --git a/src/python/pants/backend/makeself/subsystem.py b/src/python/pants/backend/makeself/subsystem.py new file mode 100644 index 00000000000..8b3eda35098 --- /dev/null +++ b/src/python/pants/backend/makeself/subsystem.py @@ -0,0 +1,102 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +import logging + +from pants.backend.makeself.goals.run import RunMakeselfArchive +from pants.core.util_rules import external_tool +from pants.core.util_rules.external_tool import ( + DownloadedExternalTool, + ExternalToolRequest, + TemplatedExternalTool, +) +from pants.engine.fs import Digest, RemovePrefix +from pants.engine.platform import Platform +from pants.engine.process import ProcessResult +from pants.engine.rules import Get, collect_rules, rule +from pants.util.logging import LogLevel +from pants.util.meta import classproperty + +logger = logging.getLogger(__name__) + + +class MakeselfSubsystem(TemplatedExternalTool): + options_scope = "makeself" + help = "A tool to generate a self-extractable compressed tar archives." + + default_version = "2.5.0" + + @classproperty + def default_known_versions(cls): + return [ + "|".join( + ( + cls.default_version, + platform, + "4d2fa9d898be22c63bb3c6bb7cc3dc97237700dea6d6ad898dcbec0289df0bc4", + "45867", + ) + ) + for platform in ["macos_arm64", "macos_x86_64", "linux_arm64", "linux_x86_64"] + ] + + default_url_template = "https://github.com/megastep/makeself/releases/download/release-{version}/makeself-{version}.run" + + +class MakeselfDistribution(DownloadedExternalTool): + """The makeself distribution. + + You can find releases here: https://github.com/megastep/makeself/releases + """ + + +@rule(desc="Download makeself distribution", level=LogLevel.DEBUG) +async def download_makeself_distribution( + options: MakeselfSubsystem, + platform: Platform, +) -> MakeselfDistribution: + tool = await Get( + DownloadedExternalTool, + ExternalToolRequest, + options.get_request(platform), + ) + logger.debug("makeself external tool: %s", tool) + return MakeselfDistribution(digest=tool.digest, exe=tool.exe) + + +class MakeselfTool(DownloadedExternalTool): + """The Makeself tool.""" + + +@rule(desc="Extract makeself distribution", level=LogLevel.DEBUG) +async def extract_makeself_distribution( + dist: MakeselfDistribution, +) -> MakeselfTool: + out = "__makeself" + result = await Get( + ProcessResult, + RunMakeselfArchive( + exe=dist.exe, + extra_args=( + "--keep", + "--accept", + "--noprogress", + "--nox11", + "--nochown", + "--nodiskspace", + "--quiet", + ), + input_digest=dist.digest, + output_directory=out, + description=f"Extracting Makeself archive: {out}", + level=LogLevel.DEBUG, + ), + ) + digest = await Get(Digest, RemovePrefix(result.output_digest, out)) + return MakeselfTool(digest=digest, exe="makeself.sh") + + +def rules(): + return [ + *collect_rules(), + *external_tool.rules(), + ] diff --git a/src/python/pants/backend/makeself/system_binaries.py b/src/python/pants/backend/makeself/system_binaries.py new file mode 100644 index 00000000000..92a57eb4158 --- /dev/null +++ b/src/python/pants/backend/makeself/system_binaries.py @@ -0,0 +1,137 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +from dataclasses import dataclass +from typing import Tuple + +from pants.backend.shell.subsystems.shell_setup import ShellSetup +from pants.backend.shell.util_rules.builtin import BASH_BUILTIN_COMMANDS +from pants.core.util_rules.system_binaries import ( + AwkBinary, + BasenameBinary, + BashBinary, + BinaryPathRequest, + BinaryShimsRequest, + CatBinary, + ChmodBinary, + CksumBinary, + CutBinary, + DateBinary, + DdBinary, + DfBinary, + DirnameBinary, + DuBinary, + ExprBinary, + FindBinary, + GzipBinary, + HeadBinary, + IdBinary, + MkdirBinary, + PwdBinary, + RmBinary, + SedBinary, + ShBinary, + SortBinary, + TailBinary, + TarBinary, + TestBinary, + TrBinary, + WcBinary, + XargsBinary, +) +from pants.engine.rules import collect_rules, rule +from pants.util.logging import LogLevel + + +@dataclass(frozen=True) +class MakeselfBinaryShimsRequest: + """Request all the binaries needed to create or run makeself archive. + + Technically, you might need different sets of binaries for creating and running the makeself + archive, but most of the binaries are the same, so we bundle them all in a single rule for + simplicity. + """ + + extra_tools: Tuple[str, ...] + rationale: str + + +@rule(desc="Find binaries required for makeself", level=LogLevel.DEBUG) +async def get_binaries_required_for_makeself( + request: MakeselfBinaryShimsRequest, + shell_setup: ShellSetup.EnvironmentAware, + awk: AwkBinary, + basename: BasenameBinary, + bash: BashBinary, + cat: CatBinary, + chmod: ChmodBinary, + cksum: CksumBinary, + cut: CutBinary, + date: DateBinary, + dd: DdBinary, + df: DfBinary, + dirname: DirnameBinary, + du: DuBinary, + expr: ExprBinary, + find: FindBinary, + gzip: GzipBinary, + head: HeadBinary, + id: IdBinary, + mkdir: MkdirBinary, + pwd: PwdBinary, + rm: RmBinary, + sed: SedBinary, + sh: ShBinary, + sort: SortBinary, + tail: TailBinary, + tar: TarBinary, + test: TestBinary, + tr: TrBinary, + wc: WcBinary, + xargs: XargsBinary, +) -> BinaryShimsRequest: + return BinaryShimsRequest( + paths=( + awk, + basename, + bash, + cat, + chmod, + cksum, + cut, + date, + dd, + df, + dirname, + du, + expr, + find, + gzip, + head, + id, + mkdir, + pwd, + rm, + sed, + sh, + sort, + tail, + tar, + test, + tr, + wc, + xargs, + ), + requests=tuple( + BinaryPathRequest( + binary_name=binary_name, + search_path=shell_setup.executable_search_path, + ) + for binary_name in request.extra_tools + if binary_name not in BASH_BUILTIN_COMMANDS + ), + rationale=request.rationale, + ) + + +def rules(): + return collect_rules() diff --git a/src/python/pants/backend/makeself/target_types.py b/src/python/pants/backend/makeself/target_types.py new file mode 100644 index 00000000000..7d1fdd52e00 --- /dev/null +++ b/src/python/pants/backend/makeself/target_types.py @@ -0,0 +1,123 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +from pants.core.goals.package import OutputPathField +from pants.engine.target import ( + COMMON_TARGET_FIELDS, + SpecialCasedDependencies, + StringField, + StringSequenceField, + Target, +) +from pants.util.docutil import bin_name +from pants.util.strutil import help_text + + +class MakeselfArthiveLabelField(StringField): + alias = "label" + help = help_text( + """ + An arbitrary text string describing the package. It will be displayed while extracting + the files. + """ + ) + + +class MakeselfArchiveStartupScriptField(StringSequenceField): + alias = "startup_script" + required = False + help = help_text( + """ + The startup script, i.e. what gets run when executing `./my_archive.run`. + + Examples: + - `["startup.sh"]` + - `["echo", "Running makeself archieve"]` + """ + ) + + +class MakeselfArchiveFilesField(SpecialCasedDependencies): + alias = "files" + help = help_text( + """ + Addresses to any `file`, `files`, or `relocated_files` targets to include in the + archive, e.g. `["resources:logo"]`. + + This is useful to include any loose files, like data files, + image assets, or config files. + + This will ignore any targets that are not `file`, `files`, or + `relocated_files` targets. + + If you instead want those files included in any packages specified in the `packages` + field for this target, then use a `resource` or `resources` target and have the original + package depend on the resources. + """ + ) + + +class MakeselfArchivePackagesField(SpecialCasedDependencies): + alias = "packages" + help = help_text( + f""" + Addresses to any targets that can be built with `{bin_name()} package`, + e.g. `["project:app"]`. + + Pants will build the assets as if you had run `{bin_name()} package`. + It will include the results in your archive using the same name they + would normally have, but without the `--distdir` prefix (e.g. `dist/`). + + You can include anything that can be built by `{bin_name()} package`, + e.g. a `pex_binary`, `python_awslambda`, or even another `makeself_archive`. + """ + ) + + +class MakeselfArchiveOutputPathField(OutputPathField): + pass + + +class MakeselfArchiveArgsField(StringSequenceField): + alias = "args" + required = False + help = help_text( + """ + [Arguments](https://github.com/megastep/makeself#usage) to pass + to the `makeself` script when building this archive. These are passed + before any file paths. + """ + ) + + +class MakeselfArchiveToolsField(StringSequenceField): + alias = "tools" + default = () + help = help_text( + """ + Specify required executable tools that might be used. + + Only the tools explicitly provided will be available on the search PATH, + and these tools must be found on the paths provided by + `[shell-setup].executable_search_paths` (which defaults to the system PATH). + """ + ) + + +class MakeselfArchiveTarget(Target): + alias = "makeself_archive" + core_fields = ( + MakeselfArthiveLabelField, + MakeselfArchiveStartupScriptField, + MakeselfArchiveFilesField, + MakeselfArchivePackagesField, + MakeselfArchiveOutputPathField, + MakeselfArchiveArgsField, + MakeselfArchiveToolsField, + *COMMON_TARGET_FIELDS, + ) + help = help_text( + """ + Self-extractable archive on Unix using [makeself](https://github.com/megastep/makeself) + tool. + """ + ) diff --git a/src/python/pants/bin/BUILD b/src/python/pants/bin/BUILD index 7ba02811a31..0570719c33c 100644 --- a/src/python/pants/bin/BUILD +++ b/src/python/pants/bin/BUILD @@ -12,10 +12,10 @@ python_sources( target( name="plugins", dependencies=[ - "src/python/pants/backend/build_files/fix/deprecations", - "src/python/pants/backend/build_files/fmt/buildifier", "src/python/pants/backend/awslambda/python", + "src/python/pants/backend/build_files/fix/deprecations", "src/python/pants/backend/build_files/fmt/black", + "src/python/pants/backend/build_files/fmt/buildifier", "src/python/pants/backend/build_files/fmt/ruff", "src/python/pants/backend/build_files/fmt/yapf", "src/python/pants/backend/codegen/protobuf/lint/buf", @@ -50,6 +50,7 @@ target( "src/python/pants/backend/experimental/kotlin", "src/python/pants/backend/experimental/kotlin/debug_goals", "src/python/pants/backend/experimental/kotlin/lint/ktlint", + "src/python/pants/backend/experimental/makeself", "src/python/pants/backend/experimental/openapi", "src/python/pants/backend/experimental/openapi/codegen/java", "src/python/pants/backend/experimental/openapi/lint/openapi_format", @@ -75,8 +76,8 @@ target( "src/python/pants/backend/experimental/terraform", "src/python/pants/backend/experimental/terraform/lint/tfsec", "src/python/pants/backend/experimental/tools/semgrep", - "src/python/pants/backend/experimental/tools/yamllint", "src/python/pants/backend/experimental/tools/workunit_logger", + "src/python/pants/backend/experimental/tools/yamllint", "src/python/pants/backend/experimental/typescript", "src/python/pants/backend/experimental/visibility", "src/python/pants/backend/google_cloud_function/python", diff --git a/src/python/pants/core/util_rules/system_binaries.py b/src/python/pants/core/util_rules/system_binaries.py index 71f8ef4b990..48b6fdd79d8 100644 --- a/src/python/pants/core/util_rules/system_binaries.py +++ b/src/python/pants/core/util_rules/system_binaries.py @@ -341,15 +341,35 @@ def extract_archive_argv( return (self.path, *prog_args, "-xf", archive_path, "-C", extract_path) -class MkdirBinary(BinaryPath): +class AwkBinary(BinaryPath): pass -class MktempBinary(BinaryPath): +class Base64Binary(BinaryPath): pass -class TouchBinary(BinaryPath): +class BasenameBinary(BinaryPath): + pass + + +class Bzip2Binary(BinaryPath): + pass + + +class Bzip3Binary(BinaryPath): + pass + + +class CatBinary(BinaryPath): + pass + + +class ChmodBinary(BinaryPath): + pass + + +class CksumBinary(BinaryPath): pass @@ -357,19 +377,19 @@ class CpBinary(BinaryPath): pass -class MvBinary(BinaryPath): +class CutBinary(BinaryPath): pass -class LnBinary(BinaryPath): +class DateBinary(BinaryPath): pass -class CatBinary(BinaryPath): +class DdBinary(BinaryPath): pass -class ChmodBinary(BinaryPath): +class DfBinary(BinaryPath): pass @@ -377,14 +397,130 @@ class DiffBinary(BinaryPath): pass +class DirnameBinary(BinaryPath): + pass + + +class DuBinary(BinaryPath): + pass + + +class ExprBinary(BinaryPath): + pass + + +class FindBinary(BinaryPath): + pass + + +class GpgBinary(BinaryPath): + pass + + +class GzipBinary(BinaryPath): + pass + + +class HeadBinary(BinaryPath): + pass + + +class IdBinary(BinaryPath): + pass + + +class LnBinary(BinaryPath): + pass + + +class Lz4Binary(BinaryPath): + pass + + +class LzopBinary(BinaryPath): + pass + + +class Md5sumBinary(BinaryPath): + pass + + +class MkdirBinary(BinaryPath): + pass + + +class MktempBinary(BinaryPath): + pass + + +class MvBinary(BinaryPath): + pass + + class OpenBinary(BinaryPath): pass +class PwdBinary(BinaryPath): + pass + + class ReadlinkBinary(BinaryPath): pass +class RmBinary(BinaryPath): + pass + + +class SedBinary(BinaryPath): + pass + + +class ShBinary(BinaryPath): + pass + + +class ShasumBinary(BinaryPath): + pass + + +class SortBinary(BinaryPath): + pass + + +class TailBinary(BinaryPath): + pass + + +class TestBinary(BinaryPath): + pass + + +class TouchBinary(BinaryPath): + pass + + +class TrBinary(BinaryPath): + pass + + +class WcBinary(BinaryPath): + pass + + +class XargsBinary(BinaryPath): + pass + + +class XzBinary(BinaryPath): + pass + + +class ZstdBinary(BinaryPath): + pass + + class GitBinaryException(Exception): pass @@ -591,46 +727,54 @@ async def find_binary( ) -@rule(desc="Finding the `zip` binary", level=LogLevel.DEBUG) -async def find_zip(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> ZipBinary: +@rule(desc="Finding the `awk` binary", level=LogLevel.DEBUG) +async def find_awk(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> AwkBinary: + request = BinaryPathRequest(binary_name="awk", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="awk file") + return AwkBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `base64` binary", level=LogLevel.DEBUG) +async def find_base64(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> Base64Binary: request = BinaryPathRequest( - binary_name="zip", - search_path=system_binaries.system_binary_paths, - test=BinaryPathTest(args=["-v"]), + binary_name="base64", search_path=system_binaries.system_binary_paths ) paths = await Get(BinaryPaths, BinaryPathRequest, request) - first_path = paths.first_path_or_raise(request, rationale="create `.zip` archives") - return ZipBinary(first_path.path, first_path.fingerprint) + first_path = paths.first_path_or_raise(request, rationale="base64 file") + return Base64Binary(first_path.path, first_path.fingerprint) -@rule(desc="Finding the `unzip` binary", level=LogLevel.DEBUG) -async def find_unzip(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> UnzipBinary: +@rule(desc="Finding the `basename` binary", level=LogLevel.DEBUG) +async def find_basename( + system_binaries: SystemBinariesSubsystem.EnvironmentAware, +) -> BasenameBinary: request = BinaryPathRequest( - binary_name="unzip", - search_path=system_binaries.system_binary_paths, - test=BinaryPathTest(args=["-v"]), + binary_name="basename", search_path=system_binaries.system_binary_paths ) paths = await Get(BinaryPaths, BinaryPathRequest, request) - first_path = paths.first_path_or_raise( - request, rationale="download the tools Pants needs to run" - ) - return UnzipBinary(first_path.path, first_path.fingerprint) + first_path = paths.first_path_or_raise(request, rationale="basename file") + return BasenameBinary(first_path.path, first_path.fingerprint) -@rule(desc="Finding the `tar` binary", level=LogLevel.DEBUG) -async def find_tar( - platform: Platform, system_binaries: SystemBinariesSubsystem.EnvironmentAware -) -> TarBinary: +@rule(desc="Finding the `bzip2` binary", level=LogLevel.DEBUG) +async def find_bzip2(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> Bzip2Binary: request = BinaryPathRequest( - binary_name="tar", - search_path=system_binaries.system_binary_paths, - test=BinaryPathTest(args=["--version"]), + binary_name="bzip2", search_path=system_binaries.system_binary_paths ) paths = await Get(BinaryPaths, BinaryPathRequest, request) - first_path = paths.first_path_or_raise( - request, rationale="download the tools Pants needs to run" + first_path = paths.first_path_or_raise(request, rationale="bzip2 file") + return Bzip2Binary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `bzip3` binary", level=LogLevel.DEBUG) +async def find_bzip3(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> Bzip3Binary: + request = BinaryPathRequest( + binary_name="bzip3", search_path=system_binaries.system_binary_paths ) - return TarBinary(first_path.path, first_path.fingerprint, platform) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="bzip3 file") + return Bzip3Binary(first_path.path, first_path.fingerprint) @rule(desc="Finding the `cat` binary", level=LogLevel.DEBUG) @@ -641,34 +785,26 @@ async def find_cat(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> return CatBinary(first_path.path, first_path.fingerprint) -@rule(desc="Finding the `mkdir` binary", level=LogLevel.DEBUG) -async def find_mkdir(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> MkdirBinary: +@rule(desc="Finding the `chmod` binary", level=LogLevel.DEBUG) +async def find_chmod(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> ChmodBinary: request = BinaryPathRequest( - binary_name="mkdir", search_path=system_binaries.system_binary_paths + binary_name="chmod", search_path=system_binaries.system_binary_paths ) paths = await Get(BinaryPaths, BinaryPathRequest, request) - first_path = paths.first_path_or_raise(request, rationale="create directories") - return MkdirBinary(first_path.path, first_path.fingerprint) - - -@rule(desc="Finding the `mktempt` binary", level=LogLevel.DEBUG) -async def find_mktemp(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> MktempBinary: - request = BinaryPathRequest( - binary_name="mktemp", search_path=system_binaries.system_binary_paths + first_path = paths.first_path_or_raise( + request, rationale="change file modes or Access Control Lists" ) - paths = await Get(BinaryPaths, BinaryPathRequest, request) - first_path = paths.first_path_or_raise(request, rationale="create temporary files/directories") - return MktempBinary(first_path.path, first_path.fingerprint) + return ChmodBinary(first_path.path, first_path.fingerprint) -@rule(desc="Finding the `touch` binary", level=LogLevel.DEBUG) -async def find_touch(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> TouchBinary: +@rule(desc="Finding the `cksum` binary", level=LogLevel.DEBUG) +async def find_cksum(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> CksumBinary: request = BinaryPathRequest( - binary_name="touch", search_path=system_binaries.system_binary_paths + binary_name="cksum", search_path=system_binaries.system_binary_paths ) paths = await Get(BinaryPaths, BinaryPathRequest, request) - first_path = paths.first_path_or_raise(request, rationale="touch file") - return TouchBinary(first_path.path, first_path.fingerprint) + first_path = paths.first_path_or_raise(request, rationale="cksum file") + return CksumBinary(first_path.path, first_path.fingerprint) @rule(desc="Finding the `cp` binary", level=LogLevel.DEBUG) @@ -679,12 +815,120 @@ async def find_cp(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> return CpBinary(first_path.path, first_path.fingerprint) -@rule(desc="Finding the `mv` binary", level=LogLevel.DEBUG) -async def find_mv(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> MvBinary: - request = BinaryPathRequest(binary_name="mv", search_path=system_binaries.system_binary_paths) +@rule(desc="Finding the `cut` binary", level=LogLevel.DEBUG) +async def find_cut(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> CutBinary: + request = BinaryPathRequest(binary_name="cut", search_path=system_binaries.system_binary_paths) paths = await Get(BinaryPaths, BinaryPathRequest, request) - first_path = paths.first_path_or_raise(request, rationale="move files") - return MvBinary(first_path.path, first_path.fingerprint) + first_path = paths.first_path_or_raise(request, rationale="cut file") + return CutBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `date` binary", level=LogLevel.DEBUG) +async def find_date(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> DateBinary: + request = BinaryPathRequest(binary_name="date", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="date file") + return DateBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `dd` binary", level=LogLevel.DEBUG) +async def find_dd(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> DdBinary: + request = BinaryPathRequest(binary_name="dd", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="dd file") + return DdBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `df` binary", level=LogLevel.DEBUG) +async def find_df(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> DfBinary: + request = BinaryPathRequest(binary_name="df", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="df file") + return DfBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `diff` binary", level=LogLevel.DEBUG) +async def find_diff(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> DiffBinary: + request = BinaryPathRequest(binary_name="diff", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="compare files line by line") + return DiffBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `dirname` binary", level=LogLevel.DEBUG) +async def find_dirname(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> DirnameBinary: + request = BinaryPathRequest( + binary_name="dirname", search_path=system_binaries.system_binary_paths + ) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="dirname file") + return DirnameBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `du` binary", level=LogLevel.DEBUG) +async def find_du(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> DuBinary: + request = BinaryPathRequest(binary_name="du", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="du file") + return DuBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `expr` binary", level=LogLevel.DEBUG) +async def find_expr(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> ExprBinary: + request = BinaryPathRequest(binary_name="expr", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="expr file") + return ExprBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `find` binary", level=LogLevel.DEBUG) +async def find_find(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> FindBinary: + request = BinaryPathRequest(binary_name="find", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="find file") + return FindBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `git` binary", level=LogLevel.DEBUG) +async def find_git(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> GitBinary: + request = BinaryPathRequest(binary_name="git", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise( + request, rationale="track changes to files in your build environment" + ) + return GitBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `gpg` binary", level=LogLevel.DEBUG) +async def find_gpg(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> GpgBinary: + request = BinaryPathRequest(binary_name="gpg", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="gpg file") + return GpgBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `gzip` binary", level=LogLevel.DEBUG) +async def find_gzip(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> GzipBinary: + request = BinaryPathRequest(binary_name="gzip", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="gzip file") + return GzipBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `head` binary", level=LogLevel.DEBUG) +async def find_head(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> HeadBinary: + request = BinaryPathRequest(binary_name="head", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="head file") + return HeadBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `id` binary", level=LogLevel.DEBUG) +async def find_id(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> IdBinary: + request = BinaryPathRequest(binary_name="id", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="id file") + return IdBinary(first_path.path, first_path.fingerprint) @rule(desc="Finding the `ln` binary", level=LogLevel.DEBUG) @@ -695,24 +939,58 @@ async def find_ln(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> return LnBinary(first_path.path, first_path.fingerprint) -@rule(desc="Finding the `chmod` binary", level=LogLevel.DEBUG) -async def find_chmod(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> ChmodBinary: +@rule(desc="Finding the `lz4` binary", level=LogLevel.DEBUG) +async def find_lz4(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> Lz4Binary: + request = BinaryPathRequest(binary_name="lz4", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="lz4 file") + return Lz4Binary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `lzop` binary", level=LogLevel.DEBUG) +async def find_lzop(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> LzopBinary: + request = BinaryPathRequest(binary_name="lzop", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="lzop file") + return LzopBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `md5sum` binary", level=LogLevel.DEBUG) +async def find_md5sum(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> Md5sumBinary: request = BinaryPathRequest( - binary_name="chmod", search_path=system_binaries.system_binary_paths + binary_name="md5sum", search_path=system_binaries.system_binary_paths ) paths = await Get(BinaryPaths, BinaryPathRequest, request) - first_path = paths.first_path_or_raise( - request, rationale="change file modes or Access Control Lists" + first_path = paths.first_path_or_raise(request, rationale="md5sum file") + return Md5sumBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `mkdir` binary", level=LogLevel.DEBUG) +async def find_mkdir(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> MkdirBinary: + request = BinaryPathRequest( + binary_name="mkdir", search_path=system_binaries.system_binary_paths ) - return ChmodBinary(first_path.path, first_path.fingerprint) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="create directories") + return MkdirBinary(first_path.path, first_path.fingerprint) -@rule(desc="Finding the `diff` binary", level=LogLevel.DEBUG) -async def find_diff(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> DiffBinary: - request = BinaryPathRequest(binary_name="diff", search_path=system_binaries.system_binary_paths) +@rule(desc="Finding the `mktempt` binary", level=LogLevel.DEBUG) +async def find_mktemp(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> MktempBinary: + request = BinaryPathRequest( + binary_name="mktemp", search_path=system_binaries.system_binary_paths + ) paths = await Get(BinaryPaths, BinaryPathRequest, request) - first_path = paths.first_path_or_raise(request, rationale="compare files line by line") - return DiffBinary(first_path.path, first_path.fingerprint) + first_path = paths.first_path_or_raise(request, rationale="create temporary files/directories") + return MktempBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `mv` binary", level=LogLevel.DEBUG) +async def find_mv(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> MvBinary: + request = BinaryPathRequest(binary_name="mv", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="move files") + return MvBinary(first_path.path, first_path.fingerprint) @rule(desc="Finding the `open` binary", level=LogLevel.DEBUG) @@ -723,6 +1001,14 @@ async def find_open(system_binaries: SystemBinariesSubsystem.EnvironmentAware) - return OpenBinary(first_path.path, first_path.fingerprint) +@rule(desc="Finding the `pwd` binary", level=LogLevel.DEBUG) +async def find_pwd(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> PwdBinary: + request = BinaryPathRequest(binary_name="pwd", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="pwd file") + return PwdBinary(first_path.path, first_path.fingerprint) + + @rule(desc="Finding the `readlink` binary", level=LogLevel.DEBUG) async def find_readlink( system_binaries: SystemBinariesSubsystem.EnvironmentAware, @@ -735,14 +1021,156 @@ async def find_readlink( return ReadlinkBinary(first_path.path, first_path.fingerprint) -@rule(desc="Finding the `git` binary", level=LogLevel.DEBUG) -async def find_git(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> GitBinary: - request = BinaryPathRequest(binary_name="git", search_path=system_binaries.system_binary_paths) +@rule(desc="Finding the `rm` binary", level=LogLevel.DEBUG) +async def find_rm(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> RmBinary: + request = BinaryPathRequest(binary_name="rm", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="rm file") + return RmBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `sed` binary", level=LogLevel.DEBUG) +async def find_sed(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> SedBinary: + request = BinaryPathRequest(binary_name="sed", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="sed file") + return SedBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `sh` binary", level=LogLevel.DEBUG) +async def find_sh(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> ShBinary: + request = BinaryPathRequest(binary_name="sh", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="sh file") + return ShBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `shasum` binary", level=LogLevel.DEBUG) +async def find_shasum(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> ShasumBinary: + request = BinaryPathRequest( + binary_name="shasum", search_path=system_binaries.system_binary_paths + ) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="shasum file") + return ShasumBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `sort` binary", level=LogLevel.DEBUG) +async def find_sort(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> SortBinary: + request = BinaryPathRequest(binary_name="sort", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="sort file") + return SortBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `tail` binary", level=LogLevel.DEBUG) +async def find_tail(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> TailBinary: + request = BinaryPathRequest(binary_name="tail", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="tail file") + return TailBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `tar` binary", level=LogLevel.DEBUG) +async def find_tar( + platform: Platform, system_binaries: SystemBinariesSubsystem.EnvironmentAware +) -> TarBinary: + request = BinaryPathRequest( + binary_name="tar", + search_path=system_binaries.system_binary_paths, + test=BinaryPathTest(args=["--version"]), + ) paths = await Get(BinaryPaths, BinaryPathRequest, request) first_path = paths.first_path_or_raise( - request, rationale="track changes to files in your build environment" + request, rationale="download the tools Pants needs to run" ) - return GitBinary(first_path.path, first_path.fingerprint) + return TarBinary(first_path.path, first_path.fingerprint, platform) + + +@rule(desc="Finding the `test` binary", level=LogLevel.DEBUG) +async def find_test(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> TestBinary: + request = BinaryPathRequest(binary_name="test", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="test file") + return TestBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `touch` binary", level=LogLevel.DEBUG) +async def find_touch(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> TouchBinary: + request = BinaryPathRequest( + binary_name="touch", search_path=system_binaries.system_binary_paths + ) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="touch file") + return TouchBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `tr` binary", level=LogLevel.DEBUG) +async def find_tr(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> TrBinary: + request = BinaryPathRequest(binary_name="tr", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="tr file") + return TrBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `unzip` binary", level=LogLevel.DEBUG) +async def find_unzip(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> UnzipBinary: + request = BinaryPathRequest( + binary_name="unzip", + search_path=system_binaries.system_binary_paths, + test=BinaryPathTest(args=["-v"]), + ) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise( + request, rationale="download the tools Pants needs to run" + ) + return UnzipBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `wc` binary", level=LogLevel.DEBUG) +async def find_wc(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> WcBinary: + request = BinaryPathRequest(binary_name="wc", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="wc file") + return WcBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `xargs` binary", level=LogLevel.DEBUG) +async def find_xargs(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> XargsBinary: + request = BinaryPathRequest( + binary_name="xargs", search_path=system_binaries.system_binary_paths + ) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="xargs file") + return XargsBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `xz` binary", level=LogLevel.DEBUG) +async def find_xz(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> XzBinary: + request = BinaryPathRequest(binary_name="xz", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="xz file") + return XzBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `zip` binary", level=LogLevel.DEBUG) +async def find_zip(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> ZipBinary: + request = BinaryPathRequest( + binary_name="zip", + search_path=system_binaries.system_binary_paths, + test=BinaryPathTest(args=["-v"]), + ) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="create `.zip` archives") + return ZipBinary(first_path.path, first_path.fingerprint) + + +@rule(desc="Finding the `zstd` binary", level=LogLevel.DEBUG) +async def find_zstd(system_binaries: SystemBinariesSubsystem.EnvironmentAware) -> ZstdBinary: + request = BinaryPathRequest(binary_name="zstd", search_path=system_binaries.system_binary_paths) + paths = await Get(BinaryPaths, BinaryPathRequest, request) + first_path = paths.first_path_or_raise(request, rationale="zstd file") + return ZstdBinary(first_path.path, first_path.fingerprint) def rules(): From 20b72101bbe9beaa7095987ba0117742ba1b7ce0 Mon Sep 17 00:00:00 2001 From: Tony Sherman <100969281+TonySherman@users.noreply.github.com> Date: Wed, 22 May 2024 20:08:32 -0400 Subject: [PATCH 18/18] Allow Extra Args to be passed to FaaS Pex Build (#20939) Similar to #20237, this allows passing arbitrary arguments to the first pex invocation when building a FaaS (AWS Lambda or Google Cloud Function) artifact. This can serve as a stop-gap for #19256, because a user can write then pass `--exclude` themselves when they really need that (or similar) functionality. --------- Co-authored-by: Huon Wilson --- docs/notes/2.22.x.md | 3 ++ .../pants/backend/awslambda/python/rules.py | 4 ++ .../backend/awslambda/python/rules_test.py | 2 + .../backend/awslambda/python/target_types.py | 2 + .../google_cloud_function/python/rules.py | 3 ++ .../python/rules_test.py | 1 + .../python/target_types.py | 2 + .../pants/backend/python/util_rules/faas.py | 20 +++++++ .../backend/python/util_rules/faas_test.py | 54 +++++++++++++++++++ 9 files changed, 91 insertions(+) diff --git a/docs/notes/2.22.x.md b/docs/notes/2.22.x.md index 7701fa222e3..f771a2730b6 100644 --- a/docs/notes/2.22.x.md +++ b/docs/notes/2.22.x.md @@ -86,6 +86,9 @@ Python tool subsystem docs and help text now include the default version of the Python tools can be [exported from their default bundled lockfiles](https://www.pantsbuild.org/2.22/docs/using-pants/setting-up-an-ide#tool-virtualenvs). For instance, when using the default `black` subsystem, `pants export --resolve=black` will export a venv containing the version of black that Pants runs. +New field `pex_build_extra_args` is available on FaaS targets [python_aws_lambda_function](https://www.pantsbuild.org/2.22/reference/targets/python_aws_lambda_function#pex_build_extra_args), +[python_aws_lambda_layer](https://www.pantsbuild.org/2.22/reference/targets/python_aws_lambda_layer#pex_build_extra_args), and [python_google_cloud_function]((https://www.pantsbuild.org/2.22/reference/targets/python_aws_lambda_layer#pex_build_extra_args). This allows passing arguments to the `pex` invocation for collecting sources and packaging. + #### Semgrep The default version of `semgrep` used by the `pants.backends.experimental.tool.semgrep` backend is now version 1.72.0, upgraded from 1.46.0. This version requires Python 3.8 or greater. diff --git a/src/python/pants/backend/awslambda/python/rules.py b/src/python/pants/backend/awslambda/python/rules.py index 37a64e1b571..e95e079be11 100644 --- a/src/python/pants/backend/awslambda/python/rules.py +++ b/src/python/pants/backend/awslambda/python/rules.py @@ -20,6 +20,7 @@ PythonFaaSCompletePlatforms, PythonFaaSLayoutField, PythonFaaSPex3VenvCreateExtraArgsField, + PythonFaaSPexBuildExtraArgs, ) from pants.backend.python.util_rules.faas import rules as faas_rules from pants.core.goals.package import BuiltPackage, OutputPathField, PackageFieldSet @@ -37,6 +38,7 @@ class _BaseFieldSet(PackageFieldSet): runtime: PythonAwsLambdaRuntime complete_platforms: PythonFaaSCompletePlatforms pex3_venv_create_extra_args: PythonFaaSPex3VenvCreateExtraArgsField + pex_build_extra_args: PythonFaaSPexBuildExtraArgs layout: PythonFaaSLayoutField output_path: OutputPathField environment: EnvironmentField @@ -73,6 +75,7 @@ async def package_python_aws_lambda_function( include_requirements=field_set.include_requirements.value, include_sources=True, pex3_venv_create_extra_args=field_set.pex3_venv_create_extra_args, + pex_build_extra_args=field_set.pex_build_extra_args, layout=field_set.layout, reexported_handler_module=PythonAwsLambdaHandlerField.reexported_handler_module, ), @@ -94,6 +97,7 @@ async def package_python_aws_lambda_layer( include_requirements=field_set.include_requirements.value, include_sources=field_set.include_sources.value, pex3_venv_create_extra_args=field_set.pex3_venv_create_extra_args, + pex_build_extra_args=field_set.pex_build_extra_args, layout=field_set.layout, # See # https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html#configuration-layers-path diff --git a/src/python/pants/backend/awslambda/python/rules_test.py b/src/python/pants/backend/awslambda/python/rules_test.py index 95e652e4e1b..fa1bf67ee81 100644 --- a/src/python/pants/backend/awslambda/python/rules_test.py +++ b/src/python/pants/backend/awslambda/python/rules_test.py @@ -34,6 +34,7 @@ from pants.backend.python.util_rules.faas import ( BuildPythonFaaSRequest, PythonFaaSPex3VenvCreateExtraArgsField, + PythonFaaSPexBuildExtraArgs, ) from pants.core.goals import package from pants.core.goals.package import BuiltPackage @@ -387,6 +388,7 @@ def test_pex3_venv_create_extra_args_are_passed_through( layout=Mock(), **{arg: Mock() for arg in extra_field_set_args}, pex3_venv_create_extra_args=extra_args_field, + pex_build_extra_args=PythonFaaSPexBuildExtraArgs(None, addr), ) observed_calls = [] diff --git a/src/python/pants/backend/awslambda/python/target_types.py b/src/python/pants/backend/awslambda/python/target_types.py index 9f3aa190127..b930220dab5 100644 --- a/src/python/pants/backend/awslambda/python/target_types.py +++ b/src/python/pants/backend/awslambda/python/target_types.py @@ -15,6 +15,7 @@ PythonFaaSKnownRuntime, PythonFaaSLayoutField, PythonFaaSPex3VenvCreateExtraArgsField, + PythonFaaSPexBuildExtraArgs, PythonFaaSRuntimeField, ) from pants.backend.python.util_rules.faas import rules as faas_rules @@ -153,6 +154,7 @@ class _AWSLambdaBaseTarget(Target): PythonAwsLambdaRuntime, PythonFaaSCompletePlatforms, PythonFaaSPex3VenvCreateExtraArgsField, + PythonFaaSPexBuildExtraArgs, PythonFaaSLayoutField, PythonResolveField, EnvironmentField, diff --git a/src/python/pants/backend/google_cloud_function/python/rules.py b/src/python/pants/backend/google_cloud_function/python/rules.py index fc5acace249..abe32db2112 100644 --- a/src/python/pants/backend/google_cloud_function/python/rules.py +++ b/src/python/pants/backend/google_cloud_function/python/rules.py @@ -17,6 +17,7 @@ PythonFaaSCompletePlatforms, PythonFaaSLayoutField, PythonFaaSPex3VenvCreateExtraArgsField, + PythonFaaSPexBuildExtraArgs, ) from pants.backend.python.util_rules.faas import rules as faas_rules from pants.core.goals.package import BuiltPackage, OutputPathField, PackageFieldSet @@ -36,6 +37,7 @@ class PythonGoogleCloudFunctionFieldSet(PackageFieldSet): runtime: PythonGoogleCloudFunctionRuntime complete_platforms: PythonFaaSCompletePlatforms pex3_venv_create_extra_args: PythonFaaSPex3VenvCreateExtraArgsField + pex_build_extra_args: PythonFaaSPexBuildExtraArgs layout: PythonFaaSLayoutField type: PythonGoogleCloudFunctionType output_path: OutputPathField @@ -55,6 +57,7 @@ async def package_python_google_cloud_function( runtime=field_set.runtime, handler=field_set.handler, pex3_venv_create_extra_args=field_set.pex3_venv_create_extra_args, + pex_build_extra_args=field_set.pex_build_extra_args, layout=field_set.layout, output_path=field_set.output_path, include_requirements=True, diff --git a/src/python/pants/backend/google_cloud_function/python/rules_test.py b/src/python/pants/backend/google_cloud_function/python/rules_test.py index e7ecb5a0e49..8eaefa21cfd 100644 --- a/src/python/pants/backend/google_cloud_function/python/rules_test.py +++ b/src/python/pants/backend/google_cloud_function/python/rules_test.py @@ -260,6 +260,7 @@ def test_pex3_venv_create_extra_args_are_passed_through() -> None: output_path=Mock(), environment=Mock(), pex3_venv_create_extra_args=extra_args_field, + pex_build_extra_args=Mock(), layout=Mock(), ) diff --git a/src/python/pants/backend/google_cloud_function/python/target_types.py b/src/python/pants/backend/google_cloud_function/python/target_types.py index f952b113ad1..db5400dc871 100644 --- a/src/python/pants/backend/google_cloud_function/python/target_types.py +++ b/src/python/pants/backend/google_cloud_function/python/target_types.py @@ -12,6 +12,7 @@ PythonFaaSHandlerField, PythonFaaSLayoutField, PythonFaaSPex3VenvCreateExtraArgsField, + PythonFaaSPexBuildExtraArgs, PythonFaaSRuntimeField, ) from pants.backend.python.util_rules.faas import rules as faas_rules @@ -120,6 +121,7 @@ class PythonGoogleCloudFunction(Target): PythonFaaSCompletePlatforms, PythonGoogleCloudFunctionType, PythonFaaSPex3VenvCreateExtraArgsField, + PythonFaaSPexBuildExtraArgs, PythonFaaSLayoutField, PythonResolveField, EnvironmentField, diff --git a/src/python/pants/backend/python/util_rules/faas.py b/src/python/pants/backend/python/util_rules/faas.py index ad722b91e56..22414c52568 100644 --- a/src/python/pants/backend/python/util_rules/faas.py +++ b/src/python/pants/backend/python/util_rules/faas.py @@ -98,6 +98,23 @@ class PythonFaaSPex3VenvCreateExtraArgsField(StringSequenceField): ) +class PythonFaaSPexBuildExtraArgs(StringSequenceField): + alias = "pex_build_extra_args" + default = () + help = help_text( + """ + Additional arguments to pass to the `pex` invocation that is used to collect the requirements + and sources for packaging. + + For example, `pex_build_extra_args=["--exclude=pypi-package-name"]` to force a package called + `pypi-package-name` isn't included in the artifact. + + Note: Excluding dependencies currently causes Pex to throw an error. You can additionally pass + the `--ignore-errors` flag. + """ + ) + + class PythonFaaSHandlerField(StringField, AsyncFieldMixin): alias = "handler" required = True @@ -436,6 +453,7 @@ class BuildPythonFaaSRequest: output_path: OutputPathField runtime: PythonFaaSRuntimeField pex3_venv_create_extra_args: PythonFaaSPex3VenvCreateExtraArgsField + pex_build_extra_args: PythonFaaSPexBuildExtraArgs layout: PythonFaaSLayoutField include_requirements: bool @@ -457,6 +475,8 @@ async def build_python_faas( # When we're executing Pex on Linux, allow a local interpreter to be resolved if # available and matching the AMI platform. "--resolve-local-platforms", + # Additional args from request + *(request.pex_build_extra_args.value or ()), ) platforms_get = Get( diff --git a/src/python/pants/backend/python/util_rules/faas_test.py b/src/python/pants/backend/python/util_rules/faas_test.py index 275b7401864..ab8cbc7d9cc 100644 --- a/src/python/pants/backend/python/util_rules/faas_test.py +++ b/src/python/pants/backend/python/util_rules/faas_test.py @@ -26,6 +26,7 @@ PythonFaaSKnownRuntime, PythonFaaSLayoutField, PythonFaaSPex3VenvCreateExtraArgsField, + PythonFaaSPexBuildExtraArgs, PythonFaaSRuntimeField, ResolvedPythonFaaSHandler, ResolvePythonFaaSHandlerRequest, @@ -415,6 +416,7 @@ def test_venv_create_extra_args_are_passed_through() -> None: output_path=OutputPathField(None, addr), runtime=Mock(), pex3_venv_create_extra_args=extra_args_field, + pex_build_extra_args=PythonFaaSPexBuildExtraArgs(None, addr), layout=PythonFaaSLayoutField(PexVenvLayout.FLAT_ZIPPED.value, addr), include_requirements=False, include_sources=False, @@ -477,6 +479,7 @@ def test_layout_should_be_passed_through_and_adjust_filename(input_layout, expec output_path=OutputPathField(None, addr), runtime=Mock(), pex3_venv_create_extra_args=Mock(), + pex_build_extra_args=PythonFaaSPexBuildExtraArgs(None, addr), layout=input_layout, include_requirements=False, include_sources=False, @@ -513,3 +516,54 @@ def test_layout_should_be_passed_through_and_adjust_filename(input_layout, expec args = mock_build.mock_calls[0].args[0] assert args.layout == input_layout assert args.output_path.name == expected_output + + +def test_pex_build_extra_args_passed_through() -> None: + addr = Address("addr") + extra_args = ("--exclude=test_package",) + + extra_pex_args_field = PythonFaaSPexBuildExtraArgs(extra_args, addr) + + request = BuildPythonFaaSRequest( + address=addr, + target_name="test", + complete_platforms=Mock(), + handler=None, + output_path=OutputPathField(None, addr), + runtime=Mock(), + pex3_venv_create_extra_args=Mock(), + pex_build_extra_args=extra_pex_args_field, + layout=PythonFaaSLayoutField(PexVenvLayout.FLAT_ZIPPED.value, addr), + include_requirements=False, + include_sources=False, + reexported_handler_module=None, + ) + + mock_build = Mock() + + # Exercise + run_rule_with_mocks( + build_python_faas, + rule_args=[request], + mock_gets=[ + MockGet( + output_type=RuntimePlatforms, + input_types=(RuntimePlatformsRequest,), + mock=lambda _: RuntimePlatforms(interpreter_version=None), + ), + MockGet( + output_type=ResolvedPythonFaaSHandler, + input_types=(ResolvePythonFaaSHandlerRequest,), + mock=lambda _: Mock(), + ), + MockGet(output_type=Digest, input_types=(CreateDigest,), mock=lambda _: EMPTY_DIGEST), + MockGet( + output_type=Pex, + input_types=(PexFromTargetsRequest,), + mock=mock_build, + ), + MockGet(output_type=PexVenv, input_types=(PexVenvRequest,), mock=Mock()), + ], + ) + + assert extra_args[0] in mock_build.mock_calls[0].args[0].additional_args