-
Notifications
You must be signed in to change notification settings - Fork 659
Proposal to change PR "Add __del__ method to BusABC"
#1519
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
Conversation
Co-authored-by: Felix Divo <4403130+felixdivo@users.noreply.github.com>
Co-authored-by: Felix Divo <4403130+felixdivo@users.noreply.github.com>
Co-authored-by: Felix Divo <4403130+felixdivo@users.noreply.github.com>
|
|
|
I think So in my opinion this is the wrong way to deal with this. Disabling warnings is obviously also not great. The shutdown methods would need to be adapted to handle partially initialized interfaces (e.g. using |
Tbruno25
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'd that this is the better approach as opposed to filtering out the warnings 👍
| _is_initialized: bool = False | ||
| _is_shutdown: bool = False |
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'm not sure we need 2 separate attributes to implement this. If we only want __del__ to execute if the class is initialized, perhaps _is_active would be more idiomatic?
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.
And yes, you are totally right. And if we are adding many hasattr() checks to shutdown() anyways, we can also delete such a marker attribute instead of checking it's value. Not sure if that is a good idea, but it's possible.
|
That's a great point @zariiii9003 Presumably if |
Thanks @zariiii9003 for looking over this, but I actually disagree with this. But you are right in that the some of the current interfaces might not gracefully handle exceptions being raised by (functions called in) their |
|
Even if you put the whole |
I think there should never be a partially initiliaized interface (from the user's perspective). So either Well, let's consider a simple example: def __init__(params, ...):
self._resource_1 = create_it_but_it_might_raise_an_exception(params)
self._resource_2 = create_other_but_it_might_raise_an_exception(params)
super().__init__(...)My point is that we should make sure that the function never returns in an unsafe state. Because if So instead, the above code should be written as: def __init__(params, ...):
try:
self._resource_1 = create_it_but_it_might_raise_an_exception(params)
except OSError as e:
raise CanInitializationError("resource 1 is not working") from e
try:
self._resource_2 = create_other_but_it_might_raise_an_exception(params)
except OSError as e:
self._resource_1.close_silently() # If we have only one socket/... this is not needed and things are quite simple
raise CanInitializationError("resource 2 is funky") from e
# We only mark the interface as initiallized after the above worked without raising
super().__init__(...) |
|
I understand your point, but regarding your example: If you don't want to repeat |
|
Okay, it seems like we can be quite sure that |
|
Are we sure it is called after |
That's a very good point, it probably won't be called. I'll check that later |
|
I think the simplest fix is to catch the AttributeError def __del__(self) -> None:
if not self._is_shutdown:
LOG.warning("%s was not properly shut down", self.__class__)
# We do some best-effort cleanup if the user
# forgot to properly close the bus instance
try:
self.shutdown()
except AttributeError:
passI also tried the |
Well yeah, but then we need to make sure to release the resources in the order in which they were assigned to the object (for the case that, for example, the exception was thrown somewhere in the middle). Good thing: This will not silence possible programming errors when calling |
|
@zariiii9003 Would you like to take over this PR? |
I think @Tbruno25 should finalize his own PR. I don't think |
|
Sounds good -- would be happy to wrap this up 👍 |
|
Then let's continue the discussion in your PR 😄 |
* Add del method * Add unittest * Satisfy black formatter * Satisfy pylint linter * PR feedback Co-authored-by: Felix Divo <4403130+felixdivo@users.noreply.github.com> * PR feedback Co-authored-by: Felix Divo <4403130+felixdivo@users.noreply.github.com> * Move to cls attribute * Add unittest * Call parent shutdown from socketcand * Wrap del in try except * Call parent shutdown from ixxat * Black & pylint * PR feedback Co-authored-by: Felix Divo <4403130+felixdivo@users.noreply.github.com> * Remove try/except & fix ordering * Fix unittest * Call parent shutdown from etas * Add warning filter * Make multicast_udp back2back test more specific * clean up test_interface_canalystii.py * carry over from #1519 * fix AttributeError --------- Co-authored-by: TJ Bruno <tj.bruno@everactive.com> Co-authored-by: Felix Divo <4403130+felixdivo@users.noreply.github.com> Co-authored-by: zariiii9003 <52598363+zariiii9003@users.noreply.github.com>
Proposal to replace #1489. The problem was that if there was an error while initializing the interface, the
__del__would still be triggered and cause weird "unhandled exception" warnings and the cleanup might not complete entirely.With this change,
__del__only takes action if__init__succeeded previously. This also allows us to re-enable the warnings that were disabled in e3f2f6e. The downside is that we need to take care where to callsuper().__init__()in the concreteBusABCimplementations.