-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
fix(framework): add warning on lost observer weakref #1142
Conversation
Hmm, I don't understand why this should be a weakref either. I would have expected an @jameinel put up this PR back in Nov 2019 that changed this from a regular dict to a weakref dict. It explains that it was done, and that "it's safe", but not really why it was done:
John, can you remember (or think of) why we would have made this a weakref dict? |
In terms of this PR, if there's a good reason for this to be a weakref, I don't mind adding the warning, but I want to get to the bottom of why we use weakref at all first. |
So we intentionally didn't want just a ref from observe() to be the thing that holds objects resident in memory. Both because it is a bit weird for TestObject() to actually have a side effect, but also because of how the actual event structure works. |
This sounds reasonable and makes sense to me. At the same time it feels like we shouldn't allow refs to simply disappear from |
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.
Some minor changes requested.
test/test_charm.py
Outdated
@@ -128,6 +128,30 @@ def _on_start(self, event: ops.EventBase): | |||
# check that the event has been seen by the observer | |||
self.assertIsInstance(charm.seen, ops.StartEvent) | |||
|
|||
def test_observe_nested_object_noninstantiated(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.
def test_observe_nested_object_noninstantiated(self): | |
def test_observer_not_referenced_warning(self): |
Also, should this test live in test_framework.py
, as the relevant code lives in framework.py
?
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 thought about it, but it felt way too complicated to implement it without referring to the charm object. If you look at the test immediately above it, I feel it makes sense to keep it here as they're very similar.
If you really want that that's a significant rework of the test.
Co-authored-by: Ben Hoyt <benhoyt@gmail.com>
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.
Fair -- thanks!
So this is a tricky one.
At times charmers are tempted to write:
and expect that both _on_start methods will be triggered, while due to some subtle implementation details, MyObj._on_start never fires because the notice is silently dropped if the weakref stored in Framework._observer has been gc'd between charm instantiation time and Framework._emit time.
Long story short, I'm not sure why that codepath is so permissive and we don't error out altogether, that is, I don't see a legitimate reason why _observer is a weakref dict instead of a regular dict, as that predates me by a bunch. Perhaps @jameinel has some more context there?
For now, I'd propose adding a warning there so that people have something to go on with when that happens. We can also consider raising an exception, or making _observer a regular dict if those are viable alternatives.