From 02c5342da23115cd3995eb40e1e4a07cb2689d4d Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 00:45:44 -0500 Subject: [PATCH 01/45] pants: add StevedoreExtension target --- contrib/runners/noop_runner/BUILD | 6 + pants-plugins/stevedore_extensions/BUILD | 1 + .../stevedore_extensions/__init__.py | 1 + .../stevedore_extensions/register.py | 12 ++ .../stevedore_extensions/target_types.py | 102 ++++++++++++++ .../target_types_rules.py | 133 ++++++++++++++++++ 6 files changed, 255 insertions(+) create mode 100644 contrib/runners/noop_runner/BUILD create mode 100644 pants-plugins/stevedore_extensions/BUILD create mode 100644 pants-plugins/stevedore_extensions/__init__.py create mode 100644 pants-plugins/stevedore_extensions/register.py create mode 100644 pants-plugins/stevedore_extensions/target_types.py create mode 100644 pants-plugins/stevedore_extensions/target_types_rules.py diff --git a/contrib/runners/noop_runner/BUILD b/contrib/runners/noop_runner/BUILD new file mode 100644 index 0000000000..0b36b3157b --- /dev/null +++ b/contrib/runners/noop_runner/BUILD @@ -0,0 +1,6 @@ +stevedore_extension( + namespace="st2common.runners.runner", + entry_points={ + "noop": "noop_runner.noop_runner", + }, +) diff --git a/pants-plugins/stevedore_extensions/BUILD b/pants-plugins/stevedore_extensions/BUILD new file mode 100644 index 0000000000..db46e8d6c9 --- /dev/null +++ b/pants-plugins/stevedore_extensions/BUILD @@ -0,0 +1 @@ +python_sources() diff --git a/pants-plugins/stevedore_extensions/__init__.py b/pants-plugins/stevedore_extensions/__init__.py new file mode 100644 index 0000000000..57d631c3f0 --- /dev/null +++ b/pants-plugins/stevedore_extensions/__init__.py @@ -0,0 +1 @@ +# coding: utf-8 diff --git a/pants-plugins/stevedore_extensions/register.py b/pants-plugins/stevedore_extensions/register.py new file mode 100644 index 0000000000..8045c208b6 --- /dev/null +++ b/pants-plugins/stevedore_extensions/register.py @@ -0,0 +1,12 @@ +# coding: utf-8 + +from stevedore_extensions import target_types_rules +from stevedore_extensions.target_types import StevedoreExtension + + +def rules(): + return [*target_types_rules.rules()] + + +def target_types(): + return [StevedoreExtension] diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py new file mode 100644 index 0000000000..4f50e6eabe --- /dev/null +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -0,0 +1,102 @@ +# coding: utf-8 +# repurposed from pants.backend.python.target_types +import os + +from dataclasses import dataclass +from typing import Dict, Optional + +from pants.engine.addresses import Address +from pants.engine.collection import Collection +from pants.engine.target import ( + AsyncFieldMixin, + COMMON_TARGET_FIELDS, + Dependencies, + DictStringToStringField, + InvalidFieldException, + SecondaryOwnerMixin, + StringField, + Target, +) +from pants.backend.python.target_types import EntryPoint +from pants.source.filespec import Filespec + + +# See `target_types_rules.py` for a dependency injection rule. +class StevedoreExtensionDependencies(Dependencies): + supports_transitive_excludes = True + + +@dataclass(frozen=True) +class StevedoreEntryPoint: + name: str + value: EntryPoint + + +class StevedoreEntryPoints(Collection[StevedoreEntryPoint]): + pass + + +class StevedoreNamespaceField(StringField): + alias = "namespace" + + +class StevedoreEntryPointsField(AsyncFieldMixin, SecondaryOwnerMixin, DictStringToStringField): + # based on pants.backend.python.target_types.PexEntryPointField + alias = "entry_points" + help = ( + "A dict that maps a stevedore extension name to the entry_point that implements it.\n\n" + # the odd spacing here minimizes diff with help text copied from PexEntryPointField + "You can specify each entry_point with " + "a full module like 'path.to.module' and 'path.to.module:func', or use a " + "shorthand to specify a file name, using the same syntax as the `sources` field:\n\n 1) " + "'app.py', Pants will convert into the module `path.to.app`;\n 2) 'app.py:func', Pants " + "will convert into `path.to.app:func`.\n\nYou must use the file name shorthand for file " + "arguments to work with this target." + ) + required = True + value: StevedoreEntryPoints + + @classmethod + def compute_value(cls, raw_value: Optional[Dict[str, str]], address: Address) -> Collection[StevedoreEntryPoint]: + # TODO: maybe support raw entry point maps like ["name = path.to.module:func"] + # raw_value: Optional[Union[Dict[str, str], List[str]]] + raw_entry_points = super().compute_value(raw_value, address) + entry_points = [] + for name, value in raw_entry_points.items(): + try: + entry_point = EntryPoint.parse(value) + except ValueError as e: + raise InvalidFieldException(str(e)) + entry_points.append(StevedoreEntryPoint(name=name, value=entry_point)) + return StevedoreEntryPoints(entry_points) + + @property + def filespec(self) -> Filespec: + includes = [] + for entry_point in self.value: + if not entry_point.value.module.endswith(".py"): + continue + full_glob = os.path.join(self.address.spec_path, entry_point.value.module) + includes.append(full_glob) + return {"includes": includes} + + +# See `target_types_rules.py` for the `ResolveStevedoreEntryPointsRequest -> ResolvedStevedoreEntryPoints` rule. +@dataclass(frozen=True) +class ResolvedStevedoreEntryPoints: + val: Optional[StevedoreEntryPoints] + + +@dataclass(frozen=True) +class ResolveStevedoreEntryPointsRequest: + """Determine the `entry_points` for a `stevedore_extension` after applying all syntactic sugar.""" + + entry_points_field: StevedoreEntryPointsField + + +class StevedoreExtension(Target): + alias = "stevedore_extension" + core_fields = (*COMMON_TARGET_FIELDS, StevedoreNamespaceField, StevedoreEntryPointsField) + help = f"Entry points used to generate setuptools metadata for stevedore." + + diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py new file mode 100644 index 0000000000..6755fb36b3 --- /dev/null +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -0,0 +1,133 @@ +# coding: utf-8 +# repurposed from pants.backend.python.target_types_rules +import dataclasses +import os + +from pants.backend.python.dependency_inference.module_mapper import PythonModule, PythonModuleOwners +from pants.backend.python.dependency_inference.rules import PythonInferSubsystem, import_rules +from pants.engine.addresses import Address +from pants.engine.fs import GlobMatchErrorBehavior, PathGlobs, Paths +from pants.engine.rules import Get, collect_rules, MultiGet, rule, UnionRule +from pants.engine.target import ( + Dependencies, + DependenciesRequest, + ExplicitlyProvidedDependencies, + InjectDependenciesRequest, + InjectedDependencies, + InvalidFieldException, + WrappedTarget, +) +from pants.source.source_root import SourceRoot, SourceRootRequest + +from stevedore_extensions.target_types import ( + ResolvedStevedoreEntryPoints, + ResolveStevedoreEntryPointsRequest, + StevedoreEntryPoints, + StevedoreEntryPointsField, + StevedoreExtensionDependencies, +) + + +@rule(desc="Determining the entry points for a `stevedore_extension` target") +async def resolve_stevedore_entry_points(request: ResolveStevedoreEntryPointsRequest) -> ResolvedStevedoreEntryPoints: + address = request.entry_points_field.address + resolved = [] + for entry_point in request.entry_points_field.value: + ep_val = entry_point.value + + # supported schemes mirror those in resolve_pex_entry_point: + # 1) this does not support None, unlike pex_entry_point. + # 2) `path.to.module` => preserve exactly. + # 3) `path.to.module:func` => preserve exactly. + # 4) `app.py` => convert into `path.to.app`. + # 5) `app.py:func` => convert into `path.to.app:func`. + + # If it's already a module (cases #2 and #3), simply use that. Otherwise, convert the file name + # into a module path (cases #4 and #5). + if not ep_val.module.endswith(".py"): + resolved.append(entry_point) + + # Use the engine to validate that the file exists and that it resolves to only one file. + full_glob = os.path.join(address.spec_path, ep_val.module) + entry_point_paths = await Get( + Paths, + PathGlobs( + [full_glob], + glob_match_error_behavior=GlobMatchErrorBehavior.error, + description_of_origin=f"{address}'s `{request.entry_points_field.alias}` field", + ), + ) + # We will have already raised if the glob did not match, i.e. if there were no files. But + # we need to check if they used a file glob (`*` or `**`) that resolved to >1 file. + if len(entry_point_paths.files) != 1: + raise InvalidFieldException( + f"Multiple files matched for the `{request.entry_points_field.alias}` " + f"{ep_val.spec!r} for the target {address}, but only one file expected. Are you using " + f"a glob, rather than a file name?\n\n" + f"All matching files: {list(entry_point_paths.files)}." + ) + entry_point_path = entry_point_paths.files[0] + source_root = await Get( + SourceRoot, + SourceRootRequest, + SourceRootRequest.for_file(entry_point_path), + ) + stripped_source_path = os.path.relpath(entry_point_path, source_root.path) + module_base, _ = os.path.splitext(stripped_source_path) + normalized_path = module_base.replace(os.path.sep, ".") + resolved_ep_val = dataclasses.replace(ep_val, module=normalized_path) + resolved.append(dataclasses.replace(entry_point, value=resolved_ep_val)) + return ResolvedStevedoreEntryPoints(StevedoreEntryPoints(resolved)) + + +class InjectStevedoreExtensionDependencies(InjectDependenciesRequest): + inject_for = StevedoreExtensionDependencies + + +@rule(desc="Inferring dependency from the stevedore_extension `entry_points` field") +async def inject_stevedore_entry_points_dependencies( + request: InjectStevedoreExtensionDependencies, python_infer_subsystem: PythonInferSubsystem +) -> InjectedDependencies: + # TODO: this might not be the best option to use as it is for "binary targets" + if not python_infer_subsystem.entry_points: + return InjectedDependencies() + original_tgt: WrappedTarget + original_tgt = await Get(WrappedTarget, Address, request.dependencies_field.address) + entry_points: ResolvedStevedoreEntryPoints + explicitly_provided_deps, entry_points = await MultiGet( + Get(ExplicitlyProvidedDependencies, DependenciesRequest(original_tgt.target[Dependencies])), + Get( + ResolvedStevedoreEntryPoints, + ResolveStevedoreEntryPointsRequest(original_tgt.target[StevedoreEntryPointsField]), + ), + ) + if entry_points.val is None: + return InjectedDependencies() + address = original_tgt.target.address + resolved_owners = [] + for i, entry_point in enumerate(entry_points.val): + owners = await Get(PythonModuleOwners, PythonModule(entry_point.value.module)) + explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference( + owners.ambiguous, + address, + import_reference="module", + context=( + f"The stevedore_extension target {address} has in its entry_points field " + f"`\"{entry_point.name}\": \"{repr(original_tgt.target[StevedoreEntryPointsField].value[i].value.spec)}\"`," + f"which maps to the Python module `{entry_point.value.module}`" + ), + ) + maybe_disambiguated = explicitly_provided_deps.disambiguated_via_ignores(owners.ambiguous) + unambiguous_owners = owners.unambiguous or ( + (maybe_disambiguated,) if maybe_disambiguated else () + ) + resolved_owners.extend(unambiguous_owners) + return InjectedDependencies(resolved_owners) + + +def rules(): + return [ + *collect_rules(), + *import_rules(), + UnionRule(InjectDependenciesRequest, InjectStevedoreExtensionDependencies), + ] From 540363999c561383807eb057af06c0d1b434a0de Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 11:22:35 -0500 Subject: [PATCH 02/45] pants: generate entry_points.txt from StevedoreExtension targets --- .../stevedore_extensions/target_types.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 4f50e6eabe..3e2990379d 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -14,6 +14,7 @@ DictStringToStringField, InvalidFieldException, SecondaryOwnerMixin, + Sources, StringField, Target, ) @@ -94,9 +95,21 @@ class ResolveStevedoreEntryPointsRequest: entry_points_field: StevedoreEntryPointsField +# based on pants.core.target_types.RelocatedFilesSources +class StevedoreSources(Sources): + # We solely register this field for codegen to work. + alias = "_stevedore_sources" + expected_num_files = 0 + + class StevedoreExtension(Target): alias = "stevedore_extension" - core_fields = (*COMMON_TARGET_FIELDS, StevedoreNamespaceField, StevedoreEntryPointsField) + core_fields = ( + *COMMON_TARGET_FIELDS, + StevedoreNamespaceField, + StevedoreEntryPointsField, + StevedoreSources, + ) help = f"Entry points used to generate setuptools metadata for stevedore." From f3105a0dc5c1cc06c7858f0945ecb2a4ef957242 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 11:36:52 -0500 Subject: [PATCH 03/45] pants: add StevedoreNamespaceField.help --- pants-plugins/stevedore_extensions/target_types.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 3e2990379d..0465cb96a6 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -39,6 +39,13 @@ class StevedoreEntryPoints(Collection[StevedoreEntryPoint]): class StevedoreNamespaceField(StringField): alias = "namespace" + help = ( + "The stevedore extension namespace.\n\nThis looks like a python module " + "'my.stevedore.namespace', but a python module of that name does not " + "need to exist. This is what a stevedore ExtensionManager uses to look up " + "relevant entry_points from pkg_resources." + ) + required = True class StevedoreEntryPointsField(AsyncFieldMixin, SecondaryOwnerMixin, DictStringToStringField): From 33e2eb01d4d8e8df779cffff681ba527df6a5f02 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 12:18:36 -0500 Subject: [PATCH 04/45] pants: Enable stevedore_extensions plugin and fix errors --- pants-plugins/stevedore_extensions/register.py | 10 ++++++++-- .../stevedore_extensions/target_types_rules.py | 1 + pants.toml | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pants-plugins/stevedore_extensions/register.py b/pants-plugins/stevedore_extensions/register.py index 8045c208b6..2f5b428613 100644 --- a/pants-plugins/stevedore_extensions/register.py +++ b/pants-plugins/stevedore_extensions/register.py @@ -1,11 +1,17 @@ # coding: utf-8 -from stevedore_extensions import target_types_rules +from pants.backend.codegen import export_codegen_goal + +from stevedore_extensions import target_types_rules, rules as stevedore_rules from stevedore_extensions.target_types import StevedoreExtension def rules(): - return [*target_types_rules.rules()] + return [ + *target_types_rules.rules(), + *stevedore_rules.rules(), + *export_codegen_goal.rules(), + ] def target_types(): diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 6755fb36b3..25433a4055 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -46,6 +46,7 @@ async def resolve_stevedore_entry_points(request: ResolveStevedoreEntryPointsReq # into a module path (cases #4 and #5). if not ep_val.module.endswith(".py"): resolved.append(entry_point) + continue # Use the engine to validate that the file exists and that it resolves to only one file. full_glob = os.path.join(address.spec_path, ep_val.module) diff --git a/pants.toml b/pants.toml index 375f9a1843..1751ff0c40 100644 --- a/pants.toml +++ b/pants.toml @@ -28,6 +28,7 @@ backend_packages = [ "pack_metadata", "sample_conf", "schemas", + "stevedore_extensions", ] # pants ignores files in .gitignore, .*/ directories, /dist/ directory, and __pycache__. pants_ignore.add = [ From 1b2ff536046e75f40e69c7bdbb6cec58e19b55e8 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 13:17:28 -0500 Subject: [PATCH 05/45] pants: shortform Get has to have only 1 comma --- pants-plugins/stevedore_extensions/target_types_rules.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 25433a4055..fb7898be2c 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -56,7 +56,7 @@ async def resolve_stevedore_entry_points(request: ResolveStevedoreEntryPointsReq [full_glob], glob_match_error_behavior=GlobMatchErrorBehavior.error, description_of_origin=f"{address}'s `{request.entry_points_field.alias}` field", - ), + ) ) # We will have already raised if the glob did not match, i.e. if there were no files. But # we need to check if they used a file glob (`*` or `**`) that resolved to >1 file. @@ -99,7 +99,7 @@ async def inject_stevedore_entry_points_dependencies( Get(ExplicitlyProvidedDependencies, DependenciesRequest(original_tgt.target[Dependencies])), Get( ResolvedStevedoreEntryPoints, - ResolveStevedoreEntryPointsRequest(original_tgt.target[StevedoreEntryPointsField]), + ResolveStevedoreEntryPointsRequest(original_tgt.target[StevedoreEntryPointsField]) ), ) if entry_points.val is None: From a20ca9634d8935fd1f9398e2e2a32527d397cb65 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 15:45:38 -0500 Subject: [PATCH 06/45] pants: add stevedore_namespaces dependency inferrence --- contrib/runners/noop_runner/BUILD | 1 + .../stevedore_extensions/register.py | 9 +- .../stevedore_dependency_inference.py | 88 +++++++++++++++++++ .../stevedore_extensions/target_types.py | 7 +- .../target_types_rules.py | 9 +- st2common/tests/unit/BUILD | 1 + 6 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 pants-plugins/stevedore_extensions/stevedore_dependency_inference.py diff --git a/contrib/runners/noop_runner/BUILD b/contrib/runners/noop_runner/BUILD index 0b36b3157b..eca07e112b 100644 --- a/contrib/runners/noop_runner/BUILD +++ b/contrib/runners/noop_runner/BUILD @@ -1,4 +1,5 @@ stevedore_extension( + name="runner", namespace="st2common.runners.runner", entry_points={ "noop": "noop_runner.noop_runner", diff --git a/pants-plugins/stevedore_extensions/register.py b/pants-plugins/stevedore_extensions/register.py index 2f5b428613..9a68216764 100644 --- a/pants-plugins/stevedore_extensions/register.py +++ b/pants-plugins/stevedore_extensions/register.py @@ -2,14 +2,21 @@ from pants.backend.codegen import export_codegen_goal -from stevedore_extensions import target_types_rules, rules as stevedore_rules +from stevedore_extensions import ( + target_types_rules, rules as stevedore_rules, stevedore_dependency_inference +) from stevedore_extensions.target_types import StevedoreExtension +# TODO: add the entry_points automatically to setup_py +# TODO: add stevedore_namespaces field to python_sources? + + def rules(): return [ *target_types_rules.rules(), *stevedore_rules.rules(), + *stevedore_dependency_inference.rules(), *export_codegen_goal.rules(), ] diff --git a/pants-plugins/stevedore_extensions/stevedore_dependency_inference.py b/pants-plugins/stevedore_extensions/stevedore_dependency_inference.py new file mode 100644 index 0000000000..c4c1f800e3 --- /dev/null +++ b/pants-plugins/stevedore_extensions/stevedore_dependency_inference.py @@ -0,0 +1,88 @@ +# coding: utf-8 +from collections import defaultdict +from dataclasses import dataclass +from typing import List, Mapping, Tuple + +from pants.backend.python.target_types import PythonTests +from pants.base.specs import AddressSpecs, DescendantAddresses +from pants.engine.addresses import Address +from pants.engine.rules import collect_rules, Get, MultiGet, rule, UnionRule +from pants.engine.target import ( + # Dependencies, + InferDependenciesRequest, + InferredDependencies, + StringSequenceField, + # SpecialCasedDependencies, + Targets, +) +from pants.util.frozendict import FrozenDict +from pants.util.logging import LogLevel +from pants.util.ordered_set import FrozenOrderedSet, OrderedSet + +from stevedore_extensions.target_types import ( + # ResolveStevedoreEntryPointsRequest, + # ResolvedStevedoreEntryPoints, + StevedoreExtension, + StevedoreNamespaceField, + StevedoreSources, +) + + +@dataclass(frozen=True) +class StevedoreExtensions: + """A mapping of stevedore namespaces to a list the targets that provide them""" + mapping: FrozenDict[str, Tuple[StevedoreExtension]] + + +# This is a lot like a SpecialCasedDependencies field, but it doesn't list targets directly. +class StevedoreNamespacesField(StringSequenceField): + alias = "stevedore_namespaces" + help = ( + "A list of stevedore namespaces to include for tests.\n\n" + "All stevedore_extension targets with these namespaces will be added as " + "dependencies so that they are available on PYTHONPATH during tests. " + "The stevedore namespace format (my.stevedore.extension) is similar " + "to a python namespace." + ) + + +@rule(desc="Creating map of stevedore_extension namespaces to StevedoreExtension targets", level=LogLevel.DEBUG) +async def map_stevedore_extensions() -> StevedoreExtensions: + all_expanded_targets = await Get(Targets, AddressSpecs([DescendantAddresses("")])) + stevedore_extensions = tuple(tgt for tgt in all_expanded_targets if tgt.has_field(StevedoreSources)) + mapping: Mapping[str, List[StevedoreExtension]] = defaultdict(list) + for extension in stevedore_extensions: + mapping[extension[StevedoreNamespaceField].value].append(extension) + return StevedoreExtensions( + FrozenDict( + (k, tuple(v)) for k, v in sorted(mapping.items()) + ) + ) + + +class InferStevedoreNamespaceDependencies(InferDependenciesRequest): + infer_from = StevedoreNamespacesField # FIXME: This is not a Sources class + + +@rule(desc="Infer stevedore_extension target dependencies for python_tests based on namespace list.") +async def infer_stevedore_dependencies( + request: InferStevedoreNamespaceDependencies, stevedore_extensions: StevedoreExtensions +) -> InferredDependencies: + print("infer_stevedore_dependencies") + namespaces: StevedoreNamespacesField = request.sources_field + + addresses = [] + for namespace in namespaces.value: + extensions = stevedore_extensions.mapping.get(namespace, ()) + addresses.extend(extension.address for extension in extensions) + + result: OrderedSet[Address] = OrderedSet(addresses) + return InferredDependencies(sorted(result), sibling_dependencies_inferrable=True) + + +def rules(): + return [ + *collect_rules(), + PythonTests.register_plugin_field(StevedoreNamespacesField), + UnionRule(InferDependenciesRequest, InferStevedoreNamespaceDependencies), + ] diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 0465cb96a6..8f21d042fd 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -22,11 +22,6 @@ from pants.source.filespec import Filespec -# See `target_types_rules.py` for a dependency injection rule. -class StevedoreExtensionDependencies(Dependencies): - supports_transitive_excludes = True - - @dataclass(frozen=True) class StevedoreEntryPoint: name: str @@ -102,9 +97,9 @@ class ResolveStevedoreEntryPointsRequest: entry_points_field: StevedoreEntryPointsField +# See `target_types_rules.py` for a dependency injection rule. # based on pants.core.target_types.RelocatedFilesSources class StevedoreSources(Sources): - # We solely register this field for codegen to work. alias = "_stevedore_sources" expected_num_files = 0 diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index fb7898be2c..57c914dca1 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -24,7 +24,7 @@ ResolveStevedoreEntryPointsRequest, StevedoreEntryPoints, StevedoreEntryPointsField, - StevedoreExtensionDependencies, + StevedoreSources, ) @@ -82,16 +82,17 @@ async def resolve_stevedore_entry_points(request: ResolveStevedoreEntryPointsReq class InjectStevedoreExtensionDependencies(InjectDependenciesRequest): - inject_for = StevedoreExtensionDependencies + inject_for = StevedoreSources # FIXME: this is not a Dependencies class @rule(desc="Inferring dependency from the stevedore_extension `entry_points` field") async def inject_stevedore_entry_points_dependencies( request: InjectStevedoreExtensionDependencies, python_infer_subsystem: PythonInferSubsystem ) -> InjectedDependencies: + print("inject_stevedore_entry_points_dependencies") # TODO: this might not be the best option to use as it is for "binary targets" - if not python_infer_subsystem.entry_points: - return InjectedDependencies() + # if not python_infer_subsystem.entry_points: + # return InjectedDependencies() original_tgt: WrappedTarget original_tgt = await Get(WrappedTarget, Address, request.dependencies_field.address) entry_points: ResolvedStevedoreEntryPoints diff --git a/st2common/tests/unit/BUILD b/st2common/tests/unit/BUILD index fe53c9f265..cfd46fc9a3 100644 --- a/st2common/tests/unit/BUILD +++ b/st2common/tests/unit/BUILD @@ -9,6 +9,7 @@ python_tests( # several files import tests.unit.base which is ambiguous. Tell pants which one to use. "st2common/tests/unit/base.py", ], + stevedore_namespaces=["st2common.runners.runner"], ) python_sources() From 0e33bd387263595fbaf7a962441d0cdb5732a612 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 17:05:58 -0500 Subject: [PATCH 07/45] pants: don't Get within a for loop --- .../stevedore_extensions/target_types_rules.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 57c914dca1..9728e2234a 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -106,16 +106,22 @@ async def inject_stevedore_entry_points_dependencies( if entry_points.val is None: return InjectedDependencies() address = original_tgt.target.address + owners_per_entry_point = await MultiGet( + Get(PythonModuleOwners, PythonModule(entry_point.value)) + for entry_point in entry_points.val + ) + original_entry_points = original_tgt.target[StevedoreEntryPointsField].value resolved_owners = [] - for i, entry_point in enumerate(entry_points.val): - owners = await Get(PythonModuleOwners, PythonModule(entry_point.value.module)) + for entry_point, owners, original_ep in zip( + entry_points.val, owners_per_entry_point, original_entry_points + ): explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference( owners.ambiguous, address, import_reference="module", context=( f"The stevedore_extension target {address} has in its entry_points field " - f"`\"{entry_point.name}\": \"{repr(original_tgt.target[StevedoreEntryPointsField].value[i].value.spec)}\"`," + f"`\"{entry_point.name}\": \"{repr(original_ep.value.spec)}\"`," f"which maps to the Python module `{entry_point.value.module}`" ), ) From 9efacef0f3669c9f625cec06146301fed53bd727 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 17:21:26 -0500 Subject: [PATCH 08/45] pants: fix stevedore_entry_point_dependencies injection --- pants-plugins/stevedore_extensions/target_types.py | 7 +++++++ pants-plugins/stevedore_extensions/target_types_rules.py | 7 +++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 8f21d042fd..1e98c615b1 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -98,8 +98,14 @@ class ResolveStevedoreEntryPointsRequest: # See `target_types_rules.py` for a dependency injection rule. +class StevedoreDependencies(Dependencies): + # dummy field for dependency injection to work + alias = "_stevedore_dependencies" + + # based on pants.core.target_types.RelocatedFilesSources class StevedoreSources(Sources): + # dummy field for codegen to work alias = "_stevedore_sources" expected_num_files = 0 @@ -111,6 +117,7 @@ class StevedoreExtension(Target): StevedoreNamespaceField, StevedoreEntryPointsField, StevedoreSources, + StevedoreDependencies, ) help = f"Entry points used to generate setuptools metadata for stevedore." diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 9728e2234a..0b61826590 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -22,9 +22,9 @@ from stevedore_extensions.target_types import ( ResolvedStevedoreEntryPoints, ResolveStevedoreEntryPointsRequest, + StevedoreDependencies, StevedoreEntryPoints, StevedoreEntryPointsField, - StevedoreSources, ) @@ -82,14 +82,13 @@ async def resolve_stevedore_entry_points(request: ResolveStevedoreEntryPointsReq class InjectStevedoreExtensionDependencies(InjectDependenciesRequest): - inject_for = StevedoreSources # FIXME: this is not a Dependencies class + inject_for = StevedoreDependencies @rule(desc="Inferring dependency from the stevedore_extension `entry_points` field") async def inject_stevedore_entry_points_dependencies( request: InjectStevedoreExtensionDependencies, python_infer_subsystem: PythonInferSubsystem ) -> InjectedDependencies: - print("inject_stevedore_entry_points_dependencies") # TODO: this might not be the best option to use as it is for "binary targets" # if not python_infer_subsystem.entry_points: # return InjectedDependencies() @@ -107,7 +106,7 @@ async def inject_stevedore_entry_points_dependencies( return InjectedDependencies() address = original_tgt.target.address owners_per_entry_point = await MultiGet( - Get(PythonModuleOwners, PythonModule(entry_point.value)) + Get(PythonModuleOwners, PythonModule(entry_point.value.module)) for entry_point in entry_points.val ) original_entry_points = original_tgt.target[StevedoreEntryPointsField].value From 7fd9363759f9ab3935120d8e3b6a46ab66f76882 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 17:46:10 -0500 Subject: [PATCH 09/45] pants: fix inject_stevedore_dependencies injection --- .../stevedore_dependency_inference.py | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/pants-plugins/stevedore_extensions/stevedore_dependency_inference.py b/pants-plugins/stevedore_extensions/stevedore_dependency_inference.py index c4c1f800e3..eaa5be1e03 100644 --- a/pants-plugins/stevedore_extensions/stevedore_dependency_inference.py +++ b/pants-plugins/stevedore_extensions/stevedore_dependency_inference.py @@ -3,25 +3,22 @@ from dataclasses import dataclass from typing import List, Mapping, Tuple -from pants.backend.python.target_types import PythonTests +from pants.backend.python.target_types import PythonTests, PythonTestsDependencies from pants.base.specs import AddressSpecs, DescendantAddresses from pants.engine.addresses import Address from pants.engine.rules import collect_rules, Get, MultiGet, rule, UnionRule from pants.engine.target import ( - # Dependencies, - InferDependenciesRequest, - InferredDependencies, + InjectDependenciesRequest, + InjectedDependencies, StringSequenceField, - # SpecialCasedDependencies, Targets, + WrappedTarget, ) from pants.util.frozendict import FrozenDict from pants.util.logging import LogLevel from pants.util.ordered_set import FrozenOrderedSet, OrderedSet from stevedore_extensions.target_types import ( - # ResolveStevedoreEntryPointsRequest, - # ResolvedStevedoreEntryPoints, StevedoreExtension, StevedoreNamespaceField, StevedoreSources, @@ -60,16 +57,20 @@ async def map_stevedore_extensions() -> StevedoreExtensions: ) -class InferStevedoreNamespaceDependencies(InferDependenciesRequest): - infer_from = StevedoreNamespacesField # FIXME: This is not a Sources class +class InjectStevedoreNamespaceDependencies(InjectDependenciesRequest): + inject_for = PythonTestsDependencies -@rule(desc="Infer stevedore_extension target dependencies for python_tests based on namespace list.") -async def infer_stevedore_dependencies( - request: InferStevedoreNamespaceDependencies, stevedore_extensions: StevedoreExtensions -) -> InferredDependencies: - print("infer_stevedore_dependencies") - namespaces: StevedoreNamespacesField = request.sources_field +@rule(desc="Inject stevedore_extension target dependencies for python_tests based on namespace list.") +async def inject_stevedore_dependencies( + request: InjectStevedoreNamespaceDependencies, stevedore_extensions: StevedoreExtensions +) -> InjectedDependencies: + original_tgt: WrappedTarget + original_tgt = await Get(WrappedTarget, Address, request.dependencies_field.address) + if original_tgt.target.get(StevedoreNamespacesField).value is None: + return InjectedDependencies() + + namespaces: StevedoreNamespacesField = original_tgt.target[StevedoreNamespacesField] addresses = [] for namespace in namespaces.value: @@ -77,12 +78,12 @@ async def infer_stevedore_dependencies( addresses.extend(extension.address for extension in extensions) result: OrderedSet[Address] = OrderedSet(addresses) - return InferredDependencies(sorted(result), sibling_dependencies_inferrable=True) + return InjectedDependencies(sorted(result)) def rules(): return [ *collect_rules(), PythonTests.register_plugin_field(StevedoreNamespacesField), - UnionRule(InferDependenciesRequest, InferStevedoreNamespaceDependencies), + UnionRule(InjectDependenciesRequest, InjectStevedoreNamespaceDependencies), ] From c9f922df9dbd3f6850f2d3e194e20347c9166b36 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 17:48:46 -0500 Subject: [PATCH 10/45] pants: rename stevedore_dependency_inference -> pytest_dependency_injection --- ...dependency_inference.py => pytest_dependency_injection.py} | 0 pants-plugins/stevedore_extensions/register.py | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename pants-plugins/stevedore_extensions/{stevedore_dependency_inference.py => pytest_dependency_injection.py} (100%) diff --git a/pants-plugins/stevedore_extensions/stevedore_dependency_inference.py b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py similarity index 100% rename from pants-plugins/stevedore_extensions/stevedore_dependency_inference.py rename to pants-plugins/stevedore_extensions/pytest_dependency_injection.py diff --git a/pants-plugins/stevedore_extensions/register.py b/pants-plugins/stevedore_extensions/register.py index 9a68216764..8c87cfa6c7 100644 --- a/pants-plugins/stevedore_extensions/register.py +++ b/pants-plugins/stevedore_extensions/register.py @@ -3,7 +3,7 @@ from pants.backend.codegen import export_codegen_goal from stevedore_extensions import ( - target_types_rules, rules as stevedore_rules, stevedore_dependency_inference + target_types_rules, rules as stevedore_rules, pytest_dependency_injection ) from stevedore_extensions.target_types import StevedoreExtension @@ -16,7 +16,7 @@ def rules(): return [ *target_types_rules.rules(), *stevedore_rules.rules(), - *stevedore_dependency_inference.rules(), + *pytest_dependency_injection.rules(), *export_codegen_goal.rules(), ] From 938706290324c2488a7d441fc6c86bef0b379775 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 18:11:59 -0500 Subject: [PATCH 11/45] pants: cleanup stevedore_extensions plugin move StevedoreNamespacesField to target_types --- .../pytest_dependency_injection.py | 22 +++++-------------- .../stevedore_extensions/target_types.py | 11 ++++++++++ .../target_types_rules.py | 10 ++++----- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py index eaa5be1e03..b439191e09 100644 --- a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py @@ -6,22 +6,22 @@ from pants.backend.python.target_types import PythonTests, PythonTestsDependencies from pants.base.specs import AddressSpecs, DescendantAddresses from pants.engine.addresses import Address -from pants.engine.rules import collect_rules, Get, MultiGet, rule, UnionRule +from pants.engine.rules import collect_rules, Get, rule, UnionRule from pants.engine.target import ( InjectDependenciesRequest, InjectedDependencies, - StringSequenceField, Targets, WrappedTarget, ) from pants.util.frozendict import FrozenDict from pants.util.logging import LogLevel -from pants.util.ordered_set import FrozenOrderedSet, OrderedSet +from pants.util.ordered_set import OrderedSet from stevedore_extensions.target_types import ( StevedoreExtension, StevedoreNamespaceField, - StevedoreSources, + StevedoreNamespacesField, + StevedoreDependencies, ) @@ -31,22 +31,10 @@ class StevedoreExtensions: mapping: FrozenDict[str, Tuple[StevedoreExtension]] -# This is a lot like a SpecialCasedDependencies field, but it doesn't list targets directly. -class StevedoreNamespacesField(StringSequenceField): - alias = "stevedore_namespaces" - help = ( - "A list of stevedore namespaces to include for tests.\n\n" - "All stevedore_extension targets with these namespaces will be added as " - "dependencies so that they are available on PYTHONPATH during tests. " - "The stevedore namespace format (my.stevedore.extension) is similar " - "to a python namespace." - ) - - @rule(desc="Creating map of stevedore_extension namespaces to StevedoreExtension targets", level=LogLevel.DEBUG) async def map_stevedore_extensions() -> StevedoreExtensions: all_expanded_targets = await Get(Targets, AddressSpecs([DescendantAddresses("")])) - stevedore_extensions = tuple(tgt for tgt in all_expanded_targets if tgt.has_field(StevedoreSources)) + stevedore_extensions = tuple(tgt for tgt in all_expanded_targets if tgt.has_field(StevedoreDependencies)) mapping: Mapping[str, List[StevedoreExtension]] = defaultdict(list) for extension in stevedore_extensions: mapping[extension[StevedoreNamespaceField].value].append(extension) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 1e98c615b1..d7161fee52 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -16,6 +16,7 @@ SecondaryOwnerMixin, Sources, StringField, + StringSequenceField, Target, ) from pants.backend.python.target_types import EntryPoint @@ -122,3 +123,13 @@ class StevedoreExtension(Target): help = f"Entry points used to generate setuptools metadata for stevedore." +# This is a lot like a SpecialCasedDependencies field, but it doesn't list targets directly. +class StevedoreNamespacesField(StringSequenceField): + alias = "stevedore_namespaces" + help = ( + "A list of stevedore namespaces to include for tests.\n\n" + "All stevedore_extension targets with these namespaces will be added as " + "dependencies so that they are available on PYTHONPATH during tests. " + "The stevedore namespace format (my.stevedore.extension) is similar " + "to a python namespace." + ) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 0b61826590..c2398f4aef 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -87,13 +87,11 @@ class InjectStevedoreExtensionDependencies(InjectDependenciesRequest): @rule(desc="Inferring dependency from the stevedore_extension `entry_points` field") async def inject_stevedore_entry_points_dependencies( - request: InjectStevedoreExtensionDependencies, python_infer_subsystem: PythonInferSubsystem + request: InjectStevedoreExtensionDependencies ) -> InjectedDependencies: - # TODO: this might not be the best option to use as it is for "binary targets" - # if not python_infer_subsystem.entry_points: - # return InjectedDependencies() - original_tgt: WrappedTarget - original_tgt = await Get(WrappedTarget, Address, request.dependencies_field.address) + original_tgt: WrappedTarget = await Get( + WrappedTarget, Address, request.dependencies_field.address + ) entry_points: ResolvedStevedoreEntryPoints explicitly_provided_deps, entry_points = await MultiGet( Get(ExplicitlyProvidedDependencies, DependenciesRequest(original_tgt.target[Dependencies])), From 214271f78e81b983e215b8f47aeb0c8cbf4db865 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 19:59:10 -0500 Subject: [PATCH 12/45] pants: cleanup --- pants-plugins/stevedore_extensions/target_types_rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index c2398f4aef..0cb2cf8218 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -4,7 +4,7 @@ import os from pants.backend.python.dependency_inference.module_mapper import PythonModule, PythonModuleOwners -from pants.backend.python.dependency_inference.rules import PythonInferSubsystem, import_rules +from pants.backend.python.dependency_inference.rules import import_rules from pants.engine.addresses import Address from pants.engine.fs import GlobMatchErrorBehavior, PathGlobs, Paths from pants.engine.rules import Get, collect_rules, MultiGet, rule, UnionRule From b7e66379b47d6bef6031b50f88385fa04da8535b Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 19 May 2021 22:24:26 -0500 Subject: [PATCH 13/45] pants: upgrade to pants 2.6.0.dev0 to use PytestPluginSetup --- pants-plugins/stevedore_extensions/target_types.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index d7161fee52..40cac870be 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -14,7 +14,6 @@ DictStringToStringField, InvalidFieldException, SecondaryOwnerMixin, - Sources, StringField, StringSequenceField, Target, @@ -104,20 +103,12 @@ class StevedoreDependencies(Dependencies): alias = "_stevedore_dependencies" -# based on pants.core.target_types.RelocatedFilesSources -class StevedoreSources(Sources): - # dummy field for codegen to work - alias = "_stevedore_sources" - expected_num_files = 0 - - class StevedoreExtension(Target): alias = "stevedore_extension" core_fields = ( *COMMON_TARGET_FIELDS, StevedoreNamespaceField, StevedoreEntryPointsField, - StevedoreSources, StevedoreDependencies, ) help = f"Entry points used to generate setuptools metadata for stevedore." From eaa747418ed37c75ed3f7b2cf8733299d2126a61 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 21 May 2021 13:10:34 -0500 Subject: [PATCH 14/45] pants: add LogLevel to rules so they show up in UI apparently they are TRACE level by default. --- .../stevedore_extensions/pytest_dependency_injection.py | 2 +- pants-plugins/stevedore_extensions/target_types_rules.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py index b439191e09..bf923677fe 100644 --- a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py @@ -49,7 +49,7 @@ class InjectStevedoreNamespaceDependencies(InjectDependenciesRequest): inject_for = PythonTestsDependencies -@rule(desc="Inject stevedore_extension target dependencies for python_tests based on namespace list.") +@rule(desc="Inject stevedore_extension target dependencies for python_tests based on namespace list.", level=LogLevel.DEBUG) async def inject_stevedore_dependencies( request: InjectStevedoreNamespaceDependencies, stevedore_extensions: StevedoreExtensions ) -> InjectedDependencies: diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 0cb2cf8218..005f6bbf85 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -18,6 +18,7 @@ WrappedTarget, ) from pants.source.source_root import SourceRoot, SourceRootRequest +from pants.util.logging import LogLevel from stevedore_extensions.target_types import ( ResolvedStevedoreEntryPoints, @@ -28,7 +29,7 @@ ) -@rule(desc="Determining the entry points for a `stevedore_extension` target") +@rule(desc="Determining the entry points for a `stevedore_extension` target", level=LogLevel.DEBUG) async def resolve_stevedore_entry_points(request: ResolveStevedoreEntryPointsRequest) -> ResolvedStevedoreEntryPoints: address = request.entry_points_field.address resolved = [] @@ -85,7 +86,7 @@ class InjectStevedoreExtensionDependencies(InjectDependenciesRequest): inject_for = StevedoreDependencies -@rule(desc="Inferring dependency from the stevedore_extension `entry_points` field") +@rule(desc="Inferring dependency from the stevedore_extension `entry_points` field", level=LogLevel.DEBUG) async def inject_stevedore_entry_points_dependencies( request: InjectStevedoreExtensionDependencies ) -> InjectedDependencies: From cab63dc3a5111cb3df360d9b92c9e44c322585f9 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 21 May 2021 14:03:15 -0500 Subject: [PATCH 15/45] pants: don't use get in a for loop --- .../target_types_rules.py | 84 ++++++++++++------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 005f6bbf85..bad8e61aa7 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -31,53 +31,81 @@ @rule(desc="Determining the entry points for a `stevedore_extension` target", level=LogLevel.DEBUG) async def resolve_stevedore_entry_points(request: ResolveStevedoreEntryPointsRequest) -> ResolvedStevedoreEntryPoints: + + # supported schemes mirror those in resolve_pex_entry_point: + # 1) this does not support None, unlike pex_entry_point. + # 2) `path.to.module` => preserve exactly. + # 3) `path.to.module:func` => preserve exactly. + # 4) `app.py` => convert into `path.to.app`. + # 5) `app.py:func` => convert into `path.to.app:func`. + address = request.entry_points_field.address - resolved = [] - for entry_point in request.entry_points_field.value: - ep_val = entry_point.value - - # supported schemes mirror those in resolve_pex_entry_point: - # 1) this does not support None, unlike pex_entry_point. - # 2) `path.to.module` => preserve exactly. - # 3) `path.to.module:func` => preserve exactly. - # 4) `app.py` => convert into `path.to.app`. - # 5) `app.py:func` => convert into `path.to.app:func`. - - # If it's already a module (cases #2 and #3), simply use that. Otherwise, convert the file name - # into a module path (cases #4 and #5). - if not ep_val.module.endswith(".py"): - resolved.append(entry_point) - continue - # Use the engine to validate that the file exists and that it resolves to only one file. - full_glob = os.path.join(address.spec_path, ep_val.module) - entry_point_paths = await Get( + # Use the engine to validate that any file exists + entry_point_paths_results = await MultiGet( + Get( Paths, PathGlobs( - [full_glob], + [os.path.join(address.spec_path, entry_point.value.module)], glob_match_error_behavior=GlobMatchErrorBehavior.error, description_of_origin=f"{address}'s `{request.entry_points_field.alias}` field", ) ) - # We will have already raised if the glob did not match, i.e. if there were no files. But - # we need to check if they used a file glob (`*` or `**`) that resolved to >1 file. + for entry_point in request.entry_points_field.value + if entry_point.value.module.endswith(".py") + ) + + # use iter so we can use next() below + iter_entry_point_paths_results = iter(entry_point_paths_results) + + # We will have already raised if the glob did not match, i.e. if there were no files. But + # we need to check if they used a file glob (`*` or `**`) that resolved to >1 file. + for entry_point in request.entry_points_field.value: + # We just need paths here. If it's already a path, skip it until the next loop. + if not entry_point.value.module.endswith(".py"): + continue + + entry_point_paths = next(iter_entry_point_paths_results) if len(entry_point_paths.files) != 1: raise InvalidFieldException( f"Multiple files matched for the `{request.entry_points_field.alias}` " - f"{ep_val.spec!r} for the target {address}, but only one file expected. Are you using " + f"{entry_point.value.spec!r} for the target {address}, but only one file expected. Are you using " f"a glob, rather than a file name?\n\n" f"All matching files: {list(entry_point_paths.files)}." ) - entry_point_path = entry_point_paths.files[0] - source_root = await Get( + + # restart the iterator + iter_entry_point_paths_results = iter(entry_point_paths_results) + + source_root_results = await MultiGet( + Get( SourceRoot, SourceRootRequest, - SourceRootRequest.for_file(entry_point_path), + SourceRootRequest.for_file(next(iter_entry_point_paths_results).files[0]) ) + for entry_point in request.entry_points_field.value + if entry_point.value.module.endswith(".py") + ) + + # restart the iterator + iter_entry_point_paths_results = iter(entry_point_paths_results) + iter_source_root_results = iter(source_root_results) + + resolved = [] + for entry_point in request.entry_points_field.value: + # If it's already a module (cases #2 and #3), we'll just use that. + # Otherwise, convert the file name into a module path (cases #4 and #5). + if not entry_point.value.module.endswith(".py"): + resolved.append(entry_point) + continue + + entry_point_path = next(iter_entry_point_paths_results).files[0] + source_root = next(iter_source_root_results) + stripped_source_path = os.path.relpath(entry_point_path, source_root.path) module_base, _ = os.path.splitext(stripped_source_path) normalized_path = module_base.replace(os.path.sep, ".") - resolved_ep_val = dataclasses.replace(ep_val, module=normalized_path) + resolved_ep_val = dataclasses.replace(entry_point.value, module=normalized_path) resolved.append(dataclasses.replace(entry_point, value=resolved_ep_val)) return ResolvedStevedoreEntryPoints(StevedoreEntryPoints(resolved)) @@ -99,7 +127,7 @@ async def inject_stevedore_entry_points_dependencies( Get( ResolvedStevedoreEntryPoints, ResolveStevedoreEntryPointsRequest(original_tgt.target[StevedoreEntryPointsField]) - ), + ) ) if entry_points.val is None: return InjectedDependencies() From de2191c0356b9f3d24c56b99231fcf28d6b1a094 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Sun, 30 May 2021 19:22:18 -0500 Subject: [PATCH 16/45] pants: reformat plugins with black But drop the final , in the Get() entries because it confuses the pants AST parsing. --- .../pytest_dependency_injection.py | 22 ++++++++---- .../stevedore_extensions/target_types.py | 8 +++-- .../target_types_rules.py | 36 ++++++++++++++----- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py index bf923677fe..bcec8e5414 100644 --- a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py @@ -28,20 +28,24 @@ @dataclass(frozen=True) class StevedoreExtensions: """A mapping of stevedore namespaces to a list the targets that provide them""" + mapping: FrozenDict[str, Tuple[StevedoreExtension]] -@rule(desc="Creating map of stevedore_extension namespaces to StevedoreExtension targets", level=LogLevel.DEBUG) +@rule( + desc="Creating map of stevedore_extension namespaces to StevedoreExtension targets", + level=LogLevel.DEBUG, +) async def map_stevedore_extensions() -> StevedoreExtensions: all_expanded_targets = await Get(Targets, AddressSpecs([DescendantAddresses("")])) - stevedore_extensions = tuple(tgt for tgt in all_expanded_targets if tgt.has_field(StevedoreDependencies)) + stevedore_extensions = tuple( + tgt for tgt in all_expanded_targets if tgt.has_field(StevedoreDependencies) + ) mapping: Mapping[str, List[StevedoreExtension]] = defaultdict(list) for extension in stevedore_extensions: mapping[extension[StevedoreNamespaceField].value].append(extension) return StevedoreExtensions( - FrozenDict( - (k, tuple(v)) for k, v in sorted(mapping.items()) - ) + FrozenDict((k, tuple(v)) for k, v in sorted(mapping.items())) ) @@ -49,9 +53,13 @@ class InjectStevedoreNamespaceDependencies(InjectDependenciesRequest): inject_for = PythonTestsDependencies -@rule(desc="Inject stevedore_extension target dependencies for python_tests based on namespace list.", level=LogLevel.DEBUG) +@rule( + desc="Inject stevedore_extension target dependencies for python_tests based on namespace list.", + level=LogLevel.DEBUG, +) async def inject_stevedore_dependencies( - request: InjectStevedoreNamespaceDependencies, stevedore_extensions: StevedoreExtensions + request: InjectStevedoreNamespaceDependencies, + stevedore_extensions: StevedoreExtensions, ) -> InjectedDependencies: original_tgt: WrappedTarget original_tgt = await Get(WrappedTarget, Address, request.dependencies_field.address) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 40cac870be..2e3a4e1d2e 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -43,7 +43,9 @@ class StevedoreNamespaceField(StringField): required = True -class StevedoreEntryPointsField(AsyncFieldMixin, SecondaryOwnerMixin, DictStringToStringField): +class StevedoreEntryPointsField( + AsyncFieldMixin, SecondaryOwnerMixin, DictStringToStringField +): # based on pants.backend.python.target_types.PexEntryPointField alias = "entry_points" help = ( @@ -60,7 +62,9 @@ class StevedoreEntryPointsField(AsyncFieldMixin, SecondaryOwnerMixin, DictString value: StevedoreEntryPoints @classmethod - def compute_value(cls, raw_value: Optional[Dict[str, str]], address: Address) -> Collection[StevedoreEntryPoint]: + def compute_value( + cls, raw_value: Optional[Dict[str, str]], address: Address + ) -> Collection[StevedoreEntryPoint]: # TODO: maybe support raw entry point maps like ["name = path.to.module:func"] # raw_value: Optional[Union[Dict[str, str], List[str]]] raw_entry_points = super().compute_value(raw_value, address) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index bad8e61aa7..82f77e4bfd 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -3,7 +3,10 @@ import dataclasses import os -from pants.backend.python.dependency_inference.module_mapper import PythonModule, PythonModuleOwners +from pants.backend.python.dependency_inference.module_mapper import ( + PythonModule, + PythonModuleOwners, +) from pants.backend.python.dependency_inference.rules import import_rules from pants.engine.addresses import Address from pants.engine.fs import GlobMatchErrorBehavior, PathGlobs, Paths @@ -29,8 +32,13 @@ ) -@rule(desc="Determining the entry points for a `stevedore_extension` target", level=LogLevel.DEBUG) -async def resolve_stevedore_entry_points(request: ResolveStevedoreEntryPointsRequest) -> ResolvedStevedoreEntryPoints: +@rule( + desc="Determining the entry points for a `stevedore_extension` target", + level=LogLevel.DEBUG, +) +async def resolve_stevedore_entry_points( + request: ResolveStevedoreEntryPointsRequest, +) -> ResolvedStevedoreEntryPoints: # supported schemes mirror those in resolve_pex_entry_point: # 1) this does not support None, unlike pex_entry_point. @@ -114,19 +122,27 @@ class InjectStevedoreExtensionDependencies(InjectDependenciesRequest): inject_for = StevedoreDependencies -@rule(desc="Inferring dependency from the stevedore_extension `entry_points` field", level=LogLevel.DEBUG) +@rule( + desc="Inferring dependency from the stevedore_extension `entry_points` field", + level=LogLevel.DEBUG, +) async def inject_stevedore_entry_points_dependencies( - request: InjectStevedoreExtensionDependencies + request: InjectStevedoreExtensionDependencies, ) -> InjectedDependencies: original_tgt: WrappedTarget = await Get( WrappedTarget, Address, request.dependencies_field.address ) entry_points: ResolvedStevedoreEntryPoints explicitly_provided_deps, entry_points = await MultiGet( - Get(ExplicitlyProvidedDependencies, DependenciesRequest(original_tgt.target[Dependencies])), + Get( + ExplicitlyProvidedDependencies, + DependenciesRequest(original_tgt.target[Dependencies]) + ), Get( ResolvedStevedoreEntryPoints, - ResolveStevedoreEntryPointsRequest(original_tgt.target[StevedoreEntryPointsField]) + ResolveStevedoreEntryPointsRequest( + original_tgt.target[StevedoreEntryPointsField] + ) ) ) if entry_points.val is None: @@ -147,11 +163,13 @@ async def inject_stevedore_entry_points_dependencies( import_reference="module", context=( f"The stevedore_extension target {address} has in its entry_points field " - f"`\"{entry_point.name}\": \"{repr(original_ep.value.spec)}\"`," + f'`"{entry_point.name}": "{repr(original_ep.value.spec)}"`,' f"which maps to the Python module `{entry_point.value.module}`" ), ) - maybe_disambiguated = explicitly_provided_deps.disambiguated_via_ignores(owners.ambiguous) + maybe_disambiguated = explicitly_provided_deps.disambiguated_via_ignores( + owners.ambiguous + ) unambiguous_owners = owners.unambiguous or ( (maybe_disambiguated,) if maybe_disambiguated else () ) From e37f5af0c8fb78ee6ea2b25f562030e6b4a525cf Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Mon, 31 May 2021 17:38:31 -0500 Subject: [PATCH 17/45] Reformat with black --- pants-plugins/stevedore_extensions/target_types_rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 82f77e4bfd..dcda637b1b 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -143,7 +143,7 @@ async def inject_stevedore_entry_points_dependencies( ResolveStevedoreEntryPointsRequest( original_tgt.target[StevedoreEntryPointsField] ) - ) + ), ) if entry_points.val is None: return InjectedDependencies() From e02c5934910bce563d464dd64b16f7f79d737680 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Mon, 14 Jun 2021 12:37:07 -0500 Subject: [PATCH 18/45] reformat with black --- pants-plugins/stevedore_extensions/target_types_rules.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index dcda637b1b..be39605f97 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -57,7 +57,7 @@ async def resolve_stevedore_entry_points( [os.path.join(address.spec_path, entry_point.value.module)], glob_match_error_behavior=GlobMatchErrorBehavior.error, description_of_origin=f"{address}'s `{request.entry_points_field.alias}` field", - ) + ), ) for entry_point in request.entry_points_field.value if entry_point.value.module.endswith(".py") @@ -89,7 +89,7 @@ async def resolve_stevedore_entry_points( Get( SourceRoot, SourceRootRequest, - SourceRootRequest.for_file(next(iter_entry_point_paths_results).files[0]) + SourceRootRequest.for_file(next(iter_entry_point_paths_results).files[0]), ) for entry_point in request.entry_points_field.value if entry_point.value.module.endswith(".py") @@ -136,13 +136,13 @@ async def inject_stevedore_entry_points_dependencies( explicitly_provided_deps, entry_points = await MultiGet( Get( ExplicitlyProvidedDependencies, - DependenciesRequest(original_tgt.target[Dependencies]) + DependenciesRequest(original_tgt.target[Dependencies]), ), Get( ResolvedStevedoreEntryPoints, ResolveStevedoreEntryPointsRequest( original_tgt.target[StevedoreEntryPointsField] - ) + ), ), ) if entry_points.val is None: From 4f560667dfabeec6e86467765975e1f25f0df3a3 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Mon, 14 Jun 2021 12:48:29 -0500 Subject: [PATCH 19/45] add license header --- pants-plugins/stevedore_extensions/__init__.py | 1 - .../pytest_dependency_injection.py | 14 +++++++++++++- pants-plugins/stevedore_extensions/register.py | 14 +++++++++++++- pants-plugins/stevedore_extensions/target_types.py | 14 +++++++++++++- .../stevedore_extensions/target_types_rules.py | 14 +++++++++++++- 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/pants-plugins/stevedore_extensions/__init__.py b/pants-plugins/stevedore_extensions/__init__.py index 57d631c3f0..e69de29bb2 100644 --- a/pants-plugins/stevedore_extensions/__init__.py +++ b/pants-plugins/stevedore_extensions/__init__.py @@ -1 +0,0 @@ -# coding: utf-8 diff --git a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py index bcec8e5414..1d9730f539 100644 --- a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py @@ -1,4 +1,16 @@ -# coding: utf-8 +# Copyright 2021 The StackStorm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from collections import defaultdict from dataclasses import dataclass from typing import List, Mapping, Tuple diff --git a/pants-plugins/stevedore_extensions/register.py b/pants-plugins/stevedore_extensions/register.py index 8c87cfa6c7..575b18b012 100644 --- a/pants-plugins/stevedore_extensions/register.py +++ b/pants-plugins/stevedore_extensions/register.py @@ -1,4 +1,16 @@ -# coding: utf-8 +# Copyright 2021 The StackStorm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from pants.backend.codegen import export_codegen_goal diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 2e3a4e1d2e..cff008ccef 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -1,4 +1,16 @@ -# coding: utf-8 +# Copyright 2021 The StackStorm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # repurposed from pants.backend.python.target_types import os diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index be39605f97..b21ae22fc8 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -1,4 +1,16 @@ -# coding: utf-8 +# Copyright 2021 The StackStorm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # repurposed from pants.backend.python.target_types_rules import dataclasses import os From 8158253046232ab1d38aace9776d333903bbc056 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Mon, 14 Jun 2021 13:02:42 -0500 Subject: [PATCH 20/45] fix flake8 identified issues --- pants-plugins/stevedore_extensions/target_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index cff008ccef..e39d4a61e8 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -127,7 +127,7 @@ class StevedoreExtension(Target): StevedoreEntryPointsField, StevedoreDependencies, ) - help = f"Entry points used to generate setuptools metadata for stevedore." + help = "Entry points used to generate setuptools metadata for stevedore." # This is a lot like a SpecialCasedDependencies field, but it doesn't list targets directly. From 92e573d8d1df1acb1bdf79031efba983fad7ad0f Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 13 Oct 2021 20:41:03 -0500 Subject: [PATCH 21/45] update stevedore_extensions pants plugin for api changes in pants 2.7 --- pants-plugins/stevedore_extensions/target_types_rules.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index b21ae22fc8..9a3a1b79a7 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -179,9 +179,7 @@ async def inject_stevedore_entry_points_dependencies( f"which maps to the Python module `{entry_point.value.module}`" ), ) - maybe_disambiguated = explicitly_provided_deps.disambiguated_via_ignores( - owners.ambiguous - ) + maybe_disambiguated = explicitly_provided_deps.disambiguated(owners.ambiguous) unambiguous_owners = owners.unambiguous or ( (maybe_disambiguated,) if maybe_disambiguated else () ) From ea686b5166c7854b278b2892da84039c70ffd9e0 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 26 May 2022 11:17:52 -0500 Subject: [PATCH 22/45] update to pants 2.8 (plugin apis, BUILD updates) --- .../stevedore_extensions/pytest_dependency_injection.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py index 1d9730f539..f2bfe7f262 100644 --- a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py @@ -15,7 +15,11 @@ from dataclasses import dataclass from typing import List, Mapping, Tuple -from pants.backend.python.target_types import PythonTests, PythonTestsDependencies +from pants.backend.python.target_types import ( + PythonTestTarget, + PythonTestsGeneratorTarget, + PythonTestsDependencies, +) from pants.base.specs import AddressSpecs, DescendantAddresses from pants.engine.addresses import Address from pants.engine.rules import collect_rules, Get, rule, UnionRule @@ -92,6 +96,7 @@ async def inject_stevedore_dependencies( def rules(): return [ *collect_rules(), - PythonTests.register_plugin_field(StevedoreNamespacesField), + PythonTestsGeneratorTarget.register_plugin_field(StevedoreNamespacesField), + PythonTestTarget.register_plugin_field(StevedoreNamespacesField), UnionRule(InjectDependenciesRequest, InjectStevedoreNamespaceDependencies), ] From 1936c6caa5c781f7836f4e080a2868bbbf76f58e Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 26 May 2022 12:05:43 -0500 Subject: [PATCH 23/45] update to pants 2.9 apis --- .../stevedore_extensions/pytest_dependency_injection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py index f2bfe7f262..bee5fe8549 100644 --- a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py @@ -18,7 +18,7 @@ from pants.backend.python.target_types import ( PythonTestTarget, PythonTestsGeneratorTarget, - PythonTestsDependencies, + PythonTestsDependenciesField, ) from pants.base.specs import AddressSpecs, DescendantAddresses from pants.engine.addresses import Address @@ -66,7 +66,7 @@ async def map_stevedore_extensions() -> StevedoreExtensions: class InjectStevedoreNamespaceDependencies(InjectDependenciesRequest): - inject_for = PythonTestsDependencies + inject_for = PythonTestsDependenciesField @rule( From 654a14c5e950bd817e472c1c1bb0bd791d79478a Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 26 May 2022 12:53:56 -0500 Subject: [PATCH 24/45] update to pants 2.10 apis, resolves --- pants-plugins/stevedore_extensions/target_types.py | 3 ++- .../stevedore_extensions/target_types_rules.py | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index e39d4a61e8..2e8a8df217 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -30,7 +30,7 @@ StringSequenceField, Target, ) -from pants.backend.python.target_types import EntryPoint +from pants.backend.python.target_types import EntryPoint, PythonResolveField from pants.source.filespec import Filespec @@ -126,6 +126,7 @@ class StevedoreExtension(Target): StevedoreNamespaceField, StevedoreEntryPointsField, StevedoreDependencies, + PythonResolveField, ) help = "Entry points used to generate setuptools metadata for stevedore." diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 9a3a1b79a7..201ff71151 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -16,10 +16,12 @@ import os from pants.backend.python.dependency_inference.module_mapper import ( - PythonModule, PythonModuleOwners, + PythonModuleOwnersRequest, ) from pants.backend.python.dependency_inference.rules import import_rules +from pants.backend.python.subsystems.setup import PythonSetup +from pants.backend.python.target_types import PythonResolveField from pants.engine.addresses import Address from pants.engine.fs import GlobMatchErrorBehavior, PathGlobs, Paths from pants.engine.rules import Get, collect_rules, MultiGet, rule, UnionRule @@ -140,6 +142,7 @@ class InjectStevedoreExtensionDependencies(InjectDependenciesRequest): ) async def inject_stevedore_entry_points_dependencies( request: InjectStevedoreExtensionDependencies, + python_setup: PythonSetup, ) -> InjectedDependencies: original_tgt: WrappedTarget = await Get( WrappedTarget, Address, request.dependencies_field.address @@ -161,8 +164,13 @@ async def inject_stevedore_entry_points_dependencies( return InjectedDependencies() address = original_tgt.target.address owners_per_entry_point = await MultiGet( - Get(PythonModuleOwners, PythonModule(entry_point.value.module)) - for entry_point in entry_points.val + Get( + PythonModuleOwners, + PythonModuleOwnersRequest( + entry_point.value.module, + resolve=original_tgt.target[PythonResolveField].normalized_value(python_setup), + ) + ) for entry_point in entry_points.val ) original_entry_points = original_tgt.target[StevedoreEntryPointsField].value resolved_owners = [] From 2fd18bb1574398aa33ffbdbebf355c8bb2d76ed8 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 26 May 2022 17:59:27 -0500 Subject: [PATCH 25/45] reformat with black --- pants-plugins/stevedore_extensions/target_types_rules.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 201ff71151..87d471b6a2 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -168,9 +168,12 @@ async def inject_stevedore_entry_points_dependencies( PythonModuleOwners, PythonModuleOwnersRequest( entry_point.value.module, - resolve=original_tgt.target[PythonResolveField].normalized_value(python_setup), - ) - ) for entry_point in entry_points.val + resolve=original_tgt.target[PythonResolveField].normalized_value( + python_setup + ), + ), + ) + for entry_point in entry_points.val ) original_entry_points = original_tgt.target[StevedoreEntryPointsField].value resolved_owners = [] From 376dd26171cc7f0db9a65f1c4e19595194b2990e Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Mon, 20 Jun 2022 15:28:05 -0500 Subject: [PATCH 26/45] rename StevedoreDependencies -> StevedoreDependenciesField --- .../stevedore_extensions/pytest_dependency_injection.py | 4 ++-- pants-plugins/stevedore_extensions/target_types.py | 4 ++-- pants-plugins/stevedore_extensions/target_types_rules.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py index bee5fe8549..c251911ac1 100644 --- a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/pytest_dependency_injection.py @@ -34,10 +34,10 @@ from pants.util.ordered_set import OrderedSet from stevedore_extensions.target_types import ( + StevedoreDependenciesField, StevedoreExtension, StevedoreNamespaceField, StevedoreNamespacesField, - StevedoreDependencies, ) @@ -55,7 +55,7 @@ class StevedoreExtensions: async def map_stevedore_extensions() -> StevedoreExtensions: all_expanded_targets = await Get(Targets, AddressSpecs([DescendantAddresses("")])) stevedore_extensions = tuple( - tgt for tgt in all_expanded_targets if tgt.has_field(StevedoreDependencies) + tgt for tgt in all_expanded_targets if tgt.has_field(StevedoreDependenciesField) ) mapping: Mapping[str, List[StevedoreExtension]] = defaultdict(list) for extension in stevedore_extensions: diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 2e8a8df217..db0e7d9d6b 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -114,7 +114,7 @@ class ResolveStevedoreEntryPointsRequest: # See `target_types_rules.py` for a dependency injection rule. -class StevedoreDependencies(Dependencies): +class StevedoreDependenciesField(Dependencies): # dummy field for dependency injection to work alias = "_stevedore_dependencies" @@ -125,7 +125,7 @@ class StevedoreExtension(Target): *COMMON_TARGET_FIELDS, StevedoreNamespaceField, StevedoreEntryPointsField, - StevedoreDependencies, + StevedoreDependenciesField, PythonResolveField, ) help = "Entry points used to generate setuptools metadata for stevedore." diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 87d471b6a2..2e02dd50cf 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -40,7 +40,7 @@ from stevedore_extensions.target_types import ( ResolvedStevedoreEntryPoints, ResolveStevedoreEntryPointsRequest, - StevedoreDependencies, + StevedoreDependenciesField, StevedoreEntryPoints, StevedoreEntryPointsField, ) @@ -133,7 +133,7 @@ async def resolve_stevedore_entry_points( class InjectStevedoreExtensionDependencies(InjectDependenciesRequest): - inject_for = StevedoreDependencies + inject_for = StevedoreDependenciesField @rule( From 1ad222c2ffb0da4d15b66e33bbcd8b2125ab01c1 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Mon, 20 Jun 2022 15:31:05 -0500 Subject: [PATCH 27/45] move pytest_dependency_injection to python_dependency_injection Targets other than python_tests will get stevedore_namespaces fields later. --- ...pendency_injection.py => python_dependency_injection.py} | 6 ++++-- pants-plugins/stevedore_extensions/register.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) rename pants-plugins/stevedore_extensions/{pytest_dependency_injection.py => python_dependency_injection.py} (94%) diff --git a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py b/pants-plugins/stevedore_extensions/python_dependency_injection.py similarity index 94% rename from pants-plugins/stevedore_extensions/pytest_dependency_injection.py rename to pants-plugins/stevedore_extensions/python_dependency_injection.py index c251911ac1..8205815093 100644 --- a/pants-plugins/stevedore_extensions/pytest_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/python_dependency_injection.py @@ -16,11 +16,12 @@ from typing import List, Mapping, Tuple from pants.backend.python.target_types import ( + PythonDistributionDependenciesField, PythonTestTarget, PythonTestsGeneratorTarget, PythonTestsDependenciesField, ) -from pants.base.specs import AddressSpecs, DescendantAddresses +from pants.base.specs import AddressSpecs, DescendantAddresses, SiblingAddresses from pants.engine.addresses import Address from pants.engine.rules import collect_rules, Get, rule, UnionRule from pants.engine.target import ( @@ -35,6 +36,7 @@ from stevedore_extensions.target_types import ( StevedoreDependenciesField, + StevedoreEntryPointsField, StevedoreExtension, StevedoreNamespaceField, StevedoreNamespacesField, @@ -73,7 +75,7 @@ class InjectStevedoreNamespaceDependencies(InjectDependenciesRequest): desc="Inject stevedore_extension target dependencies for python_tests based on namespace list.", level=LogLevel.DEBUG, ) -async def inject_stevedore_dependencies( +async def inject_stevedore_namespace_dependencies( request: InjectStevedoreNamespaceDependencies, stevedore_extensions: StevedoreExtensions, ) -> InjectedDependencies: diff --git a/pants-plugins/stevedore_extensions/register.py b/pants-plugins/stevedore_extensions/register.py index 575b18b012..2c8952e055 100644 --- a/pants-plugins/stevedore_extensions/register.py +++ b/pants-plugins/stevedore_extensions/register.py @@ -15,7 +15,7 @@ from pants.backend.codegen import export_codegen_goal from stevedore_extensions import ( - target_types_rules, rules as stevedore_rules, pytest_dependency_injection + target_types_rules, rules as stevedore_rules, python_dependency_injection ) from stevedore_extensions.target_types import StevedoreExtension @@ -28,7 +28,7 @@ def rules(): return [ *target_types_rules.rules(), *stevedore_rules.rules(), - *pytest_dependency_injection.rules(), + *python_dependency_injection.rules(), *export_codegen_goal.rules(), ] From 8d59111b6459e450e429218f4b185dce590d42c2 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 1 Jul 2022 18:01:47 -0500 Subject: [PATCH 28/45] update to pants 2.13.0a1 --- .../python_dependency_injection.py | 21 ++++++++++++------- .../stevedore_extensions/target_types.py | 5 +++++ .../target_types_rules.py | 19 +++++++++++++++-- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/pants-plugins/stevedore_extensions/python_dependency_injection.py b/pants-plugins/stevedore_extensions/python_dependency_injection.py index 8205815093..bf30b0b484 100644 --- a/pants-plugins/stevedore_extensions/python_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/python_dependency_injection.py @@ -21,7 +21,7 @@ PythonTestsGeneratorTarget, PythonTestsDependenciesField, ) -from pants.base.specs import AddressSpecs, DescendantAddresses, SiblingAddresses +from pants.base.specs import DirGlobSpec, RawSpecs from pants.engine.addresses import Address from pants.engine.rules import collect_rules, Get, rule, UnionRule from pants.engine.target import ( @@ -29,13 +29,14 @@ InjectedDependencies, Targets, WrappedTarget, + WrappedTargetRequest, ) from pants.util.frozendict import FrozenDict from pants.util.logging import LogLevel from pants.util.ordered_set import OrderedSet from stevedore_extensions.target_types import ( - StevedoreDependenciesField, + AllStevedoreExtensionTargets, StevedoreEntryPointsField, StevedoreExtension, StevedoreNamespaceField, @@ -54,11 +55,9 @@ class StevedoreExtensions: desc="Creating map of stevedore_extension namespaces to StevedoreExtension targets", level=LogLevel.DEBUG, ) -async def map_stevedore_extensions() -> StevedoreExtensions: - all_expanded_targets = await Get(Targets, AddressSpecs([DescendantAddresses("")])) - stevedore_extensions = tuple( - tgt for tgt in all_expanded_targets if tgt.has_field(StevedoreDependenciesField) - ) +async def map_stevedore_extensions( + stevedore_extensions: AllStevedoreExtensionTargets, +) -> StevedoreExtensions: mapping: Mapping[str, List[StevedoreExtension]] = defaultdict(list) for extension in stevedore_extensions: mapping[extension[StevedoreNamespaceField].value].append(extension) @@ -80,7 +79,13 @@ async def inject_stevedore_namespace_dependencies( stevedore_extensions: StevedoreExtensions, ) -> InjectedDependencies: original_tgt: WrappedTarget - original_tgt = await Get(WrappedTarget, Address, request.dependencies_field.address) + original_tgt = await Get( + WrappedTarget, + WrappedTargetRequest( + request.dependencies_field.address, + description_of_origin="inject_stevedore_namespace_dependencies", + ), + ) if original_tgt.target.get(StevedoreNamespacesField).value is None: return InjectedDependencies() diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index db0e7d9d6b..69744ba24c 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -29,6 +29,7 @@ StringField, StringSequenceField, Target, + Targets, ) from pants.backend.python.target_types import EntryPoint, PythonResolveField from pants.source.filespec import Filespec @@ -141,3 +142,7 @@ class StevedoreNamespacesField(StringSequenceField): "The stevedore namespace format (my.stevedore.extension) is similar " "to a python namespace." ) + + +class AllStevedoreExtensionTargets(Targets): + pass diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 2e02dd50cf..7a27ab0a22 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -22,10 +22,10 @@ from pants.backend.python.dependency_inference.rules import import_rules from pants.backend.python.subsystems.setup import PythonSetup from pants.backend.python.target_types import PythonResolveField -from pants.engine.addresses import Address from pants.engine.fs import GlobMatchErrorBehavior, PathGlobs, Paths from pants.engine.rules import Get, collect_rules, MultiGet, rule, UnionRule from pants.engine.target import ( + AllTargets, Dependencies, DependenciesRequest, ExplicitlyProvidedDependencies, @@ -33,11 +33,13 @@ InjectedDependencies, InvalidFieldException, WrappedTarget, + WrappedTargetRequest, ) from pants.source.source_root import SourceRoot, SourceRootRequest from pants.util.logging import LogLevel from stevedore_extensions.target_types import ( + AllStevedoreExtensionTargets, ResolvedStevedoreEntryPoints, ResolveStevedoreEntryPointsRequest, StevedoreDependenciesField, @@ -46,6 +48,15 @@ ) +@rule(desc="Find all StevedoreExtension targets in project", level=LogLevel.DEBUG) +def find_all_stevedore_extension_targets( + targets: AllTargets, +) -> AllStevedoreExtensionTargets: + return AllStevedoreExtensionTargets( + tgt for tgt in targets if tgt.has_field(StevedoreDependenciesField) + ) + + @rule( desc="Determining the entry points for a `stevedore_extension` target", level=LogLevel.DEBUG, @@ -145,7 +156,11 @@ async def inject_stevedore_entry_points_dependencies( python_setup: PythonSetup, ) -> InjectedDependencies: original_tgt: WrappedTarget = await Get( - WrappedTarget, Address, request.dependencies_field.address + WrappedTarget, + WrappedTargetRequest( + request.dependencies_field.address, + description_of_origin="inject_stevedore_entry_points_dependencies", + ), ) entry_points: ResolvedStevedoreEntryPoints explicitly_provided_deps, entry_points = await MultiGet( From 9d2af6a639b3ca29cc93eb79dfdb3acc49acf6f7 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 11 Oct 2022 20:04:08 -0500 Subject: [PATCH 29/45] pants plugins updates --- .../python_dependency_injection.py | 18 +++++++++--------- .../stevedore_extensions/target_types_rules.py | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pants-plugins/stevedore_extensions/python_dependency_injection.py b/pants-plugins/stevedore_extensions/python_dependency_injection.py index bf30b0b484..766cc857f5 100644 --- a/pants-plugins/stevedore_extensions/python_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/python_dependency_injection.py @@ -25,8 +25,8 @@ from pants.engine.addresses import Address from pants.engine.rules import collect_rules, Get, rule, UnionRule from pants.engine.target import ( - InjectDependenciesRequest, - InjectedDependencies, + InferDependenciesRequest, + InferredDependencies, Targets, WrappedTarget, WrappedTargetRequest, @@ -66,18 +66,18 @@ async def map_stevedore_extensions( ) -class InjectStevedoreNamespaceDependencies(InjectDependenciesRequest): +class InferStevedoreNamespaceDependencies(InferDependenciesRequest): inject_for = PythonTestsDependenciesField @rule( - desc="Inject stevedore_extension target dependencies for python_tests based on namespace list.", + desc="Infer stevedore_extension target dependencies for python_tests based on namespace list.", level=LogLevel.DEBUG, ) async def inject_stevedore_namespace_dependencies( - request: InjectStevedoreNamespaceDependencies, + request: InferStevedoreNamespaceDependencies, stevedore_extensions: StevedoreExtensions, -) -> InjectedDependencies: +) -> InferredDependencies: original_tgt: WrappedTarget original_tgt = await Get( WrappedTarget, @@ -87,7 +87,7 @@ async def inject_stevedore_namespace_dependencies( ), ) if original_tgt.target.get(StevedoreNamespacesField).value is None: - return InjectedDependencies() + return InferredDependencies() namespaces: StevedoreNamespacesField = original_tgt.target[StevedoreNamespacesField] @@ -97,7 +97,7 @@ async def inject_stevedore_namespace_dependencies( addresses.extend(extension.address for extension in extensions) result: OrderedSet[Address] = OrderedSet(addresses) - return InjectedDependencies(sorted(result)) + return InferredDependencies(sorted(result)) def rules(): @@ -105,5 +105,5 @@ def rules(): *collect_rules(), PythonTestsGeneratorTarget.register_plugin_field(StevedoreNamespacesField), PythonTestTarget.register_plugin_field(StevedoreNamespacesField), - UnionRule(InjectDependenciesRequest, InjectStevedoreNamespaceDependencies), + UnionRule(InferDependenciesRequest, InferStevedoreNamespaceDependencies), ] diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 7a27ab0a22..761a81c6ba 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -29,8 +29,8 @@ Dependencies, DependenciesRequest, ExplicitlyProvidedDependencies, - InjectDependenciesRequest, - InjectedDependencies, + InferDependenciesRequest, + InferredDependencies, InvalidFieldException, WrappedTarget, WrappedTargetRequest, @@ -143,7 +143,7 @@ async def resolve_stevedore_entry_points( return ResolvedStevedoreEntryPoints(StevedoreEntryPoints(resolved)) -class InjectStevedoreExtensionDependencies(InjectDependenciesRequest): +class InferStevedoreExtensionDependencies(InferDependenciesRequest): inject_for = StevedoreDependenciesField @@ -152,9 +152,9 @@ class InjectStevedoreExtensionDependencies(InjectDependenciesRequest): level=LogLevel.DEBUG, ) async def inject_stevedore_entry_points_dependencies( - request: InjectStevedoreExtensionDependencies, + request: InferStevedoreExtensionDependencies, python_setup: PythonSetup, -) -> InjectedDependencies: +) -> InferredDependencies: original_tgt: WrappedTarget = await Get( WrappedTarget, WrappedTargetRequest( @@ -176,7 +176,7 @@ async def inject_stevedore_entry_points_dependencies( ), ) if entry_points.val is None: - return InjectedDependencies() + return InferredDependencies() address = original_tgt.target.address owners_per_entry_point = await MultiGet( Get( @@ -210,12 +210,12 @@ async def inject_stevedore_entry_points_dependencies( (maybe_disambiguated,) if maybe_disambiguated else () ) resolved_owners.extend(unambiguous_owners) - return InjectedDependencies(resolved_owners) + return InferredDependencies(resolved_owners) def rules(): return [ *collect_rules(), *import_rules(), - UnionRule(InjectDependenciesRequest, InjectStevedoreExtensionDependencies), + UnionRule(InferDependenciesRequest, InferStevedoreExtensionDependencies), ] From b4b3a8f4ea46a393700f54c2abe2a0b51df1a64f Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Sat, 15 Oct 2022 23:54:28 -0500 Subject: [PATCH 30/45] finish updating stevedore_extensions pants plugin for new API in pants 2.14 --- .../python_dependency_injection.py | 29 ++++++------- .../stevedore_extensions/target_types.py | 8 ---- .../target_types_rules.py | 41 +++++++++---------- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/pants-plugins/stevedore_extensions/python_dependency_injection.py b/pants-plugins/stevedore_extensions/python_dependency_injection.py index 766cc857f5..bc86ac7915 100644 --- a/pants-plugins/stevedore_extensions/python_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/python_dependency_injection.py @@ -25,11 +25,10 @@ from pants.engine.addresses import Address from pants.engine.rules import collect_rules, Get, rule, UnionRule from pants.engine.target import ( + FieldSet, InferDependenciesRequest, InferredDependencies, Targets, - WrappedTarget, - WrappedTargetRequest, ) from pants.util.frozendict import FrozenDict from pants.util.logging import LogLevel @@ -66,30 +65,28 @@ async def map_stevedore_extensions( ) +@dataclass(frozen=True) +class PythonTestsStevedoreNamespaceInferenceFieldSet(FieldSet): + required_fields = (PythonTestsDependenciesField, StevedoreNamespacesField) + + stevedore_namespaces: StevedoreNamespacesField + + class InferStevedoreNamespaceDependencies(InferDependenciesRequest): - inject_for = PythonTestsDependenciesField + infer_from = PythonTestsStevedoreNamespaceInferenceFieldSet @rule( desc="Infer stevedore_extension target dependencies for python_tests based on namespace list.", level=LogLevel.DEBUG, ) -async def inject_stevedore_namespace_dependencies( +async def infer_stevedore_namespace_dependencies( request: InferStevedoreNamespaceDependencies, stevedore_extensions: StevedoreExtensions, ) -> InferredDependencies: - original_tgt: WrappedTarget - original_tgt = await Get( - WrappedTarget, - WrappedTargetRequest( - request.dependencies_field.address, - description_of_origin="inject_stevedore_namespace_dependencies", - ), - ) - if original_tgt.target.get(StevedoreNamespacesField).value is None: - return InferredDependencies() - - namespaces: StevedoreNamespacesField = original_tgt.target[StevedoreNamespacesField] + namespaces: StevedoreNamespacesField = request.field_set.stevedore_namespaces + if namespaces.value is None: + return InferredDependencies(()) addresses = [] for namespace in namespaces.value: diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 69744ba24c..dae4ac93cf 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -22,7 +22,6 @@ from pants.engine.target import ( AsyncFieldMixin, COMMON_TARGET_FIELDS, - Dependencies, DictStringToStringField, InvalidFieldException, SecondaryOwnerMixin, @@ -114,19 +113,12 @@ class ResolveStevedoreEntryPointsRequest: entry_points_field: StevedoreEntryPointsField -# See `target_types_rules.py` for a dependency injection rule. -class StevedoreDependenciesField(Dependencies): - # dummy field for dependency injection to work - alias = "_stevedore_dependencies" - - class StevedoreExtension(Target): alias = "stevedore_extension" core_fields = ( *COMMON_TARGET_FIELDS, StevedoreNamespaceField, StevedoreEntryPointsField, - StevedoreDependenciesField, PythonResolveField, ) help = "Entry points used to generate setuptools metadata for stevedore." diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 761a81c6ba..e57b165e57 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -14,6 +14,7 @@ # repurposed from pants.backend.python.target_types_rules import dataclasses import os +from dataclasses import dataclass from pants.backend.python.dependency_inference.module_mapper import ( PythonModuleOwners, @@ -29,11 +30,10 @@ Dependencies, DependenciesRequest, ExplicitlyProvidedDependencies, + FieldSet, InferDependenciesRequest, InferredDependencies, InvalidFieldException, - WrappedTarget, - WrappedTargetRequest, ) from pants.source.source_root import SourceRoot, SourceRootRequest from pants.util.logging import LogLevel @@ -42,7 +42,6 @@ AllStevedoreExtensionTargets, ResolvedStevedoreEntryPoints, ResolveStevedoreEntryPointsRequest, - StevedoreDependenciesField, StevedoreEntryPoints, StevedoreEntryPointsField, ) @@ -53,7 +52,7 @@ def find_all_stevedore_extension_targets( targets: AllTargets, ) -> AllStevedoreExtensionTargets: return AllStevedoreExtensionTargets( - tgt for tgt in targets if tgt.has_field(StevedoreDependenciesField) + tgt for tgt in targets if tgt.has_field(StevedoreEntryPointsField) ) @@ -143,54 +142,52 @@ async def resolve_stevedore_entry_points( return ResolvedStevedoreEntryPoints(StevedoreEntryPoints(resolved)) +@dataclass(frozen=True) +class StevedoreEntryPointsInferenceFieldSet(FieldSet): + required_fields = (StevedoreEntryPointsField, Dependencies, PythonResolveField) + + entry_points: StevedoreEntryPointsField + dependencies: Dependencies + resolve: PythonResolveField + + class InferStevedoreExtensionDependencies(InferDependenciesRequest): - inject_for = StevedoreDependenciesField + infer_from = StevedoreEntryPointsInferenceFieldSet @rule( desc="Inferring dependency from the stevedore_extension `entry_points` field", level=LogLevel.DEBUG, ) -async def inject_stevedore_entry_points_dependencies( +async def infer_stevedore_entry_points_dependencies( request: InferStevedoreExtensionDependencies, python_setup: PythonSetup, ) -> InferredDependencies: - original_tgt: WrappedTarget = await Get( - WrappedTarget, - WrappedTargetRequest( - request.dependencies_field.address, - description_of_origin="inject_stevedore_entry_points_dependencies", - ), - ) entry_points: ResolvedStevedoreEntryPoints explicitly_provided_deps, entry_points = await MultiGet( Get( ExplicitlyProvidedDependencies, - DependenciesRequest(original_tgt.target[Dependencies]), + DependenciesRequest(request.field_set.dependencies), ), Get( ResolvedStevedoreEntryPoints, - ResolveStevedoreEntryPointsRequest( - original_tgt.target[StevedoreEntryPointsField] - ), + ResolveStevedoreEntryPointsRequest(request.field_set.entry_points), ), ) if entry_points.val is None: return InferredDependencies() - address = original_tgt.target.address + address = request.field_set.address owners_per_entry_point = await MultiGet( Get( PythonModuleOwners, PythonModuleOwnersRequest( entry_point.value.module, - resolve=original_tgt.target[PythonResolveField].normalized_value( - python_setup - ), + resolve=request.field_set.resolve.normalized_value(python_setup), ), ) for entry_point in entry_points.val ) - original_entry_points = original_tgt.target[StevedoreEntryPointsField].value + original_entry_points = request.field_set.entry_points.value resolved_owners = [] for entry_point, owners, original_ep in zip( entry_points.val, owners_per_entry_point, original_entry_points From 239d0e5419b44f9746f6e74550a91efaf0c67349 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 17 Jan 2023 16:38:17 -0600 Subject: [PATCH 31/45] disable stevedore_extensions plugin until next PR --- contrib/runners/noop_runner/BUILD | 14 +++++++------- pants.toml | 2 +- st2common/tests/unit/BUILD | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contrib/runners/noop_runner/BUILD b/contrib/runners/noop_runner/BUILD index eca07e112b..02983d4e61 100644 --- a/contrib/runners/noop_runner/BUILD +++ b/contrib/runners/noop_runner/BUILD @@ -1,7 +1,7 @@ -stevedore_extension( - name="runner", - namespace="st2common.runners.runner", - entry_points={ - "noop": "noop_runner.noop_runner", - }, -) +# stevedore_extension( +# name="runner", +# namespace="st2common.runners.runner", +# entry_points={ +# "noop": "noop_runner.noop_runner", +# }, +# ) diff --git a/pants.toml b/pants.toml index 1751ff0c40..22a8a4b1a4 100644 --- a/pants.toml +++ b/pants.toml @@ -28,7 +28,7 @@ backend_packages = [ "pack_metadata", "sample_conf", "schemas", - "stevedore_extensions", + #"stevedore_extensions", ] # pants ignores files in .gitignore, .*/ directories, /dist/ directory, and __pycache__. pants_ignore.add = [ diff --git a/st2common/tests/unit/BUILD b/st2common/tests/unit/BUILD index cfd46fc9a3..2b645538d5 100644 --- a/st2common/tests/unit/BUILD +++ b/st2common/tests/unit/BUILD @@ -9,7 +9,7 @@ python_tests( # several files import tests.unit.base which is ambiguous. Tell pants which one to use. "st2common/tests/unit/base.py", ], - stevedore_namespaces=["st2common.runners.runner"], + # stevedore_namespaces=["st2common.runners.runner"], ) python_sources() From df851af3cef8fbb2b10c4888459033df6b79b252 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 17 Jan 2023 22:10:21 -0600 Subject: [PATCH 32/45] improve stevedore_extensions field documentation --- .../stevedore_extensions/target_types.py | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index dae4ac93cf..328bfa3a5e 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -32,6 +32,7 @@ ) from pants.backend.python.target_types import EntryPoint, PythonResolveField from pants.source.filespec import Filespec +from pants.util.strutil import softwrap @dataclass(frozen=True) @@ -46,11 +47,14 @@ class StevedoreEntryPoints(Collection[StevedoreEntryPoint]): class StevedoreNamespaceField(StringField): alias = "namespace" - help = ( - "The stevedore extension namespace.\n\nThis looks like a python module " - "'my.stevedore.namespace', but a python module of that name does not " - "need to exist. This is what a stevedore ExtensionManager uses to look up " - "relevant entry_points from pkg_resources." + help = softwrap( + """ + Set the stevedore extension namespace. + + This looks like a python module 'my.stevedore.namespace', but a python module + of that name does not need to exist. This is what a stevedore ExtensionManager + uses to look up relevant entry_points from pkg_resources. + """ ) required = True @@ -60,15 +64,19 @@ class StevedoreEntryPointsField( ): # based on pants.backend.python.target_types.PexEntryPointField alias = "entry_points" - help = ( - "A dict that maps a stevedore extension name to the entry_point that implements it.\n\n" - # the odd spacing here minimizes diff with help text copied from PexEntryPointField - "You can specify each entry_point with " - "a full module like 'path.to.module' and 'path.to.module:func', or use a " - "shorthand to specify a file name, using the same syntax as the `sources` field:\n\n 1) " - "'app.py', Pants will convert into the module `path.to.app`;\n 2) 'app.py:func', Pants " - "will convert into `path.to.app:func`.\n\nYou must use the file name shorthand for file " - "arguments to work with this target." + help = softwrap( + """ + Map stevedore extension names to the entry_point that implements each name. + + Specify each entry_point to a module stevedore should use for the given extension name. + You can specify a full module like 'path.to.module' and 'path.to.module:func', or use a + shorthand to specify a file name, using the same syntax as the `sources` field: + + 1) 'app.py', Pants will convert into the module `path.to.app`; + 2) 'app.py:func', Pants will convert into `path.to.app:func`. + + You must use the file name shorthand for file arguments to work with this target. + """ ) required = True value: StevedoreEntryPoints @@ -127,12 +135,15 @@ class StevedoreExtension(Target): # This is a lot like a SpecialCasedDependencies field, but it doesn't list targets directly. class StevedoreNamespacesField(StringSequenceField): alias = "stevedore_namespaces" - help = ( - "A list of stevedore namespaces to include for tests.\n\n" - "All stevedore_extension targets with these namespaces will be added as " - "dependencies so that they are available on PYTHONPATH during tests. " - "The stevedore namespace format (my.stevedore.extension) is similar " - "to a python namespace." + help = softwrap( + """ + List the stevedore namespaces required by this target. + + All stevedore_extension targets with these namespaces will be added as + dependencies so that they are available on PYTHONPATH during tests. + The stevedore namespace format (my.stevedore.extension) is similar + to a python namespace. + """ ) From d37ef5115db0105d943309965fd8972ab85e960a Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 17 Jan 2023 22:34:09 -0600 Subject: [PATCH 33/45] add provenance to stevedore_extension EntryPoint.parse call This should improve error messages to make it easier to identify any issues. --- pants-plugins/stevedore_extensions/target_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 328bfa3a5e..f1d084581d 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -91,7 +91,7 @@ def compute_value( entry_points = [] for name, value in raw_entry_points.items(): try: - entry_point = EntryPoint.parse(value) + entry_point = EntryPoint.parse(value, provenance=f"for {name} on {address}") except ValueError as e: raise InvalidFieldException(str(e)) entry_points.append(StevedoreEntryPoint(name=name, value=entry_point)) From be248d44f5d5551640a2c97548d06c1820fa39de Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 18 Jan 2023 01:20:38 -0600 Subject: [PATCH 34/45] describe stevedore_extensions in pants-plugins/README.md --- pants-plugins/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pants-plugins/README.md b/pants-plugins/README.md index 8f99aa3d5e..d006ca3989 100644 --- a/pants-plugins/README.md +++ b/pants-plugins/README.md @@ -8,6 +8,9 @@ The plugins here add custom goals or other logic into pants. To see available goals, do "./pants help goals" and "./pants help $goal". +These plugins might be useful outside of the StackStorm project: +- `stevedore_extensions` + These StackStorm-specific plugins might be useful in other StackStorm-related repos. - `pack_metadata` @@ -66,3 +69,12 @@ the `fmt` goal (eg `./pants fmt contrib/schemas::`), the schemas will be regenerated if any of the files used to generate them have changed. Also, running the `lint` goal will fail if the schemas need to be regenerated. + +### `stevedore_extensions` plugin + +This plugin teaches pants how to infer dependencies on stevedore +extensions (python plugins loaded at runtime via entry points). +This includes the `stevedore_extensions` target and the +`stevedore_namespaces` field. When necessary, it generates an +`entry_points.txt` file so that stevedore can work correctly +within pants sandboxes. From 993daae7712dc511fca97a7aef132ef0a7b98ee7 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 18 Jan 2023 01:46:53 -0600 Subject: [PATCH 35/45] make linters happy --- .../stevedore_extensions/python_dependency_injection.py | 6 +----- pants-plugins/stevedore_extensions/register.py | 4 +++- pants-plugins/stevedore_extensions/target_types.py | 4 +++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pants-plugins/stevedore_extensions/python_dependency_injection.py b/pants-plugins/stevedore_extensions/python_dependency_injection.py index bc86ac7915..62ee2ddd13 100644 --- a/pants-plugins/stevedore_extensions/python_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/python_dependency_injection.py @@ -16,19 +16,16 @@ from typing import List, Mapping, Tuple from pants.backend.python.target_types import ( - PythonDistributionDependenciesField, PythonTestTarget, PythonTestsGeneratorTarget, PythonTestsDependenciesField, ) -from pants.base.specs import DirGlobSpec, RawSpecs from pants.engine.addresses import Address -from pants.engine.rules import collect_rules, Get, rule, UnionRule +from pants.engine.rules import collect_rules, rule, UnionRule from pants.engine.target import ( FieldSet, InferDependenciesRequest, InferredDependencies, - Targets, ) from pants.util.frozendict import FrozenDict from pants.util.logging import LogLevel @@ -36,7 +33,6 @@ from stevedore_extensions.target_types import ( AllStevedoreExtensionTargets, - StevedoreEntryPointsField, StevedoreExtension, StevedoreNamespaceField, StevedoreNamespacesField, diff --git a/pants-plugins/stevedore_extensions/register.py b/pants-plugins/stevedore_extensions/register.py index 2c8952e055..57aa8f8244 100644 --- a/pants-plugins/stevedore_extensions/register.py +++ b/pants-plugins/stevedore_extensions/register.py @@ -15,7 +15,9 @@ from pants.backend.codegen import export_codegen_goal from stevedore_extensions import ( - target_types_rules, rules as stevedore_rules, python_dependency_injection + target_types_rules, + rules as stevedore_rules, + python_dependency_injection, ) from stevedore_extensions.target_types import StevedoreExtension diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index f1d084581d..99ad7f60bd 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -91,7 +91,9 @@ def compute_value( entry_points = [] for name, value in raw_entry_points.items(): try: - entry_point = EntryPoint.parse(value, provenance=f"for {name} on {address}") + entry_point = EntryPoint.parse( + value, provenance=f"for {name} on {address}" + ) except ValueError as e: raise InvalidFieldException(str(e)) entry_points.append(StevedoreEntryPoint(name=name, value=entry_point)) From 016e2c51e60b62a279a22b86bd22240e22606bab Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 18 Jan 2023 12:13:49 -0600 Subject: [PATCH 36/45] move find_all_stevedore_extension_targets rule --- .../python_dependency_injection.py | 21 +++++++++++++++++++ .../target_types_rules.py | 12 +++-------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/pants-plugins/stevedore_extensions/python_dependency_injection.py b/pants-plugins/stevedore_extensions/python_dependency_injection.py index 62ee2ddd13..8237fc38c0 100644 --- a/pants-plugins/stevedore_extensions/python_dependency_injection.py +++ b/pants-plugins/stevedore_extensions/python_dependency_injection.py @@ -23,6 +23,7 @@ from pants.engine.addresses import Address from pants.engine.rules import collect_rules, rule, UnionRule from pants.engine.target import ( + AllTargets, FieldSet, InferDependenciesRequest, InferredDependencies, @@ -33,12 +34,27 @@ from stevedore_extensions.target_types import ( AllStevedoreExtensionTargets, + StevedoreEntryPointsField, StevedoreExtension, StevedoreNamespaceField, StevedoreNamespacesField, ) +# ----------------------------------------------------------------------------------------------- +# Utility rules to analyze all `StevedoreExtension` targets +# ----------------------------------------------------------------------------------------------- + + +@rule(desc="Find all StevedoreExtension targets in project", level=LogLevel.DEBUG) +def find_all_stevedore_extension_targets( + targets: AllTargets, +) -> AllStevedoreExtensionTargets: + return AllStevedoreExtensionTargets( + tgt for tgt in targets if tgt.has_field(StevedoreEntryPointsField) + ) + + @dataclass(frozen=True) class StevedoreExtensions: """A mapping of stevedore namespaces to a list the targets that provide them""" @@ -61,6 +77,11 @@ async def map_stevedore_extensions( ) +# ----------------------------------------------------------------------------------------------- +# Dependencies for `python_test` and `python_tests` targets +# ----------------------------------------------------------------------------------------------- + + @dataclass(frozen=True) class PythonTestsStevedoreNamespaceInferenceFieldSet(FieldSet): required_fields = (PythonTestsDependenciesField, StevedoreNamespacesField) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index e57b165e57..e2e0f09239 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -26,7 +26,6 @@ from pants.engine.fs import GlobMatchErrorBehavior, PathGlobs, Paths from pants.engine.rules import Get, collect_rules, MultiGet, rule, UnionRule from pants.engine.target import ( - AllTargets, Dependencies, DependenciesRequest, ExplicitlyProvidedDependencies, @@ -39,7 +38,6 @@ from pants.util.logging import LogLevel from stevedore_extensions.target_types import ( - AllStevedoreExtensionTargets, ResolvedStevedoreEntryPoints, ResolveStevedoreEntryPointsRequest, StevedoreEntryPoints, @@ -47,13 +45,9 @@ ) -@rule(desc="Find all StevedoreExtension targets in project", level=LogLevel.DEBUG) -def find_all_stevedore_extension_targets( - targets: AllTargets, -) -> AllStevedoreExtensionTargets: - return AllStevedoreExtensionTargets( - tgt for tgt in targets if tgt.has_field(StevedoreEntryPointsField) - ) +# ----------------------------------------------------------------------------------------------- +# `StevedoreExtension` target rules +# ----------------------------------------------------------------------------------------------- @rule( From 0c385d0fb3b3e567a2219b6551d21c28b50adfbf Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 18 Jan 2023 13:37:53 -0600 Subject: [PATCH 37/45] add test for resolve_stevedore_entry_points --- pants-plugins/stevedore_extensions/BUILD | 4 + .../target_types_rules.py | 1 + .../target_types_rules_test.py | 159 ++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 pants-plugins/stevedore_extensions/target_types_rules_test.py diff --git a/pants-plugins/stevedore_extensions/BUILD b/pants-plugins/stevedore_extensions/BUILD index db46e8d6c9..0eea8b1cf1 100644 --- a/pants-plugins/stevedore_extensions/BUILD +++ b/pants-plugins/stevedore_extensions/BUILD @@ -1 +1,5 @@ python_sources() + +python_tests( + name="tests", +) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index e2e0f09239..a0cf8f67f3 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -57,6 +57,7 @@ async def resolve_stevedore_entry_points( request: ResolveStevedoreEntryPointsRequest, ) -> ResolvedStevedoreEntryPoints: + # based on: pants.backend.python.target_types_rules.resolve_pex_entry_point # supported schemes mirror those in resolve_pex_entry_point: # 1) this does not support None, unlike pex_entry_point. diff --git a/pants-plugins/stevedore_extensions/target_types_rules_test.py b/pants-plugins/stevedore_extensions/target_types_rules_test.py new file mode 100644 index 0000000000..29db0c9569 --- /dev/null +++ b/pants-plugins/stevedore_extensions/target_types_rules_test.py @@ -0,0 +1,159 @@ +# Copyright 2023 The StackStorm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +import pytest + +from pants.backend.python.target_types import ( + EntryPoint, + PythonTestTarget, + PythonTestsGeneratorTarget, +) +from pants.backend.python.target_types_rules import rules as python_target_types_rules +from pants.engine.addresses import Address +from pants.engine.internals.scheduler import ExecutionError +from pants.engine.target import InferDependenciesRequest, InferredDependencies, Target +from pants.testutil.rule_runner import QueryRule, RuleRunner + +from .target_types_rules import ( + InferStevedoreExtensionDependencies, + resolve_stevedore_entry_points, + rules as stevedore_target_types_rules, +) +from .target_types import ( + ResolveStevedoreEntryPointsRequest, + ResolvedStevedoreEntryPoints, + StevedoreEntryPoint, + StevedoreEntryPoints, + StevedoreEntryPointsField, # on stevedore_extension target + StevedoreExtension, + StevedoreNamespaceField, # on stevedore_extension target + StevedoreNamespacesField, # on other targets +) + + +def test_resolve_stevedore_entry_points() -> None: + # based on: pants.backend.python.target_types_test.test_resolve_pex_binary_entry_point + rule_runner = RuleRunner( + rules=[ + resolve_stevedore_entry_points, + QueryRule( + ResolvedStevedoreEntryPoints, (ResolveStevedoreEntryPointsRequest,) + ), + ] + ) + + def assert_resolved( + *, entry_point: str | None, expected: EntryPoint | None + ) -> None: + plugin_name = "plugin" + addr = Address("src/python/project") + rule_runner.write_files( + { + "src/python/project/app.py": "", + "src/python/project/f2.py": "", + } + ) + ep_field = StevedoreEntryPointsField({plugin_name: entry_point}, addr) + + result = rule_runner.request( + ResolvedStevedoreEntryPoints, [ResolveStevedoreEntryPointsRequest(ep_field)] + ) + + assert result.val is not None + assert len(result.val) == 1 + assert result.val[0].name == plugin_name + assert result.val[0].value == expected + + # Full module provided. + assert_resolved( + entry_point="custom.entry_point", expected=EntryPoint("custom.entry_point") + ) + assert_resolved( + entry_point="custom.entry_point:func", + expected=EntryPoint.parse("custom.entry_point:func"), + ) + + # File names are expanded into the full module path. + assert_resolved(entry_point="app.py", expected=EntryPoint(module="project.app")) + assert_resolved( + entry_point="app.py:func", + expected=EntryPoint(module="project.app", function="func"), + ) + + with pytest.raises(ExecutionError): + assert_resolved( + entry_point="doesnt_exist.py", expected=EntryPoint("doesnt matter") + ) + # Resolving >1 file is an error. + with pytest.raises(ExecutionError): + assert_resolved(entry_point="*.py", expected=EntryPoint("doesnt matter")) + + # Test with multiple entry points (keep indiviudual asserts above, + # despite apparent duplication below, to simplify finding bugs). + rule_runner.write_files( + { + "src/python/project/app.py": "", + "src/python/project/f2.py": "", + } + ) + entry_points_field = StevedoreEntryPointsField( + { + "a": "custom.entry_point", + "b": "custom.entry_point:func", + "c": "app.py", + "d": "app.py:func", + }, + Address("src/python/project"), + ) + + resolved = rule_runner.request( + ResolvedStevedoreEntryPoints, + [ResolveStevedoreEntryPointsRequest(entry_points_field)], + ) + + assert resolved.val is not None + assert set(resolved.val) == { + StevedoreEntryPoint("a", EntryPoint(module="custom.entry_point")), + StevedoreEntryPoint( + "b", EntryPoint(module="custom.entry_point", function="func") + ), + StevedoreEntryPoint("c", EntryPoint(module="project.app")), + StevedoreEntryPoint("d", EntryPoint(module="project.app", function="func")), + } + + +# async def infer_stevedore_entry_points_dependencies( +# request: InferStevedoreExtensionDependencies, +# python_setup: PythonSetup, +# ) -> InferredDependencies: +# Get ResolvedStevedoreEntryPoints + + +def test_infer_stevedore_entry_points_dependencies() -> None: + rule_runner = RuleRunner( + rules=[ + *python_target_types_rules(), + *stevedore_target_types_rules(), + QueryRule( + ResolvedStevedoreEntryPoints, (ResolveStevedoreEntryPointsRequest,) + ), + QueryRule(InferredDependencies, (InferStevedoreExtensionDependencies,)), + ], + target_types=[ + PythonTestTarget, + PythonTestsGeneratorTarget, + StevedoreExtension, + ], + ) From d5700edf27286840db5439b330610a2379f171dd Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 18 Jan 2023 15:25:06 -0600 Subject: [PATCH 38/45] add test for infer_stevedore_entry_points_dependencies and fix a bug identified by the test (Dependencies field was missing). --- .../stevedore_extensions/target_types.py | 2 + .../target_types_rules_test.py | 107 +++++++++++++++--- 2 files changed, 91 insertions(+), 18 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 99ad7f60bd..6730ba5aa4 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -22,6 +22,7 @@ from pants.engine.target import ( AsyncFieldMixin, COMMON_TARGET_FIELDS, + Dependencies, DictStringToStringField, InvalidFieldException, SecondaryOwnerMixin, @@ -129,6 +130,7 @@ class StevedoreExtension(Target): *COMMON_TARGET_FIELDS, StevedoreNamespaceField, StevedoreEntryPointsField, + Dependencies, PythonResolveField, ) help = "Entry points used to generate setuptools metadata for stevedore." diff --git a/pants-plugins/stevedore_extensions/target_types_rules_test.py b/pants-plugins/stevedore_extensions/target_types_rules_test.py index 29db0c9569..637e19d1c9 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules_test.py +++ b/pants-plugins/stevedore_extensions/target_types_rules_test.py @@ -13,21 +13,24 @@ # limitations under the License. from __future__ import annotations +from textwrap import dedent + import pytest from pants.backend.python.target_types import ( EntryPoint, - PythonTestTarget, - PythonTestsGeneratorTarget, + PythonSourceTarget, + PythonSourcesGeneratorTarget, ) from pants.backend.python.target_types_rules import rules as python_target_types_rules from pants.engine.addresses import Address from pants.engine.internals.scheduler import ExecutionError -from pants.engine.target import InferDependenciesRequest, InferredDependencies, Target +from pants.engine.target import InferredDependencies from pants.testutil.rule_runner import QueryRule, RuleRunner from .target_types_rules import ( InferStevedoreExtensionDependencies, + StevedoreEntryPointsInferenceFieldSet, resolve_stevedore_entry_points, rules as stevedore_target_types_rules, ) @@ -35,11 +38,8 @@ ResolveStevedoreEntryPointsRequest, ResolvedStevedoreEntryPoints, StevedoreEntryPoint, - StevedoreEntryPoints, StevedoreEntryPointsField, # on stevedore_extension target StevedoreExtension, - StevedoreNamespaceField, # on stevedore_extension target - StevedoreNamespacesField, # on other targets ) @@ -134,26 +134,97 @@ def assert_resolved( } -# async def infer_stevedore_entry_points_dependencies( -# request: InferStevedoreExtensionDependencies, -# python_setup: PythonSetup, -# ) -> InferredDependencies: -# Get ResolvedStevedoreEntryPoints - - def test_infer_stevedore_entry_points_dependencies() -> None: rule_runner = RuleRunner( rules=[ *python_target_types_rules(), *stevedore_target_types_rules(), - QueryRule( - ResolvedStevedoreEntryPoints, (ResolveStevedoreEntryPointsRequest,) - ), QueryRule(InferredDependencies, (InferStevedoreExtensionDependencies,)), ], target_types=[ - PythonTestTarget, - PythonTestsGeneratorTarget, + PythonSourceTarget, + PythonSourcesGeneratorTarget, StevedoreExtension, ], ) + rule_runner.write_files( + { + "runners/foobar_runner/BUILD": dedent( + """\ + stevedore_extension( + name="runner", + namespace="st2common.runners.runner", + entry_points={ + "foobar": "foobar_runner.foobar_runner", + }, + ) + + stevedore_extension( + name="foobar", + namespace="example.foobar", + entry_points={ + "thing1": "foobar_runner.thing1:ThingBackend", + "thing2": "foobar_runner.thing2:ThingBackend", + }, + ) + """ + ), + "runners/foobar_runner/foobar_runner/BUILD": "python_sources()", + "runners/foobar_runner/foobar_runner/__init__.py": "", + "runners/foobar_runner/foobar_runner/foobar_runner.py": "", + "runners/foobar_runner/foobar_runner/thing1.py": dedent( + """\ + class ThingBackend: + pass + """ + ), + "runners/foobar_runner/foobar_runner/thing2.py": dedent( + """\ + class ThingBackend: + pass + """ + ), + } + ) + + def run_dep_inference(address: Address) -> InferredDependencies: + args = [ + "--source-root-patterns=runners/*_runner", + "--python-infer-assets", + ] + rule_runner.set_options(args, env_inherit={"PATH", "PYENV_ROOT", "HOME"}) + target = rule_runner.get_target(address) + return rule_runner.request( + InferredDependencies, + [ + InferStevedoreExtensionDependencies( + StevedoreEntryPointsInferenceFieldSet.create(target) + ) + ], + ) + + assert run_dep_inference( + Address("runners/foobar_runner", target_name="runner") + ) == InferredDependencies( + [ + Address( + "runners/foobar_runner/foobar_runner", + relative_file_path="foobar_runner.py", + ), + ], + ) + + assert run_dep_inference( + Address("runners/foobar_runner", target_name="foobar") + ) == InferredDependencies( + [ + Address( + "runners/foobar_runner/foobar_runner", + relative_file_path="thing1.py", + ), + Address( + "runners/foobar_runner/foobar_runner", + relative_file_path="thing2.py", + ), + ], + ) From 4f1b936a7ceb3115eabf0e2c9d9d8f3b2694180a Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 18 Jan 2023 15:28:02 -0600 Subject: [PATCH 39/45] rename python_dependency_injection to python_target_dependencies to better reflect the purpose of rules in that file --- ..._dependency_injection.py => python_target_dependencies.py} | 0 pants-plugins/stevedore_extensions/register.py | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename pants-plugins/stevedore_extensions/{python_dependency_injection.py => python_target_dependencies.py} (100%) diff --git a/pants-plugins/stevedore_extensions/python_dependency_injection.py b/pants-plugins/stevedore_extensions/python_target_dependencies.py similarity index 100% rename from pants-plugins/stevedore_extensions/python_dependency_injection.py rename to pants-plugins/stevedore_extensions/python_target_dependencies.py diff --git a/pants-plugins/stevedore_extensions/register.py b/pants-plugins/stevedore_extensions/register.py index 57aa8f8244..4006f53077 100644 --- a/pants-plugins/stevedore_extensions/register.py +++ b/pants-plugins/stevedore_extensions/register.py @@ -17,7 +17,7 @@ from stevedore_extensions import ( target_types_rules, rules as stevedore_rules, - python_dependency_injection, + python_target_dependencies, ) from stevedore_extensions.target_types import StevedoreExtension @@ -30,7 +30,7 @@ def rules(): return [ *target_types_rules.rules(), *stevedore_rules.rules(), - *python_dependency_injection.rules(), + *python_target_dependencies.rules(), *export_codegen_goal.rules(), ] From 718dcff1c17a1f050aab3d45d1fea0719de910a4 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 18 Jan 2023 17:51:17 -0600 Subject: [PATCH 40/45] add tests for pants-plugins/stevedore_extensions/python_target_dependencies --- .../python_target_dependencies.py | 2 +- .../python_target_dependencies_test.py | 182 ++++++++++++++++++ .../target_types_rules_test.py | 1 - 3 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 pants-plugins/stevedore_extensions/python_target_dependencies_test.py diff --git a/pants-plugins/stevedore_extensions/python_target_dependencies.py b/pants-plugins/stevedore_extensions/python_target_dependencies.py index 8237fc38c0..afe4a8b270 100644 --- a/pants-plugins/stevedore_extensions/python_target_dependencies.py +++ b/pants-plugins/stevedore_extensions/python_target_dependencies.py @@ -94,7 +94,7 @@ class InferStevedoreNamespaceDependencies(InferDependenciesRequest): @rule( - desc="Infer stevedore_extension target dependencies for python_tests based on namespace list.", + desc="Infer stevedore_extension target dependencies based on namespace list.", level=LogLevel.DEBUG, ) async def infer_stevedore_namespace_dependencies( diff --git a/pants-plugins/stevedore_extensions/python_target_dependencies_test.py b/pants-plugins/stevedore_extensions/python_target_dependencies_test.py new file mode 100644 index 0000000000..156e8d2b06 --- /dev/null +++ b/pants-plugins/stevedore_extensions/python_target_dependencies_test.py @@ -0,0 +1,182 @@ +# Copyright 2023 The StackStorm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +from textwrap import dedent + +import pytest + +from pants.backend.python.target_types import ( + PythonSourceTarget, + PythonSourcesGeneratorTarget, + PythonTestTarget, + PythonTestsGeneratorTarget, +) +from pants.backend.python.target_types_rules import rules as python_target_types_rules +from pants.engine.addresses import Address +from pants.engine.target import InferredDependencies +from pants.testutil.rule_runner import QueryRule, RuleRunner +from pants.util.frozendict import FrozenDict + +from .python_target_dependencies import ( + InferStevedoreNamespaceDependencies, + PythonTestsStevedoreNamespaceInferenceFieldSet, + StevedoreExtensions, + rules as stevedore_dep_rules, +) +from .target_types import ( + AllStevedoreExtensionTargets, + StevedoreExtension, +) + + +# random set of runner names to use in tests +st2_runners = ["noop", "python", "foobar"] + + +@pytest.fixture +def rule_runner() -> RuleRunner: + rule_runner = RuleRunner( + rules=[ + *python_target_types_rules(), + *stevedore_dep_rules(), + QueryRule(AllStevedoreExtensionTargets, ()), + QueryRule(StevedoreExtensions, ()), + QueryRule(InferredDependencies, (InferStevedoreNamespaceDependencies,)), + ], + target_types=[ + PythonSourceTarget, + PythonSourcesGeneratorTarget, + PythonTestTarget, + PythonTestsGeneratorTarget, + StevedoreExtension, + ], + ) + for runner in st2_runners: + rule_runner.write_files( + { + f"runners/{runner}_runner/BUILD": dedent( + f"""\ + stevedore_extension( + name="runner", + namespace="st2common.runners.runner", + entry_points={{ + "{runner}": "{runner}_runner.{runner}_runner", + }}, + ) + stevedore_extension( + name="thing", + namespace="some.thing.else", + entry_points={{ + "{runner}": "{runner}_runner.thing", + }}, + ) + """ + ), + f"runners/{runner}_runner/{runner}_runner/BUILD": "python_sources()", + f"runners/{runner}_runner/{runner}_runner/__init__.py": "", + f"runners/{runner}_runner/{runner}_runner/{runner}_runner.py": "", + f"runners/{runner}_runner/{runner}_runner/thing.py": "", + } + ) + args = [ + "--source-root-patterns=runners/*_runner", + ] + rule_runner.set_options(args, env_inherit={"PATH", "PYENV_ROOT", "HOME"}) + return rule_runner + + +# ----------------------------------------------------------------------------------------------- +# Tests for utility rules +# ----------------------------------------------------------------------------------------------- + + +def test_find_all_stevedore_extension_targets(rule_runner: RuleRunner) -> None: + assert rule_runner.request( + AllStevedoreExtensionTargets, [] + ) == AllStevedoreExtensionTargets( + rule_runner.get_target( + Address(f"runners/{runner}_runner", target_name=target_name) + ) + for runner in sorted(st2_runners) + for target_name in ["runner", "thing"] + ) + + +def test_map_stevedore_extensions(rule_runner: RuleRunner) -> None: + assert rule_runner.request(StevedoreExtensions, []) == StevedoreExtensions( + FrozenDict( + { + "some.thing.else": tuple( + rule_runner.get_target( + Address(f"runners/{runner}_runner", target_name="thing") + ) + for runner in sorted(st2_runners) + ), + "st2common.runners.runner": tuple( + rule_runner.get_target( + Address(f"runners/{runner}_runner", target_name="runner") + ) + for runner in sorted(st2_runners) + ), + } + ) + ) + + +# ----------------------------------------------------------------------------------------------- +# Tests for dependency inference of python targets (python_tests, etc) +# ----------------------------------------------------------------------------------------------- + + +def test_infer_stevedore_namespace_dependencies(rule_runner: RuleRunner) -> None: + rule_runner.write_files( + { + "src/foobar/BUILD": dedent( + """\ + python_tests( + name="tests", + stevedore_namespaces=["st2common.runners.runner"], + ) + """ + ), + "src/foobar/test_something.py": "", + } + ) + + def run_dep_inference(address: Address) -> InferredDependencies: + target = rule_runner.get_target(address) + return rule_runner.request( + InferredDependencies, + [ + InferStevedoreNamespaceDependencies( + PythonTestsStevedoreNamespaceInferenceFieldSet.create(target) + ) + ], + ) + + # this asserts that only the st2common.runners.runner namespace gets selected. + assert run_dep_inference( + Address( + "src/foobar", target_name="tests", relative_file_path="test_something.py" + ), + ) == InferredDependencies( + [ + Address( + f"runners/{runner}_runner", + target_name="runner", + ) + for runner in st2_runners + ], + ) diff --git a/pants-plugins/stevedore_extensions/target_types_rules_test.py b/pants-plugins/stevedore_extensions/target_types_rules_test.py index 637e19d1c9..fa27866ac2 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules_test.py +++ b/pants-plugins/stevedore_extensions/target_types_rules_test.py @@ -190,7 +190,6 @@ class ThingBackend: def run_dep_inference(address: Address) -> InferredDependencies: args = [ "--source-root-patterns=runners/*_runner", - "--python-infer-assets", ] rule_runner.set_options(args, env_inherit={"PATH", "PYENV_ROOT", "HOME"}) target = rule_runner.get_target(address) From 409b5b7ddd12b9b3511b32972236f00e03acc1b6 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 18 Jan 2023 20:40:14 -0600 Subject: [PATCH 41/45] update copyright to 2023 in pants-plugins/stevedore_extensions --- .../stevedore_extensions/python_target_dependencies.py | 2 +- pants-plugins/stevedore_extensions/register.py | 2 +- pants-plugins/stevedore_extensions/target_types.py | 2 +- pants-plugins/stevedore_extensions/target_types_rules.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pants-plugins/stevedore_extensions/python_target_dependencies.py b/pants-plugins/stevedore_extensions/python_target_dependencies.py index afe4a8b270..5a0ebb46dc 100644 --- a/pants-plugins/stevedore_extensions/python_target_dependencies.py +++ b/pants-plugins/stevedore_extensions/python_target_dependencies.py @@ -1,4 +1,4 @@ -# Copyright 2021 The StackStorm Authors. +# Copyright 2023 The StackStorm Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pants-plugins/stevedore_extensions/register.py b/pants-plugins/stevedore_extensions/register.py index 4006f53077..6f13676d41 100644 --- a/pants-plugins/stevedore_extensions/register.py +++ b/pants-plugins/stevedore_extensions/register.py @@ -1,4 +1,4 @@ -# Copyright 2021 The StackStorm Authors. +# Copyright 2023 The StackStorm Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pants-plugins/stevedore_extensions/target_types.py b/pants-plugins/stevedore_extensions/target_types.py index 6730ba5aa4..da4757e153 100644 --- a/pants-plugins/stevedore_extensions/target_types.py +++ b/pants-plugins/stevedore_extensions/target_types.py @@ -1,4 +1,4 @@ -# Copyright 2021 The StackStorm Authors. +# Copyright 2023 The StackStorm Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index a0cf8f67f3..7d98ecbacc 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -1,4 +1,4 @@ -# Copyright 2021 The StackStorm Authors. +# Copyright 2023 The StackStorm Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 45e8d0425da836fb4b1211b1e4654413c8157aa7 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 20 Jan 2023 22:23:46 -0600 Subject: [PATCH 42/45] simplify resolve_stevedore_entry_points based on PR feedback --- .../target_types_rules.py | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 7d98ecbacc..4a04596482 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -82,17 +82,9 @@ async def resolve_stevedore_entry_points( if entry_point.value.module.endswith(".py") ) - # use iter so we can use next() below - iter_entry_point_paths_results = iter(entry_point_paths_results) - # We will have already raised if the glob did not match, i.e. if there were no files. But # we need to check if they used a file glob (`*` or `**`) that resolved to >1 file. - for entry_point in request.entry_points_field.value: - # We just need paths here. If it's already a path, skip it until the next loop. - if not entry_point.value.module.endswith(".py"): - continue - - entry_point_paths = next(iter_entry_point_paths_results) + for entry_point_paths in entry_point_paths_results: if len(entry_point_paths.files) != 1: raise InvalidFieldException( f"Multiple files matched for the `{request.entry_points_field.alias}` " @@ -101,20 +93,16 @@ async def resolve_stevedore_entry_points( f"All matching files: {list(entry_point_paths.files)}." ) - # restart the iterator - iter_entry_point_paths_results = iter(entry_point_paths_results) - source_root_results = await MultiGet( Get( SourceRoot, SourceRootRequest, - SourceRootRequest.for_file(next(iter_entry_point_paths_results).files[0]), + SourceRootRequest.for_file(entry_point_path.files[0]), ) - for entry_point in request.entry_points_field.value - if entry_point.value.module.endswith(".py") + for entry_point_path in entry_point_paths_results ) - # restart the iterator + # use iter so we can use next() below iter_entry_point_paths_results = iter(entry_point_paths_results) iter_source_root_results = iter(source_root_results) From 06d7141797aac3ac25a90c8d7015f73be1ac1266 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 20 Jan 2023 22:59:40 -0600 Subject: [PATCH 43/45] partially revert to resolve flake8 identified issue --- .../stevedore_extensions/target_types_rules.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pants-plugins/stevedore_extensions/target_types_rules.py b/pants-plugins/stevedore_extensions/target_types_rules.py index 4a04596482..f93eb6079e 100644 --- a/pants-plugins/stevedore_extensions/target_types_rules.py +++ b/pants-plugins/stevedore_extensions/target_types_rules.py @@ -82,9 +82,20 @@ async def resolve_stevedore_entry_points( if entry_point.value.module.endswith(".py") ) + # use iter so we can use next() below + iter_entry_point_paths_results = iter(entry_point_paths_results) + # We will have already raised if the glob did not match, i.e. if there were no files. But # we need to check if they used a file glob (`*` or `**`) that resolved to >1 file. - for entry_point_paths in entry_point_paths_results: + # + # It is clearer to iterate over entry_point_paths_results, but we need to include + # the original glob in the error message, so we have to check for ".py" again. + for entry_point in request.entry_points_field.value: + # We only need paths/globs for this check. Ignore any modules. + if not entry_point.value.module.endswith(".py"): + continue + + entry_point_paths = next(iter_entry_point_paths_results) if len(entry_point_paths.files) != 1: raise InvalidFieldException( f"Multiple files matched for the `{request.entry_points_field.alias}` " From 0776a138b80c13f5d19aa73147d98a3b0b30e4ec Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Mon, 23 Jan 2023 10:15:05 -0600 Subject: [PATCH 44/45] address doc comment PR feedback --- .../stevedore_extensions/python_target_dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pants-plugins/stevedore_extensions/python_target_dependencies.py b/pants-plugins/stevedore_extensions/python_target_dependencies.py index 5a0ebb46dc..6ca1d902e0 100644 --- a/pants-plugins/stevedore_extensions/python_target_dependencies.py +++ b/pants-plugins/stevedore_extensions/python_target_dependencies.py @@ -57,7 +57,7 @@ def find_all_stevedore_extension_targets( @dataclass(frozen=True) class StevedoreExtensions: - """A mapping of stevedore namespaces to a list the targets that provide them""" + """A mapping of stevedore namespaces to a list of StevedoreExtension targets that provide them""" mapping: FrozenDict[str, Tuple[StevedoreExtension]] From 45cb0234e5e576d54b3449cc8caab082495c4630 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 18 Jan 2023 01:14:57 -0600 Subject: [PATCH 45/45] update changelog entry --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3d37b236c2..b01e7c206e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,7 +14,7 @@ Added working on StackStorm, improve our security posture, and improve CI reliability thanks in part to pants' use of PEX lockfiles. This is not a user-facing addition. #5778 #5789 #5817 #5795 #5830 #5833 #5834 #5841 #5840 #5838 #5842 #5837 #5849 #5850 - #5846 #5853 #5848 #5847 #5858 #5857 #5860 #5868 #5871 + #5846 #5853 #5848 #5847 #5858 #5857 #5860 #5868 #5871 #5869 Contributed by @cognifloyd * Added a joint index to solve the problem of slow mongo queries for scheduled executions. #5805