## @brief Defines a class to create abstract methods # # @author Ivo Timmermans # @date 2004/01/23 # @version 1.1 # # Example: # @code # import Abstract; # class Foo: # __metaclass__ = Abstract.Metaclass # foo = Abstract.AbstractMethod('foo') # @endcode class AbstractMethod (object): ## @brief Constructor # # @param func name of the function (used when raising an # exception). Its type is str. def __init__(self, func): self._function = func ## @brief Get callable object # # @return An instance of AbstractMethodHelper. # This trickery is needed to get the name of the class for which # an abstract method was requested, otherwise it would be # sufficient to include a __call__ method in the AbstractMethod # class itself. def __get__(self, obj, type): return self.AbstractMethodHelper(self._function, type) ## @brief Abstract method helper class # # An AbstractMethodHelper instance is a callable object that # represents an abstract method. class AbstractMethodHelper (object): def __init__(self, func, cls): self._function = func self._class = cls ## @brief Call abstract method # # Raises a TypeError, because abstract methods can not be # called. def __call__(self, *args, **kwargs): raise TypeError('Abstract method `' + self._class.__name__ \ + '.' + self._function + '\' called') ## @brief Configure a new class to be abstract # # @author Ivo Timmermans # @date 2004/01/23 # @version 1.1 class Metaclass (type): ## Configure a new class # # @param cls Class object # @param name Name of the class # @param bases All base classes for cls def __init__(cls, name, bases, *args, **kwargs): super(Metaclass, cls).__init__(cls, name, bases, *args, **kwargs) # Detach cls.new() from class Metaclass, and make it a method # of cls. cls.__new__ = staticmethod(cls.new) # Find all abstract methods, and assign the resulting list to # cls.__abstractmethods__, so we can read that variable when a # request for allocation (__new__) is done. abstractmethods = [] ancestors = list(cls.__mro__) ancestors.reverse() # Start with __builtin__.object for ancestor in ancestors: for clsname, clst in ancestor.__dict__.items(): if isinstance(clst, AbstractMethod): abstractmethods.append(clsname) else: if clsname in abstractmethods: abstractmethods.remove(clsname) abstractmethods.sort() setattr(cls, '__abstractmethods__', abstractmethods) ## @brief Allocator for class cls # # @param self Class object for which an instance should be # created. # @param cls Same as self. def new(self, cls): if len(cls.__abstractmethods__): raise NotImplementedError('Can\'t instantiate class `' + \ cls.__name__ + '\';\n' + \ 'Abstract methods: ' + \ ", ".join(cls.__abstractmethods__)) return object.__new__(self)