-
Notifications
You must be signed in to change notification settings - Fork 2k
Add pre/post transaction plugin hooks #14754
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 pre/post transaction plugin hooks #14754
Conversation
73dff89 to
0204a0d
Compare
0204a0d to
ddab30b
Compare
CodSpeed Performance ReportMerging #14754 will not alter performanceComparing Summary
|
|
Clever! This is cleaner and might actually work. Pinging @jezdez too. My two cents:
|
jezdez
left a comment
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.
Agree with Jaime, let’s not rename the execute functions like this and let’s publish Action for sure. A few more comments in-line.
20e3d70 to
1130cec
Compare
1130cec to
e71511b
Compare
Okay, some benchmarks on my local machine, using the tests in Let me see if there's a straightforward way to mock the solve, but keep the |
|
Okay, measuring execution times it looks like if the hook is called on every action, i.e. [no overhead] elapsed time: 3.9611058235168457 But if for example the hooks use [no overhead] elapsed time: 3.776529550552368 So it really depends on what action type is being targeted. |
You can use an The 400s one for
Do you think that's a good idea? (Names can be different) Alternatively, really loud warnings in the documentation would help. Do we actually have use cases for the |
|
Alternatively, we could prevent users from passing |
|
I like that! I think we can easily achieve that with |
|
Yep, thinking the same thing 🤝 |
|
Cool, so the way to do this is to use |
bd402cc to
a471442
Compare
If it makes you feel any better, we also deprecate things with underscores in front of them:
So, making it "private" won't really change the way we treat it. |
travishathaway
left a comment
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 this is looking good! Great job on even taking the time to update the "deep dives" section.
Co-authored-by: Travis Hathaway <travis.j.hathaway@gmail.com>
| @dataclass | ||
| class PrefixActionGroupV2: | ||
| """A container for groups of actions carried out during an UnlinkLinkTransaction. | ||
| :param remove_menu_action_groups: Actions which remove menu items | ||
| :param unlink_action_groups: Actions which unlink files | ||
| :param unregister_action_groups: Actions which unregister environment locations | ||
| :param link_action_groups: Actions which link files | ||
| :param register_action_groups: Actions which register environment locations | ||
| :param compile_action_groups: Actions which compile pyc files | ||
| :param make_menu_action_groups: Actions which create menu items | ||
| :param entry_point_action_groups: Actions which create python entry points | ||
| :param prefix_record_groups: Actions which create package json files in ``conda-meta/`` | ||
| :param initial_action_groups: User-defined actions which run before all other actions | ||
| :param final_action_groups: User-defined actions which run after all other actions | ||
| """ | ||
|
|
||
| remove_menu_action_groups: Iterable[ActionGroup] | ||
| unlink_action_groups: Iterable[ActionGroup] | ||
| unregister_action_groups: Iterable[ActionGroup] | ||
| link_action_groups: Iterable[ActionGroup] | ||
| register_action_groups: Iterable[ActionGroup] | ||
| compile_action_groups: Iterable[ActionGroup] | ||
| make_menu_action_groups: Iterable[ActionGroup] | ||
| entry_point_action_groups: Iterable[ActionGroup] | ||
| prefix_record_groups: Iterable[ActionGroup] | ||
| initial_action_groups: Iterable[ActionGroup] = () | ||
| final_action_groups: Iterable[ActionGroup] = () | ||
|
|
||
| def __iter__(self) -> Generator[Iterable[ActionGroup], None, None]: | ||
| for field in fields(self): | ||
| yield getattr(self, field.name) | ||
|
|
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.
Can we instead just make the new dataclass quack like a NamedTuple and just deprecate the tuple parts? Something like:
from dataclasses import dataclass, fields, astuple, field
from typing import Iterable, Generator, Any, Iterator, Tuple
from conda.deprecations import deprecated
def deprecate_PrefixActionGroup_tuple() -> None:
deprecated.topic(
"25.9",
"26.3",
topic="`conda.core.link.PrefixActionGroup` as a NamedTuple",
addendum="Utilize `conda.core.link.PrefixActionGroup` as a dataclass instead.",
)
@dataclass
class PrefixActionGroup:
remove_menu_action_groups: Iterable[ActionGroup]
unlink_action_groups: Iterable[ActionGroup]
unregister_action_groups: Iterable[ActionGroup]
link_action_groups: Iterable[ActionGroup]
register_action_groups: Iterable[ActionGroup]
compile_action_groups: Iterable[ActionGroup]
make_menu_action_groups: Iterable[ActionGroup]
entry_point_action_groups: Iterable[ActionGroup]
prefix_record_groups: Iterable[ActionGroup]
initial_action_groups: Iterable[ActionGroup] = ()
final_action_groups: Iterable[ActionGroup] = ()
# quack like a tuple
def __getitem__(self, index):
deprecate_PrefixActionGroup_tuple()
return astuple(self)[index]
def __iter__(self) -> Iterator[Iterable[ActionGroup]]:
deprecate_PrefixActionGroup_tuple()
return iter(astuple(self))
def __len__(self) -> int:
deprecate_PrefixActionGroup_tuple()
return len(fields(self))
def __iter_fields__(self) -> Generator[Iterable[ActionGroup], None, None]:
yield from astuple(self)
def __eq__(self, other):
if isinstance(other, tuple):
deprecate_PrefixActionGroup_tuple()
return astuple(self) == other
return super().__eq__(other)
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.
We definitely can, but is this actually substantially better than what we have? Yes, it does avoid us having a PrefixActionGroupV2, but making it quack like a NamedTuple doesn't actually make it a NamedTuple. So if the primary concern here is about backwards compatibility, issubclass(PrefixActionGroup, tuple) == False whereas issubclass(PrefixActionGroupV2, tuple) == True. And to my knowledge we are not using __len__, __iter_fields__, __eq__, or __getitem__, so I just don't see the need here.
We could probably cook up some multiple inheritance scheme to get this to actually behave like a namedtuple. If other folks are really interested in having this or the suggestion above be the path forward I'm happy to do it, but again for what is effectively an internal only implementation detail I just don't see the 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.
Can define __instancecheck__ and __subclasscheck__ magic methods to address isinstance and issubclass
🤷🏼♂️ just trying to think of alternatives to needing to introduce a V2 suffix
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.
Can we instead do something like this?
- Introduce PrefixActionGroupDataclass, already deprecated and scheduled to be renamed as simply PrefixActionGroup
- Deprecate the old PrefixActionGroup, which will be replaced by PrefixActionGroupDataclass when time comes
- We start using PrefixActionGroupDataclass internally
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.
Although this is a bit like mutually canceling deprecations; downstream users won't have the chance to change their code in time 😬 I'd rather stick to well-known APIs instead of trying to each objects how to be have like others. Even if we make it quack like a tuple, the amount and order of elements is different, so it's still an API breakage.
I say we keep the solution as is. If we don't like the V2 suffix, I think Dataclass is descriptive enough. Other alternatives:
PrefixActionsGroupedPrefixActionsPrefixActionGrouperActionGroupsForPrefix
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 vote for what @jaimergp suggested. Let's choose one of the names above.
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 vote for PrefixActions.
jezdez
left a comment
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.
Let's fix those names and then merge this thing. This is awesome work @peytondmurray!
|
Okay, here's where we stand:
Thanks folks for the support! |
|
Sorry, not |
|
I'm noticing an increasing number of Java-style class names, and I don't like it ;) |
|
100% agreed 🙅 ☕ 🙅 |
|
@peytondmurray We need to pay much closer attention to detail here. The PrefixAction class is marked as deprecated and points to I think the intent is to rename one to the other. However, this doesn't really work since the deprecation warnings are circular and so downstream users cannot switch from one class to the other to remove the warnings. If the two offer identical functionality and are drop-in replacements, a workable approach is to instead simply do the replacement now and leave PrefixActions as a pointer to PrefixActionGroup with a deprecation warning on PrefixActions only. If that won't work, then you'll need to go through two deprecation cycles or pick a new unique name or something. |
I wouldn't call this an inattention to detail - you'll notice that the original implementation here included a
As discussed above |
They literally cannot solve it. We can't ship warnings that people cannot solve by following the instructions in the warnings.
OK. Then I will adjust the deprecation of one to be after the other or we can ignore one of the warnings for now. |
Description
This PR adds a pre/post-transaction hooks plugin system. Pre- (post-) transaction hooks accept a subclass of
Action, and are guaranteed to run before (after) every other action is run. Plugin authors must define the following methods:verifyexecutereverse(only ifexecuteraises an exception)cleanupand have access to the following attributes:
transaction_contexttarget_prefixunlink_precslink_precsremove_specsupdate_specsneutered_specsIn this way, you can define plugins that have access to e.g. what links are being generated/removed and where. For tests, I wrote a few that followed pretty closely the ones I found in that same plugin test directory.
Closes #13795.
Related: #13842
Checklist - did you ...
newsdirectory (using the template) for the next release's release notes?