jpo38
jpo38

Reputation: 21544

Why does using `logging.Formatter` prevent widget from being deleted?

Here is a MCVE, very simple piece of code creating a QMainWindow, with a central empty QWidget:

import sys

from PyQt5.QtWidgets import QWidget

class MyWidget(QWidget):
    """QWidget based class to configure a Simulator object."""
    
    def __init__(self,parent : QWidget):
        """
        Construct a new object.

        param: parent : QWidget
            Parent class
        """
        super().__init__(parent)

        print("Created GUI")
        
    def __del__(self): 
        print("Deleting GUI")
            
if __name__ == '__main__':
    
    from PyQt5.QtWidgets import QMainWindow, QApplication
    
    import logging
    
    class CustomFormatter(logging.Formatter):

        def format(self, record):
            formatter = logging.Formatter("%(message)s")
            return formatter.format(record)
    
    logger = logging.getLogger("my_logger")
    logger.setLevel(logging.INFO)
    ch = logging.StreamHandler()
    ch.setLevel(logging.INFO)
    #ch.setFormatter(CustomFormatter())
    logger.addHandler(ch)
    
    app = QApplication(sys.argv)
    
    wnd = QMainWindow()
    widget = MyWidget(wnd)
    
    wnd.setCentralWidget( widget )
    wnd.show()
    
    sys.exit(app.exec())

Execute it, a GUI opens, click the close button, GUI closes and output is then:

Created GUI
Deleting GUI

Now, uncomment ch.setFormatter(CustomFormatter()), repeat.

Now output does not show Deleting GUI. I tried to add an assert here, code does not assert. Why does MyWidget.__del__ not get called when using a custom formatter?

Upvotes: 1

Views: 40

Answers (1)

AKX
AKX

Reputation: 169338

If you mean to use __del__ for cleanup, you shouldn't. It's worth reading the myriad caveats the docs have about __del__.

Context managers or close() (and contextlib.closing() to help along) are the Pythonic idiom for "disposable" objects.

Basically, I think it's likely just pure luck that setting up a formatter happens to create enough GC traffic that your Widget does get __del__'d before the interpreter shuts down (and Python likely (didn't check!) skips __del__ing things when it's shutting down – little point in doing cleanup when the process will end anyway).

Wrapping your code in a def main(): (so the objects are locals, not globals) seems to (more) reliably make __del__ get called once widget is no longer a local variable. So long as they're globals, they're still easily referred-to...

Upvotes: 2

Related Questions