-2

Class CumulativeSender is sending email at the end of aplication. I read about problems with garbage collector and __del__ and I would like to know if there is case when __del__ for staticCumulativeSenderObject will not be called at the end of application and why?

CumulativeSender.py:

import smtplib;

_staticCumulativeSenderObject = None;


class CumulativeSender(object):

    def __init__(self):

        recipients['sender'] = 'from@domain.com';
        recipients['emails'] = ['to@domain.com'];
        recipients['subject'] = 'Test Email';
        recipients['smtp_host'] = 'localhost';

        self.message = "";
        self.fromaddr = recipients['sender'];
        self.toaddrs  = recipients['emails'];
        self.subject = recipients['subject'];

        self.server = smtplib.SMTP(recipients['smtp_host']);

    def __del__(self):
        if(self.message):
            msg = ("From: %s\r\nTo: %s\r\n" % (self.fromaddr, ", ".join(self.toaddrs))) + "Subject: " + self.subject + "\r\n\r\n" + self.message;
            self.server.sendmail(self.fromaddr, self.toaddrs, msg);
        self.server.quit();

    def immediateSend():
        global _staticCumulativeSenderObject;
        if(_staticCumulativeSenderObject is not None):
            toDelete = _staticCumulativeSenderObject;
            _staticCumulativeSenderObject = None;
            del toDelete;


    immediateSend = staticmethod(immediateSend);


    def addEvent(msg):
        global _staticCumulativeSenderObject;
        if(_staticCumulativeSenderObject is None):
            _staticCumulativeSenderObject = CumulativeSender();
        _staticCumulativeSenderObject.message += msg + "\r\n";

    addEvent = staticmethod(addEvent);


    def addAlert(msg, code = None):
        CumulativeSender.addEvent(buildMessage("Allert",msg,code));

    addAlert = staticmethod(addAlert);


    def addError(msg, code = None):
        CumulativeSender.addEvent(buildMessage("Error",msg,code));

    addError = staticmethod(addError);

simple use:

import CumulativeSender;
CumulativeSender.CumulativeSender.addAlert("alert1", 345);
CumulativeSender.CumulativeSender.addAlert("alert2", 346);

Or maybe there is another way to do what I want?

Logman
  • 4,031
  • 1
  • 23
  • 35

2 Answers2

2

Well you should never relay on __del__. But your question is „when __del__ method will not be called?”. That actually is simply, whenever you have a circular references that point to your object. In that case Python cannot distinguish which object should be deleted first, so Python won't call any __del__ method at all. The harder question is when you actually can get a circular reference, but that will not solve you problem.

The wrong use of __del__ method usually come from trying to use RAII technique.

Well you code will run well until some one make a reference to _staticCumulativeSenderObject from a object involved in circular reference. But even if it looks like a private and no one should make any references, it is a bad design. This code will do exactly what atexit module is for.

import atexit

@atexit.register
def _sendAtExit():
    global _staticCumulativeSenderObject
    if _staticCumulativeSenderObject:
         #do what your __del__ is doing
        _staticCumulativeSenderObject.send_and_close()
        _staticCumulativeSenderObject=None
Community
  • 1
  • 1
Arpegius
  • 5,817
  • 38
  • 53
  • Thx for answer different from "`__del__` is devil" and for providing me something that I can use instead of `__del__` – Logman Jun 06 '14 at 16:39
0

You need to have a really good reason to override __del__. In the vast majority of the cases the disadvantages outweigh the advantages. You can find plenty of reading about __del__ using a websearch engine of your choice.

You didn't explain why you prefer an implicit approach, rather than adding a send() method and calling it explicitly when you want to send the email. That would be my ultimate suggestion.

However, trusting that you do have a good reason for the implicit approach, you can define a context manager, by defining __enter__ and __exit__ methods (or by using the contextlib.contextmanager shortcut). Usage would be something like:

with CumulativeSender(...) as sender:
    sender.addEvent(...)
    sender.add...(...)
    sender.add...(...)

When exiting this block, the __exit__ method is called. You can send the email there.

Note that __exit__ is also called if an exception leaves that block, so you should decide weather or not you want to send if an exception is raised (probably not).

But again, explicit is better than implicit.

shx2
  • 61,779
  • 13
  • 130
  • 153
  • Thanks for answering. Static methods of this class (`addAlert` and `addError`) will be used in many different places in code (different files different classes) but there is more then one entry point (many application). I can't guarantee that someone will call `send()` at the end of his application. Class user don't realy use class objects only static methods of class. There is realy no implicite for him. He use that methods and buffer is flushed at the end of application. And next line from Zen: Simple is better than complex – Logman Jun 06 '14 at 16:27
  • @Logman What does your `main()` function look like? why can't you explicitly call `send()` as the last line of `main`? Also, in this context, what do you consider "simple" and "complex". – shx2 Jun 06 '14 at 16:40
  • There is multiple main function it's not a single application. Look at this class more like module to more complex system. – Logman Jun 06 '14 at 18:51
  • When I use function(something) it's really simple, but when I need to manage some object by myself it's more complex. – Logman Jun 06 '14 at 19:00