Skip to content

Commit a607b02

Browse files
committed
Add targets for lockfiles.
[ci skip-rust] [ci skip-build-wheels]
1 parent e88cb82 commit a607b02

File tree

4 files changed

+106
-14
lines changed

4 files changed

+106
-14
lines changed

src/python/pants/backend/python/goals/lockfile.py

+38-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33

44
from __future__ import annotations
55

6-
import logging
6+
import itertools
7+
import os.path
78
from collections import defaultdict
89
from dataclasses import dataclass
10+
from operator import itemgetter
911
from typing import Iterable
1012

1113
from pants.backend.python.pip_requirement import PipRequirement
@@ -35,6 +37,8 @@
3537
)
3638
from pants.core.util_rules.lockfile_metadata import calculate_invalidation_digest
3739
from pants.engine.fs import CreateDigest, Digest, DigestContents, FileContent, MergeDigests
40+
from pants.engine.internals.synthetic_targets import SyntheticAddressMaps, SyntheticTargetsRequest
41+
from pants.engine.internals.target_adaptor import TargetAdaptor
3842
from pants.engine.process import ProcessCacheScope, ProcessResult
3943
from pants.engine.rules import Get, collect_rules, rule, rule_helper
4044
from pants.engine.target import AllTargets
@@ -43,8 +47,6 @@
4347
from pants.util.logging import LogLevel
4448
from pants.util.ordered_set import FrozenOrderedSet
4549

46-
logger = logging.getLogger(__name__)
47-
4850

4951
@dataclass(frozen=True)
5052
class GeneratePythonLockfile(GenerateLockfile):
@@ -268,10 +270,43 @@ async def setup_user_lockfile_requests(
268270
)
269271

270272

273+
@dataclass(frozen=True)
274+
class PythonSyntheticLockfileTargetsRequest(SyntheticTargetsRequest):
275+
path: str = "" # Indicate that all targets are provided with a single request.
276+
277+
278+
@rule
279+
async def python_lockfile_synthetic_targets(
280+
request: PythonSyntheticLockfileTargetsRequest,
281+
python_setup: PythonSetup,
282+
) -> SyntheticAddressMaps:
283+
if not python_setup.enable_resolves:
284+
return SyntheticAddressMaps()
285+
286+
resolves = [
287+
(os.path.dirname(lockfile), os.path.basename(lockfile), name)
288+
for name, lockfile in python_setup.resolves.items()
289+
]
290+
return SyntheticAddressMaps.for_targets_request(
291+
request,
292+
[
293+
(
294+
os.path.join(spec_path, "BUILD.python-lockfiles"),
295+
tuple(
296+
TargetAdaptor("lockfiles", name=name, sources=[lockfile])
297+
for _, lockfile, name in lockfiles
298+
),
299+
)
300+
for spec_path, lockfiles in itertools.groupby(sorted(resolves), key=itemgetter(0))
301+
],
302+
)
303+
304+
271305
def rules():
272306
return (
273307
*collect_rules(),
274308
UnionRule(GenerateLockfile, GeneratePythonLockfile),
275309
UnionRule(KnownUserResolveNamesRequest, KnownPythonUserResolveNamesRequest),
276310
UnionRule(RequestedUserResolveNames, RequestedPythonUserResolveNames),
311+
UnionRule(SyntheticTargetsRequest, PythonSyntheticLockfileTargetsRequest),
277312
)

