1
# -*- test-case-name: epsilon.test.test_modes -*-
5
class ModalMethod(object):
6
"""A descriptor wrapping multiple implementations of a particular method.
8
When called on an instance, the implementation used will be
9
selected based on an attribute of the instance. There are no
10
unbound ModalMethods at this point.
12
@ivar name: The name of this method.
13
@ivar methods: A mapping of modes to callable objects.
15
@ivar modeAttribute: The name of the attribute on instances which
16
is bound to the instance's current mode.
19
def __init__(self, name, methods, modeAttribute):
21
self.methods = methods
22
self.modeAttribute = modeAttribute
24
def __get__(self, instance, owner):
26
raise AttributeError(self.name)
28
mode = getattr(instance, self.modeAttribute)
29
except AttributeError:
31
"Mode attribute %r missing from %r, "
32
"cannot get %r" % (self.modeAttribute, instance, self.name))
35
func = self.methods[mode]
38
"Method %r missing from mode %r on %r" % (self.name, mode, instance))
40
return new.instancemethod(func, instance, owner)
44
Base class for mode definitions. Subclass this in classes of type
45
ModalType and provide the implementations of various methods for
46
that particular mode as methods of the mode subclass. The
47
subclass should have the same name as the mode it is defining.
50
# XXX fix the simple, but wrong, __dict__ magic in ModalType.__new__ so
51
# that this __enter__ and __exit__ are actually called, maybe we can even
52
# do some logging or something.
56
The mode has just been exited.
61
The mode has just been entered.
64
def _getInheritedAttribute(classname, attrname, bases, attrs):
66
return attrs[attrname]
70
return _getInheritedAttribute(classname, attrname,
76
raise TypeError('%r does not define required attribute %r' %
82
class ModalType(type):
83
"""Metaclass for defining modal classes.
85
@type modeAttribute: C{str}
86
@ivar modeAttribute: The attribute to which the current mode is
87
bound. Classes should not define the attribute this names; it
88
will be bound automatically to the value of initialMode.
90
@type initialMode: C{str} (for now)
91
@ivar initialMode: The mode in which instances will start.
93
def __new__(cls, name, bases, attrs):
94
modeAttribute = _getInheritedAttribute(name, 'modeAttribute', bases, attrs)
95
initialMode = attrs['initialMode'] = _getInheritedAttribute(name, 'initialMode', bases, attrs)
97
# Dict mapping names of methods to another dict. The inner
98
# dict maps names of modes to implementations of that method
102
keepAttrs = {'mode': initialMode}
103
for (k, v) in attrs.iteritems():
104
if isinstance(v, type) and issubclass(v, mode):
105
for (methName, methDef) in v.__dict__.iteritems():
106
if methName not in ('__module__', '__file__', '__name__'):
107
implementations.setdefault(methName, {})[k] = methDef
110
for (methName, methDefs) in implementations.iteritems():
111
keepAttrs[methName] = ModalMethod(methName, methDefs, modeAttribute)
113
return super(ModalType, cls).__new__(cls, name, bases, keepAttrs)
117
__metaclass__ = ModalType
118
modeAttribute = 'mode'
127
def transitionTo(self, stateName):
129
self.mode = stateName