diff --git a/pants.toml b/pants.toml index ae48fcdd8c1..79e161c2d57 100644 --- a/pants.toml +++ b/pants.toml @@ -11,6 +11,7 @@ backend_packages.add = [ "pants.backend.python.lint.docformatter", "pants.backend.python.lint.flake8", "pants.backend.python.lint.isort", + "pants.backend.python.lint.yapf", "pants.backend.python.typecheck.mypy", "pants.backend.python.mixed_interpreter_constraints", "pants.backend.shell", @@ -75,6 +76,16 @@ root_patterns = [ experimental_lockfile = "3rdparty/python/lockfile.txt" interpreter_constraints = [">=3.7,<3.10"] +[python-protobuf.mypy-plugin] +experimental_lockfile = "src/python/pants/backend/codegen/protobuf/python/mypy_protobuf_lockfile.txt" + +[lambdex] +experimental_lockfile = "src/python/pants/backend/awslambda/python/lambdex_lockfile.txt" + +[black] +# TODO(#12200): Figure out how to get this lockfile to work with multiple interpreters. +# experimental_lockfile = "src/python/pants/backend/python/lint/black/lockfile.txt" + [docformatter] args = ["--wrap-summaries=100", "--wrap-descriptions=100"] experimental_lockfile = "src/python/pants/backend/python/lint/docformatter/lockfile.txt" @@ -87,7 +98,12 @@ extra_requirements.add = [ ] [isort] -version = "isort[pyproject,colors]>=5.5.1,<5.6" +experimental_lockfile = "src/python/pants/backend/python/lint/isort/lockfile.txt" + +[yapf] +# We only activate this to generate and maintain a lockfile - we don't actually want to use yapf. +skip = true +experimental_lockfile = "src/python/pants/backend/python/lint/yapf/lockfile.txt" [shellcheck] args = ["--external-sources"] @@ -166,6 +182,7 @@ extra_env_vars = [ [coverage-py] interpreter_constraints = [">=3.7,<3.10"] +experimental_lockfile = "src/python/pants/backend/python/subsystems/coverage_py_lockfile.txt" [sourcefile-validation] config = "@build-support/regexes/config.yaml" diff --git a/src/python/pants/backend/awslambda/python/lambdex.py b/src/python/pants/backend/awslambda/python/lambdex.py index 05475f368df..2a212b829ba 100644 --- a/src/python/pants/backend/awslambda/python/lambdex.py +++ b/src/python/pants/backend/awslambda/python/lambdex.py @@ -1,8 +1,15 @@ # Copyright 2019 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). +from pants.backend.experimental.python.lockfile import ( + PythonToolLockfileRequest, + PythonToolLockfileSentinel, +) from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript +from pants.engine.rules import collect_rules, rule +from pants.engine.unions import UnionRule +from pants.util.docutil import git_url class Lambdex(PythonToolBase): @@ -10,6 +17,26 @@ class Lambdex(PythonToolBase): help = "A tool for turning .pex files into AWS Lambdas (https://github.com/wickman/lambdex)." default_version = "lambdex==0.1.4" + default_main = ConsoleScript("lambdex") + register_interpreter_constraints = True default_interpreter_constraints = ["CPython>=3.6"] - default_main = ConsoleScript("lambdex") + + register_lockfile = True + default_lockfile_resource = ("pants.backend.awslambda.python", "lambdex_lockfile.txt") + default_lockfile_url = git_url("src/python/pants/backend/awslambda/python/lambdex_lockfile.txt") + + +class LambdexLockfileSentinel(PythonToolLockfileSentinel): + pass + + +@rule +def setup_lambdex_lockfile( + _: LambdexLockfileSentinel, lambdex: Lambdex +) -> PythonToolLockfileRequest: + return PythonToolLockfileRequest.from_tool(lambdex) + + +def rules(): + return (*collect_rules(), UnionRule(PythonToolLockfileSentinel, LambdexLockfileSentinel)) diff --git a/src/python/pants/backend/awslambda/python/lambdex_lockfile.txt b/src/python/pants/backend/awslambda/python/lambdex_lockfile.txt new file mode 100644 index 00000000000..0a10b1ab350 --- /dev/null +++ b/src/python/pants/backend/awslambda/python/lambdex_lockfile.txt @@ -0,0 +1,14 @@ +# +# This file is autogenerated by pip-compile with python 3.6 +# To update, run: +# +# pip-compile --allow-unsafe --generate-hashes --output-file=src/python/pants/backend/awslambda/python/lambdex_lockfile.txt requirements_lambdex.in +# +lambdex==0.1.4 \ + --hash=sha256:189e3f7aa492fd3653a6dcafb7bb493d2fa60a32f6ab8122c2ee53feb97967cc \ + --hash=sha256:51e4e92a20cf1472e560b7b5a927286308d9e6252261a79d94b8549a3ba39bd3 + # via -r requirements_lambdex.in +pex==2.1.43 \ + --hash=sha256:a2ac38d27730e00ffc9c51e8775cef2e3de9f7337eeaa4d84d0a2efdeb08e712 \ + --hash=sha256:edc55cfae83d6cab51dde79a1b86f04d4227b5ec85dbed789090451ad9a798f2 + # via lambdex diff --git a/src/python/pants/backend/awslambda/python/register.py b/src/python/pants/backend/awslambda/python/register.py index 82ebe384de0..274b1f23053 100644 --- a/src/python/pants/backend/awslambda/python/register.py +++ b/src/python/pants/backend/awslambda/python/register.py @@ -6,13 +6,14 @@ See https://www.pantsbuild.org/docs/awslambda-python. """ +from pants.backend.awslambda.python import lambdex from pants.backend.awslambda.python import rules as python_rules from pants.backend.awslambda.python.target_types import PythonAWSLambda from pants.backend.awslambda.python.target_types import rules as target_types_rules def rules(): - return (*python_rules.rules(), *target_types_rules()) + return (*python_rules.rules(), *target_types_rules(), *lambdex.rules()) def target_types(): diff --git a/src/python/pants/backend/codegen/protobuf/python/mypy_protobuf_lockfile.txt b/src/python/pants/backend/codegen/protobuf/python/mypy_protobuf_lockfile.txt new file mode 100644 index 00000000000..0e2d4bba4c2 --- /dev/null +++ b/src/python/pants/backend/codegen/protobuf/python/mypy_protobuf_lockfile.txt @@ -0,0 +1,43 @@ +# +# This file is autogenerated by pip-compile with python 3.6 +# To update, run: +# +# pip-compile --allow-unsafe --generate-hashes --output-file=src/python/pants/backend/codegen/protobuf/python/mypy_protobuf_lockfile.txt requirements_python-protobuf.mypy-plugin.in +# +mypy-protobuf==2.4 \ + --hash=sha256:1986dc8a7994a4e549be5c8ac586aa8247caf92456c67159d71dbe5005809651 \ + --hash=sha256:77e10c476cdd3ee14535c2357e64deac6b1a69f33eb500d795b064acda48c66f + # via -r requirements_python-protobuf.mypy-plugin.in +protobuf==3.17.3 \ + --hash=sha256:13ee7be3c2d9a5d2b42a1030976f760f28755fcf5863c55b1460fd205e6cd637 \ + --hash=sha256:145ce0af55c4259ca74993ddab3479c78af064002ec8227beb3d944405123c71 \ + --hash=sha256:14c1c9377a7ffbeaccd4722ab0aa900091f52b516ad89c4b0c3bb0a4af903ba5 \ + --hash=sha256:1556a1049ccec58c7855a78d27e5c6e70e95103b32de9142bae0576e9200a1b0 \ + --hash=sha256:26010f693b675ff5a1d0e1bdb17689b8b716a18709113288fead438703d45539 \ + --hash=sha256:2ae692bb6d1992afb6b74348e7bb648a75bb0d3565a3f5eea5bec8f62bd06d87 \ + --hash=sha256:2bfb815216a9cd9faec52b16fd2bfa68437a44b67c56bee59bc3926522ecb04e \ + --hash=sha256:4ffbd23640bb7403574f7aff8368e2aeb2ec9a5c6306580be48ac59a6bac8bde \ + --hash=sha256:59e5cf6b737c3a376932fbfb869043415f7c16a0cf176ab30a5bbc419cd709c1 \ + --hash=sha256:6902a1e4b7a319ec611a7345ff81b6b004b36b0d2196ce7a748b3493da3d226d \ + --hash=sha256:6ce4d8bf0321e7b2d4395e253f8002a1a5ffbcfd7bcc0a6ba46712c07d47d0b4 \ + --hash=sha256:6d847c59963c03fd7a0cd7c488cadfa10cda4fff34d8bc8cba92935a91b7a037 \ + --hash=sha256:72804ea5eaa9c22a090d2803813e280fb273b62d5ae497aaf3553d141c4fdd7b \ + --hash=sha256:7a4c97961e9e5b03a56f9a6c82742ed55375c4a25f2692b625d4087d02ed31b9 \ + --hash=sha256:85d6303e4adade2827e43c2b54114d9a6ea547b671cb63fafd5011dc47d0e13d \ + --hash=sha256:8727ee027157516e2c311f218ebf2260a18088ffb2d29473e82add217d196b1c \ + --hash=sha256:99938f2a2d7ca6563c0ade0c5ca8982264c484fdecf418bd68e880a7ab5730b1 \ + --hash=sha256:9b7a5c1022e0fa0dbde7fd03682d07d14624ad870ae52054849d8960f04bc764 \ + --hash=sha256:a22b3a0dbac6544dacbafd4c5f6a29e389a50e3b193e2c70dae6bbf7930f651d \ + --hash=sha256:a38bac25f51c93e4be4092c88b2568b9f407c27217d3dd23c7a57fa522a17554 \ + --hash=sha256:a981222367fb4210a10a929ad5983ae93bd5a050a0824fc35d6371c07b78caf6 \ + --hash=sha256:ab6bb0e270c6c58e7ff4345b3a803cc59dbee19ddf77a4719c5b635f1d547aa8 \ + --hash=sha256:c56c050a947186ba51de4f94ab441d7f04fcd44c56df6e922369cc2e1a92d683 \ + --hash=sha256:e76d9686e088fece2450dbc7ee905f9be904e427341d289acbe9ad00b78ebd47 \ + --hash=sha256:ebcb546f10069b56dc2e3da35e003a02076aaa377caf8530fe9789570984a8d2 \ + --hash=sha256:f0e59430ee953184a703a324b8ec52f571c6c4259d496a19d1cabcdc19dabc62 \ + --hash=sha256:ffea251f5cd3c0b9b43c7a7a912777e0bc86263436a87c2555242a348817221b + # via mypy-protobuf +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 + # via protobuf 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 6ea1debeb14..f34610728ff 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 @@ -4,6 +4,10 @@ from typing import cast from pants.backend.codegen.protobuf.target_types import ProtobufDependencies +from pants.backend.experimental.python.lockfile import ( + PythonToolLockfileRequest, + PythonToolLockfileSentinel, +) from pants.backend.python.subsystems.python_tool_base import PythonToolRequirementsBase from pants.engine.addresses import Addresses, UnparsedAddressInputs from pants.engine.rules import Get, collect_rules, rule @@ -11,7 +15,7 @@ from pants.engine.unions import UnionRule from pants.option.custom_types import target_option from pants.option.subsystem import Subsystem -from pants.util.docutil import doc_url +from pants.util.docutil import doc_url, git_url class PythonProtobufSubsystem(Subsystem): @@ -59,9 +63,30 @@ class PythonProtobufMypyPlugin(PythonToolRequirementsBase): ) default_version = "mypy-protobuf==2.4" + register_interpreter_constraints = True default_interpreter_constraints = ["CPython>=3.6"] + register_lockfile = True + default_lockfile_resource = ( + "pants.backend.codegen.protobuf.python", + "mypy_protobuf_lockfile.txt", + ) + default_lockfile_url = git_url( + "src/python/pants/backend/codegen/protobuf/python/mypy_protobuf_lockfile.txt" + ) + + +class MypyProtobufLockfileSentinel(PythonToolLockfileSentinel): + pass + + +@rule +def setup_bandit_lockfile( + _: MypyProtobufLockfileSentinel, mypy_protobuf: PythonProtobufMypyPlugin +) -> PythonToolLockfileRequest: + return PythonToolLockfileRequest.from_tool(mypy_protobuf) + class InjectPythonProtobufDependencies(InjectDependenciesRequest): inject_for = ProtobufDependencies @@ -79,4 +104,5 @@ def rules(): return [ *collect_rules(), UnionRule(InjectDependenciesRequest, InjectPythonProtobufDependencies), + UnionRule(PythonToolLockfileSentinel, MypyProtobufLockfileSentinel), ] diff --git a/src/python/pants/backend/experimental/python/lockfile.py b/src/python/pants/backend/experimental/python/lockfile.py index fecdee49454..17f9abe5051 100644 --- a/src/python/pants/backend/experimental/python/lockfile.py +++ b/src/python/pants/backend/experimental/python/lockfile.py @@ -7,7 +7,10 @@ from dataclasses import dataclass from typing import cast -from pants.backend.python.subsystems.python_tool_base import PythonToolBase +from pants.backend.python.subsystems.python_tool_base import ( + PythonToolBase, + PythonToolRequirementsBase, +) from pants.backend.python.target_types import ConsoleScript, PythonRequirementsField from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints from pants.backend.python.util_rules.pex import PexRequest, PexRequirements, VenvPex, VenvPexProcess @@ -188,6 +191,15 @@ class PythonToolLockfileRequest: requirements: tuple[str, ...] interpreter_constraints: InterpreterConstraints + @classmethod + def from_tool(cls, subsystem: PythonToolRequirementsBase) -> PythonToolLockfileRequest: + return cls( + tool_name=subsystem.options_scope, + lockfile_path=subsystem.lockfile, + requirements=subsystem.all_requirements, + interpreter_constraints=subsystem.interpreter_constraints, + ) + # TODO(#12314): Unify this goal with `lock` once we figure out how to unify the semantics, # particularly w/ CLI specs. This is a separate goal only to facilitate progress. diff --git a/src/python/pants/backend/python/goals/coverage_py.py b/src/python/pants/backend/python/goals/coverage_py.py index e3c63c5f2b9..a77858e56b8 100644 --- a/src/python/pants/backend/python/goals/coverage_py.py +++ b/src/python/pants/backend/python/goals/coverage_py.py @@ -12,6 +12,10 @@ import toml +from pants.backend.experimental.python.lockfile import ( + PythonToolLockfileRequest, + PythonToolLockfileSentinel, +) from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript from pants.backend.python.util_rules.pex import PexRequest, VenvPex, VenvPexProcess @@ -46,6 +50,7 @@ from pants.engine.unions import UnionRule from pants.option.custom_types import file_option from pants.source.source_root import AllSourceRoots +from pants.util.docutil import git_url from pants.util.logging import LogLevel """ @@ -99,9 +104,14 @@ class CoverageSubsystem(PythonToolBase): default_version = "coverage[toml]>=5.0.3,<5.1" default_main = ConsoleScript("coverage") + register_interpreter_constraints = True default_interpreter_constraints = ["CPython>=3.6"] + register_lockfile = True + default_lockfile_resource = ("pants.backend.python.subsystems", "coverage_py_lockfile.txt") + default_lockfile_url = git_url("src/python/pants/backend/python/goals/coverage_py.py") + @classmethod def register_options(cls, register): super().register_options(register) @@ -207,6 +217,17 @@ def global_report(self) -> bool: return cast(bool, self.options.global_report) +class CoveragePyLockfileSentinel(PythonToolLockfileSentinel): + pass + + +@rule +def setup_coverage_lockfile( + _: CoveragePyLockfileSentinel, coverage: CoverageSubsystem +) -> PythonToolLockfileRequest: + return PythonToolLockfileRequest.from_tool(coverage) + + @dataclass(frozen=True) class PytestCoverageData(CoverageData): address: Address @@ -532,4 +553,8 @@ def _get_coverage_report( def rules(): - return [*collect_rules(), UnionRule(CoverageDataCollection, PytestCoverageDataCollection)] + return [ + *collect_rules(), + UnionRule(CoverageDataCollection, PytestCoverageDataCollection), + UnionRule(PythonToolLockfileSentinel, CoveragePyLockfileSentinel), + ] diff --git a/src/python/pants/backend/python/lint/black/lockfile.txt b/src/python/pants/backend/python/lint/black/lockfile.txt new file mode 100644 index 00000000000..42fd95720e2 --- /dev/null +++ b/src/python/pants/backend/python/lint/black/lockfile.txt @@ -0,0 +1,130 @@ +# +# This file is autogenerated by pip-compile with python 3.6 +# To update, run: +# +# pip-compile --allow-unsafe --generate-hashes --output-file=src/python/pants/backend/python/lint/black/lockfile.txt requirements_black.in +# +appdirs==1.4.4 \ + --hash=sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41 \ + --hash=sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128 + # via black +black==21.5b2 \ + --hash=sha256:1fc0e0a2c8ae7d269dfcf0c60a89afa299664f3e811395d40b1922dff8f854b5 \ + --hash=sha256:e5cf21ebdffc7a9b29d73912b6a6a9a4df4ce70220d523c21647da2eae0751ef + # via -r requirements_black.in +click==8.0.1 \ + --hash=sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a \ + --hash=sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6 + # via black +dataclasses==0.8 \ + --hash=sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf \ + --hash=sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97 + # via black +importlib-metadata==4.6.1 \ + --hash=sha256:079ada16b7fc30dfbb5d13399a5113110dab1aa7c2bc62f66af75f0b717c8cac \ + --hash=sha256:9f55f560e116f8643ecf2922d9cd3e1c7e8d52e683178fecd9d08f6aa357e11e + # via click +mypy-extensions==0.4.3 \ + --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ + --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 + # via black +pathspec==0.8.1 \ + --hash=sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd \ + --hash=sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d + # via black +regex==2021.7.6 \ + --hash=sha256:0eb2c6e0fcec5e0f1d3bcc1133556563222a2ffd2211945d7b1480c1b1a42a6f \ + --hash=sha256:15dddb19823f5147e7517bb12635b3c82e6f2a3a6b696cc3e321522e8b9308ad \ + --hash=sha256:173bc44ff95bc1e96398c38f3629d86fa72e539c79900283afa895694229fe6a \ + --hash=sha256:1c78780bf46d620ff4fff40728f98b8afd8b8e35c3efd638c7df67be2d5cddbf \ + --hash=sha256:2366fe0479ca0e9afa534174faa2beae87847d208d457d200183f28c74eaea59 \ + --hash=sha256:2bceeb491b38225b1fee4517107b8491ba54fba77cf22a12e996d96a3c55613d \ + --hash=sha256:2ddeabc7652024803666ea09f32dd1ed40a0579b6fbb2a213eba590683025895 \ + --hash=sha256:2fe5e71e11a54e3355fa272137d521a40aace5d937d08b494bed4529964c19c4 \ + --hash=sha256:319eb2a8d0888fa6f1d9177705f341bc9455a2c8aca130016e52c7fe8d6c37a3 \ + --hash=sha256:3f5716923d3d0bfb27048242a6e0f14eecdb2e2a7fac47eda1d055288595f222 \ + --hash=sha256:422dec1e7cbb2efbbe50e3f1de36b82906def93ed48da12d1714cabcd993d7f0 \ + --hash=sha256:4c9c3155fe74269f61e27617529b7f09552fbb12e44b1189cebbdb24294e6e1c \ + --hash=sha256:4f64fc59fd5b10557f6cd0937e1597af022ad9b27d454e182485f1db3008f417 \ + --hash=sha256:564a4c8a29435d1f2256ba247a0315325ea63335508ad8ed938a4f14c4116a5d \ + --hash=sha256:59506c6e8bd9306cd8a41511e32d16d5d1194110b8cfe5a11d102d8b63cf945d \ + --hash=sha256:598c0a79b4b851b922f504f9f39a863d83ebdfff787261a5ed061c21e67dd761 \ + --hash=sha256:59c00bb8dd8775473cbfb967925ad2c3ecc8886b3b2d0c90a8e2707e06c743f0 \ + --hash=sha256:6110bab7eab6566492618540c70edd4d2a18f40ca1d51d704f1d81c52d245026 \ + --hash=sha256:6afe6a627888c9a6cfbb603d1d017ce204cebd589d66e0703309b8048c3b0854 \ + --hash=sha256:791aa1b300e5b6e5d597c37c346fb4d66422178566bbb426dd87eaae475053fb \ + --hash=sha256:8394e266005f2d8c6f0bc6780001f7afa3ef81a7a2111fa35058ded6fce79e4d \ + --hash=sha256:875c355360d0f8d3d827e462b29ea7682bf52327d500a4f837e934e9e4656068 \ + --hash=sha256:89e5528803566af4df368df2d6f503c84fbfb8249e6631c7b025fe23e6bd0cde \ + --hash=sha256:99d8ab206a5270c1002bfcf25c51bf329ca951e5a169f3b43214fdda1f0b5f0d \ + --hash=sha256:9a854b916806c7e3b40e6616ac9e85d3cdb7649d9e6590653deb5b341a736cec \ + --hash=sha256:b85ac458354165405c8a84725de7bbd07b00d9f72c31a60ffbf96bb38d3e25fa \ + --hash=sha256:bc84fb254a875a9f66616ed4538542fb7965db6356f3df571d783f7c8d256edd \ + --hash=sha256:c92831dac113a6e0ab28bc98f33781383fe294df1a2c3dfd1e850114da35fd5b \ + --hash=sha256:cbe23b323988a04c3e5b0c387fe3f8f363bf06c0680daf775875d979e376bd26 \ + --hash=sha256:ccb3d2190476d00414aab36cca453e4596e8f70a206e2aa8db3d495a109153d2 \ + --hash=sha256:d8bbce0c96462dbceaa7ac4a7dfbbee92745b801b24bce10a98d2f2b1ea9432f \ + --hash=sha256:db2b7df831c3187a37f3bb80ec095f249fa276dbe09abd3d35297fc250385694 \ + --hash=sha256:e586f448df2bbc37dfadccdb7ccd125c62b4348cb90c10840d695592aa1b29e0 \ + --hash=sha256:e5983c19d0beb6af88cb4d47afb92d96751fb3fa1784d8785b1cdf14c6519407 \ + --hash=sha256:e6a1e5ca97d411a461041d057348e578dc344ecd2add3555aedba3b408c9f874 \ + --hash=sha256:eaf58b9e30e0e546cdc3ac06cf9165a1ca5b3de8221e9df679416ca667972035 \ + --hash=sha256:ed693137a9187052fc46eedfafdcb74e09917166362af4cc4fddc3b31560e93d \ + --hash=sha256:edd1a68f79b89b0c57339bce297ad5d5ffcc6ae7e1afdb10f1947706ed066c9c \ + --hash=sha256:f080248b3e029d052bf74a897b9d74cfb7643537fbde97fe8225a6467fb559b5 \ + --hash=sha256:f9392a4555f3e4cb45310a65b403d86b589adc773898c25a39184b1ba4db8985 \ + --hash=sha256:f98dc35ab9a749276f1a4a38ab3e0e2ba1662ce710f6530f5b0a6656f1c32b58 + # via black +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via black +typed-ast==1.4.3 \ + --hash=sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace \ + --hash=sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff \ + --hash=sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266 \ + --hash=sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528 \ + --hash=sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6 \ + --hash=sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808 \ + --hash=sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4 \ + --hash=sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363 \ + --hash=sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341 \ + --hash=sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04 \ + --hash=sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41 \ + --hash=sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e \ + --hash=sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3 \ + --hash=sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899 \ + --hash=sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805 \ + --hash=sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c \ + --hash=sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c \ + --hash=sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39 \ + --hash=sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a \ + --hash=sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3 \ + --hash=sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7 \ + --hash=sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f \ + --hash=sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075 \ + --hash=sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0 \ + --hash=sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40 \ + --hash=sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428 \ + --hash=sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927 \ + --hash=sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3 \ + --hash=sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f \ + --hash=sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65 + # via black +typing-extensions==3.10.0.0 \ + --hash=sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497 \ + --hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \ + --hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84 + # via + # black + # importlib-metadata +zipp==3.5.0 \ + --hash=sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3 \ + --hash=sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4 + # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +setuptools==57.1.0 \ + --hash=sha256:cfca9c97e7eebbc8abe18d5e5e962a08dcad55bb63afddd82d681de4d22a597b \ + --hash=sha256:ddae4c1b9220daf1e32ba9d4e3714df6019c5b583755559be84ff8199f7e1fe3 + # via -r requirements_black.in diff --git a/src/python/pants/backend/python/lint/black/register.py b/src/python/pants/backend/python/lint/black/register.py index 406d6b4f0ad..92cdf0bdf19 100644 --- a/src/python/pants/backend/python/lint/black/register.py +++ b/src/python/pants/backend/python/lint/black/register.py @@ -9,8 +9,8 @@ from pants.backend.python.lint import python_fmt from pants.backend.python.lint.black import rules as black_rules -from pants.backend.python.lint.black import skip_field +from pants.backend.python.lint.black import skip_field, subsystem def rules(): - return (*black_rules.rules(), *python_fmt.rules(), *skip_field.rules()) + return (*black_rules.rules(), *python_fmt.rules(), *skip_field.rules(), *subsystem.rules()) diff --git a/src/python/pants/backend/python/lint/black/subsystem.py b/src/python/pants/backend/python/lint/black/subsystem.py index e26b460d6b6..51246799d69 100644 --- a/src/python/pants/backend/python/lint/black/subsystem.py +++ b/src/python/pants/backend/python/lint/black/subsystem.py @@ -6,10 +6,17 @@ import os.path from typing import Iterable, cast +from pants.backend.experimental.python.lockfile import ( + PythonToolLockfileRequest, + PythonToolLockfileSentinel, +) from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript from pants.core.util_rules.config_files import ConfigFilesRequest +from pants.engine.rules import collect_rules, rule +from pants.engine.unions import UnionRule from pants.option.custom_types import file_option, shell_str +from pants.util.docutil import git_url class Black(PythonToolBase): @@ -19,9 +26,14 @@ class Black(PythonToolBase): default_version = "black==21.5b2" default_extra_requirements = ["setuptools"] default_main = ConsoleScript("black") + register_interpreter_constraints = True default_interpreter_constraints = ["CPython>=3.6"] + register_lockfile = True + default_lockfile_resource = ("pants.backend.python.lint.black", "lockfile.txt") + default_lockfile_url = git_url("src/python/pants/backend/python/lint/black/lockfile.txt") + @classmethod def register_options(cls, register): super().register_options(register) @@ -89,3 +101,16 @@ def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: discovery=cast(bool, self.options.config_discovery), check_content=candidates, ) + + +class BlackLockfileSentinel(PythonToolLockfileSentinel): + pass + + +@rule +def setup_black_lockfile(_: BlackLockfileSentinel, black: Black) -> PythonToolLockfileRequest: + return PythonToolLockfileRequest.from_tool(black) + + +def rules(): + return (*collect_rules(), UnionRule(PythonToolLockfileSentinel, BlackLockfileSentinel)) diff --git a/src/python/pants/backend/python/lint/docformatter/rules.py b/src/python/pants/backend/python/lint/docformatter/rules.py index 53a472d7893..bd87563afcd 100644 --- a/src/python/pants/backend/python/lint/docformatter/rules.py +++ b/src/python/pants/backend/python/lint/docformatter/rules.py @@ -1,7 +1,6 @@ # Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -import importlib.resources from dataclasses import dataclass from typing import Tuple @@ -10,11 +9,11 @@ from pants.backend.python.lint.python_fmt import PythonFmtRequest from pants.backend.python.target_types import PythonSources from pants.backend.python.util_rules import pex -from pants.backend.python.util_rules.pex import PexRequest, PexRequirements, VenvPex, VenvPexProcess +from pants.backend.python.util_rules.pex import PexRequest, VenvPex, VenvPexProcess from pants.core.goals.fmt import FmtResult from pants.core.goals.lint import LintRequest, LintResult, LintResults from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest -from pants.engine.fs import Digest, FileContent +from pants.engine.fs import Digest from pants.engine.process import FallibleProcessResult, Process, ProcessResult from pants.engine.rules import Get, MultiGet, collect_rules, rule from pants.engine.target import FieldSet, Target @@ -58,32 +57,14 @@ def generate_args( @rule(level=LogLevel.DEBUG) async def setup_docformatter(setup_request: SetupRequest, docformatter: Docformatter) -> Setup: - if docformatter.lockfile == "": - requirements = docformatter.pex_requirements - elif docformatter.lockfile == "": - requirements = PexRequirements( - file_content=FileContent( - "docformatter_default_lockfile.txt", - importlib.resources.read_binary( - "pants.backend.python.lint.docformatter", "lockfile.txt" - ), - ) - ) - else: - requirements = PexRequirements( - file_path=docformatter.lockfile, - file_path_description_of_origin="the option `[docformatter].experimental_lockfile`", - ) - docformatter_pex_get = Get( VenvPex, PexRequest( output_filename="docformatter.pex", internal_only=True, - requirements=requirements, + requirements=docformatter.pex_requirements, interpreter_constraints=docformatter.interpreter_constraints, main=docformatter.main, - is_lockfile=docformatter.lockfile != "", ), ) source_files_get = Get( diff --git a/src/python/pants/backend/python/lint/docformatter/subsystem.py b/src/python/pants/backend/python/lint/docformatter/subsystem.py index 2db64d496bd..4a84e16a7de 100644 --- a/src/python/pants/backend/python/lint/docformatter/subsystem.py +++ b/src/python/pants/backend/python/lint/docformatter/subsystem.py @@ -12,6 +12,7 @@ from pants.engine.rules import collect_rules, rule from pants.engine.unions import UnionRule from pants.option.custom_types import shell_str +from pants.util.docutil import git_url class Docformatter(PythonToolBase): @@ -20,9 +21,14 @@ class Docformatter(PythonToolBase): default_version = "docformatter>=1.4,<1.5" default_main = ConsoleScript("docformatter") + register_interpreter_constraints = True default_interpreter_constraints = ["CPython>=3.6"] + register_lockfile = True + default_lockfile_resource = ("pants.backend.python.lint.docformatter", "lockfile.txt") + default_lockfile_url = git_url("src/python/pants/backend/python/lint/docformatter/lockfile.txt") + @classmethod def register_options(cls, register): super().register_options(register) @@ -44,26 +50,6 @@ def register_options(cls, register): f'`--{cls.options_scope}-args="--wrap-summaries=100 --pre-summary-newline"`.' ), ) - register( - "--experimental-lockfile", - type=str, - default="", - advanced=True, - help=( - "Path to a lockfile used for installing the tool.\n\n" - "Set to the string '' to use a lockfile provided by " - "Pants, so long as you have not changed the `--version`, `--extra-requirements`, " - "and `--interpreter-constraints` options. See {} for the default lockfile " - "contents.\n\n" - "Set to the string '' to opt out of using a lockfile. We do not recommend " - "this, as lockfiles are essential for reproducible builds.\n\n" - "To use a custom lockfile, set this option to a file path relative to the build " - "root, then activate the backend_package `pants.backend.experimental.python` and " - "run `./pants tool-lock`.\n\n" - "This option is experimental and will likely change. It does not follow the normal " - "deprecation cycle." - ), - ) @property def skip(self) -> bool: @@ -73,10 +59,6 @@ def skip(self) -> bool: def args(self) -> Tuple[str, ...]: return tuple(self.options.args) - @property - def lockfile(self) -> str: - return cast(str, self.options.experimental_lockfile) - class DocformatterLockfileSentinel(PythonToolLockfileSentinel): pass @@ -86,12 +68,7 @@ class DocformatterLockfileSentinel(PythonToolLockfileSentinel): def setup_lockfile_request( _: DocformatterLockfileSentinel, docformatter: Docformatter ) -> PythonToolLockfileRequest: - return PythonToolLockfileRequest( - tool_name=docformatter.options_scope, - lockfile_path=docformatter.lockfile, - requirements=docformatter.all_requirements, - interpreter_constraints=docformatter.interpreter_constraints, - ) + return PythonToolLockfileRequest.from_tool(docformatter) def rules(): diff --git a/src/python/pants/backend/python/lint/isort/lockfile.txt b/src/python/pants/backend/python/lint/isort/lockfile.txt new file mode 100644 index 00000000000..0c22e47ff81 --- /dev/null +++ b/src/python/pants/backend/python/lint/isort/lockfile.txt @@ -0,0 +1,20 @@ +# +# This file is autogenerated by pip-compile with python 3.6 +# To update, run: +# +# pip-compile --allow-unsafe --generate-hashes --output-file=src/python/pants/backend/python/lint/isort/lockfile.txt requirements_isort.in +# +colorama==0.4.4 \ + --hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b \ + --hash=sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2 + # via isort +isort[colors,pyproject]==5.5.5 \ + --hash=sha256:47e0fdc03aed3a9ba507284f90e4b3b6f2a4725d919816a7b547675befc38ffb \ + --hash=sha256:87355bbc3465bf096a8bf09c4dd949b6b9294958c478740442fd9fbd01b817f2 + # via -r requirements_isort.in + +# The following packages are considered to be unsafe in a requirements file: +setuptools==57.2.0 \ + --hash=sha256:a7e88b9bbaece494162d697d19525c567ab60c85dd7e2fd90cb7dfe8d8129a62 \ + --hash=sha256:b4bb42f3123fbb01e4f5f5ea9ed6d6b5f7021d18b3bc64236477a82aedf2190e + # via -r requirements_isort.in diff --git a/src/python/pants/backend/python/lint/isort/register.py b/src/python/pants/backend/python/lint/isort/register.py index bf06480bf16..17f28149b23 100644 --- a/src/python/pants/backend/python/lint/isort/register.py +++ b/src/python/pants/backend/python/lint/isort/register.py @@ -9,8 +9,8 @@ from pants.backend.python.lint import python_fmt from pants.backend.python.lint.isort import rules as isort_rules -from pants.backend.python.lint.isort import skip_field +from pants.backend.python.lint.isort import skip_field, subsystem def rules(): - return (*isort_rules.rules(), *python_fmt.rules(), *skip_field.rules()) + return (*isort_rules.rules(), *python_fmt.rules(), *skip_field.rules(), *subsystem.rules()) diff --git a/src/python/pants/backend/python/lint/isort/subsystem.py b/src/python/pants/backend/python/lint/isort/subsystem.py index ea0b54787f3..bc536aa6d78 100644 --- a/src/python/pants/backend/python/lint/isort/subsystem.py +++ b/src/python/pants/backend/python/lint/isort/subsystem.py @@ -6,21 +6,33 @@ import os.path from typing import Iterable, cast +from pants.backend.experimental.python.lockfile import ( + PythonToolLockfileRequest, + PythonToolLockfileSentinel, +) from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript from pants.core.util_rules.config_files import ConfigFilesRequest +from pants.engine.rules import collect_rules, rule +from pants.engine.unions import UnionRule from pants.option.custom_types import file_option, shell_str +from pants.util.docutil import git_url class Isort(PythonToolBase): options_scope = "isort" help = "The Python import sorter tool (https://timothycrosley.github.io/isort/)." - default_version = "isort[pyproject]>=5.5.1,<5.6" + default_version = "isort[pyproject,colors]>=5.5.1,<5.6" default_extra_requirements = ["setuptools"] + default_main = ConsoleScript("isort") + register_interpreter_constraints = True default_interpreter_constraints = ["CPython>=3.6"] - default_main = ConsoleScript("isort") + + register_lockfile = True + default_lockfile_resource = ("pants.backend.python.lint.isort", "lockfile.txt") + default_lockfile_url = git_url("src/python/pants/backend/python/lint/isort/lockfile.txt") @classmethod def register_options(cls, register): @@ -113,3 +125,16 @@ def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: check_existence=check_existence, check_content=check_content, ) + + +class IsortLockfileSentinel(PythonToolLockfileSentinel): + pass + + +@rule +def setup_isort_lockfile(_: IsortLockfileSentinel, isort: Isort) -> PythonToolLockfileRequest: + return PythonToolLockfileRequest.from_tool(isort) + + +def rules(): + return (*collect_rules(), UnionRule(PythonToolLockfileSentinel, IsortLockfileSentinel)) diff --git a/src/python/pants/backend/python/lint/yapf/lockfile.txt b/src/python/pants/backend/python/lint/yapf/lockfile.txt new file mode 100644 index 00000000000..3b6d5203c72 --- /dev/null +++ b/src/python/pants/backend/python/lint/yapf/lockfile.txt @@ -0,0 +1,20 @@ +# +# This file is autogenerated by pip-compile with python 3.6 +# To update, run: +# +# pip-compile --allow-unsafe --generate-hashes --output-file=src/python/pants/backend/python/lint/yapf/lockfile.txt requirements_yapf.in +# +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via -r requirements_yapf.in +yapf==0.31.0 \ + --hash=sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d \ + --hash=sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e + # via -r requirements_yapf.in + +# The following packages are considered to be unsafe in a requirements file: +setuptools==57.2.0 \ + --hash=sha256:a7e88b9bbaece494162d697d19525c567ab60c85dd7e2fd90cb7dfe8d8129a62 \ + --hash=sha256:b4bb42f3123fbb01e4f5f5ea9ed6d6b5f7021d18b3bc64236477a82aedf2190e + # via -r requirements_yapf.in diff --git a/src/python/pants/backend/python/lint/yapf/register.py b/src/python/pants/backend/python/lint/yapf/register.py index 53da8782a21..36537b55c2d 100644 --- a/src/python/pants/backend/python/lint/yapf/register.py +++ b/src/python/pants/backend/python/lint/yapf/register.py @@ -9,8 +9,8 @@ from pants.backend.python.lint import python_fmt from pants.backend.python.lint.yapf import rules as yapf_rules -from pants.backend.python.lint.yapf import skip_field +from pants.backend.python.lint.yapf import skip_field, subsystem def rules(): - return (*yapf_rules.rules(), *python_fmt.rules(), *skip_field.rules()) + return (*yapf_rules.rules(), *python_fmt.rules(), *skip_field.rules(), *subsystem.rules()) diff --git a/src/python/pants/backend/python/lint/yapf/rules.py b/src/python/pants/backend/python/lint/yapf/rules.py index 3a058b58b8b..bf0ffdc0fd8 100644 --- a/src/python/pants/backend/python/lint/yapf/rules.py +++ b/src/python/pants/backend/python/lint/yapf/rules.py @@ -9,8 +9,7 @@ from pants.backend.python.lint.yapf.subsystem import Yapf from pants.backend.python.target_types import PythonSources from pants.backend.python.util_rules import pex -from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints -from pants.backend.python.util_rules.pex import PexRequest, PexRequirements, VenvPex, VenvPexProcess +from pants.backend.python.util_rules.pex import PexRequest, VenvPex, VenvPexProcess from pants.core.goals.fmt import FmtResult from pants.core.goals.lint import LintRequest, LintResult, LintResults from pants.core.util_rules.config_files import ConfigFiles, ConfigFilesRequest @@ -73,8 +72,8 @@ async def setup_yapf(setup_request: SetupRequest, yapf: Yapf) -> Setup: PexRequest( output_filename="yapf.pex", internal_only=True, - requirements=PexRequirements(yapf.all_requirements), - interpreter_constraints=InterpreterConstraints(yapf.interpreter_constraints), + requirements=yapf.pex_requirements, + interpreter_constraints=yapf.interpreter_constraints, main=yapf.main, ), ) diff --git a/src/python/pants/backend/python/lint/yapf/subsystem.py b/src/python/pants/backend/python/lint/yapf/subsystem.py index 7a1ba7dd2b5..f693c2da0f4 100644 --- a/src/python/pants/backend/python/lint/yapf/subsystem.py +++ b/src/python/pants/backend/python/lint/yapf/subsystem.py @@ -6,10 +6,17 @@ import os.path from typing import Iterable, cast +from pants.backend.experimental.python.lockfile import ( + PythonToolLockfileRequest, + PythonToolLockfileSentinel, +) from pants.backend.python.subsystems.python_tool_base import PythonToolBase from pants.backend.python.target_types import ConsoleScript from pants.core.util_rules.config_files import ConfigFilesRequest +from pants.engine.rules import collect_rules, rule +from pants.engine.unions import UnionRule from pants.option.custom_types import file_option, shell_str +from pants.util.docutil import git_url class Yapf(PythonToolBase): @@ -18,9 +25,14 @@ class Yapf(PythonToolBase): default_version = "yapf==0.31.0" default_extra_requirements = ["setuptools", "toml"] + default_main = ConsoleScript("yapf") + register_interpreter_constraints = True default_interpreter_constraints = ["CPython>=3.6"] - default_main = ConsoleScript("yapf") + + register_lockfile = True + default_lockfile_resource = ("pants.backend.python.lint.yapf", "lockfile.txt") + default_lockfile_url = git_url("src/python/pants/backend/python/lint/yapf/lockfile.txt") @classmethod def register_options(cls, register): @@ -104,3 +116,16 @@ def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: check_existence=check_existence, check_content=check_content, ) + + +class YapfLockfileSentinel(PythonToolLockfileSentinel): + pass + + +@rule +def setup_yapf_lockfile(_: YapfLockfileSentinel, yapf: Yapf) -> PythonToolLockfileRequest: + return PythonToolLockfileRequest.from_tool(yapf) + + +def rules(): + return (*collect_rules(), UnionRule(PythonToolLockfileSentinel, YapfLockfileSentinel)) diff --git a/src/python/pants/backend/python/subsystems/coverage_py_lockfile.txt b/src/python/pants/backend/python/subsystems/coverage_py_lockfile.txt new file mode 100644 index 00000000000..758d487f618 --- /dev/null +++ b/src/python/pants/backend/python/subsystems/coverage_py_lockfile.txt @@ -0,0 +1,43 @@ +# +# This file is autogenerated by pip-compile with python 3.7 +# To update, run: +# +# pip-compile --allow-unsafe --generate-hashes --output-file=src/python/pants/backend/python/subsystems/coverage_py_lockfile.txt requirements_coverage-py.in +# +coverage[toml]==5.0.4 \ + --hash=sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0 \ + --hash=sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30 \ + --hash=sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b \ + --hash=sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0 \ + --hash=sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823 \ + --hash=sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe \ + --hash=sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037 \ + --hash=sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6 \ + --hash=sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31 \ + --hash=sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd \ + --hash=sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892 \ + --hash=sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1 \ + --hash=sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78 \ + --hash=sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac \ + --hash=sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006 \ + --hash=sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014 \ + --hash=sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2 \ + --hash=sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7 \ + --hash=sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8 \ + --hash=sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7 \ + --hash=sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9 \ + --hash=sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1 \ + --hash=sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307 \ + --hash=sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a \ + --hash=sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435 \ + --hash=sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0 \ + --hash=sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5 \ + --hash=sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441 \ + --hash=sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732 \ + --hash=sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de \ + --hash=sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1 + # via -r requirements_coverage-py.in +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via coverage 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 895b012a3e2..f2acf2862c9 100644 --- a/src/python/pants/backend/python/subsystems/python_tool_base.py +++ b/src/python/pants/backend/python/subsystems/python_tool_base.py @@ -3,11 +3,13 @@ from __future__ import annotations +import importlib.resources from typing import ClassVar, Sequence, cast from pants.backend.python.target_types import ConsoleScript, EntryPoint, MainSpecification from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints from pants.backend.python.util_rules.pex import PexRequirements +from pants.engine.fs import FileContent from pants.option.errors import OptionsError from pants.option.subsystem import Subsystem @@ -23,6 +25,16 @@ class PythonToolRequirementsBase(Subsystem): default_interpreter_constraints: ClassVar[Sequence[str]] = [] register_interpreter_constraints: ClassVar[bool] = False + # If this tool does not mix with user requirements (e.g. Flake8 and Isort, but not Pylint and + # Pytest), you should set this to True. + # + # You also need to subclass `PythonToolLockfileSentinel` and create a rule that goes from + # it -> PythonToolLockfileRequest by calling `PythonLockFileRequest.from_python_tool()`. + # Register the UnionRule. + register_lockfile: ClassVar[bool] = False + default_lockfile_resource: ClassVar[tuple[str, str] | None] = None + default_lockfile_url: ClassVar[str | None] = None + @classmethod def register_options(cls, register): super().register_options(register) @@ -43,6 +55,7 @@ def register_options(cls, register): "tool allows you to install plugins or if you need to constrain a dependency to " "a certain version.", ) + if cls.default_interpreter_constraints and not cls.register_interpreter_constraints: raise ValueError( f"`default_interpreter_constraints` are configured for `{cls.options_scope}`, but " @@ -59,6 +72,44 @@ def register_options(cls, register): help="Python interpreter constraints for this tool.", ) + if cls.register_lockfile and ( + not cls.default_lockfile_resource or not cls.default_lockfile_url + ): + raise ValueError( + "The class property `default_lockfile_resource` and `default_lockfile_url` " + f"must be set if `register_lockfile` is set. See `{cls.options_scope}`." + ) + if cls.register_lockfile and not cls.register_interpreter_constraints: + # TODO(#12314): Figure out how to determine the interpreter constraints for tools that + # get ICs from code, like FLake8 and Bandit. We could theoretically scan the repo to + # see all constraints (`py-constraints` goal). What should the default lockfile we + # bundle use? + raise ValueError( + "For now, you cannot set `register_lockfile` without also setting " + f"`register_interpreter_constraints`. See `{cls.options_scope}`." + ) + if cls.register_lockfile: + register( + "--experimental-lockfile", + type=str, + default="", + advanced=True, + help=( + "Path to a lockfile used for installing the tool.\n\n" + "Set to the string '' to use a lockfile provided by " + "Pants, so long as you have not changed the `--version`, " + "`--extra-requirements`, and `--interpreter-constraints` options. See " + f"{cls.default_lockfile_url} for the default lockfile contents.\n\n" + "Set to the string '' to opt out of using a lockfile. We do not " + "recommend this, as lockfiles are essential for reproducible builds.\n\n" + "To use a custom lockfile, set this option to a file path relative to the " + "build root, then activate the backend_package " + "`pants.backend.experimental.python` and run `./pants tool-lock`.\n\n" + "This option is experimental and will likely change. It does not follow the " + "normal deprecation cycle." + ), + ) + @property def version(self) -> str: return cast(str, self.options.version) @@ -82,9 +133,32 @@ def pex_requirements(self) -> PexRequirements: If the tool supports lockfiles, the returned type will install from the lockfile rather than `all_requirements`. """ - # TODO(#11898): move lockfile option registration into PythonToolBase, and update this to - # set the PexRequirements appropriately. - return PexRequirements(self.all_requirements) + if not self.register_lockfile or self.lockfile == "": + return PexRequirements(self.all_requirements) + if self.lockfile == "": + assert self.default_lockfile_resource is not None + return PexRequirements( + file_content=FileContent( + f"{self.options_scope}_default_lockfile.txt", + importlib.resources.read_binary(*self.default_lockfile_resource), + ), + is_lockfile=True, + ) + return PexRequirements( + file_path=self.lockfile, + file_path_description_of_origin=( + f"the option `[{self.options_scope}].experimental_lockfile`" + ), + is_lockfile=True, + ) + + @property + def lockfile(self) -> str: + """The path to a lockfile or special strings '' and ''. + + This assumes you have set the class property `register_lockfile = True`. + """ + return cast(str, self.options.experimental_lockfile) @property def interpreter_constraints(self) -> InterpreterConstraints: diff --git a/src/python/pants/backend/python/util_rules/pex.py b/src/python/pants/backend/python/util_rules/pex.py index 6069f03b641..08953b62869 100644 --- a/src/python/pants/backend/python/util_rules/pex.py +++ b/src/python/pants/backend/python/util_rules/pex.py @@ -65,6 +65,7 @@ class PexRequirements: file_content: FileContent | None file_path: str | None file_path_description_of_origin: str | None + is_lockfile: bool def __init__( self, @@ -73,11 +74,13 @@ def __init__( file_content: FileContent | None = None, file_path: str | None = None, file_path_description_of_origin: str | None = None, + is_lockfile: bool = False, ) -> None: self.req_strings = FrozenOrderedSet(sorted(req_strings)) self.file_content = file_content self.file_path = file_path self.file_path_description_of_origin = file_path_description_of_origin + self.is_lockfile = is_lockfile if self.file_path and not self.file_path_description_of_origin: raise ValueError( @@ -145,7 +148,6 @@ class PexRequest(EngineAwareParameter): repository_pex: Pex | None additional_args: Tuple[str, ...] pex_path: Tuple[Pex, ...] - is_lockfile: bool apply_requirement_constraints: bool description: str | None = dataclasses.field(compare=False) @@ -163,7 +165,6 @@ def __init__( repository_pex: Pex | None = None, additional_args: Iterable[str] = (), pex_path: Iterable[Pex] = (), - is_lockfile: bool = False, apply_requirement_constraints: bool = True, description: str | None = None, ) -> None: @@ -205,7 +206,6 @@ def __init__( self.repository_pex = repository_pex self.additional_args = tuple(additional_args) self.pex_path = tuple(pex_path) - self.is_lockfile = is_lockfile self.apply_requirement_constraints = apply_requirement_constraints self.description = description self.__post_init__() @@ -334,7 +334,7 @@ async def build_pex( ] ) - if request.is_lockfile: + if request.requirements.is_lockfile: argv.append("--no-transitive") python: PythonExecutable | None = None @@ -388,7 +388,7 @@ async def build_pex( constraint_file_digest = EMPTY_DIGEST if ( - not request.is_lockfile and request.apply_requirement_constraints + not request.requirements.is_lockfile and request.apply_requirement_constraints ) and python_setup.requirement_constraints is not None: argv.extend(["--constraints", python_setup.requirement_constraints]) constraint_file_digest = await Get( diff --git a/src/python/pants/backend/python/util_rules/pex_from_targets.py b/src/python/pants/backend/python/util_rules/pex_from_targets.py index 912956f342d..4d77f160dcd 100644 --- a/src/python/pants/backend/python/util_rules/pex_from_targets.py +++ b/src/python/pants/backend/python/util_rules/pex_from_targets.py @@ -303,10 +303,10 @@ async def pex_from_targets(request: PexFromTargetsRequest, python_setup: PythonS file_path_description_of_origin=( "the option `[python-setup].experimental_lockfile`" ), + is_lockfile=True, ), interpreter_constraints=interpreter_constraints, platforms=request.platforms, - is_lockfile=True, additional_args=request.additional_args, ), )