# used for filtering out 'builtin' methods upon dir() of custom class
std_class_methods = dir(object)
std_instance_methods = dir(object())
# ------------- Components -------------------------------
class _ComponentAccessor(object):
def __init__(self):
# flags to prevent infinite recursion during obj '__get__'
self._breaks = {}
def __get__(self, instance, owner):
# if an 'instance' is passed
# return dict of {component attr: component value}
# else use 'owner'
# return dict of {component attr: component obj}
if instance is None:
obj = owner
else:
obj = instance
if self._get_break(obj):
return None
self._set_break(obj)
_d = {}
attrs = [a for a in dir(owner) if a not in std_class_methods]
for attr in attrs:
component = getattr(owner, attr)
if getattr(component, '_is_component', False):
_d[attr] = getattr(obj, attr)
self._release_break(obj)
return _d
def __set__(self, instance, value):
raise ValueError("Cannot change a '_ComponentAccessor'")
def __delete__(self, instance, value):
raise ValueError("Cannot delete a '_ComponentAccessor'")
def _get_break(self, obj):
return self._breaks.get(id(obj), False)
def _set_break(self, obj):
self._breaks[id(obj)] = True
def _release_break(self, obj):
self._breaks[id(obj)] = False
class ClassWithComponents(object):
""" inherit from this to provide component introspection """
# introspection flag
_utilizes_components = True
# introspection 'property' (read only) that will return
# the class' current Components
components = _ComponentAccessor()
class Component(object):
""" the data descriptor to add to a class """
_is_component = True
def __init__(self, type=None):
""" type - verify component is set to an instance
of 'type' when assigned
"""
self._type = type
self._cache = {} # TODO: make into weakref dict
def __get__(self, instance, owner):
if instance is None:
return self
return self._cache.get(id(instance), None)
def __set__(self, instance, value):
# check value against self._type
if value is not None:
if self._type is not None and not isinstance(value, self._type):
raise TypeError("Invalid type '%s' for component! Needs to be '%s'" %\
(value.__class__.__name__, self._type.__name__,))
self._cache[id(instance)] = value
def __delete__(self, instance):
del(self._cache[id(instance)])
# ------------- /Components ------------------------------
# ------------- Abilities --------------------------------
class _Ability(object):
def __init__(self, method_name, *bound_methods):
self.name = method_name
# TODO: could look at method # of args, etc...
self._methods = []
for bm in bound_methods:
self.append(bm)
def __str__(self):
return self.name
def __call__(self, *args, **kwargs):
if len(self._methods) == 0:
return None
elif len(self._methods) == 1:
# only 1 method, return its return value
return self._methods[0](*args, **kwargs)
else:
# call all of our methods in order, no return value
for m in self._methods:
m(*args, **kwargs)
def __add__(self, other):
if not isinstance(other, _Ability):
raise TypeError("Can only add _Abilities together!")
if other.name != self.name:
raise ValueError("Abilities have mismatched names!")
meth_list = self._methods + other._methods
return _Ability(self.name, *meth_list)
def append(self, bound_method):
if bound_method in self._methods:
raise ValueError("Cannot add duplicate methods to ability!")
self._methods.append(bound_method)
class _AbilityAccessor(tuple):
""" placeholder object for ability methods """
def __new__(cls, abilities=None):
if abilities is None:
abilities = []
# check for duplicate ability.names combine them
_ab_d = {}
for abil in abilities:
if abil.name not in _ab_d:
_ab_d[abil.name] = []
_ab_d[abil.name].append(abil)
abilities = []
for name, ab_list in _ab_d.items():
abil = ab_list.pop(0)
for ab in ab_list:
abil += ab
abilities.append(abil)
return tuple.__new__(cls, abilities)
def __init__(self, abilities=None):
tuple.__init__(self)
# create a method on self to pass call onto ability
# ignore passed in 'abilities' arg, use self
for ab in self:
object.__setattr__(self, ab.name, ab)
def __add__(self, other):
if isinstance(other, _AbilityAccessor):
abs = tuple.__add__(self, other)
elif isinstance(other, _Ability):
abs = []
abs.extend(self)
abs.append(other)
else:
raise TypeError("Can only add to other _AbilityAccessors!")
return _AbilityAccessor(abs)
class _AbilityLookup(object):
def __init__(self):
# dict of flags to prevent infinite recursion during '__get__'
self._breaks = {} # id(obj) -> True/False
def __get__(self, instance, owner):
# if an 'instance' is passed
# return dict of {ability attr: [ability value]}
# else use 'owner'
# return dict of {ability attr: ability obj}
_d = {}
if instance is None:
if self._get_break(owner):
return None
self._set_break(owner)
attrs = [a for a in dir(owner) if a not in std_class_methods]
for attr in attrs:
obj = getattr(owner, attr)
if getattr(obj, '_is_ability_method', False):
_d[attr] = obj
self._release_break(owner)
return _d
else:
if self._get_break(instance):
return None
self._set_break(instance)
ability_accessor = _AbilityAccessor()
attrs = [a for a in dir(instance) if a not in std_instance_methods]
for attr in attrs:
obj = getattr(instance, attr)
# check if the obj is an ability method
if getattr(obj, '_is_ability_method', False):
ability_accessor += _Ability(attr, obj)
# else check if the obj is a class with abilities
elif getattr(obj, '_utilizes_abilities', False):
# get that obj's abilities and extend ours
ability_accessor += obj.abilities
self._release_break(instance)
return ability_accessor
def __set__(self, instance, value):
raise ValueError("Cannot change an '_AbilityLookup'")
def __delete__(self, instance, value):
raise ValueError("Cannot delete an '_AbilityLookup'")
def _get_break(self, obj):
return self._breaks.get(id(obj), False)
def _set_break(self, obj):
self._breaks[id(obj)] = True
def _release_break(self, obj):
self._breaks[id(obj)] = False
def abilitymethod(func):
""" method decorator to mark it as an 'abilitymethod' """
# check if func has an 'invalid' name
if func.func_name in dir(_AbilityAccessor):
raise ValueError("Invalid name for an abilitymethod! '%s'" %\
(func.func_name,))
func._is_ability_method = True
return func
class ClassWithAbilities(object):
""" inherit from this to provide ability introspection """
_utilizes_abilities = True
abilities = _AbilityLookup()
# ------------- /Abilities -------------------------------
if __name__ == "__main__":
class RobotFirmware(ClassWithAbilities):
@abilitymethod
def power_on(self):
print 'RobotFirmware.power_on'
self.power_on_checks()
def power_on_checks(self):
""" demonstrates object encapsulation of methods """
print 'RobotFirmware.power_on_checks'
class UpgradedRobotFirmware(RobotFirmware):
@abilitymethod
def laser_eyes(self, wattage):
print "UpgradedRobotFirmware.laser_eyes(%d)" % wattage
class RobotArm(ClassWithAbilities):
@abilitymethod
def power_on(self):
print 'RobotArm.power_on'
@abilitymethod
def bend_girder(self):
print 'RobotArm.bend_girder'
class Robot(ClassWithComponents, ClassWithAbilities):
firmware = Component(RobotFirmware)
arm = Component()
@abilitymethod
def power_on(self):
print 'Robot.power_on'
def kill_all_humans(self):
""" demonstrates a method that components didn't take over """
print 'Robot.kill_all_humans'
print '-- Components --------'
print 'Robot.components:', Robot.components
r = Robot()
print 'r.components: ', r.components
print '\tuploading firmware to robot...'
try:
r.firmware = RobotArm()
except Exception, e:
print "\tMALFUNCTION! %s" % (e,)
print '\tuploading firmware to robot try #2...'
r.firmware = RobotFirmware()
print '\t...success'
print 'r.components: ', r.components
del(r)
print '----------------------'
print ''
print '-- Abilities ---------'
print "Robot.abilities:", Robot.abilities
r = Robot()
print "r.abilities: ", r.abilities
print "r.abilities (by name): ", [ab.name for ab in r.abilities]
print '\tuploading firmware to robot...'
r.firmware = RobotFirmware()
print "r.abilities (by name): ", [ab.name for ab in r.abilities]
print "r.firmware.abilities: ", r.firmware.abilities
print '\tattaching arm to robot...'
r.arm = RobotArm()
print "r.abilities (by name): ", [ab.name for ab in r.abilities]
print "\twhat abilities does 'UpgradedRobotFirmware' provide?"
print "\t", [x for x in UpgradedRobotFirmware.abilities]
print "\tOooh laser eyes! upgrading...",
r.firmware = UpgradedRobotFirmware()
print "...done"
print "\tPowering on..."
r.abilities.power_on()
print "\tTesting out laser eyes"
r.abilities.laser_eyes(300)
print "\tDeleting firmware"
r.firmware = None
print "\tPowering on..."
r.abilities.power_on()
print '----------------------'