forked from pex-tool/pex
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement support for
--exclude <req>
.
When excluding a requirement from a PEX, any resolved distribution matching that requirement, as well as any of its transitive dependencies not also needed by non-excluded requirements, are elided from the PEX. At runtime these missing dependencies will not trigger boot resolve errors, but they will cause errors if the modules they would have provided are attempted to be imported. If the intention is to load the modules from the runtime environment, then `--pex-inherit-path` / `PEX_INHERIT_PATH` or `PEX_EXTRA_SYS_PATH` knobs must be used to allow the PEX to see distributions installed in the runtime environment. Clearly, you must know what you're doing to use this option and not encounter runtime errors due to import errors. Be ware! A forthcoming `--provided` option, with similar effects on the PEX contents, will both automatically inherit any needed missing distributions from the runtime environment and require all missing distributions are found; failing fast if they are not. Work towards pex-tool#2097.
- Loading branch information
Showing
13 changed files
with
1,002 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import absolute_import | ||
|
||
from collections import defaultdict | ||
|
||
from pex import pex_warnings | ||
from pex.dist_metadata import Requirement | ||
from pex.environment import PEXEnvironment | ||
from pex.exclude_configuration import ExcludeConfiguration | ||
from pex.fingerprinted_distribution import FingerprintedDistribution | ||
from pex.orderedset import OrderedSet | ||
from pex.pep_503 import ProjectName | ||
from pex.pex_builder import PEXBuilder | ||
from pex.pex_info import PexInfo | ||
from pex.resolve.resolvers import Installed | ||
from pex.tracer import TRACER | ||
from pex.typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from typing import DefaultDict, Iterable, Iterator | ||
|
||
import attr # vendor:skip | ||
else: | ||
from pex.third_party import attr | ||
|
||
|
||
@attr.s | ||
class DependencyManager(object): | ||
_requirements = attr.ib(factory=OrderedSet) # type: OrderedSet[Requirement] | ||
_distributions = attr.ib(factory=OrderedSet) # type: OrderedSet[FingerprintedDistribution] | ||
|
||
def add_from_pex(self, pex): | ||
# type: (str) -> None | ||
|
||
pex_info = PexInfo.from_pex(pex) | ||
self._requirements.update(Requirement.parse(req) for req in pex_info.requirements) | ||
|
||
pex_environment = PEXEnvironment.mount(pex, pex_info=pex_info) | ||
self._distributions.update(pex_environment.iter_distributions()) | ||
|
||
def add_from_installed(self, installed): | ||
# type: (Installed) -> None | ||
|
||
for installed_dist in installed.installed_distributions: | ||
self._requirements.update(installed_dist.direct_requirements) | ||
self._distributions.add(installed_dist.fingerprinted_distribution) | ||
|
||
def configure( | ||
self, | ||
pex_builder, # type: PEXBuilder | ||
excluded=(), # type: Iterable[str] | ||
): | ||
# type: (...) -> None | ||
|
||
exclude_configuration = ExcludeConfiguration.create(excluded) | ||
exclude_configuration.configure(pex_builder.info) | ||
|
||
dists_by_project_name = defaultdict( | ||
OrderedSet | ||
) # type: DefaultDict[ProjectName, OrderedSet[FingerprintedDistribution]] | ||
for dist in self._distributions: | ||
dists_by_project_name[dist.distribution.metadata.project_name].add(dist) | ||
|
||
def iter_non_excluded_distributions(requirements): | ||
# type: (Iterable[Requirement]) -> Iterator[FingerprintedDistribution] | ||
for req in requirements: | ||
candidate_dists = dists_by_project_name[req.project_name] | ||
for candidate_dist in tuple(candidate_dists): | ||
if candidate_dist.distribution not in req: | ||
continue | ||
candidate_dists.discard(candidate_dist) | ||
|
||
excluded_by = exclude_configuration.excluded_by(candidate_dist.distribution) | ||
if excluded_by: | ||
excludes = " and ".join(map(str, excluded_by)) | ||
TRACER.log( | ||
"Skipping adding {candidate}: excluded by {excludes}".format( | ||
candidate=candidate_dist.distribution, excludes=excludes | ||
) | ||
) | ||
for root_req in self._requirements: | ||
if candidate_dist.distribution in root_req: | ||
pex_warnings.warn( | ||
"The distribution {dist} was required by the input requirement " | ||
"{root_req} but excluded by configured excludes: " | ||
"{excludes}".format( | ||
dist=candidate_dist.distribution, | ||
root_req=root_req, | ||
excludes=excludes, | ||
) | ||
) | ||
continue | ||
|
||
yield candidate_dist | ||
for dep in iter_non_excluded_distributions( | ||
candidate_dist.distribution.requires() | ||
): | ||
yield dep | ||
|
||
for fingerprinted_dist in iter_non_excluded_distributions(self._requirements): | ||
pex_builder.add_distribution( | ||
dist=fingerprinted_dist.distribution, fingerprint=fingerprinted_dist.fingerprint | ||
) | ||
|
||
for requirement in self._requirements: | ||
pex_builder.add_requirement(requirement) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import absolute_import | ||
|
||
from typing import Iterable, Tuple, Union | ||
|
||
from pex.dist_metadata import Distribution, Requirement | ||
from pex.pex_info import PexInfo | ||
from pex.typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from typing import Iterable | ||
|
||
import attr # vendor:skip | ||
else: | ||
from pex.third_party import attr | ||
|
||
|
||
@attr.s(frozen=True) | ||
class ExcludeConfiguration(object): | ||
@classmethod | ||
def create(cls, excluded): | ||
# type: (Iterable[str]) -> ExcludeConfiguration | ||
return cls(excluded=tuple(Requirement.parse(req) for req in excluded)) | ||
|
||
_excluded = attr.ib(factory=tuple) # type: Tuple[Requirement, ...] | ||
|
||
def configure(self, pex_info): | ||
# type: (PexInfo) -> None | ||
for excluded in self._excluded: | ||
pex_info.add_excluded(excluded) | ||
|
||
def excluded_by(self, item): | ||
# type: (Union[Distribution, Requirement]) -> Iterable[Requirement] | ||
if isinstance(item, Distribution): | ||
return tuple(req for req in self._excluded if item in req) | ||
return tuple(req for req in self._excluded if item.project_name == req.project_name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import absolute_import | ||
|
||
import os.path | ||
import pkgutil | ||
|
||
|
||
def load(rel_path): | ||
# type: (str) -> bytes | ||
data = pkgutil.get_data(__name__, rel_path) | ||
if data is None: | ||
raise ValueError( | ||
"No resource found at {rel_path} from package {name}.".format( | ||
rel_path=rel_path, name=__name__ | ||
) | ||
) | ||
return data | ||
|
||
|
||
def path(*rel_path): | ||
# type: (*str) -> str | ||
path = os.path.join(os.path.dirname(__file__), *rel_path) | ||
if not os.path.isfile(path): | ||
raise ValueError("No resource found at {path}.".format(path=path)) | ||
return path |
2 changes: 1 addition & 1 deletion
2
tests/data/platforms/__init__.py → testing/data/locks/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). | ||
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). |
Oops, something went wrong.