-
Notifications
You must be signed in to change notification settings - Fork 7
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
[4.0] support for custom events #41
Conversation
From a user / tester point of view it would be great not to have to tell scenario about those paths. We are already telling scenario about the charm that we want to use and this charm observes some custom events. It feels like scenario already has all the info it needs. |
That however does not generalize: it might be the charm lib the one that observes the custom event, not the charm. So the charm might be totally unaware of the custom event. What do we do then? Recursively search all child We could make some cautious assumptions, of course, so that this situation is still nicely surfaced but we handle the common case automagically. Another, completely different approach to implementing this could be to skip altogether the ctx = Context(MyCharm)
ctx.call_method(MyCharm._on_my_custom_event, args=(MyCustomEventType(MyMockedArg1, MyMockedArg2))) WDYT? |
I agree that it's possible and often the case that the listening of the event is done in the charm library file but my point is that in the end, it's still the charm that observes it regardless in which "file" / "part of the code" it's done. Listening to event is always done in the charm's init, either directly or by instantiating other "Object" objects right? I wouldn't necessarily search all file recursively from the get go, since there can be a bunch of events that the charm is not listening to in the libs if those classes aren't instantiated. I think I'd start by whatever the charm is instantiating (?). And yes calling the charm's |
No my point is that we can't assume that this is the case.
How do we test Somewhat simpler case: |
The same point could be made about exercising custom events in isolation in the first place: maybe the code leading up to that event being emitted is buggy, and the event is not emitted when we think it should be. |
I agree with @PietroPasotti that we should be explicit here. However, I don't love the proposed API -- having a separate I wonder if we can do something more like >>> pkgutil.resolve_name('ops.CharmBase.on.config_changed')
<BoundEvent ConfigChangedEvent bound to CharmEvents.config_changed at 0x7fa7e183fa60> (The target-getting code in My thinking is we'd just use the existing trigger(State(), 'MyCharm.obj.my_on.foo', meta=...)
# or maybe
trigger(State(), 'obj.my_on.foo', meta=...) Also in my mind is the new trigger(State(), 'ops.Unit.on.collect_status')
# or
trigger(State(), 'unit.on.collect_status') I realize having Nit: I think |
I really like the idea, I'd thought about it as well but my main concern was (as with many 'string-based' approaches) the lack of type hinting and the burden of having to keep that in sync as your code changes. I don't like the idea of baking the charm name in the event emitter path for example. I thought of a workaround, reminiscent of a piece of code I wrote long ago: def get_event(charm_class: Type[T]) -> T: # fake return type to leverage type hints and autocompletion
"""Helper class to construct an event path."""
return _PathBuilder([])
class _PathBuilder:
"""Proxy class to construct a path of strings based on getattr."""
path: List[str]
def __getattr__(self, attr):
return _PathBuilder(self.path + [attr]) This way, you'd be able to write trigger(State(), get_event(MyCharm).foo.bar.on.baz, meta=...) and enjoy autocompletion and type checking on the path to Pros:
Cons:
|
decided to go with this suggestion of Ben. |
Fixes #38
Added custom event API: