-
-
Notifications
You must be signed in to change notification settings - Fork 646
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 lifecycle handlers to allow extensions to hook session lifecycle #10988
Conversation
Example of how the extension was able to hook a session lifecycle:
|
src/python/pants/core/register.py
Outdated
import logging | ||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class CoreSessionLifecycleHandler(SessionLifecycleHandler): | ||
def on_session_start(self): | ||
logger.info("on_session_start") | ||
|
||
def on_session_end(self): | ||
logger.info("on_session_end") | ||
|
||
|
||
class CoreLifecycleHandler(ExtensionLifecycleHandler): | ||
def on_session_create(self, options: Options) -> Optional[SessionLifecycleHandler]: | ||
logger.info("on_session_create") | ||
return CoreSessionLifecycleHandler() | ||
|
||
|
||
def lifecycle_handlers(): | ||
return [CoreLifecycleHandler()] |
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.
This change is just to provide a quick example of how a lifecycle handler would work in practice. It would be removed in the final version of the PR.
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.
Removed this.
[ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
2c603a0
to
9c3d29c
Compare
@Eric-Arellano: I've updated the PR as per our discussion offline to use keyword parameters on the lifecycle handlers.
|
@asherf: You may be interested in this PR. It adds a way for Pants plugins to hook into life cycle of a Pants session. The goal in part is to have the Buildsense plugin not need to maintain mutable state on a |
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.
Would it be better for lifecycle_handlers to return classes to instantiate instead of an instance?
Yes, I think so. Each lifecycle handler class will implement a uniform .create()
method (or similar name), and Pants controls the creation of it.
Is there any good place to put an integration test of lifecycle_handlers?
Yes, adding a test to src/python/pants/init
would be awesome. I think you can maybe use run_pants()
: https://www.pantsbuild.org/docs/rules-api-testing#approach-4-run_pants-integration-tests-for-pants. You would set up a register.py
file in the test with your simple plugin, and set --backend-packages
and --pythonpath
to load it, similar to this:
pants/src/python/pants/base/exception_sink_integration_test.py
Lines 27 to 28 in bf2548b
f"--pythonpath=+['{testproject_backend_src_dir}']", | |
f"--backend-packages=+['{testproject_backend_pkg_name}']", |
@@ -55,6 +56,7 @@ class LocalPantsRunner: | |||
union_membership: UnionMembership | |||
profile_path: Optional[str] | |||
_run_tracker: RunTracker | |||
_session_lifecycle_handlers: List[SessionLifecycleHandler] |
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.
This should probably be Tuple[SessionLifecycleHandler, ...]
so that it's hashable. Even though the dataclass isn't frozen, we always bias towards immutability.
@@ -0,0 +1,23 @@ | |||
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). |
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.
Should probably rename the file to lifecycle_handler.py
.
from pants.option.options import Options | ||
|
||
|
||
class SessionLifecycleHandler: |
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.
If you envision multiple types of LifecycleHandler
s, I recommend a LifecycleHandler
superclass. It's fine if it's empty for now, perhaps subclass ABC
to express intent that it's abstract. We can fill the methods in as we add more use cases and the common interface emerges from usage.
I think you can delete ExtensionLifecycleHandler
. BuildConfiguration
should store a bunch of LifecycleHandler
, where each is a certain concrete subclass. Then, we can call isinstance(SessionLifecycleHandler)
etc to determine which type of handler we're dealing with in our local_pants_runner.py
code.
|
||
|
||
class SessionLifecycleHandler: | ||
def on_session_start(self): |
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 basic docstring would be helpful for both of these. Even though we're not documenting on the website yet, it will help future contributors to understand what's going on.
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.
It might be fine to call these start()
and end()
, as the SessionLifecycleHandler
already expresses when those events happen.
That will make it easier for us to factor the methods up into the general LifecycleHandler
if/when we're ready to generalize. I can imagine the interface being create()
, start()
, and end()
, and having subclasses like SessionLifecycleHandler
vs. RuleInvocationLifecycleHandler
.
def on_session_create( | ||
self, *, build_root: str, options: Options, specs: Specs | ||
) -> Optional[SessionLifecycleHandler]: | ||
pass |
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.
I think you can move this into SessionLifecycleHandler
and call it create()
. No longer return Optional
; we already validated it's a SessionLifecycleHandler
, so we know the creation should not fail.
Also it should be a classmethod, as it's a factory method to create a new instance.
session_lifecycle_handler = lifecycle_handler.on_session_create( | ||
build_root=build_root, options=options, specs=specs | ||
) | ||
if session_lifecycle_handler: | ||
session_lifecycle_handlers.append(session_lifecycle_handler) |
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.
See lifecycle.py
for how I recommend changing the modeling of this all. Here, you would check if instance(handler, SessionLifecycleHandler)
, then call handler.create()
.
It feels like consumers of this API are likely to have pretty heavy overlap with pants/src/python/pants/reporting/streaming_workunit_handler.py Lines 36 to 95 in bf2548b
StreamingWorkunitHandler (possibly with a rename?), or was this a conscious decision to have lots of small interfaces? If lots of small interfaces, would recommend moving them closer together (the reporting package) and aligning their APIs.
|
The motivation for this PR in part is that Eric mentioned that a development goal for Pants was to remove mutable state on pants/src/python/pants/option/subsystem.py Line 236 in 92a2087
The initial implementation of counters hard-coded the setup of the metrics aggregation handler into LocalPantsRunner. This seemed like a code smell to me. Moreover, the aggregation code was implemented on the Re "smaller interfaces", that was my intent. The better approach in my opinion was to have hooks that would be instantiated for particular phases of a Pants run. These hooks seem to me to be more general than just reporting. I actually would like to modify that workunit handler setup after this PR so that the lifecycle handlers can explicitly register for receiving workunits programmatically. (This is how I envision the counters PR actually operating without having to hard-code them into LocalPantsRunner.) Thoughts? |
Got it. I didn't realize that, but we'll need to adjust that anyway to unblock #7654, so would be good to avoid having multiple APIs in the meantime. The To adjust that API, I expect that we'd want to construct an instance of pants/src/python/pants/bin/local_pants_runner.py Lines 243 to 249 in 3760987
LocalPantsRunner.run has a new Session created here)
|
So it sounds like you're suggesting roughly three commits that:
? If so, that sounds reasonable. I expect that the first two commits could probably be accomplished in one PR though... this one probably? |
Closing since we decided this PR is unnecessary currently. |
Problem
Extensions have no generic way to hook into the lifecycle of a Pants session.
Solution
Add a
lifecycle_handlers
entry point to extensions to allow extensions to hook session creation and start/end. This will be used in #10889 to allow the metrics reporting to manage mutable state during the lifetime of each session.Result
Added test for life cycle registration.