Skip to content

Commit

Permalink
Get rid of the LockfileContent class (pantsbuild#18305)
Browse files Browse the repository at this point in the history
This is a step towards greater unification of user and tool lockfiles.

LockfileContent existed primarily to carry the default lockfile content
for tools, as read from a resource. This led to a lot of 
`Lockfile | LockfileContent` type signatures and
subsequent type-testing on the receiving end. 

Instead, the Lockfile class now takes an URL instead of a path (with
path-like URLs still supported), and we introduce a 
`resource://package/path` URL to represent lockfiles embedded in 
the Pants binary as a resource.
  • Loading branch information
benjyw authored Feb 21, 2023
1 parent 79652d5 commit 43b5d3c
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 167 deletions.
4 changes: 2 additions & 2 deletions src/python/pants/backend/python/goals/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,8 @@ async def export_virtualenv_for_resolve(
if lockfile_path:
# It's a user resolve.
lockfile = Lockfile(
file_path=lockfile_path,
file_path_description_of_origin=f"the resolve `{resolve}`",
url=lockfile_path,
url_description_of_origin=f"the resolve `{resolve}`",
resolve_name=resolve,
)

Expand Down
31 changes: 13 additions & 18 deletions src/python/pants/backend/python/subsystems/python_tool_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from __future__ import annotations

import importlib.resources
from typing import ClassVar, Iterable, Sequence

from pants.backend.python.target_types import ConsoleScript, EntryPoint, MainSpecification
Expand All @@ -14,12 +13,11 @@
LoadedLockfile,
LoadedLockfileRequest,
Lockfile,
LockfileContent,
PexRequirements,
)
from pants.core.goals.generate_lockfiles import DEFAULT_TOOL_LOCKFILE, NO_TOOL_LOCKFILE
from pants.core.util_rules.lockfile_metadata import calculate_invalidation_digest
from pants.engine.fs import Digest, FileContent
from pants.engine.fs import Digest
from pants.engine.internals.selectors import Get
from pants.engine.rules import rule_helper
from pants.option.errors import OptionsError
Expand Down Expand Up @@ -152,24 +150,21 @@ def pex_requirements(

hex_digest = calculate_invalidation_digest(requirements)

lockfile: LockfileContent | Lockfile
if self.lockfile == DEFAULT_TOOL_LOCKFILE:
assert self.default_lockfile_resource is not None
lockfile = LockfileContent(
file_content=FileContent(
f"{self.options_scope}_default.lock",
importlib.resources.read_binary(*self.default_lockfile_resource),
),
lockfile_hex_digest=hex_digest,
resolve_name=self.options_scope,
)
pkg, path = self.default_lockfile_resource
url = f"resource://{pkg}/{path}"
origin = f"The built-in default lockfile for {self.options_scope}"
else:
lockfile = Lockfile(
file_path=self.lockfile,
file_path_description_of_origin=f"the option `[{self.options_scope}].lockfile`",
lockfile_hex_digest=hex_digest,
resolve_name=self.options_scope,
)
url = self.lockfile
origin = f"the option `[{self.options_scope}].lockfile`"

lockfile = Lockfile(
url=url,
url_description_of_origin=origin,
lockfile_hex_digest=hex_digest,
resolve_name=self.options_scope,
)
return EntireLockfile(lockfile, complete_req_strings=tuple(requirements))

@property
Expand Down
4 changes: 2 additions & 2 deletions src/python/pants/backend/python/typecheck/mypy/subsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ def extra_type_stubs_pex_request(
requirements = PexRequirements(self.extra_type_stubs)
else:
tool_lockfile = Lockfile(
file_path=self.extra_type_stubs_lockfile,
file_path_description_of_origin=(
url=self.extra_type_stubs_lockfile,
url_description_of_origin=(
f"the option `[{self.options_scope}].extra_type_stubs_lockfile`"
),
lockfile_hex_digest=calculate_invalidation_digest(self.extra_type_stubs),
Expand Down
35 changes: 18 additions & 17 deletions src/python/pants/backend/python/util_rules/lockfile_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
LoadedLockfile,
LoadedLockfileRequest,
Lockfile,
LockfileContent,
strip_comments_from_pex_json_lockfile,
)
from pants.base.exceptions import EngineError
from pants.core.goals.generate_lockfiles import LockfileDiff, LockfilePackages, PackageName
Expand Down Expand Up @@ -72,42 +72,43 @@ def _pex_lockfile_requirements(


@rule_helper
async def _parse_lockfile(lockfile: Lockfile | LockfileContent) -> FrozenDict[str, Any] | None:
async def _parse_lockfile(lockfile: Lockfile) -> FrozenDict[str, Any] | None:
try:
loaded = await Get(
LoadedLockfile,
LoadedLockfileRequest(lockfile),
)
fc = await Get(DigestContents, Digest, loaded.lockfile_digest)
parsed_lockfile = json.loads(fc[0].content)
return FrozenDict.deep_freeze(parsed_lockfile)
parsed = await _parse_lockfile_content(next(iter(fc)).content, lockfile.url)
return parsed
except EngineError:
# May fail in case the file doesn't exist, which is expected when parsing the "old" lockfile
# the first time a new lockfile is generated.
return None


@rule_helper
async def _parse_lockfile_content(content: bytes, url: str) -> FrozenDict[str, Any] | None:
try:
parsed_lockfile = json.loads(content)
return FrozenDict.deep_freeze(parsed_lockfile)
except json.JSONDecodeError as e:
file_path = (
lockfile.file_path if isinstance(lockfile, Lockfile) else lockfile.file_content.path
)
logger.debug(f"{file_path}: Failed to parse lockfile contents: {e}")
logger.debug(f"{url}: Failed to parse lockfile contents: {e}")
return None


@rule_helper
async def _generate_python_lockfile_diff(
digest: Digest, resolve_name: str, path: str
) -> LockfileDiff:
new_content = await Get(DigestContents, Digest, digest)
new = await _parse_lockfile(
LockfileContent(
file_content=next(c for c in new_content if c.path == path),
resolve_name=resolve_name,
)
)
new_digest_contents = await Get(DigestContents, Digest, digest)
new_content = next(c for c in new_digest_contents if c.path == path).content
new_content = strip_comments_from_pex_json_lockfile(new_content)
new = await _parse_lockfile_content(new_content, path)
old = await _parse_lockfile(
Lockfile(
file_path=path,
file_path_description_of_origin="generated lockfile",
url=path,
url_description_of_origin="existing lockfile",
resolve_name=resolve_name,
)
)
Expand Down
6 changes: 1 addition & 5 deletions src/python/pants/backend/python/util_rules/pex.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
EntireLockfile,
LoadedLockfile,
LoadedLockfileRequest,
Lockfile,
)
from pants.backend.python.util_rules.pex_requirements import (
PexRequirements as PexRequirements, # Explicit re-export.
Expand Down Expand Up @@ -617,10 +616,7 @@ def _build_pex_description(request: PexRequest) -> str:

if isinstance(request.requirements, EntireLockfile):
lockfile = request.requirements.lockfile
if isinstance(lockfile, Lockfile):
desc_suffix = f"from {lockfile.file_path}"
else:
desc_suffix = f"from {lockfile.file_content.path}"
desc_suffix = f"from {lockfile.url}"
else:
if not request.requirements.req_strings:
return f"Building {request.output_filename}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@ def maybe_get_resolve(t: Target) -> str | None:
return ChosenPythonResolve(
name=chosen_resolve,
lockfile=Lockfile(
file_path=python_setup.resolves[chosen_resolve],
file_path_description_of_origin=(
url=python_setup.resolves[chosen_resolve],
url_description_of_origin=(
f"the resolve `{chosen_resolve}` (from `[python].resolves`)"
),
resolve_name=chosen_resolve,
Expand Down Expand Up @@ -568,7 +568,7 @@ async def get_repository_pex(
PexRequest(
description=softwrap(
f"""
Installing {chosen_resolve.lockfile.file_path} for the resolve
Installing {chosen_resolve.lockfile.url} for the resolve
`{chosen_resolve.name}`
"""
),
Expand Down
Loading

0 comments on commit 43b5d3c

Please sign in to comment.