::A more thorough Interface class::

May 19th, 2011 by hamish download the zooToolBox

I did a bit more messing about this evening with the interface class idea. I think the below implementation is fairly complete. It handles multiple inheritance cases where the interface gets satisfied by one of the parent classes.

def interfaceTypeFactory( metaclassSuper=type ):
	'''
	returns an "Interface" metaclass.  Interface classes work as you'd expect.  Every method implemented
	on the interface class must be implemented on subclasses otherwise a TypeError will be raised at
	class creation time.

	usage:
		class IFoo( metaclass=interfaceTypeFactory() ):
			def bar( self ): pass

		subclasses must implement the bar method

	NOTE: the metaclass that is returned inherits from the metaclassSuper arg, which defaults to type.  So
	if you want to mix together metaclasses, you can inherit from a subclass of type.  For example:
		class IFoo( metaclass=interfaceTypeFactory( trackableTypeFactory() ) ):
			def bar( self ): pass

		class Foo(IFoo):
			def bar( self ): return None

		print( IFoo.GetSubclasses() )
	'''
	class _AbstractType(metaclassSuper):
		_METHODS_TO_IMPLEMENT = None
		_INTERFACE_CLASS = None

		def _(): pass
		_FUNC_TYPE = type( _ )

		def __new__( cls, name, bases, attrs ):
			newCls = metaclassSuper.__new__( cls, name, bases, attrs )

			#if this hasn't been defined, then cls must be the interface class
			if cls._METHODS_TO_IMPLEMENT is None:
				cls._METHODS_TO_IMPLEMENT = methodsToImplement = []
				cls._INTERFACE_CLASS = newCls
				for name, obj in attrs.items():
					if type( obj ) is cls._FUNC_TYPE:
						methodsToImplement.append( name )

			#otherwise it is a subclass that should be implementing the interface
			else:
				if cls._INTERFACE_CLASS in bases:
					for methodName in cls._METHODS_TO_IMPLEMENT:

						#if the newCls' methodName attribute is the same method as the interface
						#method, then the method hasn't been implemented.  Its done this way because
						#the newCls may be inheriting from multiple classes, one of which satisfies
						#the interface - so we can't just look up the methodName in the attrs dict
						if getattr( newCls, methodName, None ).im_func is getattr( cls._INTERFACE_CLASS, methodName ).im_func:
							raise TypeError( "The class %s doesn't implement the required method %s!" % (name, methodName) )

			return newCls

	return _AbstractType

class ITest( metaclass=interfaceTypeFactory() ):
	def something( self ): pass
	def otherthing( self ): pass

class Test_implementsAll(ITest):
	def something( self ): pass
	def otherthing( self ): pass

class Test_subclassImplementsAll(Test_implementsAll):
	pass

class SimilarInterface(object):
	def something( self ): pass
	def otherthing( self ): pass

class MultipleInheritanceTest(SimilarInterface, ITest):
	pass

#will throw TypeError
class Test_implementsSome(ITest):
	def something( self ): pass

#will throw TypeError
class Test_implementsNone(ITest):
	pass

There are some simple tests at the bottom that demonstrate the idea.

As you can see, the class MultipleInheritanceTest has no methods, but its parent class SimilarInterface does implement the required methods, so the class passes without issue.

Anyway – I figured after the previous post I should at least finish the thought. The implementation in the last post wasn’t complete.

Share

This post is public domain

This entry was posted on Thursday, May 19th, 2011 at 21:16 and is filed under main. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.