views:

127

answers:

2

Hi SO's,

Many thanks for the advice you have given me thus far. Using testbenches is something this forum has really shown me the light on and for that I am appreciative. My problem is that I am playing with a singleton and normally I won't del it, but in a testbench I will need to. So can anyone show me how to del the thing? I've started with a basic example and built it up into a testbench so I can see whats going on. Now I don't know how to get rid of it!

Many thanks!!

import sys
import logging
import unittest

LOGLEVEL = logging.DEBUG

class Singleton:
    """ A python singleton """

    class __impl:
        """ Implementation of the singleton interface """
        def __init__(self):
            self.log = logging.getLogger()
            self.log.debug("Init %s" % self.__class__.__name__)

        def id(self):
            """ Test method, return singleton id """
            return id(self)


    # storage for the instance reference
    __instance = None

    def __init__(self):
        """ Create singleton instance """
        # Check whether we already have an instance
        if Singleton.__instance is None:
            # Create and remember instance
            Singleton.__instance = Singleton.__impl()

        # Store instance reference as the only member in the handle
        self.__dict__['_Singleton__instance'] = Singleton.__instance

    def __getattr__(self, attr):
        """ Delegate access to implementation """
        return getattr(self.__instance, attr)

    def __setattr__(self, attr, value):
        """ Delegate access to implementation """
        return setattr(self.__instance, attr, value)

class A:
    def __init__(self):
        self.log = logging.getLogger()
        self.log.debug("Init %s" % self.__class__.__name__)
        self.lowclass = Singleton()
        self.id = self.lowclass.id()
        self.log.debug("ID: %s" % self.id)

class B:
    def __init__(self):
        self.log = logging.getLogger()
        self.log.debug("Init %s" % self.__class__.__name__)
        self.lowclass = Singleton()
        self.id = self.lowclass.id()
        self.log.debug("ID: %s" % self.id)


class ATests(unittest.TestCase):

    def testOne(self):
        a = A()
        aid = a.id
        b = B()
        bid = b.id
        self.assertEqual(a.id, b.id)

        #
        # How do I destroy this thing??
        #

        del a
        del b

        a1 = A()
        a1id = a1.id
        self.assertNotEqual(a1id, aid)

if __name__ == '__main__':
    # Set's up a basic logger
    logging.basicConfig( format="%(asctime)s %(levelname)-8s %(module)s %(funcName)s %(message)s", 
                         datefmt="%H:%M:%S", stream=sys.stderr )
    log = logging.getLogger("")
    log.setLevel(LOGLEVEL)
    # 
    suite = unittest.TestLoader().loadTestsFromTestCase(ATests)
    sys.exit(unittest.TextTestRunner(verbosity=LOGLEVEL).run(suite))
A: 

Considering you'll have many of those classes, I wouldn't exactly call them singletons. You just defer the attribute to a singleton class. It seems better to make sure the class actually is a singleton.

The problem with your solution is that you would have to implement both a del method (which is fine) but also a reference-counter, which seems like a bad idea. :-)

Here is a question with several implementations: http://stackoverflow.com/questions/31875/is-there-a-simple-elegant-way-to-define-singletons-in-python

Which one is for you depends on what kind of singleton you want. One object per a certain value, but for any value? A set of predefined singletons? A proper singleton, ie, just one single object?

Lennart Regebro
+3  A: 

As Borg's author I obviously second @mjv's comment, but, with either Borg (aka "monostate") or Highlander (aka "singleton"), you need to add a "drop everything" method to support the tearDown in your test suite. Naming such method with a single leading underscore tells other parts of the sw to leave it alone, but tests are atypical beasts and often need to muck with such otherwise-internal attributes.

So, for your specific case,

class Singleton:
   ...
   def _drop(self):
   "Drop the instance (for testing purposes)."
   Singleton.__instance = None
   del self._Singleton__instance

Similarly, for Borg, a _drop method would release and clear the shared dictionary and replace it with a brand new one.

Alex Martelli
So I'm reviewing the Borg. I want to instantiate a single (file) logger class that will go away if there isn't any issues but if there are it sends off an email giving you the log file. But doing this in every subclass is foolish and a big penalty. Hence the idea of using a Highlander. The first time it's called gets it started everyone else becomes notified if problems occur. Since I can't be sure of how the logger gets called I can't verify a Borg will work. A Highlander kinda guarantees it? Am I missing something?
rh0dium
I'm not too sure what kind of guarantee you're thinking you get about your single instance "going away" -- but it's probably weaker than you think it is. Use the atexit module of the standard library to register a function that _is_ reasonably guaranteed to be called as your program is terminating, but when everything is still in a usable state for such tasks as logging, mailing, removing temporary files, and so forth ("reasonably" because a crash or `kill -9` gives no guarantees;-). Now all you need to be unique is (as usual) _state_, and Borg's fine.
Alex Martelli