Viewing file: slotmachine.py (5.78 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# -*- test-case-name: axiom.test.test_slotmachine -*-
hyper = super
_NOSLOT = object()
class Allowed(object): """ An attribute that's allowed to be set. """ def __init__(self, name, default=_NOSLOT): self.name = name self.default = default
def __get__(self, oself, otype=None): if otype is not None and oself is None: return self if self.name in oself.__dict__: return oself.__dict__[self.name] if self.default is not _NOSLOT: return self.default raise AttributeError("%r object did not have attribute %r" %(oself.__class__.__name__, self.name))
def __delete__(self, oself): if self.name not in oself.__dict__: # Returning rather than raising here because that's what # member_descriptor does, and Axiom relies upon that behavior. ## raise AttributeError('%r object has no attribute %r' % ## (oself.__class__.__name__, self.name)) return del oself.__dict__[self.name]
def __set__(self, oself, value): oself.__dict__[self.name] = value
class _SlotMetaMachine(type): def __new__(meta, name, bases, dictionary): dictionary['__name__'] = name slots = list(meta.determineSchema(dictionary)) for slot in slots: for base in bases: defval = getattr(base, slot, _NOSLOT) if defval is not _NOSLOT: break dictionary[slot] = Allowed(slot, defval) nt = type.__new__(meta, name, bases, dictionary) return nt
def determineSchema(meta, dictionary): return dictionary.get("slots", [])
determineSchema = classmethod(determineSchema)
class DescriptorWithDefault(object): def __init__(self, default, original): self.original = original self.default = default
def __get__(self, oself, type=None): if type is not None: if oself is None: return self.default return getattr(oself, self.original, self.default)
def __set__(self, oself, value): setattr(oself, self.original, value)
def __delete__(self, oself): delattr(oself, self.original)
class Attribute(object): def __init__(self, doc=''): self.doc = doc
def requiredSlots(self, modname, classname, attrname): self.name = attrname yield attrname
def __get__(self, oself, type=None): assert oself is None, "%s: should be masked" % (self.name,) return self
_RAISE = object() class SetOnce(Attribute):
def __init__(self, doc='', default=_RAISE): Attribute.__init__(self) if default is _RAISE: self.default = () else: self.default = (default,)
def requiredSlots(self, modname, classname, attrname): self.name = attrname t = self.trueattr = ('_' + self.name) yield t
def __set__(self, iself, value): if not hasattr(iself, self.trueattr): setattr(iself, self.trueattr, value) else: raise AttributeError('%s.%s may only be set once' % ( type(iself).__name__, self.name))
def __get__(self, iself, type=None): if type is not None and iself is None: return self return getattr(iself, self.trueattr, *self.default)
class SchemaMetaMachine(_SlotMetaMachine):
def determineSchema(meta, dictionary): attrs = dictionary['__attributes__'] = [] name = dictionary['__name__'] moduleName = dictionary['__module__'] dictitems = dictionary.items() dictitems.sort() for k, v in dictitems: if isinstance(v, Attribute): attrs.append((k, v)) for slot in v.requiredSlots(moduleName, name, k): if slot == k: del dictionary[k] yield slot
determineSchema = classmethod(determineSchema)
class _Strict(object): """ I disallow all attributes from being set that do not have an explicit data descriptor. """
def __setattr__(self, name, value): """ Like PyObject_GenericSetAttr, but call descriptors only. """ try: allowed = type(self).__dict__['_Strict__setattr__allowed'] except KeyError: allowed = type(self)._Strict__setattr__allowed = {} for cls in type(self).__mro__: for attrName, slot in cls.__dict__.iteritems(): if attrName in allowed: # It was found earlier in the mro, overriding # whatever this is. Ignore it and move on. continue setter = getattr(slot, '__set__', _NOSLOT) if setter is not _NOSLOT: # It is a data descriptor, so remember the setter # for it in the cache. allowed[attrName] = setter else: # It is something else, so remember None for it in # the cache to indicate it cannot have its value # set. allowed[attrName] = None
try: setter = allowed[name] except KeyError: pass else: if setter is not None: setter(self, value) return
# It wasn't found in the setter cache or it was found to be None, # indicating a non-data descriptor which cannot be set. raise AttributeError( "%r can't set attribute %r" % (self.__class__.__name__, name))
class SchemaMachine(_Strict): __metaclass__ = SchemaMetaMachine
class SlotMachine(_Strict): __metaclass__ = _SlotMetaMachine
|