Mini Shell
# -*- 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__ = []
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
Zerion Mini Shell 1.0