Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Python requirement target addrs in tool requirements. #19014

Merged
merged 9 commits into from May 17, 2023

Conversation

benjyw
Copy link
Sponsor Contributor

@benjyw benjyw commented May 16, 2023

Currently users can enumerate requirements for a Python tool, which will cause
Pants to use that requirement subset from the tool's lockfile. This supports resolving
a tool from a larger lockfile, when that makes sense. If no requirements are provided
the entire lockfile is used.

Two examples of when resolving a tool from a subset of a larger lockfile is useful:

  1. Resolving pytest as a tool at the same version as its runtime library without
    specifying it in two places.
  2. Having a single lockfile for all tools, and user code, to be consumed by VSCode.

However, the value of this requirements option is typically redundant with the
input requirements used to generate the lockfile.

This change allows requirements to specify addresses (or address specs).
This can be used to avoid the redundancy: You can point to some
python_requirement targets that are a subset of all those used to generate
the lockfile.

The implementation turns out to be reasonably neat, as it just pushes
some code that turns addresses into requirement strings to slightly
later in the pex creation logic, and actually allowed us to reduce some
redundancy and trim some code.

This addresses #18816.

@@ -344,7 +352,7 @@ def to_pex_request(
return PexRequest(
output_filename=f"{self.options_scope.replace('-', '_')}.pex",
internal_only=True,
requirements=self.pex_requirements(extra_requirements=extra_requirements),
requirements=requirements,
Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This call to self.pex_requirements() was redundant even before any of the other changes in this PR, so this is just an obvious cleanup.

@@ -333,13 +333,8 @@ class _PexRequirementsRequest:
async def determine_requirement_strings_in_closure(
request: _PexRequirementsRequest, global_requirement_constraints: GlobalRequirementConstraints
) -> PexRequirements:
transitive_targets = await Get(TransitiveTargets, TransitiveTargetsRequest(request.addresses))
Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This computation is now pushed to later.

@classmethod
def req_strings_from_requirement_fields(
cls, fields: Iterable[PythonRequirementsField]
) -> FrozenOrderedSet[str]:
"""A convenience when you only need the raw requirement strings from fields and don't need
to consider things like constraints or resolves."""
return cls.create_from_requirement_fields(fields, constraints_strings=()).req_strings
return FrozenOrderedSet(
Copy link
Sponsor Contributor Author

@benjyw benjyw May 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should always have been implemented this way. It seems like a silly oversight that it was implemented using a transient object returned by create_from_requirement_fields. Fixing this allows us to delete create_from_requirement_fields entirely.

@benjyw
Copy link
Sponsor Contributor Author

benjyw commented May 16, 2023

Note for reviewers: I wanted to get feedback on the approach. Am working on adding some tests.

@benjyw benjyw requested review from stuhood and Eric-Arellano and removed request for kaos and thejcannon May 16, 2023 06:42
addrs.append(req_str_or_addr)
else:
assert isinstance(req_str_or_addr, str)
# A local or VCS requirement will have an ampersand in it, and any other
Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewers, do you concur?

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so. But you could also explicitly recommend prefixing an address with // in case of ambiguity, and skip the @ check (note: not ampersand).

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, I meant asperand.

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would have to require prefixing, we can't just recommend it. Otherwise we'll see vcs/local requirements as addresses with no escape hatch.

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And yeah, I think we should require the // prefix, because apparently wheel paths can be treated as requirements.

Copy link
Sponsor Member

@stuhood stuhood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks!

addrs.append(req_str_or_addr)
else:
assert isinstance(req_str_or_addr, str)
# A local or VCS requirement will have an ampersand in it, and any other
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so. But you could also explicitly recommend prefixing an address with // in case of ambiguity, and skip the @ check (note: not ampersand).

Comment on lines 492 to 493
# TODO: Plumb the origin through in PexRequirements?
description_of_origin="User-specified requirements",
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would probably be good, yea... maybe as an optional field (initially), at the very least? I would be less concerned about this, but particularly since heuristics are being used to distinguish addresses from requirements...

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW there may be a future cleanup (post removing tool lockfiles) where this only takes addresses, I'd have to dive more deeply to be sure.

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to requiring the // prefix, for extra security (really I just check for a single /).

Also added descriptions of origin.

Also add documentation, and apply this in the Pants repo.

PTAL

Copy link
Sponsor Member

@stuhood stuhood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Comment on lines 480 to 482
# Require a `//` prefix, to distinguish address specs from
# local or VCS requirements.
if req_str_or_addr.startswith(os.path.sep):
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be double //, otherwise it's ambiguous with what someone might try as an absolute file path.

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An absolute file path is definitely not any kind of valid requirement, but sure.

@benjyw benjyw added this to the 2.16.x milestone May 17, 2023
@benjyw benjyw merged commit 9d845f0 into pantsbuild:main May 17, 2023
17 checks passed
@benjyw benjyw deleted the addrs_in_pex_requirements branch May 17, 2023 15:09
benjyw added a commit to benjyw/pants that referenced this pull request May 17, 2023
…uild#19014)

Currently users can enumerate `requirements` for a Python tool, which
will cause Pants to use that requirement subset from the tool's lockfile.
This supports resolving a tool from a larger lockfile, when that makes sense.
If no requirements are provided the entire lockfile is used.

Two examples of when resolving a tool from a subset of a larger lockfile
is useful:
1. Resolving `pytest` as a tool at the same version as its runtime
  library without specifying it in two places.
2. Having a single lockfile for all tools, and user code, to be consumed
  by VSCode.

However, the value of this `requirements` option is typically redundant
with the input requirements used to generate the lockfile.

This change allows `requirements` to specify addresses (or address
specs). This can be used to avoid the redundancy: You can point to some
python_requirement targets that are a subset of all those used to
generate the lockfile.

The implementation turns out to be reasonably neat, as it just pushes
some code that turns addresses into requirement strings to slightly
later in the pex creation logic, and actually allowed us to reduce some
redundancy and trim some code.

This fixes pantsbuild#18816.
stuhood pushed a commit that referenced this pull request May 18, 2023
…-pick of #19014) (#19023)

Currently users can enumerate `requirements` for a Python tool, which
will cause
Pants to use that requirement subset from the tool's lockfile. This
supports resolving
a tool from a larger lockfile, when that makes sense. If no requirements
are provided
the entire lockfile is used.

Two examples of when resolving a tool from a subset of a larger lockfile
is useful:
1. Resolving `pytest` as a tool at the same version as its runtime
library without
  specifying it in two places.
2. Having a single lockfile for all tools, and user code, to be consumed
by VSCode.

However, the value of this `requirements` option is typically redundant
with the
input requirements used to generate the lockfile. 

This change allows `requirements` to specify addresses (or address
specs).
This can be used to avoid the redundancy: You can point to some
python_requirement targets that are a subset of all those used to
generate
the lockfile.

The implementation turns out to be reasonably neat, as it just pushes
some code that turns addresses into requirement strings to slightly 
later in the pex creation logic, and actually allowed us to reduce some
redundancy and trim some code.

This addresses #18816.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants