Skip to content

Instantly share code, notes, and snippets.

@dir01
Created March 29, 2012 09:06
Show Gist options
  • Save dir01/2235251 to your computer and use it in GitHub Desktop.
Save dir01/2235251 to your computer and use it in GitHub Desktop.
Interface proxy
# -*-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