Created
March 29, 2012 09:06
-
-
Save dir01/2235251 to your computer and use it in GitHub Desktop.
Interface proxy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*-coding: utf8 -*- | |
import unittest2 | |
from zope.interface import Interface | |
from zope.interface.declarations import implements, classImplements | |
class Storage(object): | |
CLASS_TEMPLATE = """ | |
class %(class_name)s(object): | |
def __init__(self, instance): | |
self.instance = instance | |
%(methods)s | |
""" | |
METHOD_TEMPLATE = """ | |
def %(method_name)s(%(class_method_args)s): | |
return self.instance.%(method_name)s%(method_args)s | |
""" | |
def __init__(self, class_name): | |
self.class_name = class_name | |
self.methods = [] | |
def add_method(self, method_name, method_args): | |
self.methods.append( | |
self.build_method_string(method_name, method_args) | |
) | |
def build_method_string(self, method_name, method_args): | |
stripped_method_args = method_args[1:-1] | |
class_method_args = 'self, %s' % stripped_method_args if stripped_method_args else 'self' | |
return self.METHOD_TEMPLATE % locals() | |
def get_as_string(self): | |
return self.CLASS_TEMPLATE % { | |
'class_name': self.class_name, | |
'methods': ''.join(self.methods) | |
} | |
class ProxyBuilder(object): | |
def __init__(self, interface, instance): | |
self.interface = interface | |
self.instance = instance | |
def build_proxy(self): | |
cls = self.build_proxy_class() | |
return cls(self.instance) | |
def build_proxy_class(self): | |
class_name = self.instance.__class__.__name__ | |
source = self.build_proxy_class_source(class_name) | |
fake_globals = {} | |
exec(source, fake_globals) | |
cls = fake_globals[class_name] | |
return cls | |
def build_proxy_class_source(self, class_name): | |
storage = Storage(class_name) | |
for method in self.iterate_interface_methods(): | |
storage.add_method(method.getName(), method.getSignatureString()) | |
return storage.get_as_string() | |
def iterate_interface_methods(self): | |
for name in self.interface: | |
yield self.interface[name] | |
def interface_proxy(interface, instance, debug=False): | |
if debug: | |
return instance | |
return ProxyBuilder(interface, instance).build_proxy() | |
class TestInterfaceProxy(unittest2.TestCase): | |
class Duck(Interface): | |
def quack(): | |
pass | |
def fly(direction): | |
pass | |
class DuckImplementation(object): | |
def quack(self): | |
return 'qua' | |
def fly(self): | |
pass | |
def meow(self): | |
pass | |
duck = Duck | |
def setUp(self): | |
classImplements(self.DuckImplementation, self.Duck) | |
self.duck = interface_proxy(self.Duck, self.DuckImplementation()) | |
def test_method_presents_in_interface(self): | |
self.assertEqual('qua', self.duck.quack()) | |
def test_method_not_presents_in_interface(self): | |
with self.assertRaises(AttributeError): | |
self.duck.meow() | |
def test_implementation_has_wrong_args(self): | |
with self.assertRaises(TypeError): | |
self.duck.fly('far', 'away') | |
if __name__ == '__main__': | |
unittest2.main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment