-
-
Notifications
You must be signed in to change notification settings - Fork 748
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add pants-plugins/stevedore_extensions
to add teach pants' dependency inference about our runtime-loaded plugins
#5869
Conversation
pants-plugins/stevedore_extensions
to add teach pants' dependency inference about our runtime-loaded plugins
138135c
to
f06f8c1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few queries.
pants-plugins/stevedore_extensions/python_target_dependencies.py
Outdated
Show resolved
Hide resolved
async def resolve_stevedore_entry_points( | ||
request: ResolveStevedoreEntryPointsRequest, | ||
) -> ResolvedStevedoreEntryPoints: | ||
# based on: pants.backend.python.target_types_rules.resolve_pex_entry_point |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b4515dd
to
0eb1354
Compare
Rebased on master |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy with the changes, just the change on the comment as per suggestion to add.
pants-plugins/stevedore_extensions/python_target_dependencies.py
Outdated
Show resolved
Hide resolved
Targets other than python_tests will get stevedore_namespaces fields later.
This should improve error messages to make it easier to identify any issues.
and fix a bug identified by the test (Dependencies field was missing).
to better reflect the purpose of rules in that file
3731eec
to
45cb023
Compare
rebased |
This backend provides dependency inference for apps that use openstack/stevedore to load plugins at runtime. This was originally developed for the StackStorm project: StackStorm/st2#5869
I submitted pantsbuild/pants#18132 to try and get this plugin into upstream pants instead of in our repo. In doing that I realized I could vastly simplify it, so I did a major refactor. Even if it's not accepted upstream, the new version of the plugin will be easier to review with fewer and smaller PRs to get it all in. I'm marking this as draft until I see where the upstreaming effort lands. |
This adds `pants.backend.python.framework.stevedore`. This was originally developed for the StackStorm project in StackStorm/st2#5869 but it seems general enough to include in pants itself. Other people on [slack](https://pantsbuild.slack.com/archives/C01CQHVDMMW/p1674865736900199) agree, so here's the PR. ## What is openstack/stevedore? Python projects can use [openstack/stevedore](https://github.com/openstack/stevedore) for dynamic (discovered at runtime) extensions/plugins. I find this [page in the stevedore docs](https://docs.openstack.org/stevedore/latest/user/tutorial/naming.html) to be most helpful in understanding what stevedore does. Here are some of the key points: > Stevedore uses setuptools entry points to define and load plugins. An entry point is standard way to refer to a named object defined inside a Python module or package. The name can be a reference to any class, function, or instance, as long as it is created when the containing module is imported (i.e., it needs to be a module-level global). > > Entry points are registered using a _name_ in a _namespace_. > > Entry point names are usually considered user-visible. ... Because they are public, names are typically as short as possible while remaining descriptive. ... > > Namespaces, on the other hand, are an implementation detail, and while they are known to developers they are not usually exposed to users. The namespace naming syntax looks a lot like Python’s package syntax (*a.b.c*) but _namespaces do not correspond to Python packages._ ... > > Each namespace is owned by the code that consumes the plugins and is used to search for entry points. ... ## About the stevedore pants plugin The primary focus of this plugin is to facilitate testing projects that use `openstack/stevedore` by adding the extensions/plugins to the pytest sandbox and ensuring they are "discoverable" by stevedore. ### How does this plugin facilitate testing code that uses stevedore? Since a stevedore extension does not use the python import system, pants does not know how to infer dependencies on any of this code. We need to extend the dependency inference system to teach it how to handle stevedore extensions. This PR adds a pants plugin that strives to accomplish that by allowing us to: - use `python_distribution(entry_points={...}, ...)` to define the stevedore namespaces and plugins, - differentiate namespaces in the `entry_points` field with a special `stevedore_namespace` object. - use `stevedore_namespaces` fields to record dependencies on extensions in the given namespaces. So far, I've only added the `stevedore_namespaces` field to the `python_test` and `python_tests` targets as testing has been my primary focus. We could add it to other targets later if anyone finds that helpful. When a target has the `stevedore_namespaces` field, this plugin will: 1. look up all of the `python_distribution` targets with an `entry_points` field that have `stevedore_namespace` tagged keys. - [`@rule python_target_dependencies.find_all_python_distributions_with_any_stevedore_entry_points`](https://github.com/pantsbuild/pants/pull/18132/files#diff-f50253674b1c85da837a8a04c18c23cad6dec5220b6b8a1ac62838e40346e01dR56) - [`@rule python_target_dependencies.map_stevedore_extensions`](https://github.com/pantsbuild/pants/pull/18132/files#diff-f50253674b1c85da837a8a04c18c23cad6dec5220b6b8a1ac62838e40346e01dR85) - [`@rule python_target_dependencies.find_python_distributions_with_entry_points_in_stevedore_namespaces`](https://github.com/pantsbuild/pants/pull/18132/files#diff-f50253674b1c85da837a8a04c18c23cad6dec5220b6b8a1ac62838e40346e01dR103) 2. add/infer dependencies from the test targets to all of the python code that provides the entry points in the required namespaces; in other words, we infer dependencies on a subset of the `python_distribution` target, not on the `python_distribution` target itself. - [`@rule python_target_dependencies.infer_stevedore_namespace_dependencies`](https://github.com/pantsbuild/pants/pull/18132/files#diff-f50253674b1c85da837a8a04c18c23cad6dec5220b6b8a1ac62838e40346e01dR142) 3. generate a `{module_path}.egg-info/entry_points.txt` file in the pytest sandbox for each relevant `python_distribution` target (the `entry_points.txt` file will only contain entry_points for the required namespaces). - [`@rule rules.generate_entry_points_txt_from_stevedore_extension`](https://github.com/pantsbuild/pants/pull/18132/files#diff-1d487c2b704cac1a36e9398cc29dd75e5601535e53224f67923f641c8c320a4eR42) For example, if an project used the namespace `st2common.runners.runner`, then we could set `stevedore_namespaces=["st2common.runners.runner"]` on a `python_tests()` target. Then pants will include all of he python code that provides the named entry points in those namespaces. And then the generated `entry_points.txt` files make that python code appear to be "installed" (not just added to PYTHONPATH), thus allowing stevedore to discover the entry points and load the plugins during the tests.
Woohoo! This was merged upstream: pantsbuild/pants#18132 I will revisit this once there is a pants release we can use (prob dev or rc) that includes this plugin. Then we'll be able to add all the stevedore entry points metadata without adding the plugin. In the meantime, I plan to add the |
Background
This is another part of introducing pants, as discussed in various TSC meetings.
Related PRs can be found in:
Overview of this PR
We use openstack/stevedore for several dynamic (discovered at runtime) StackStorm plugins. We use this to load:
Since a stevedore extension does not use the python import system, pants does not know how to infer dependencies on any of this code. We need to extend the dependency inference system to teach it how to handle stevedore extensions. This PR adds a pants plugin that strives to accomplish that by allowing us to:
stevedore_extension
targets to inform pants about what code is required for a given extension (where each extension is identified by a stevedore namespace and a plugin name), andstevedore_namespace
fields to record dependencies on extensions in the given namespace.When a target has the
stevedore_namespace
field, this plugin will add dependencies on all of thestevedore_extension
targets that define plugins in that namespace. For example, that means that pants will include all of our runners whenever code depends on thest2common.runners.runner
namespace.Scope of this PR
This PR only adds the
stevedore_extension
target and adds thestevedore_namespace
field to thepython_tests
andpython_test
targets.After adding the dependency inference (in this PR) there is one more thing required for
python_test
targets to successfully get access, via stevedore, to the relevant python code. We need to generate anentry_points.txt
file. We will include that in the next PR, which you can preview at: pants-plugins-stevedore_extensions...pants-plugins-stevedore_extensions-entry_points_txtAnother follow-up PR will also add the
stevedore_namespaces
field topython_distribution
targets (a wheel is a distribution). If you want a preview of that PR, you can look at: pants-plugins-stevedore_extensions-entry_points_txt...pants-plugins-stevedore_extensions-setup_pyNote, also, that I have not enabled the plugin yet:
st2/pants.toml
Line 30 in 7148058
That keeps this PR focused on the plugin itself including adding tests for it. A third follow-up PR will add the
stevedore_extension
target andstevedore_namespaces
field metadata to our BUILD files. If you want to explore some of that metadata, you can look at: pants-plugins-stevedore_extensions-setup_py...pants-stevedore_extensionsstevedore_extension
targetIn the plugin, the
stevedore_extension
target is defined here:st2/pants-plugins/stevedore_extensions/target_types.py
Lines 124 to 132 in 7148058
There are several fields on this target. In this example BUILD file, we see three fields:
name
,namespace
, andentry_points
. (Yes, there are other fields available on the target. But, those are only needed for other pants features; we won't use them in our BUILD files.)st2/contrib/runners/noop_runner/BUILD
Lines 1 to 7 in b0d27d8
Look in
target_types.py
to see the definition of thenamespace
(StevedoreNamespaceField
) andentry_points
(StevedoreEntryPointsField
) fields. In particular, please review the help text for each of these fields.stevedore_namespaces
field (onpython_tests
/python_test
targets)In the plugin, the
stevedore_namespaces
field is defined here:st2/pants-plugins/stevedore_extensions/target_types.py
Lines 136 to 147 in 7148058
When we identify tests that need particular stevedore extensions, we list the required namespaces in BUILD files like this:
st2/st2common/tests/unit/BUILD
Lines 6 to 13 in b0d27d8
In this example, the unit tests get a dependency on all of the runners.
Dependency inference rules
To make dependency inference add these dependencies, the pants plugin adds several rules.
Dependencies on
stevedore_extension
targetsStarting with the inferred dependencies, and going backwards in the rule graph, here are the rules to infer dependencies on
stevedore_extension
targets.st2/pants-plugins/stevedore_extensions/python_dependency_injection.py
Lines 79 to 86 in 7148058
This rule needs
StevedoreExtensions
which comes from this rule:st2/pants-plugins/stevedore_extensions/python_dependency_injection.py
Lines 53 to 59 in 7148058
Which needs
AllStevedoreExtensions
from this rule (which getsAllTargets
from pants provided rules):st2/pants-plugins/stevedore_extensions/target_types_rules.py
Lines 50 to 53 in 7148058
stevedore_extension
targets depend on python codeThe
stevedore_extension
targets would not be very helpful if they didn't pull in dependencies on relevant python code. So, we parse the entry_points field and infer dependencies on all of the python code that provides those entry points. Starting fromst2/pants-plugins/stevedore_extensions/target_types_rules.py
Lines 158 to 165 in 7148058
In that rule, it requests
ResolvedStevedoreEntryPoints
from the pants engine, which pulls in this rule:st2/pants-plugins/stevedore_extensions/target_types_rules.py
Lines 59 to 65 in 7148058
Thus we have an inferred set of dependencies from
python_test
throughstevedore_extension
topython_source
.Developing pants plugins
Pants has extensive documentation on the plugin API, including how to create targets, how to write rules, and there's even a mini tutorial about how to add a formatter (adding formatters is a very common thing for plugins to do).