class ITest:
def test(): pass
class Test:
__implements__ = ( 'ITest', 'interface_pkg.interfaces.iclone.clone.Cloneable', 'serializable.Serializable' )
def sample(self): pass
def serialize(self): pass
def clone(self): pass
def test(self): pass
A syntax hasonlo mint zopenal (de azt nem tudom hogy ellenorzi). Lathato hogy modul/csomag nevet fel kell tunetni az osztaly neve elott. Ha nincs odairva semmi (mint az ITest-nel) akkor az aktualis fileban fogja keresni az adott osztalyt(interfacet).
Az ellenorzes pedig a kovetkezokeppen mukodik. A modul importalast hookoltam, igy a sajat importerem lefog futni minden import elott (ezert kell torolni az import cachet). Ez az importer ellenorzi az importalando fileban levo osszes osztalyt, megnezi van-e valamelyiknek __implements__ static attributuma. Ha talal ilyet, akkor az abban talalhato interfaceket betolti, es lekerdezi a benne levo metodusokat. Majd megnezi hogy az implementalo oszalyban ezek benne vannak-e. Ha igen akkor visszater a modullal, egyebkent hibat ir es None-t kuld vissza, ami ImportError-t fog okozni.
Hasznalat pedig a kovetkezo. Eloszor interface_checker -t kell importalni, ami hookolja magat. Innentol kezdve ha valamit importalunk akkor azt leellenorzni. Ebbol viszont mar ki lehet talalni mi a problema ezzel a modszerrel. A __main__ modul betoltesenel hamarabb nem tudja hookolni magat (mert legkorabban ott lehet importalni) igy az abban levo osztalyokat nem ellenorzni.
Ezenkivul kicsit olyan haxolos benyomast kelthet a dolog. Azert teszem megis koze, mert a import hookolas egy jo feature, es hata inspiral masokat egyeb problemak
megoldasara. Pl az urlimport - amivel httprol lehet python modult betolteni - is ezt hasznalja. De pl olyat meg nem lattam hogy adatabazisbol lehetne ugyanigy importalni, pedig ez is hasznos lehet.
Ime InterfaceImporter highly experimental verzioja:
class InterfaceImporter:
"""
TODO: check number of parameters
TODO: asserterror
TODO: check main module
"""
disable = False
def __init__(self, path):
print '[init]: at', path
def find_module(self, fullname, path=None):
if InterfaceImporter.disable: return None
print '[find module]: %s at %s' % ( fullname, path )
return self
def load_module(self, fullname):
print '[load module] at', fullname
InterfaceImporter.disable = True
try:
mod = __import__(fullname)
# get all class from the module
classes = [ i for i in dir( mod ) if type(getattr(mod, i)) == ClassType ]
print 'Module %s contains %s class(es)' % (fullname, classes)
try:
for clazz in classes:
clazz = getattr(mod, clazz)
if not hasattr( clazz, '__implements__'):
# no __implements___ field found, skip this class
print 'No interface in %s' % (clazz)
continue
interfaces = getattr( clazz, '__implements__')
print '%s implements %s interface(s)' % ( clazz, interfaces )
iobjes = []
for inf in interfaces:
if inf.rfind( '.' ) == -1:
ipkg = fullname
iname = inf
inf = ipkg + '.' + inf
else:
iname = inf[inf.rindex('.')+1:]
ipkg = inf[:inf.rindex('.')]
iobj = __import__( ipkg )
for isub in inf.split('.')[1:]:
iobj = getattr(iobj, isub )
iobjes.append( iobj )
self.checkInterfaces( clazz, iobjes )
except AssertionError, e:
print "implementation error: %s" % (str(e))
return None
except Exception, ex:
print 'Error:', str(ex)
return None
else:
print 'All method implemented'
finally:
InterfaceImporter.disable = False
return mod
def getMethods( self, clz ):
return [i for i in dir(clz) if type(getattr(clz, i)) == MethodType]
def checkInterfaces( self, clazz, interfaces ):
cmethods = self.getMethods( clazz )
print 'class methods of %s: %s' % ( clazz,cmethods )
for inf in interfaces:
imethods = self.getMethods( inf )
print 'interface methods of %s: %s' % ( inf, imethods )
for i in imethods:
assert i in cmethods, `i` + ' not correctly implemented'
# import hook
sys.path_hooks = [x for x in sys.path_hooks if x.__name__ != 'InterfaceImporter']
sys.path_hooks.append(InterfaceImporter)
sys.path_importer_cache.clear()
Az import hookolasrol bovebben pep-0302-ben lehet olvasni. Itt azt is leirjak hogy milyen protocolt kell kovetnie az importer osztalynak. Ez a protocol dolog olyan a pythonnal mint az interface statictyping nyelveknel. Ez mindossze annyit tesz hogy doksiba leirjak hogy milyen metodusok szuksegesek ahhoz h mukodjon. Ha valamit kihagyunk majd runtime kiderul, de amig hapog es duck-kent viselkedik addig duck :)
Egyebkent meg unittest rulez.

0 comments:
Post a Comment