|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- # -*- test-case-name: constantly.test.test_constants -*-
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
-
- """
- Symbolic constant support, including collections and constants with text,
- numeric, and bit flag values.
- """
-
- from __future__ import division, absolute_import
-
- __all__ = [
- 'NamedConstant', 'ValueConstant', 'FlagConstant',
- 'Names', 'Values', 'Flags']
-
- from functools import partial
- from itertools import count
- from operator import and_, or_, xor
-
- _unspecified = object()
- _constantOrder = partial(next, count())
-
-
- class _Constant(object):
- """
- @ivar _index: A C{int} allocated from a shared counter in order to keep
- track of the order in which L{_Constant}s are instantiated.
-
- @ivar name: A C{str} giving the name of this constant; only set once the
- constant is initialized by L{_ConstantsContainer}.
-
- @ivar _container: The L{_ConstantsContainer} subclass this constant belongs
- to; C{None} until the constant is initialized by that subclass.
- """
- def __init__(self):
- self._container = None
- self._index = _constantOrder()
-
-
- def __repr__(self):
- """
- Return text identifying both which constant this is and which
- collection it belongs to.
- """
- return "<%s=%s>" % (self._container.__name__, self.name)
-
-
- def __lt__(self, other):
- """
- Implements C{<}. Order is defined by instantiation order.
-
- @param other: An object.
-
- @return: C{NotImplemented} if C{other} is not a constant belonging to
- the same container as this constant, C{True} if this constant is
- defined before C{other}, otherwise C{False}.
- """
- if (
- not isinstance(other, self.__class__) or
- not self._container == other._container
- ):
- return NotImplemented
- return self._index < other._index
-
-
- def __le__(self, other):
- """
- Implements C{<=}. Order is defined by instantiation order.
-
- @param other: An object.
-
- @return: C{NotImplemented} if C{other} is not a constant belonging to
- the same container as this constant, C{True} if this constant is
- defined before or equal to C{other}, otherwise C{False}.
- """
- if (
- not isinstance(other, self.__class__) or
- not self._container == other._container
- ):
- return NotImplemented
- return self is other or self._index < other._index
-
-
- def __gt__(self, other):
- """
- Implements C{>}. Order is defined by instantiation order.
-
- @param other: An object.
-
- @return: C{NotImplemented} if C{other} is not a constant belonging to
- the same container as this constant, C{True} if this constant is
- defined after C{other}, otherwise C{False}.
- """
- if (
- not isinstance(other, self.__class__) or
- not self._container == other._container
- ):
- return NotImplemented
- return self._index > other._index
-
-
- def __ge__(self, other):
- """
- Implements C{>=}. Order is defined by instantiation order.
-
- @param other: An object.
-
- @return: C{NotImplemented} if C{other} is not a constant belonging to
- the same container as this constant, C{True} if this constant is
- defined after or equal to C{other}, otherwise C{False}.
- """
- if (
- not isinstance(other, self.__class__) or
- not self._container == other._container
- ):
- return NotImplemented
- return self is other or self._index > other._index
-
-
- def _realize(self, container, name, value):
- """
- Complete the initialization of this L{_Constant}.
-
- @param container: The L{_ConstantsContainer} subclass this constant is
- part of.
-
- @param name: The name of this constant in its container.
-
- @param value: The value of this constant; not used, as named constants
- have no value apart from their identity.
- """
- self._container = container
- self.name = name
-
-
-
- class _ConstantsContainerType(type):
- """
- L{_ConstantsContainerType} is a metaclass for creating constants container
- classes.
- """
- def __new__(self, name, bases, attributes):
- """
- Create a new constants container class.
-
- If C{attributes} includes a value of C{None} for the C{"_constantType"}
- key, the new class will not be initialized as a constants container and
- it will behave as a normal class.
-
- @param name: The name of the container class.
- @type name: L{str}
-
- @param bases: A tuple of the base classes for the new container class.
- @type bases: L{tuple} of L{_ConstantsContainerType} instances
-
- @param attributes: The attributes of the new container class, including
- any constants it is to contain.
- @type attributes: L{dict}
- """
- cls = super(_ConstantsContainerType, self).__new__(
- self, name, bases, attributes)
-
- # Only realize constants in concrete _ConstantsContainer subclasses.
- # Ignore intermediate base classes.
- constantType = getattr(cls, '_constantType', None)
- if constantType is None:
- return cls
-
- constants = []
- for (name, descriptor) in attributes.items():
- if isinstance(descriptor, cls._constantType):
- if descriptor._container is not None:
- raise ValueError(
- "Cannot use %s as the value of an attribute on %s" % (
- descriptor, cls.__name__))
- constants.append((descriptor._index, name, descriptor))
-
- enumerants = {}
- for (index, enumerant, descriptor) in sorted(constants):
- value = cls._constantFactory(enumerant, descriptor)
- descriptor._realize(cls, enumerant, value)
- enumerants[enumerant] = descriptor
-
- # Save the dictionary which contains *only* constants (distinct from
- # any other attributes the application may have given the container)
- # where the class can use it later (eg for lookupByName).
- cls._enumerants = enumerants
-
- return cls
-
-
-
- # In Python3 metaclasses are defined using a C{metaclass} keyword argument in
- # the class definition. This would cause a syntax error in Python2.
- # So we use L{type} to introduce an intermediate base class with the desired
- # metaclass.
- # See:
- # * http://docs.python.org/2/library/functions.html#type
- # * http://docs.python.org/3/reference/datamodel.html#customizing-class-creation
- class _ConstantsContainer(_ConstantsContainerType('', (object,), {})):
- """
- L{_ConstantsContainer} is a class with attributes used as symbolic
- constants. It is up to subclasses to specify what kind of constants are
- allowed.
-
- @cvar _constantType: Specified by a L{_ConstantsContainer} subclass to
- specify the type of constants allowed by that subclass.
-
- @cvar _enumerants: A C{dict} mapping the names of constants (eg
- L{NamedConstant} instances) found in the class definition to those
- instances.
- """
-
- _constantType = None
-
- def __new__(cls):
- """
- Classes representing constants containers are not intended to be
- instantiated.
-
- The class object itself is used directly.
- """
- raise TypeError("%s may not be instantiated." % (cls.__name__,))
-
-
- @classmethod
- def _constantFactory(cls, name, descriptor):
- """
- Construct the value for a new constant to add to this container.
-
- @param name: The name of the constant to create.
-
- @param descriptor: An instance of a L{_Constant} subclass (eg
- L{NamedConstant}) which is assigned to C{name}.
-
- @return: L{NamedConstant} instances have no value apart from identity,
- so return a meaningless dummy value.
- """
- return _unspecified
-
-
- @classmethod
- def lookupByName(cls, name):
- """
- Retrieve a constant by its name or raise a C{ValueError} if there is no
- constant associated with that name.
-
- @param name: A C{str} giving the name of one of the constants defined
- by C{cls}.
-
- @raise ValueError: If C{name} is not the name of one of the constants
- defined by C{cls}.
-
- @return: The L{NamedConstant} associated with C{name}.
- """
- if name in cls._enumerants:
- return getattr(cls, name)
- raise ValueError(name)
-
-
- @classmethod
- def iterconstants(cls):
- """
- Iteration over a L{Names} subclass results in all of the constants it
- contains.
-
- @return: an iterator the elements of which are the L{NamedConstant}
- instances defined in the body of this L{Names} subclass.
- """
- constants = cls._enumerants.values()
-
- return iter(
- sorted(constants, key=lambda descriptor: descriptor._index))
-
-
-
- class NamedConstant(_Constant):
- """
- L{NamedConstant} defines an attribute to be a named constant within a
- collection defined by a L{Names} subclass.
-
- L{NamedConstant} is only for use in the definition of L{Names}
- subclasses. Do not instantiate L{NamedConstant} elsewhere and do not
- subclass it.
- """
-
-
-
- class Names(_ConstantsContainer):
- """
- A L{Names} subclass contains constants which differ only in their names and
- identities.
- """
- _constantType = NamedConstant
-
-
-
- class ValueConstant(_Constant):
- """
- L{ValueConstant} defines an attribute to be a named constant within a
- collection defined by a L{Values} subclass.
-
- L{ValueConstant} is only for use in the definition of L{Values} subclasses.
- Do not instantiate L{ValueConstant} elsewhere and do not subclass it.
- """
- def __init__(self, value):
- _Constant.__init__(self)
- self.value = value
-
-
-
- class Values(_ConstantsContainer):
- """
- A L{Values} subclass contains constants which are associated with arbitrary
- values.
- """
- _constantType = ValueConstant
-
- @classmethod
- def lookupByValue(cls, value):
- """
- Retrieve a constant by its value or raise a C{ValueError} if there is
- no constant associated with that value.
-
- @param value: The value of one of the constants defined by C{cls}.
-
- @raise ValueError: If C{value} is not the value of one of the constants
- defined by C{cls}.
-
- @return: The L{ValueConstant} associated with C{value}.
- """
- for constant in cls.iterconstants():
- if constant.value == value:
- return constant
- raise ValueError(value)
-
-
-
- def _flagOp(op, left, right):
- """
- Implement a binary operator for a L{FlagConstant} instance.
-
- @param op: A two-argument callable implementing the binary operation. For
- example, C{operator.or_}.
-
- @param left: The left-hand L{FlagConstant} instance.
- @param right: The right-hand L{FlagConstant} instance.
-
- @return: A new L{FlagConstant} instance representing the result of the
- operation.
- """
- value = op(left.value, right.value)
- names = op(left.names, right.names)
- result = FlagConstant()
- result._realize(left._container, names, value)
- return result
-
-
-
- class FlagConstant(_Constant):
- """
- L{FlagConstant} defines an attribute to be a flag constant within a
- collection defined by a L{Flags} subclass.
-
- L{FlagConstant} is only for use in the definition of L{Flags} subclasses.
- Do not instantiate L{FlagConstant} elsewhere and do not subclass it.
- """
- def __init__(self, value=_unspecified):
- _Constant.__init__(self)
- self.value = value
-
-
- def _realize(self, container, names, value):
- """
- Complete the initialization of this L{FlagConstant}.
-
- This implementation differs from other C{_realize} implementations in
- that a L{FlagConstant} may have several names which apply to it, due to
- flags being combined with various operators.
-
- @param container: The L{Flags} subclass this constant is part of.
-
- @param names: When a single-flag value is being initialized, a C{str}
- giving the name of that flag. This is the case which happens when
- a L{Flags} subclass is being initialized and L{FlagConstant}
- instances from its body are being realized. Otherwise, a C{set} of
- C{str} giving names of all the flags set on this L{FlagConstant}
- instance. This is the case when two flags are combined using C{|},
- for example.
- """
- if isinstance(names, str):
- name = names
- names = set([names])
- elif len(names) == 1:
- (name,) = names
- else:
- name = "{" + ",".join(sorted(names)) + "}"
- _Constant._realize(self, container, name, value)
- self.value = value
- self.names = names
-
-
- def __or__(self, other):
- """
- Define C{|} on two L{FlagConstant} instances to create a new
- L{FlagConstant} instance with all flags set in either instance set.
- """
- return _flagOp(or_, self, other)
-
-
- def __and__(self, other):
- """
- Define C{&} on two L{FlagConstant} instances to create a new
- L{FlagConstant} instance with only flags set in both instances set.
- """
- return _flagOp(and_, self, other)
-
-
- def __xor__(self, other):
- """
- Define C{^} on two L{FlagConstant} instances to create a new
- L{FlagConstant} instance with only flags set on exactly one instance
- set.
- """
- return _flagOp(xor, self, other)
-
-
- def __invert__(self):
- """
- Define C{~} on a L{FlagConstant} instance to create a new
- L{FlagConstant} instance with all flags not set on this instance set.
- """
- result = FlagConstant()
- result._realize(self._container, set(), 0)
- for flag in self._container.iterconstants():
- if flag.value & self.value == 0:
- result |= flag
- return result
-
-
- def __iter__(self):
- """
- @return: An iterator of flags set on this instance set.
- """
- return (self._container.lookupByName(name) for name in self.names)
-
-
- def __contains__(self, flag):
- """
- @param flag: The flag to test for membership in this instance
- set.
-
- @return: C{True} if C{flag} is in this instance set, else
- C{False}.
- """
- # Optimization for testing membership without iteration.
- return bool(flag & self)
-
-
- def __nonzero__(self):
- """
- @return: C{False} if this flag's value is 0, else C{True}.
- """
- return bool(self.value)
- __bool__ = __nonzero__
-
-
-
- class Flags(Values):
- """
- A L{Flags} subclass contains constants which can be combined using the
- common bitwise operators (C{|}, C{&}, etc) similar to a I{bitvector} from a
- language like C.
- """
- _constantType = FlagConstant
-
- _value = 1
-
- @classmethod
- def _constantFactory(cls, name, descriptor):
- """
- For L{FlagConstant} instances with no explicitly defined value, assign
- the next power of two as its value.
-
- @param name: The name of the constant to create.
-
- @param descriptor: An instance of a L{FlagConstant} which is assigned
- to C{name}.
-
- @return: Either the value passed to the C{descriptor} constructor, or
- the next power of 2 value which will be assigned to C{descriptor},
- relative to the value of the last defined L{FlagConstant}.
- """
- if descriptor.value is _unspecified:
- value = cls._value
- cls._value <<= 1
- else:
- value = descriptor.value
- cls._value = value << 1
- return value
|