Closed vitschwilk closed 7 months ago
Unless I'm missing something, you use-case isn't supported by Python. Relying on GC to cleanup resource isn't supported by Python AFAIK (and I believe no language with GC support this).
The GC do not guarantee that object get deleted immediately when they lost a reference (your function exit). CPython is a bit special as it do it immediately in some case (when there is no circular reference and paho-mqtt make no promise on circular reference).
If you want to ensure any resource get cleanup, you have to cleanup them at the location you want to cleanup them. Usually using with
context (e.g. not fileobj = open("filename")
but with open("filename") as fileobj:
). But using a finally work well also
So either make your wrapper class usable as with-statement context or add a finally to cleanup resource.
Hi, thank you for your timely answer. That is odd to not support. I don't understand how this client object is different from any other member in my wrapper-class. How can a member object keep the containing object alive?
I do agree that the GC does not "need" to clean up immediately. My issue is, I'm using the same approach in an open-api-backend. Therefore, my python never exits the __main__.py
-"script". After running a while and accessing some routs that use this wrapper class, I noticed that the threads from mqtt are never closed. This should have happened in my __dell__
method that is never called.
I previously did try using the class with a context manager, but did not fully delete the client object. I tried again and got it working. For the purpose of searchability, here is the code snipped:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("exiting")
self.client.loop_stop()
def __del__(self):
print("destructor of MqttWrapper")
if self.client:
self.client.loop_stop()
I'm closing the issue as I believe the problem is solved by last comment. Feel free to reopen if that not the case.
I don't understand how this client object is different from any other member in my wrapper-class. How can a member object keep the containing object alive?
The paho-mqtt Client (the value of field self.client
in your wrapper class) is still referenced by the thread started by loop_start
(that thread run the paho Client). The paho Client have reference to the method _callback_on_publish
(thought on_publish
). The method had reference to its instance. Therefor even if you no longer have any reference to your wrapper class, it's still hold by this chain: paho thread -> paho Client -> on_publish -> the wrapper class. The "issue" is the presence of callback (unavoidable with paho-mqtt).
Bug Description
I wrote a wrapper class for the mqtt client creation to publish data once in a while. The call is used as an object inside another class. This containing class is only used when needed therefor the object will be created inside a function scope. When exiting the function, I assumed the garbage-collector (gc) deletes the object. This does not happen.
After exiting the function, I still see references to this class in the gc. Therefore
__del__()
of my wrapper-class is never called and the loop is not stopped.Reproduction
wrapper class:
usage of the wrapper class:
This leads to the following output:
As you can see the object of test-client1 is never deleted. I cannot access it out of scope obviously but it is still in memory (0x7f29259ebc70).
As a workaround, I now stop the loop inside the scrope where the wrapper-object exists and delete the client member of it. Then the gc works as intended. But this is verry easy to forger/not-know because it is verry different from any other python experience and In my opinion an incorrect behaviour.
So by adding:
and removing the
self.client.loop_stop()
in__del__()
to the functions, I get the following output:Now only one object exists when creating the second client. In addition the print from
__del__()
now is also present.Environment
Logs
mosquitto.log entries when running the script:
run with enabled logging: