import unittest
import mop
from . import InMemoryDatabase
# in a real implementation, we'd add more functionality to the mop itself to
# allow for calling superclass methods, but that would be complicated enough to
# obscure the implementation and make it not as easy to follow (we would have
# to manage call stacks ourselves), and so we just do this instead for now
def call_method_at_class(c, method_name, invocant, *args, **kwargs):
return c.all_methods()[method_name].execute(invocant, args, kwargs)
class OverridesTest(unittest.TestCase):
def test_accessor_generation(self):
AccessorsMetaclass = mop.Class(
name="AccessorsMetaclass",
superclass=mop.Class,
)
def add_attribute(self, attr):
name = attr.name()
call_method_at_class(mop.Class, "add_attribute", self, attr)
self.add_method(self.method_class()(
name=name,
body=lambda self: self.metaclass.all_attributes()[name].value(self),
))
AccessorsMetaclass.add_method(AccessorsMetaclass.metaclass.method_class()(
name="add_attribute",
body=add_attribute,
))
AccessorsMetaclass.finalize()
Point = AccessorsMetaclass(
name="Point",
superclass=AccessorsMetaclass.base_object_class(),
)
Point.add_attribute(Point.attribute_class()(name="x", default=0))
Point.add_attribute(Point.attribute_class()(name="y", default=0))
Point.finalize()
point = Point(x=1, y=2)
assert point.x() == 1
assert point.y() == 2
def test_trace_method_calls(self):
methods_called = []
TraceMethod = mop.Class(
name="TraceMethod",
superclass=mop.Method,
)
def execute(self, invocant, args, kwargs):
methods_called.append(self.name())
return call_method_at_class(mop.Method, "execute", self, invocant, args, kwargs)
TraceMethod.add_method(TraceMethod.metaclass.method_class()(
name="execute",
body=execute,
))
TraceMethod.finalize()
TraceClass = mop.Class(
name="TraceClass",
superclass=mop.Class,
)
TraceClass.add_method(TraceClass.metaclass.method_class()(
name="method_class",
body=lambda self: TraceMethod,
))
TraceClass.finalize()
Point = TraceClass(
name="Point",
superclass=TraceClass.base_object_class(),
)
Point.add_attribute(Point.attribute_class()(name="x", default=0))
Point.add_attribute(Point.attribute_class()(name="y", default=0))
Point.add_method(Point.method_class()(
name="x",
body=lambda self: self.metaclass.all_attributes()["x"].value(self)
))
Point.add_method(Point.method_class()(
name="y",
body=lambda self: self.metaclass.all_attributes()["y"].value(self)
))
Point.finalize()
point = Point(x=1, y=2)
assert methods_called == []
assert point.x() == 1
assert methods_called == ['x']
assert point.y() == 2
assert methods_called == ['x', 'y']
def test_db_backed_object(self):
DatabaseAttribute = mop.Class(
name="DatabaseAttribute",
superclass=mop.Attribute,
)
DatabaseAttribute.add_attribute(DatabaseAttribute.attribute_class()(
name="db",
))
DatabaseAttribute.add_method(DatabaseAttribute.method_class()(
name="db",
body=lambda self: self.metaclass.all_attributes()["db"].value(self)
))
def value(self, instance):
key = str(hash(instance)) + ":" + self.name()
return self.db().lookup(key)
DatabaseAttribute.add_method(DatabaseAttribute.method_class()(
name="value",
body=value,
))
def set_value(self, instance, new_value):
key = str(hash(instance)) + ":" + self.name()
self.db().insert(key, new_value)
DatabaseAttribute.add_method(DatabaseAttribute.method_class()(
name="set_value",
body=set_value,
))
DatabaseAttribute.finalize()
DatabaseBackedClass = mop.Class(
name="DatabaseBackedClass",
superclass=mop.Class,
)
DatabaseBackedClass.add_attribute(DatabaseBackedClass.attribute_class()(
name="db",
))
DatabaseBackedClass.add_method(DatabaseBackedClass.method_class()(
name="db",
body=lambda self: self.metaclass.all_attributes()["db"].value(self)
))
def add_attribute(self, attr):
attr.metaclass.all_attributes()["db"].set_value(attr, self.db())
call_method_at_class(mop.Class, "add_attribute", self, attr)
DatabaseBackedClass.add_method(DatabaseBackedClass.method_class()(
name="add_attribute",
body=add_attribute,
))
DatabaseBackedClass.add_method(DatabaseBackedClass.method_class()(
name="attribute_class",
body=lambda self: DatabaseAttribute,
))
DatabaseBackedClass.finalize()
Point = DatabaseBackedClass(
name="Point",
superclass=DatabaseBackedClass.base_object_class(),
db=InMemoryDatabase(),
)
Point.add_attribute(Point.attribute_class()(name="x", default=0))
Point.add_attribute(Point.attribute_class()(name="y", default=0))
Point.add_method(Point.method_class()(
name="x",
body=lambda self: self.metaclass.all_attributes()["x"].value(self)
))
Point.add_method(Point.method_class()(
name="y",
body=lambda self: self.metaclass.all_attributes()["y"].value(self)
))
Point.add_method(Point.method_class()(
name="set_x",
body=lambda self, new_value: self.metaclass.all_attributes()["x"].set_value(self, new_value)
))
Point.finalize()
point = Point(x=3, y=7)
assert point.x() == 3
assert point.y() == 7
assert point.slots == {}
point.set_x(12)
assert point.x() == 12
assert point.y() == 7
assert point.slots == {}
assert len(Point.db().store) == 2
point2 = Point(x=123, y=-5)
assert point.x() == 12
assert point.y() == 7
assert point.slots == {}
assert point2.x() == 123
assert point2.y() == -5
assert point2.slots == {}
assert len(Point.db().store) == 4