src/python/pants/backend/python/macros/python_requirements.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
TypeStubsModuleMappingField,
1515
)
1616
from pants.backend.python.pip_requirement import PipRequirement
17+
from pants.backend.python.subsystems.setup import PythonSetup
1718
from pants.backend.python.target_types import (
1819
PythonRequirementModulesField,
1920
PythonRequirementResolveField,
@@ -82,7 +83,9 @@ class GenerateFromPythonRequirementsRequest(GenerateTargetsRequest):
8283

8384
@rule(desc="Generate `python_requirement` targets from requirements.txt", level=LogLevel.DEBUG)
8485
async def generate_from_python_requirement(
85-
request: GenerateFromPythonRequirementsRequest, union_membership: UnionMembership
86+
request: GenerateFromPythonRequirementsRequest,
87+
union_membership: UnionMembership,
88+
python_setup: PythonSetup,
8689
) -> GeneratedTargets:
8790
generator = request.generator
8891
requirements_rel_path = generator[PythonRequirementsSourceField].value
@@ -102,6 +105,15 @@ async def generate_from_python_requirement(
102105
union_membership,
103106
)
104107

108+
req_deps = [file_tgt.address.spec]
109+
110+
resolve = request.template.get(
111+
PythonRequirementResolveField.alias, python_setup.default_resolve
112+
)
113+
lockfile = python_setup.resolves.get(resolve) if python_setup.enable_resolves else None
114+
if lockfile:
115+
req_deps.append(f"{lockfile}:{resolve}")
116+
105117
digest_contents = await Get(
106118
DigestContents,
107119
PathGlobs(
@@ -126,9 +138,7 @@ def generate_tgt(
126138
normalized_proj_name = canonicalize_project_name(project_name)
127139
tgt_overrides = overrides.pop(normalized_proj_name, {})
128140
if Dependencies.alias in tgt_overrides:
129-
tgt_overrides[Dependencies.alias] = list(tgt_overrides[Dependencies.alias]) + [
130-
file_tgt.address.spec
131-
]
141+
tgt_overrides[Dependencies.alias] = list(tgt_overrides[Dependencies.alias]) + req_deps
132142

133143
return PythonRequirementTarget(
134144
{
@@ -140,7 +150,7 @@ def generate_tgt(
140150
),
141151
# This may get overridden by `tgt_overrides`, which will have already added in
142152
# the file tgt.
143-
Dependencies.alias: [file_tgt.address.spec],
153+
Dependencies.alias: req_deps,
144154
**tgt_overrides,
145155
},
146156
request.template_address.create_generated(project_name),

src/python/pants/core/register.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
FileTarget,
3030
GenericTarget,
3131
HTTPSource,
32+
LockfilesGeneratorTarget,
33+
LockfileTarget,
3234
RelocatedFiles,
3335
ResourcesGeneratorTarget,
3436
ResourceTarget,
@@ -91,15 +93,17 @@ def rules():
9193
def target_types():
9294
return [
9395
ArchiveTarget,
94-
FileTarget,
96+
DockerEnvironmentTarget,
9597
FilesGeneratorTarget,
98+
FileTarget,
9699
GenericTarget,
97-
ResourceTarget,
98-
ResourcesGeneratorTarget,
99-
RelocatedFiles,
100100
LocalEnvironmentTarget,
101-
DockerEnvironmentTarget,
101+
LockfilesGeneratorTarget,
102+
LockfileTarget,
103+
RelocatedFiles,
102104
RemoteEnvironmentTarget,
105+
ResourcesGeneratorTarget,
106+
ResourceTarget,
103107
]
104108

105109

src/python/pants/core/target_types.py

+44-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ async def _hydrate_asset_source(request: GenerateSourcesRequest) -> GeneratedSou
187187

188188

189189
# -----------------------------------------------------------------------------------------------
190-
# `file` and`files` targets
190+
# `file` and `files` targets
191191
# -----------------------------------------------------------------------------------------------
192192
class FileSourceField(AssetSourceField):
193193
uses_source_roots = False
@@ -721,6 +721,49 @@ async def package_archive_target(field_set: ArchiveFieldSet) -> BuiltPackage:
721721
return BuiltPackage(archive, (BuiltPackageArtifact(output_filename),))
722722

723723

724+
# -----------------------------------------------------------------------------------------------
725+
# `lockfile` and `lockfiles` target
726+
# -----------------------------------------------------------------------------------------------
727+
728+
729+
class LockfileSourceField(SingleSourceField):
730+
uses_source_roots = False
731+
required = True
732+
733+
734+
class LockfileDependenciesField(Dependencies):
735+
pass
736+
737+
738+
class LockfileTarget(Target):
739+
alias = "lockfile"
740+
core_fields = (*COMMON_TARGET_FIELDS, LockfileSourceField, LockfileDependenciesField)
741+
help = softwrap(
742+
"""
743+
A target for lockfiles in order to include them in the dependency graph of other targets.
744+
745+
This tracks them so that `--changed-since --changed-dependees` works properly for targets
746+
relying on a particular lockfile.
747+
"""
748+
)
749+
750+
751+
class LockfilesGeneratorSourcesField(MultipleSourcesField):
752+
pass
753+
754+
755+
class LockfilesGeneratorTarget(TargetFilesGenerator):
756+
alias = "lockfiles"
757+
core_fields = (
758+
*COMMON_TARGET_FIELDS,
759+
LockfilesGeneratorSourcesField,
760+
)
761+
generated_target_cls = LockfileTarget
762+
copied_fields = COMMON_TARGET_FIELDS
763+
moved_fields = (LockfileDependenciesField,)
764+
help = "Generate a `lockfile` target for each file in the `sources` field."
765+
766+
724767
def rules():
725768
return (
726769
*collect_rules(),

0 commit comments

Comments
 (0)