# Phase 0: absolute basics that we must start with
# things that must be provided by the underlying system: a place to store the
# mop itself, a data structure for instance data (with no associated behavior),
# and a way to run a chunk of code, given an invocant and argument list
Class = None
Object = None
Method = None
Attribute = None
class BasicInstance(object):
def __init__(self, metaclass, slots):
self.metaclass = metaclass
self.slots = slots
def execute_method(body, invocant, args, kwargs):
return body(invocant, *args, **kwargs)
# shim layer to interface with python - in a real system, this wouldn't be
# necessary, but this allows us to pass python-level method calls through to
# our mop infrastructure
UNDERLYING_CLASSES = {}
def python_class_for(c, name=None):
key = hash(c)
if key not in UNDERLYING_CLASSES.keys():
if name is None:
name = c.name()
UNDERLYING_CLASSES[key] = type(name, (object,), {})
return UNDERLYING_CLASSES[key]
def python_install_method(c, name, method):
setattr(
python_class_for(c),
name,
lambda self, *args, **kwargs: method.execute(self, args, kwargs)
)
def bootstrap():
# Phase 1: construct the core classes
def bootstrap_create_class(name, superclass):
return BasicInstance(
globals().get("Class"),
{
"name": name,
"superclass": superclass,
"methods": {},
"attributes": {},
},
)
def bootstrap_create_method(name, body):
return BasicInstance(
globals().get("Method"),
{
"name": name,
"body": body,
}
)
def bootstrap_create_attribute(name, default):
return BasicInstance(
globals().get("Attribute"),
{
"name": name,
"default": default,
}
)
global Class, Object, Method, Attribute
Class = bootstrap_create_class('Class', None)
Object = bootstrap_create_class('Object', None)
Method = bootstrap_create_class('Method', Object)
Attribute = bootstrap_create_class('Attribute', Object)
# need to make sure we call this explicitly with the class name during the
# bootstrap, since we won't have name() yet
python_class_for(Class, 'Class')
python_class_for(Object, 'Object')
python_class_for(Method, 'Method')
python_class_for(Attribute, 'Attribute')
Class.__class__ = python_class_for(Class)
Object.__class__ = python_class_for(Class)
Method.__class__ = python_class_for(Class)
Attribute.__class__ = python_class_for(Class)
# Phase 2: tie the knot
Class.metaclass = Class
Class.slots["superclass"] = Object
# Phase 3: manually assemble enough scaffolding to allow object construction
# this add_method implementation is temporary, since it touches the slots
# directly and fiddles with method.__class__ and such - once the full mop
# is complete, we won't need to do those things (and they might even be the
# wrong things to do), so we will replace it at the end
def add_method(self, method):
name = method.slots["name"]
self.slots["methods"][name] = method
method.__class__ = python_class_for(Method)
python_install_method(self, name, method)
method_add_method = bootstrap_create_method(
"add_method", add_method
)
Class.slots["methods"]["add_method"] = method_add_method
method_add_method.__class__ = python_class_for(Method)
python_install_method(Class, "add_method", method_add_method)
# same here
def execute(self, invocant, args, kwargs):
return execute_method(self.slots["body"], invocant, args, kwargs)
method_execute = bootstrap_create_method(
"execute", execute
)
Method.slots["methods"]["execute"] = method_execute
method_execute.__class__ = python_class_for(Method)
# note: not using python_install_method here, since that installs a method
# which calls method.execute, and this is where we have the recursion base
# case
setattr(python_class_for(Method), "execute", method_execute.slots["body"])
# temporary, we'll have a better version later
def gen_reader(name):
return lambda self: self.slots[name]
# mro needs superclass
Class.add_method(bootstrap_create_method(
"superclass", gen_reader("superclass")
))
# all_attributes requires mro
def mro(self):
mro = [ self ]
parent = self.superclass()
if parent:
mro.extend(parent.mro())
return mro
Class.add_method(bootstrap_create_method(
"mro", mro
))
# all_attributes requires local_attributes
Class.add_method(bootstrap_create_method(
"local_attributes", gen_reader("attributes")
))
# create_instance requires all_attributes
def all_attributes(self):
attributes = {}
for c in reversed(self.mro()):
attributes.update(c.local_attributes())
return attributes
Class.add_method(bootstrap_create_method(
"all_attributes", all_attributes
))
# default_for_instance requires default
Attribute.add_method(bootstrap_create_method(
"default", gen_reader("default")
))
# create_instance requires default_for_instance
def default_for_instance(self):
default = self.default()
if callable(default):
default = default()
return default
Attribute.add_method(bootstrap_create_method(
"default_for_instance", default_for_instance
))
# set_value requires name
Attribute.add_method(bootstrap_create_method(
"name", gen_reader("name")
))
# create_instance requires set_value
def set_value(self, instance, new_value):
instance.slots[self.name()] = new_value
Attribute.add_method(bootstrap_create_method(
name="set_value", body=set_value
))
# new requires create_instance
def create_instance(self, kwargs):
instance = BasicInstance(self, {})
instance.__class__ = python_class_for(self)
attrs = self.all_attributes()
for attr_name in attrs:
attr = attrs[attr_name]
if attr_name in kwargs.keys():
attr.set_value(instance, kwargs[attr_name])
else:
attr.set_value(instance, attr.default_for_instance())
return instance
Class.add_method(bootstrap_create_method(
"create_instance", create_instance
))
# using __call__ in order to make the syntax look like normal python
# constructors, but this is really just a normal static method, it's still
# not anything python-specific
def new(self, **kwargs):
return self.create_instance(kwargs)
Class.add_method(bootstrap_create_method(
"__call__", new
))
# Phase 4: Object construction works, just need attributes to construct with
def add_attribute(self, attr):
self.local_attributes()[attr.name()] = attr
Class.add_method(bootstrap_create_method(
"add_attribute", add_attribute
))
attr_name = bootstrap_create_attribute("name", None)
attr_name.__class__ = python_class_for(Attribute)
Attribute.add_attribute(attr_name)
attr_default = bootstrap_create_attribute("default", None)
attr_default.__class__ = python_class_for(Attribute)
Attribute.add_attribute(attr_default)
# and now object creation works! add the method attributes now to allow
# creating method objects
Method.add_attribute(Attribute(name="name"))
Method.add_attribute(Attribute(name="body"))
# Phase 5: now we can populate the rest of the mop
def value(self, instance):
return instance.slots[self.name()]
Attribute.add_method(Method(
name="value", body=value
))
# here's the better implementation
# we'll replace the accessors generated by the earlier implementation later
def gen_reader(name):
return lambda self: self.metaclass.all_attributes()[name].value(self)
Method.add_method(Method(
name="name", body=gen_reader("name")
))
Method.add_method(Method(
name="body", body=gen_reader("body")
))
Class.add_attribute(Attribute(name="name"))
Class.add_attribute(Attribute(name="superclass"))
Class.add_attribute(Attribute(name="attributes", default=lambda: {}))
Class.add_attribute(Attribute(name="methods", default=lambda: {}))
Class.add_method(Method(
name="name", body=gen_reader("name")
))
Class.add_method(Method(
name="local_methods", body=gen_reader("methods")
))
def all_methods(self):
methods = {}
for c in reversed(self.mro()):
methods.update(c.local_methods())
return methods
Class.add_method(Method(
name="all_methods", body=all_methods
))
Class.add_method(Method(
name="attribute_class", body=lambda self: Attribute
))
Class.add_method(Method(
name="method_class", body=lambda self: Method
))
Class.add_method(Method(
name="base_object_class", body=lambda self: Object
))
def finalize(self):
for method in self.all_methods().values():
python_install_method(self, method.name(), method)
Class.add_method(Method(
name="finalize", body=finalize
))
def isa(self, other):
mro = self.metaclass.mro()
return other in mro
Object.add_method(Method(
name="isa", body=isa
))
def can(self, method_name):
return self.metaclass.all_methods().get(method_name)
Object.add_method(Method(
name="can", body=can
))
# Phase 6: now we have to clean up after ourselves
def add_method(self, method):
self.local_methods()[method.name()] = method
Class.add_method(Method(
name="add_method", body=add_method
))
Class.finalize()
Object.finalize()
Attribute.finalize()
# we can't call Method.finalize(), since that would overwrite our base
# implementation of Method.execute and lead to infinite recursion
for method in Object.local_methods().values():
python_install_method(Method, method.name(), method)
# we add the better version of Method.execute to the internal method map,
# but we don't actually install it. this way, all method subclasses will
# use this implementation, but the base Method class will not (which is
# safe because we know exactly how the base Method class is implemented)
def execute(self, invocant, args, kwargs):
body = self.metaclass.all_attributes()["body"].value(self)
return execute_method(body, invocant, args, kwargs)
Method.add_method(Method(
name="execute", body=execute
))
# do the same thing with accessor methods that we installed with our
# temporary version of gen_reader
Class.add_method(Method(
name="superclass", body=gen_reader("superclass")
))
Class.add_method(Method(
name="local_attributes", body=gen_reader("attributes")
))
Attribute.add_method(Method(
name="default", body=gen_reader("default")
))
Attribute.add_method(Method(
name="name", body=gen_reader("name")
))
bootstrap()