diff --git a/docs/notes/2.34.x.md b/docs/notes/2.34.x.md index 161da74f163..cffa15083ef 100644 --- a/docs/notes/2.34.x.md +++ b/docs/notes/2.34.x.md @@ -170,6 +170,12 @@ Fixed S3 downloads failing with HTTP 403 `SignatureDoesNotMatch` when the object #### Shell +#### Terraform + +The default version of `python-hcl2`, which the `terraform-hcl2-parser` subsystem uses to infer dependencies between Terraform modules, has been upgraded from `4.3.0` to [`8.1.4`](https://github.com/amplify-education/python-hcl2/releases/tag/v8.1.4). This fixes parse errors on valid Terraform, such as `for` used as an attribute name ([#21845](https://github.com/pantsbuild/pants/issues/21845)), `in` used as a block name ([#22401](https://github.com/pantsbuild/pants/issues/22401)), and conditional expressions split over multiple lines. + +If you install the parser from your own resolve with `[terraform-hcl2-parser].install_from_resolve`, support for `python-hcl2` versions older than 8.0 in that resolve is deprecated and will be removed in Pants 2.35. Pants logs a deprecation warning when it parses Terraform with one of them. Upgrade that resolve to `python-hcl2` 8.1.4 or newer. + #### Javascript #### TypeScript diff --git a/src/python/pants/backend/terraform/dependency_inference.py b/src/python/pants/backend/terraform/dependency_inference.py index 1a9499547b4..f35d19d0ca2 100644 --- a/src/python/pants/backend/terraform/dependency_inference.py +++ b/src/python/pants/backend/terraform/dependency_inference.py @@ -15,6 +15,7 @@ setup_venv_pex_process, ) from pants.backend.python.util_rules.pex import rules as pex_rules +from pants.backend.terraform.hcl2_parser import LEGACY_HCL2_MARKER from pants.backend.terraform.target_types import ( TerraformBackendTarget, TerraformDependenciesField, @@ -23,6 +24,7 @@ TerraformModuleSourcesField, TerraformVarFileTarget, ) +from pants.base.deprecated import warn_or_error from pants.base.glob_match_error_behavior import GlobMatchErrorBehavior from pants.base.specs import DirGlobSpec, DirLiteralSpec, RawSpecs from pants.core.target_types import LockfileTarget @@ -61,8 +63,7 @@ class TerraformHcl2Parser(PythonToolRequirementsBase): options_scope = "terraform-hcl2-parser" help_short = "Used to parse Terraform modules to infer their dependencies." - # versions 4.3.2+ have parsing issues; bump once resolved - default_requirements = ["python-hcl2>=3.0.5,<=4.3.0"] + default_requirements = ["python-hcl2>=8.1.4,<9"] register_interpreter_constraints = True @@ -120,6 +121,23 @@ async def setup_process_for_parse_terraform_module_sources( return process +def warn_if_legacy_hcl2(parser_stderr: bytes) -> None: + """Warn if the parser reported running with a python-hcl2 release older than 8.0.""" + if LEGACY_HCL2_MARKER not in parser_stderr: + return + scope = TerraformHcl2Parser.options_scope + warn_or_error( + removal_version="2.35.0.dev0", + entity=f"support for `python-hcl2` versions older than 8.0 in `[{scope}]`", + hint=softwrap( + f""" + Pants now uses `python-hcl2` 8.1.4 by default. Upgrade `python-hcl2` to 8.1.4 or + newer in the resolve named by `[{scope}].install_from_resolve`. + """ + ), + ) + + @dataclass(frozen=True) class TerraformModuleDependenciesInferenceFieldSet(FieldSet): required_fields = (TerraformModuleSourcesField, TerraformDependenciesField) @@ -257,6 +275,7 @@ async def _infer_dependencies_from_sources( ) ) ) + warn_if_legacy_hcl2(result.stderr) candidate_spec_paths = [line for line in result.stdout.decode("utf-8").split("\n") if line] # For each path, see if there is a `terraform_module` target at the specified spec_path. candidate_targets = await resolve_targets( diff --git a/src/python/pants/backend/terraform/dependency_inference_test.py b/src/python/pants/backend/terraform/dependency_inference_test.py index ec13ddec278..376acee571e 100644 --- a/src/python/pants/backend/terraform/dependency_inference_test.py +++ b/src/python/pants/backend/terraform/dependency_inference_test.py @@ -12,8 +12,10 @@ TerraformDeploymentDependenciesInferenceFieldSet, TerraformHcl2Parser, TerraformModuleDependenciesInferenceFieldSet, + warn_if_legacy_hcl2, ) from pants.backend.terraform.goals.lockfiles import rules as terraform_lockfile_rules +from pants.backend.terraform.hcl2_parser import LEGACY_HCL2_MARKER from pants.backend.terraform.target_types import ( TerraformBackendTarget, TerraformDeploymentTarget, @@ -242,6 +244,71 @@ def test_hcl_parser_wrapper_runs(rule_runner: RuleRunner, major_minor_interprete lines = {line for line in result.stdout.decode("utf-8").splitlines() if line} assert lines == {"grok", "foo/hello/world"} + assert LEGACY_HCL2_MARKER not in result.stderr + + +def test_hcl_parser_handles_keywords_and_multiline_conditionals(rule_runner: RuleRunner) -> None: + """python-hcl2 4.3.0 and earlier failed to parse each of these constructs.""" + rule_runner.write_files( + { + "foo/BUILD": "terraform_module(name='t')\n", + "foo/main.tf": textwrap.dedent( + """\ + # `for` as an attribute name: https://github.com/pantsbuild/pants/issues/21845 + resource "grafana_rule_group" "example" { + rule { + name = "example" + for = "5m" + } + } + + # `in` as a block name: https://github.com/pantsbuild/pants/issues/22401 + data "snowflake_schemas" "in" { + in { + database = "example" + } + } + + # A conditional expression split over multiple lines. + locals { + flavor = ( + var.enabled + ? "on" + : "off" + ) + } + + module "foo" { + source = "../grok" + } + """ + ), + } + ) + target = rule_runner.get_target(Address("foo", target_name="t")) + sources = rule_runner.request(HydratedSources, [HydrateSourcesRequest(target[SourcesField])]) + result = rule_runner.request( + ProcessResult, + [ + ParseTerraformModuleSources( + sources_digest=sources.snapshot.digest, paths=("foo/main.tf",) + ) + ], + ) + + lines = {line for line in result.stdout.decode("utf-8").splitlines() if line} + assert lines == {"grok"} + assert LEGACY_HCL2_MARKER not in result.stderr + + +def test_warn_if_legacy_hcl2(caplog: pytest.LogCaptureFixture) -> None: + warn_if_legacy_hcl2(b"") + assert not caplog.records + + warn_if_legacy_hcl2(LEGACY_HCL2_MARKER + b"\n") + assert len(caplog.records) == 1 + assert "DEPRECATED: support for `python-hcl2` versions older than 8.0" in caplog.text + assert "[terraform-hcl2-parser].install_from_resolve" in caplog.text def test_generate_lockfile_without_python_backend() -> None: diff --git a/src/python/pants/backend/terraform/hcl2.lock b/src/python/pants/backend/terraform/hcl2.lock index 9b2c73b4b5b..7fb3126e78f 100644 --- a/src/python/pants/backend/terraform/hcl2.lock +++ b/src/python/pants/backend/terraform/hcl2.lock @@ -36,21 +36,430 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "6c76d1d523b6fb8ca6d1f70308459f310853e124c4bbfe9fcc018bcacdc78a40", - "url": "https://files.pythonhosted.org/packages/0d/58/32f70aa0d776a467472ee0077929e03d4febb681f6b564cec1d58c09d95b/python_hcl2-4.3.0-py3-none-any.whl" + "hash": "75738bd95717d692a683495babf4d00063f1b211e2eca04b156c6d79b9a0feae", + "url": "https://files.pythonhosted.org/packages/5a/c7/9b8918ab4954ecad677b03f36f9121d5467a4285f41507fdc6789a33dc3c/python_hcl2-8.1.4-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "41e37e2a9b3d223da5e8ebc99e72b40d2315087e966f458f7eac13c7831d9b9e", - "url": "https://files.pythonhosted.org/packages/0b/8c/9c7858604212b25e040652fba3eb06514b363b857544f7321ca97185f001/python-hcl2-4.3.0.tar.gz" + "hash": "b4145c930540e99e3e9b4f7de297775bdf856df29782a7abdb0916cbfcab7865", + "url": "https://files.pythonhosted.org/packages/ff/3b/a66df999c382abf2d37f6c7b618a782958615acd41a53615f994dfd6611b/python_hcl2-8.1.4.tar.gz" } ], "project_name": "python-hcl2", "requires_dists": [ - "lark<2,>=1" + "lark<2.0,>=1.1.5", + "regex>=2024.4.16" ], - "requires_python": ">=3.7.0", - "version": "4.3.0" + "requires_python": ">=3.8.0", + "version": "8.1.4" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "951733b1bbdb71e377cec567b409f1a7881b47cfcad84121aa74cb575fa425ea", + "url": "https://files.pythonhosted.org/packages/8e/6e/69824d9aee1fd41c54ea7264654a47c8d9d84d8a228e11c2bcf4c201ed81/regex-2026.9.29-cp314-cp314t-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "f0fe9834e5aeccaf19a0d8feb296d66a24be1a7c9922002f842a682cd5abb787", + "url": "https://files.pythonhosted.org/packages/00/71/c87fc7a2e21a42f9d57489db32951c37eef56d153840459a80d464f0321d/regex-2026.9.29-cp313-cp313-macosx_10_13_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "fe3fa1dd453ed5c7f5ea23a26218329790ed7197a99b90e94330e313959a7f52", + "url": "https://files.pythonhosted.org/packages/01/4f/e1a3e46bb5315a4e18b01a990e7a28e2a16595609d50c442baf2815a3c65/regex-2026.9.29-cp314-cp314-musllinux_1_2_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "9e4482589065c8ecd761cff522dcd85f2d39e62f551e37e025d1c7d54772def3", + "url": "https://files.pythonhosted.org/packages/01/e1/9c7486d4afe8fdd1fe0ad60139f8aa91427381f409af6a29b609d8fdcb3a/regex-2026.9.29-cp311-cp311-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "f37964e4a5e993d2fd45147741e9dff7f34a2d8c00ab94c4ea0514a4677f959e", + "url": "https://files.pythonhosted.org/packages/06/7c/e10ea17fba31fb4a1f9d13ed53a2d2a9066a2aea58d7557e263f6d99e7b0/regex-2026.9.29-cp314-cp314t-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "c90fcf7804ea0a54b896ce0f2b9565350220b8d4890fd0db461a476a4c687963", + "url": "https://files.pythonhosted.org/packages/11/9e/aa0f4cde3bc4688c1d58b0cd8415edd708339bc0bc401a195b0b1e8c8f0c/regex-2026.9.29-cp313-cp313-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "ebb8912f565b8cdbbf27debfe00df04202c20e2f651b9e32767930c5eace3621", + "url": "https://files.pythonhosted.org/packages/12/62/d0314bcedfd3586197e4596931fa220260eb2385bf53184e5b9ae67db24b/regex-2026.9.29-cp313-cp313-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "a540abfab208e1b7ef2df231c40ef3b6cbb30a0aad6204e9b6a81c10a6794628", + "url": "https://files.pythonhosted.org/packages/14/4b/dddef8fc15c63e4347cc9efb138d0cd306f30e6c98acbcc81a8f780083b9/regex-2026.9.29-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "d06fcdecc10fc7954d7c8f27a03c96055fe525274dc84a7b0dbdc3d6b9e03dab", + "url": "https://files.pythonhosted.org/packages/1a/52/e3da19fc3cc15ef67ab67e121e87887c3bccfdb683a7a9ec557c460ca5b7/regex-2026.9.29-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "87fb80cbe3557e27e7b28b995c2b2eedf689b8886f941ab93e0e288f0976518a", + "url": "https://files.pythonhosted.org/packages/1b/07/58a3c0153c7476898430f6a7cf3d9062a1d17fbea4f43399ecaf411c7b4c/regex-2026.9.29-cp312-cp312-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "a760da040b47767b4b873adfb7c3b691e9ba2fc60f113f9d0b88f1a62f323e85", + "url": "https://files.pythonhosted.org/packages/21/32/1b03534c4715aca3b564416d28d518083ed4dab3bc913267600d2256140d/regex-2026.9.29-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "0c992c19cd45058a4b92f68f139c93db168b48fb1f322c9a7cd620806afb6b51", + "url": "https://files.pythonhosted.org/packages/24/00/aeebdb540c620a0f7317f6d6fad80a47729ecf0599a24b5c34ec155351f5/regex-2026.9.29-cp313-cp313-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "39ab5894d971f9ac68baa6eca5c50387db579cfcacf36ae8df3feceb1815e6d0", + "url": "https://files.pythonhosted.org/packages/26/83/d2fbd2e4e3afb1167daa825187d196f313cbaa1a4768f311fb041bb0e3d2/regex-2026.9.29-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "864e9b87ac33c3fb9fb4ad48166d4fdb579c351d5c77deb0d34bccb36a775cd9", + "url": "https://files.pythonhosted.org/packages/26/a3/de43ac6b877b7d09c19a3a426b1bd5acdd209eaaf68f406466f80439ccf6/regex-2026.9.29-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "d60030baaa7bfbb02d650c126cdcddcb6e33dbff14d819434c8fa2fdcaeeeba5", + "url": "https://files.pythonhosted.org/packages/26/c7/49d008ff5f741d9a9799d7315556f3a12b983ff0fcd2cdfb62904bedafbf/regex-2026.9.29-cp311-cp311-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "0476e5bcbe6e1ba3d1c4cc7bbb1c3ba78e3b979b5c8a88d0a6a8cdd4992b8c84", + "url": "https://files.pythonhosted.org/packages/2a/b4/e1c32fdee0462f1373c52a73325cafbde653fac0b0cc1e9badac74154f4a/regex-2026.9.29-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "3c5c2ef13797466aa64170cbb66ad98a32351dd4127694cea7199f80f213750d", + "url": "https://files.pythonhosted.org/packages/2a/e8/161b94d39164520e21a7befe0245569bf7fda4c7cf1fc4e2df2b5def49da/regex-2026.9.29-cp312-cp312-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "bb90e7177944b6684738c1fc36aabd2dd00d1de3be7dbe09f91e196f1bc0dc81", + "url": "https://files.pythonhosted.org/packages/2c/57/ba8809847fbae8d2cbc71367c6ded510a7ec88bf52493c65efc1acf4effb/regex-2026.9.29-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "0cc63b5e47c12a48d90c7e9d7de6a035dd14f62868aaedbb4e0ff8ba2b8bfe7b", + "url": "https://files.pythonhosted.org/packages/2c/fe/f303b4acfda44e1ff1379368748c1ef2dad04a6a8e9c0ecbc970b19d97ca/regex-2026.9.29-cp314-cp314-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "3a21a9509d0ee88e7a70e1ad228cd2f0e0fd1e187458db132e8a8d18c97daf9d", + "url": "https://files.pythonhosted.org/packages/2e/1c/4ee3e97c76f53940488dfe7a7e18705e78daac8cd7fb161d246b9e328449/regex-2026.9.29-cp312-cp312-macosx_10_13_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "f57dc6b8fef170f105d2cf5cdce254f47b137d7755086cf7050f47e16582abba", + "url": "https://files.pythonhosted.org/packages/37/14/f3f0ba083d2094392d5eabf56db5ea6ba469fd6e927afd187042054ea68a/regex-2026.9.29-cp312-cp312-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "b7b893976e7fe42053da64f2aa27239c24252fd2ec6df471e1be197c0addc3b1", + "url": "https://files.pythonhosted.org/packages/3a/a5/0c791a0e83ad1013d262c13247c4c77e0f4a8d05bdc167df96aba6681c0d/regex-2026.9.29-cp311-cp311-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "0dd8af32e9f7b56b7f95cc1fd79b23054c3bdc172392ae560acc24d57b7ffe71", + "url": "https://files.pythonhosted.org/packages/42/5b/ba61f6fe062eb8562e742367d177bb75370434138ef6c9d2a27114f8d613/regex-2026.9.29-cp312-cp312-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "e31f72490b7c12f7790e1e25c3afffd20503ee1bfb43461d7838b871ff244b19", + "url": "https://files.pythonhosted.org/packages/44/48/d58e5081119f5c223bbb37d2340acde3d069e1df8e8cd166c37502eee4da/regex-2026.9.29-cp314-cp314t-macosx_10_15_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "14e953ff3607c92d7675bf79c4d4509ef6782aa8c08509f179f9b3d6d0679e86", + "url": "https://files.pythonhosted.org/packages/45/0d/944b13e8286ecba61a3baeca28b375550821a279800bdb18fac41ade6637/regex-2026.9.29-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "c1a9a6651197fbed6f0212591418b9def774fc3f8324f78d1bf0e6a63e5f8aa1", + "url": "https://files.pythonhosted.org/packages/46/0b/eb429a7016610d44fc89a597163f8c9127505f0d7dc724dc9effbb6a3ac0/regex-2026.9.29-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "a655d34b2a6943af32401f3d94f72e9d731f6ad16285815550bf2b4ee69d420a", + "url": "https://files.pythonhosted.org/packages/4b/32/d76c9d91f5d798e2e9e67f6f85ec4ae35445ac425f7454797311cecb80ca/regex-2026.9.29-cp313-cp313-musllinux_1_2_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "6f7121a8914ed13fcfe2099f895341bfb789f004d4c5a0bdece8fa667da10849", + "url": "https://files.pythonhosted.org/packages/4f/18/cac15fb2829c33218ececf76333b66869a96e4f973480ec32494921750f0/regex-2026.9.29-cp310-cp310-musllinux_1_2_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "5eeb8edc6110d9194a4d0d54610f64c37a31c605b5dbb7e407fc6ec7fa34a4a1", + "url": "https://files.pythonhosted.org/packages/59/13/79f0b1846f5f342f92ddbd4b27b18bcb86da96d902c1a0be26520bde98d7/regex-2026.9.29-cp311-cp311-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "34b6925af9853bf461950e6508910f179fd6e9b1a7ec8548e069606b7e51a26b", + "url": "https://files.pythonhosted.org/packages/5c/a6/0086ad31cebb183c637d3198547075aa493afde308e1ff61fccccb29ba6e/regex-2026.9.29-cp314-cp314-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "724184b4aafed865e4f13ca313fdcb43024300c028ec67319cfa16847d84685e", + "url": "https://files.pythonhosted.org/packages/60/b6/b4f7e99249f596017c60ccad5faf9310fc8e3e59bb2244940a90a1b0bdff/regex-2026.9.29-cp314-cp314-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "044265d77d94f5e3cb2fd72c76723807c429cb8c533e9d4672d0334a6f14f588", + "url": "https://files.pythonhosted.org/packages/62/14/9940763201c51d537786304984c67d0fc3d2ed18837ffb6f09a869f6b6c9/regex-2026.9.29-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "7020ed44df30b3aa492c00ee3b52d0548c1f30c2c6c5bb13ae897680900d3413", + "url": "https://files.pythonhosted.org/packages/64/6b/32c2e6fc617e1d3f247e250fea31a9a35b1265bd32f585968aa13b9999b9/regex-2026.9.29-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "e1172147d28d8fbcf8cb8d26c41506169f5ad8fe9ec969cb116835a19d4d8eca", + "url": "https://files.pythonhosted.org/packages/6b/2d/755c6d13ef9c657378013676c391c7a402166b3f419a464a3e058dcbe533/regex-2026.9.29-cp314-cp314t-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "888d60953908dcf761aa320c3e390ab8556efbdb551ace63921de90f6ae0848d", + "url": "https://files.pythonhosted.org/packages/6c/d0/5808c95f9c79ed27b5eedaafc3df6239ec56a49f2e23ea8f831b18427c82/regex-2026.9.29-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "92f05c9c42bde5785dc48770bc2194d9f7442544156f951e19cd31b096cec562", + "url": "https://files.pythonhosted.org/packages/6c/fc/e1cab183b9dafe8597f58c1c766da9bf96204d3b2f232bcf3eeb75ff7b6c/regex-2026.9.29-cp314-cp314t-musllinux_1_2_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "80ea96f5c1a30bf09007d48466521d9c294bebe197c708c3359096e3e3691632", + "url": "https://files.pythonhosted.org/packages/72/3c/c49945287d4f9efee7d41f98072f8ad880efb8f430595a612fbdea996a4e/regex-2026.9.29-cp314-cp314t-macosx_10_15_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "8873c4a11c50b9989168881aeb3f08859f469d809941866aa1feefd8be5431f6", + "url": "https://files.pythonhosted.org/packages/72/6d/116db2946888bd60db8e2033739471be6d80aa47c833afd952c9ed40654c/regex-2026.9.29-cp310-cp310-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "49ee178ca31c94621294bf9b8b676a92a2e6bba8af0529591753719e57edb621", + "url": "https://files.pythonhosted.org/packages/76/a7/378f6f558d9e4444af315a307c5953565a511d1e3666f1bb7bdc82012b6b/regex-2026.9.29-cp311-cp311-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "f1a0d5117230dd46b399a30a38afa44f79c99f3168988fdc4f425c3f928b39df", + "url": "https://files.pythonhosted.org/packages/79/d5/6080f7d1a6e7e36aa720f806ac93c035ba39c209ae6cc510e8ef4c0279c6/regex-2026.9.29-cp313-cp313-macosx_10_13_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "fb00027a09a8f9f08028b40dce4c933cf73e4833240ed356583fdc9cfa721566", + "url": "https://files.pythonhosted.org/packages/7c/98/41fe60ae6ccf3e166fe7bc73495aa15e9a1a7fe3a1f9f0f95a3d64a2df0f/regex-2026.9.29-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "dc79d36d0618752265f0d575915bdc5c5130ecb9c9f6b3bcefeae32e4bdfafcf", + "url": "https://files.pythonhosted.org/packages/84/48/3fdcde9a0baa84d7d25571223265d6e434e114763b438601d54a8028bf3e/regex-2026.9.29-cp312-cp312-macosx_10_13_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "6398d5145689503412cc1748895242598d8846b8967b851133b20dc2ed1e21e8", + "url": "https://files.pythonhosted.org/packages/86/60/5e1c5c6e85132e757ae77bd7693f1809046edb2c8853658c9200f4c8593c/regex-2026.9.29-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "612b709381c0355b70d89cdb51b7f670591ed5cbbc0e3b5337488019dc667b65", + "url": "https://files.pythonhosted.org/packages/8a/70/aa08d1d2b294894b365e5f8ba5380fe3f8546acdb81f10639dfd74209c37/regex-2026.9.29-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "59b49507f47479e299a9e1bc41b5cb83a7afda0540625f1dbae886615978acbf", + "url": "https://files.pythonhosted.org/packages/8f/07/3b02ed829aa2decdc1955d222bd1e2f99d1c8bb4873bbb9a66b2f0a36bff/regex-2026.9.29-cp312-cp312-musllinux_1_2_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "e11edba5bc344a32b029a7af9d4b3173982dd79eeafa0b9dbd787364414b0509", + "url": "https://files.pythonhosted.org/packages/90/d4/e835c487850ed922a8d6074f953b888c8ea99775c76b9ed5f8a4d72eab92/regex-2026.9.29-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "957bb708e8057ab1649ba566456429d691ec9b90d1c9ad1af1ba7ffbbeaf05f2", + "url": "https://files.pythonhosted.org/packages/93/1f/d9dc6f02f569625faf67a4daec926cd5023472dcd69bb44286dccd5a5ab3/regex-2026.9.29-cp314-cp314-macosx_10_15_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "ccb64d887a9db1cd76dbc0f92051a1a478a2a67e7f56c62d915cb881d7734704", + "url": "https://files.pythonhosted.org/packages/97/19/05af70dec9f2eed6ba34e08d2dcc6a48e7ae5e307659d5fe4201a5d7bbee/regex-2026.9.29-cp311-cp311-musllinux_1_2_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "dabee8f4935e731fb46b2a3091bdda0d3d94b3bbfb907d2b4f12eefce4009619", + "url": "https://files.pythonhosted.org/packages/97/e6/a09ec3a23ae41d6179880e67f0aace9284b2d95f2d7b326eff203f8eec5e/regex-2026.9.29-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "26ec4ccce55aa533fbd603d08911b01101a8fcfec987845ac3ae2c7087b2bde3", + "url": "https://files.pythonhosted.org/packages/99/b9/d5a41adc08360f5eee0dc4846c578f002366947211fc8af5a69a64ee7b9f/regex-2026.9.29-cp313-cp313-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "d49c18f1ea294cf4adde2e5ac256e98c82ea9d708462ce4bf799dffa7cfe8a2c", + "url": "https://files.pythonhosted.org/packages/9a/8e/c1ed81f55f992f6aa0b699a592a50c1ce9e6d44ff1aee2c14c0537dcef9c/regex-2026.9.29-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "c9b602fae1e00b7c035d661ce85575365719192a7b46784bd71cf64c68053aa0", + "url": "https://files.pythonhosted.org/packages/9c/83/9b693a3fd1451381e812031a8961ec5b3b8f0c8cc6871f14c5223642804d/regex-2026.9.29-cp314-cp314-macosx_10_15_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "1d9fe8091b2e89d470df68a9331111ed008ae8aae6bf1e8e1fba4086a495c84e", + "url": "https://files.pythonhosted.org/packages/9d/1d/aefd12fb5cd62b748aa6ed28725b375c71102853fcbf15fb6481f806a2ba/regex-2026.9.29-cp310-cp310-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "ddfa987262763c3c22a8367d2a49c244b018a74c3a8e3ab1a864119ad45c5633", + "url": "https://files.pythonhosted.org/packages/9f/cb/38daabed32d28f7e58a06e9344ce00dc67952e9996bc578ed6a29fe1240e/regex-2026.9.29-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "2f7f7aa47b229f2b39a2ae2596d2ad5625d77b5eb9856fac2dab3eb506cdd0a0", + "url": "https://files.pythonhosted.org/packages/a9/4d/041d9458a645fee4fce4d642a89d27271a3cfcd91095104f6dde44da70bf/regex-2026.9.29-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "3e778bfccd63075167709136afbc251c1f683758d5bf49c803c60ac3f894ce6b", + "url": "https://files.pythonhosted.org/packages/ad/bc/5a6886eb470e41040e21e05b75024a18b6ebfe7ea400b72094a60f949101/regex-2026.9.29-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "066d0e3dbfdd739bce2bf8c2a41dd16f73e3d8adc2eb06dd803a36a307f56075", + "url": "https://files.pythonhosted.org/packages/b1/07/9bf3607d8d13a12e436ab9d63f9791e10706827d535695b23964ad79fd79/regex-2026.9.29-cp311-cp311-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "b9d74e4eee9ddb64c2e92d5d61472c59c21684c059eb7b68767be9628e977859", + "url": "https://files.pythonhosted.org/packages/b8/55/967676e4089a25d5f54b9d62522f9fe09b87677d1db58f207888d4ccfbe3/regex-2026.9.29-cp310-cp310-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "ae4613d7d9dda60fcba95f846cc6f808017f1843f392cf9daad14a6534493d71", + "url": "https://files.pythonhosted.org/packages/bf/72/f041177f3c7a4606f7c81a95fe7eea03e2a0c4e8bff9e439a01432cbc9f2/regex-2026.9.29-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "d9b77b25b4f395f92de6099ab08e8ae2bc7e51dfe157f22900902243a5cc90c7", + "url": "https://files.pythonhosted.org/packages/bf/c4/4383eed7aa5aef67616cb1b3f3ad06b7c624c4e6cced48630cd5ce133d85/regex-2026.9.29-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "ed511a0708e2297e1d6431e7fb217e3402791e491e02da800658ace4973df1bb", + "url": "https://files.pythonhosted.org/packages/bf/d3/021ca2638671ad20603bcd9b4d5bfa35d2610cd216a043ea7f0b44ea39f6/regex-2026.9.29-cp314-cp314t-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "f93bc1c3486ef3747e07c9d7c1d0a147b8fbaab975f80e348aed6f71309dfaca", + "url": "https://files.pythonhosted.org/packages/c9/72/67e7a8ce17f1aea49df215564048efb49cc8c2b31a0e0fc30f36838f8516/regex-2026.9.29-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "686ac5350fceae63830bb98805fcb8039325bf4c06d9f6f048ff65229d5bffa5", + "url": "https://files.pythonhosted.org/packages/cb/52/6d951d453b023c6edb880f1ba474291b53b8ce1cc438b96a9db6d791d991/regex-2026.9.29-cp313-cp313-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "db5e82ba15c142425b8406690032df89e39cca4a2e8afbbb9a3d84edc2373ac3", + "url": "https://files.pythonhosted.org/packages/cc/27/767259b20e8a842948990f5e99138d6c077248fd42f8b5468b1d9ca4b814/regex-2026.9.29-cp312-cp312-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "bec37990e3d6121f29ecfb594bd8f1bf009e9f7926daba2e50e3b27d3892a783", + "url": "https://files.pythonhosted.org/packages/d0/4e/a78948e11dd715e0e46716c2e0f3404b3fe6a44e2a2e9abdc7d965cab2b3/regex-2026.9.29-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "2089fe39c406784d90101c726755ffa1497bb74638fd434300d2b88006186de8", + "url": "https://files.pythonhosted.org/packages/d3/e1/c842d8df0b23245ebf202f8ab9c39fd48e2db39959454ec39a41c8c72082/regex-2026.9.29-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "addd736a0547d553283adaf4e05d7104e7f2c7b0b092e9b4d28756825f14531f", + "url": "https://files.pythonhosted.org/packages/d5/a0/f9005cba3f629a859573fc5d1224ea4e1f97919ec8581d018e03a351a604/regex-2026.9.29-cp314-cp314-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "0def9fb6abac55492d6d51cddb7225d07d6f279e774e0adc08569a54a5fc8d46", + "url": "https://files.pythonhosted.org/packages/d8/c1/98622479e3c354a446a75232e522d747d2b3df23092dcd8a5309380a2020/regex-2026.9.29-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "edf06545875f3efa31560d94121e95c7fd70d98b1dfedc0157097d79b13b52ea", + "url": "https://files.pythonhosted.org/packages/dc/26/9e4f158459e37e41633c96f49766a465f801c5daba9507260609ed76d783/regex-2026.9.29-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "b97a38fb4c732b6832db6bf108963adbcd82ef1268ba2025dce390f45af75efa", + "url": "https://files.pythonhosted.org/packages/dc/fc/cf50671215ee0057046980b4571ef8646a005819bb67f0957e779ed107a5/regex-2026.9.29-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "0166844493626c5015c6088ee15c9ca2fd060ca15b7641d1657da6a58432ae33", + "url": "https://files.pythonhosted.org/packages/dd/5f/52bc2abc3fef040cd9de76ab29c918d6a717a454ae2b9dd7938b0c95656d/regex-2026.9.29-cp314-cp314-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "45010bcfe66df41522d56c9b6114e87ecc597a08970ff6a2ced24415c141ae5f", + "url": "https://files.pythonhosted.org/packages/e5/13/301b394f32a8db86ae3280533c9186229595fed766376f18e87959f92f96/regex-2026.9.29-cp310-cp310-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "6abb75ab16bc3281714a5b99548a2225db70dba1f995f6d7f7419b76eb5a8fbe", + "url": "https://files.pythonhosted.org/packages/e8/6b/6dea87689c3a06a6e79d254bf824e6f3e3d724b5ba027c6112559aa6cd2c/regex-2026.9.29-cp311-cp311-macosx_10_9_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "4fb41211d2333eb930a51e0546a65999761cf1f572a4da56ef9b8a62966c06f2", + "url": "https://files.pythonhosted.org/packages/ec/99/eae371ca63f7ea1f1eac025021527adb57ce3a4b67ff608b896fd31ea447/regex-2026.9.29-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "143533cc4b6fbc5b95aca0a5b8d541088d374831593def000ec89322c220221d", + "url": "https://files.pythonhosted.org/packages/f1/8d/bb4165d61860ed443da5fb0fafe629c1ee9202526394eead7925bdee2be4/regex-2026.9.29-cp310-cp310-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "a5758353650079898dc1b2b0e95aa51fa23a30d020e06f62c430dd08ee56cdd8", + "url": "https://files.pythonhosted.org/packages/f6/27/0c647127db32760b2dfc4c38da9ca509a3c4a8d3d93a586121a10fef2701/regex-2026.9.29-cp310-cp310-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "9916fda742cd4eede63b286f58c06718324265d727ce0856eb1aac86d0d150d6", + "url": "https://files.pythonhosted.org/packages/f6/37/404442e7296554792e2de4c0c8f8b95ee491a3c227d6ff90d4cebf2dbde3/regex-2026.9.29-cp310-cp310-macosx_10_9_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "9e1d3a4cb7993b708f0ada8d0c84590efd853f169e7147d2202c9da503180242", + "url": "https://files.pythonhosted.org/packages/f6/78/25436bcfd4d2260b4b4090094d55d7ab53ec8a1ab4865a0b8bcb33c7d5c0/regex-2026.9.29-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "554bffadcbcb6d5f4e5fb10a61cc52084b9a63d1dab5f10bcd2c4343972e8e2c", + "url": "https://files.pythonhosted.org/packages/f9/1f/688cb61c3d4cf7bcc1ed444b5cc49399eba3e51c469ae285cf87fea3022e/regex-2026.9.29-cp314-cp314t-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "8b5fcc4771732191b2b7d1dd68d8f0353f47f8d90b6150f6dce58bf1112442cb", + "url": "https://files.pythonhosted.org/packages/fc/f2/af1da9d3ceed77bfcdce40427d49ba0be94e4fe84245e3bfef68c10e75b6/regex-2026.9.29.tar.gz" + } + ], + "project_name": "regex", + "requires_dists": [], + "requires_python": ">=3.10", + "version": "2026.9.29" } ], "marker": null, @@ -61,11 +470,11 @@ "only_wheels": [], "overridden": [], "path_mappings": {}, - "pex_version": "2.96.1", - "pip_version": "26.1.2", + "pex_version": "2.104.1", + "pip_version": "26.2.2", "prefer_older_binary": false, "requirements": [ - "python-hcl2<=4.3.0,>=3.0.5" + "python-hcl2<9,>=8.1.4" ], "requires_python": [ "CPython<3.15,>=3.10" diff --git a/src/python/pants/backend/terraform/hcl2.lock.metadata b/src/python/pants/backend/terraform/hcl2.lock.metadata index 4349c2a2fa1..105088b79aa 100644 --- a/src/python/pants/backend/terraform/hcl2.lock.metadata +++ b/src/python/pants/backend/terraform/hcl2.lock.metadata @@ -4,7 +4,7 @@ "CPython<3.15,>=3.10" ], "generated_with_requirements": [ - "python-hcl2<=4.3.0,>=3.0.5" + "python-hcl2<9,>=8.1.4" ], "manylinux": "manylinux2014", "requirement_constraints": [], diff --git a/src/python/pants/backend/terraform/hcl2_parser.py b/src/python/pants/backend/terraform/hcl2_parser.py index 06dd38bb789..a4f2b0d5491 100644 --- a/src/python/pants/backend/terraform/hcl2_parser.py +++ b/src/python/pants/backend/terraform/hcl2_parser.py @@ -9,6 +9,10 @@ # Note: This file is used as a pex entry point in the execution sandbox. # +# Printed to stderr when the installed python-hcl2 is older than 8.0, so that Pants can warn that +# support for it is deprecated. +LEGACY_HCL2_MARKER = b"__pants_legacy_python_hcl2__" + # PurePath does not have the Path.resolve method which resolves ".." components, thus we need to # code our own version for PurePath's. @@ -33,7 +37,17 @@ def extract_module_source_paths(path: PurePath, raw_content: bytes) -> Set[str]: import hcl2 # type: ignore[import-not-found] # pants: no-infer-dep content = raw_content.decode("utf-8") - parsed_content = hcl2.loads(content) + if hasattr(hcl2, "SerializationOptions"): + # python-hcl2 keeps the quotes around string values (e.g. `"./foo"`) unless asked to strip + # them. + parsed_content = hcl2.loads( + content, serialization_options=hcl2.SerializationOptions(strip_string_quotes=True) + ) + else: + # TODO: Remove support for python-hcl2 < 8.0 in Pants 2.35.0.dev0, along with the warning in + # `main`. Those versions have no `SerializationOptions`, and strip the quotes around string + # values themselves. + parsed_content = hcl2.loads(content) # Note: The `module` key is a list where each entry is a dict with a single entry where the key is the # module name and the values are a dict for that module's actual values. @@ -57,6 +71,12 @@ def extract_module_source_paths(path: PurePath, raw_content: bytes) -> Set[str]: def main(args): + import hcl2 # pants: no-infer-dep + + # TODO: Remove in Pants 2.35.0.dev0, along with the fallback in `extract_module_source_paths`. + if not hasattr(hcl2, "SerializationOptions"): + sys.stderr.buffer.write(LEGACY_HCL2_MARKER + b"\n") + paths = set() for filename in args: with open(filename, "rb") as f: diff --git a/src/python/pants/backend/terraform/hcl2_parser_test.py b/src/python/pants/backend/terraform/hcl2_parser_test.py index 8752601947b..ad5e2c68f01 100644 --- a/src/python/pants/backend/terraform/hcl2_parser_test.py +++ b/src/python/pants/backend/terraform/hcl2_parser_test.py @@ -1,11 +1,14 @@ # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -from pathlib import PurePath +import sys +from pathlib import Path, PurePath +from types import ModuleType +from typing import Any import pytest -from pants.backend.terraform.hcl2_parser import resolve_pure_path +from pants.backend.terraform.hcl2_parser import LEGACY_HCL2_MARKER, main, resolve_pure_path def test_resolve_pure_path() -> None: @@ -20,3 +23,52 @@ def test_resolve_pure_path() -> None: assert resolve_pure_path(PurePath("foo/bar/hello/world"), PurePath("./grok")) == PurePath( "foo/bar/hello/world/grok" ) + + +PARSED_MODULE = {"module": [{"grok": {"source": "../grok"}}]} + + +def run_main_with_fake_hcl2( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path, **hcl2_attrs: Any +) -> None: + hcl2 = ModuleType("hcl2") + hcl2.__dict__.update(hcl2_attrs) + monkeypatch.setitem(sys.modules, "hcl2", hcl2) + monkeypatch.chdir(tmp_path) + (tmp_path / "foo").mkdir() + (tmp_path / "foo" / "main.tf").write_text('module "grok" {\n source = "../grok"\n}\n') + main(["foo/main.tf"]) + + +def test_main_strips_string_quotes( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsysbinary: pytest.CaptureFixture[bytes] +) -> None: + class SerializationOptions: + def __init__(self, **kwargs: Any) -> None: + self.kwargs = kwargs + + def loads(content: str, *, serialization_options: SerializationOptions) -> dict: + assert serialization_options.kwargs == {"strip_string_quotes": True} + return PARSED_MODULE + + run_main_with_fake_hcl2( + monkeypatch, tmp_path, loads=loads, SerializationOptions=SerializationOptions + ) + + out, err = capsysbinary.readouterr() + assert out.splitlines() == [b"grok"] + assert LEGACY_HCL2_MARKER not in err + + +def test_main_falls_back_for_legacy_hcl2( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsysbinary: pytest.CaptureFixture[bytes] +) -> None: + # python-hcl2 before 8.x has no `SerializationOptions`, and strips the quotes itself. + def loads(content: str) -> dict: + return PARSED_MODULE + + run_main_with_fake_hcl2(monkeypatch, tmp_path, loads=loads) + + out, err = capsysbinary.readouterr() + assert out.splitlines() == [b"grok"] + assert LEGACY_HCL2_MARKER in err