diff --git a/src/python/pants/backend/python/goals/pytest_runner_test.py b/src/python/pants/backend/python/goals/pytest_runner_test.py index d231e8592f3..9a42339f722 100644 --- a/src/python/pants/backend/python/goals/pytest_runner_test.py +++ b/src/python/pants/backend/python/goals/pytest_runner_test.py @@ -13,7 +13,11 @@ from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints from pants.backend.python.util_rules.lockfile_metadata import PythonLockfileMetadataV3 from pants.backend.python.util_rules.pex import PexRequirementsInfo -from pants.backend.python.util_rules.pex_requirements import LoadedLockfile, Lockfile +from pants.backend.python.util_rules.pex_requirements import ( + LoadedLockfile, + Lockfile, + LockfileFormat, +) from pants.engine.fs import DigestContents, FileContent from pants.engine.internals.native_engine import EMPTY_DIGEST from pants.testutil.option_util import create_subsystem @@ -98,7 +102,9 @@ def validate(reqs: list[str]) -> None: only_binary=set(), no_binary=set(), ) - loaded_lockfile = LoadedLockfile(EMPTY_DIGEST, "", metadata, 0, True, None, lockfile) + loaded_lockfile = LoadedLockfile( + EMPTY_DIGEST, "", metadata, 0, LockfileFormat.Pex, None, lockfile + ) run_rule_with_mocks( validate_pytest_cov_included, rule_args=[tool], diff --git a/src/python/pants/backend/python/subsystems/python_tool_base_test.py b/src/python/pants/backend/python/subsystems/python_tool_base_test.py index a493d072b3a..a00f08a18eb 100644 --- a/src/python/pants/backend/python/subsystems/python_tool_base_test.py +++ b/src/python/pants/backend/python/subsystems/python_tool_base_test.py @@ -9,6 +9,7 @@ from pants.backend.python.util_rules.pex_requirements import ( LoadedLockfile, Lockfile, + LockfileFormat, PexRequirements, Resolve, ) @@ -52,7 +53,9 @@ def test_get_lockfile_metadata() -> None: no_binary=set(), ) lockfile = Lockfile("dummy_url", "dummy_description_of_origin", "dummy_resolve") - loaded_lockfile = LoadedLockfile(EMPTY_DIGEST, "", metadata, 0, True, None, lockfile) + loaded_lockfile = LoadedLockfile( + EMPTY_DIGEST, "", metadata, 0, LockfileFormat.Pex, None, lockfile + ) assert ( run_rule_with_mocks( get_lockfile_metadata, diff --git a/src/python/pants/backend/python/util_rules/pex.py b/src/python/pants/backend/python/util_rules/pex.py index 0dc5f9580d5..a8380e9c928 100644 --- a/src/python/pants/backend/python/util_rules/pex.py +++ b/src/python/pants/backend/python/util_rules/pex.py @@ -44,6 +44,7 @@ LoadedLockfile, LoadedLockfileRequest, Lockfile, + LockfileFormat, Resolve, ResolvePexConfigRequest, determine_resolve_pex_config, @@ -571,7 +572,7 @@ async def _build_uv_venv( uv_request.requirements.from_superset, **implicitly() ) loaded_lockfile = await load_lockfile(LoadedLockfileRequest(lockfile), **implicitly()) - if loaded_lockfile.is_pex_native: + if loaded_lockfile.lockfile_format == LockfileFormat.Pex: try: digest_contents = await get_digest_contents(loaded_lockfile.lockfile_digest) lockfile_bytes = next( @@ -793,7 +794,7 @@ async def _setup_pex_requirements( if loaded_lockfile: argv = ( ["--lock", loaded_lockfile.lockfile_path, *pex_lock_resolver_args] - if loaded_lockfile.is_pex_native + if loaded_lockfile.lockfile_format == LockfileFormat.Pex # We use pip to resolve a requirements.txt pseudo-lockfile, possibly with hashes. else [ "--requirement", @@ -843,7 +844,7 @@ async def _setup_pex_requirements( loaded_lockfile = await load_lockfile(LoadedLockfileRequest(lockfile), **implicitly()) # NB: This is also validated in the constructor. - assert loaded_lockfile.is_pex_native + assert loaded_lockfile.lockfile_format == LockfileFormat.Pex if not reqs_info.req_strings: return _BuildPexRequirementsSetup([], [], concurrency_available) 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 bb97143c48a..61ab7816edc 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 @@ -34,6 +34,7 @@ EntireLockfile, LoadedLockfileRequest, Lockfile, + LockfileFormat, PexRequirements, Resolve, load_lockfile, @@ -570,7 +571,7 @@ async def _determine_requirements_for_pex_from_targets( loaded_lockfile = await load_lockfile( LoadedLockfileRequest(chosen_resolve.lockfile), **implicitly() ) - pex_native_subsetting_supported = loaded_lockfile.is_pex_native + pex_native_subsetting_supported = loaded_lockfile.lockfile_format == LockfileFormat.Pex if loaded_lockfile.as_constraints_strings: requirements = dataclasses.replace( requirements, diff --git a/src/python/pants/backend/python/util_rules/pex_from_targets_test.py b/src/python/pants/backend/python/util_rules/pex_from_targets_test.py index 800bf9b1299..852318129dd 100644 --- a/src/python/pants/backend/python/util_rules/pex_from_targets_test.py +++ b/src/python/pants/backend/python/util_rules/pex_from_targets_test.py @@ -51,6 +51,7 @@ ) from pants.backend.python.util_rules.pex_requirements import ( EntireLockfile, + LockfileFormat, PexRequirements, Resolve, ) @@ -172,10 +173,12 @@ class RequirementMode(Enum): global_requirement_constraints = ["constraint1", "constraint2"] resolve__pex = Resolve("pex", False) - loaded_lockfile__pex = Mock(is_pex_native=True, as_constraints_strings=None) + loaded_lockfile__pex = Mock(lockfile_format=LockfileFormat.Pex, as_constraints_strings=None) chosen_resolve__pex = Mock(lockfile=Mock()) chosen_resolve__pex.name = "pex" # name has special meaning in Mock(), so must set it here. - loaded_lockfile__not_pex = Mock(is_pex_native=False, as_constraints_strings=req_strings) + loaded_lockfile__not_pex = Mock( + lockfile_format=LockfileFormat.ConstraintsDeprecated, as_constraints_strings=req_strings + ) chosen_resolve__not_pex = Mock(lockfile=Mock()) chosen_resolve__not_pex.name = "not_pex" # ditto. diff --git a/src/python/pants/backend/python/util_rules/pex_requirements.py b/src/python/pants/backend/python/util_rules/pex_requirements.py index bbe507ae1df..f6a97611fed 100644 --- a/src/python/pants/backend/python/util_rules/pex_requirements.py +++ b/src/python/pants/backend/python/util_rules/pex_requirements.py @@ -8,6 +8,7 @@ import logging from collections.abc import Iterable, Iterator from dataclasses import dataclass, field +from enum import StrEnum, auto from typing import TYPE_CHECKING from urllib.parse import urlparse @@ -50,6 +51,13 @@ logger = logging.getLogger(__name__) +class LockfileFormat(StrEnum): + Pex = auto() + # The very old, deprecated constraints-based "lockfile" that should + # be removed entirely. + ConstraintsDeprecated = auto() + + @dataclass(frozen=True) class Resolve: # A named resolve for a "user lockfile". @@ -101,10 +109,10 @@ class LoadedLockfile: # An estimate of the number of requirements in this lockfile, to be used as a heuristic for # available parallelism. requirement_estimate: int - # True if the loaded lockfile is in PEX's native format. - is_pex_native: bool - # If !is_pex_native, the lockfile parsed as constraints strings, for use when the lockfile - # needs to be subsetted (see #15031, ##12222). + # The format of the loaded lockfile. + lockfile_format: LockfileFormat + # If lockfile_format is ConstraintsDeprecated, the lockfile parsed as constraints strings, + # for use when the lockfile needs to be subsetted (see #15031, ##12222). as_constraints_strings: FrozenOrderedSet[str] | None # The original file or file content (which may not have identical content to the output # `lockfile_digest`). @@ -225,7 +233,11 @@ async def load_lockfile( lockfile_contents = await get_digest_contents(lockfile_digest) lock_bytes = lockfile_contents[0].content - is_pex_native = is_probably_pex_json_lockfile(lock_bytes) + lockfile_format = ( + LockfileFormat.Pex + if is_probably_pex_json_lockfile(lock_bytes) + else LockfileFormat.ConstraintsDeprecated + ) constraints_strings = None metadata_url = PythonLockfileMetadata.metadata_location_for_lockfile(lockfile.url) @@ -256,7 +268,7 @@ async def load_lockfile( pass if not metadata: - if is_pex_native: + if lockfile_format == LockfileFormat.Pex: header_delimiter = "//" stripped_lock_bytes = strip_comments_from_pex_json_lockfile(lock_bytes) lockfile_digest = await create_digest( @@ -286,7 +298,7 @@ async def load_lockfile( lockfile_path, metadata, requirement_estimate, - is_pex_native, + lockfile_format, constraints_strings, original_lockfile=lockfile, ) diff --git a/src/python/pants/backend/python/util_rules/pex_test.py b/src/python/pants/backend/python/util_rules/pex_test.py index b53680a6de1..8094accacd2 100644 --- a/src/python/pants/backend/python/util_rules/pex_test.py +++ b/src/python/pants/backend/python/util_rules/pex_test.py @@ -47,6 +47,7 @@ EntireLockfile, LoadedLockfile, Lockfile, + LockfileFormat, PexRequirements, Resolve, ResolvePexConfig, @@ -703,7 +704,9 @@ def create_loaded_lockfile(is_pex_lock: bool) -> LoadedLockfile: lockfile_path, metadata=None, requirement_estimate=2, - is_pex_native=is_pex_lock, + lockfile_format=LockfileFormat.Pex + if is_pex_lock + else LockfileFormat.ConstraintsDeprecated, as_constraints_strings=None, original_lockfile=lockfile_obj, ) diff --git a/src/python/pants/core/goals/update_build_files_test.py b/src/python/pants/core/goals/update_build_files_test.py index 2f642eeef81..3bfe887ded8 100644 --- a/src/python/pants/core/goals/update_build_files_test.py +++ b/src/python/pants/core/goals/update_build_files_test.py @@ -17,7 +17,11 @@ from pants.backend.python.util_rules import pex from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints from pants.backend.python.util_rules.lockfile_metadata import PythonLockfileMetadata -from pants.backend.python.util_rules.pex_requirements import LoadedLockfile, Lockfile +from pants.backend.python.util_rules.pex_requirements import ( + LoadedLockfile, + Lockfile, + LockfileFormat, +) from pants.core.goals.update_build_files import ( FormatWithBlackRequest, FormatWithBuildifierRequest, @@ -185,7 +189,7 @@ def assert_ics( "black.lock", metadata=metadata, requirement_estimate=1, - is_pex_native=True, + lockfile_format=LockfileFormat.Pex, as_constraints_strings=None, original_lockfile=Lockfile( "black.lock", url_description_of_origin="foo", resolve_name="black"