1# -*- test-case-name: twisted.python.test.test_constants -*-
2# Copyright (c) Twisted Matrix Laboratories.
3# See LICENSE for details.
4
5"""
6Symbolic constant support, including collections and constants with text,
7numeric, and bit flag values.
8"""
9
10from __future__ import division, absolute_import
11
12__all__ = [
13    'NamedConstant', 'ValueConstant', 'FlagConstant',
14    'Names', 'Values', 'Flags']
15
16from functools import partial
17from itertools import count
18from operator import and_, or_, xor
19
20_unspecified = object()
21_constantOrder = partial(next, count())
22
23
24class _Constant(object):
25    """
26    @ivar _index: A C{int} allocated from a shared counter in order to keep
27        track of the order in which L{_Constant}s are instantiated.
28
29    @ivar name: A C{str} giving the name of this constant; only set once the
30        constant is initialized by L{_ConstantsContainer}.
31
32    @ivar _container: The L{_ConstantsContainer} subclass this constant belongs
33        to; C{None} until the constant is initialized by that subclass.
34    """
35    def __init__(self):
36        self._container = None
37        self._index = _constantOrder()
38
39
40    def __repr__(self):
41        """
42        Return text identifying both which constant this is and which
43        collection it belongs to.
44        """
45        return "<%s=%s>" % (self._container.__name__, self.name)
46
47
48    def __lt__(self, other):
49        """
50        Implements C{<}.  Order is defined by instantiation order.
51
52        @param other: An object.
53
54        @return: C{NotImplemented} if C{other} is not a constant belonging to
55            the same container as this constant, C{True} if this constant is
56            defined before C{other}, otherwise C{False}.
57        """
58        if (
59            not isinstance(other, self.__class__) or
60            not self._container == other._container
61        ):
62            return NotImplemented
63        return self._index < other._index
64
65
66    def __le__(self, other):
67        """
68        Implements C{<=}.  Order is defined by instantiation order.
69
70        @param other: An object.
71
72        @return: C{NotImplemented} if C{other} is not a constant belonging to
73            the same container as this constant, C{True} if this constant is
74            defined before or equal to C{other}, otherwise C{False}.
75        """
76        if (
77            not isinstance(other, self.__class__) or
78            not self._container == other._container
79        ):
80            return NotImplemented
81        return self is other or self._index < other._index
82
83
84    def __gt__(self, other):
85        """
86        Implements C{>}.  Order is defined by instantiation order.
87
88        @param other: An object.
89
90        @return: C{NotImplemented} if C{other} is not a constant belonging to
91            the same container as this constant, C{True} if this constant is
92            defined after C{other}, otherwise C{False}.
93        """
94        if (
95            not isinstance(other, self.__class__) or
96            not self._container == other._container
97        ):
98            return NotImplemented
99        return self._index > other._index
100
101
102    def __ge__(self, other):
103        """
104        Implements C{>=}.  Order is defined by instantiation order.
105
106        @param other: An object.
107
108        @return: C{NotImplemented} if C{other} is not a constant belonging to
109            the same container as this constant, C{True} if this constant is
110            defined after or equal to C{other}, otherwise C{False}.
111        """
112        if (
113            not isinstance(other, self.__class__) or
114            not self._container == other._container
115        ):
116            return NotImplemented
117        return self is other or self._index > other._index
118
119
120    def _realize(self, container, name, value):
121        """
122        Complete the initialization of this L{_Constant}.
123
124        @param container: The L{_ConstantsContainer} subclass this constant is
125            part of.
126
127        @param name: The name of this constant in its container.
128
129        @param value: The value of this constant; not used, as named constants
130            have no value apart from their identity.
131        """
132        self._container = container
133        self.name = name
134
135
136
137class _ConstantsContainerType(type):
138    """
139    L{_ConstantsContainerType} is a metaclass for creating constants container
140    classes.
141    """
142    def __new__(self, name, bases, attributes):
143        """
144        Create a new constants container class.
145
146        If C{attributes} includes a value of C{None} for the C{"_constantType"}
147        key, the new class will not be initialized as a constants container and
148        it will behave as a normal class.
149
150        @param name: The name of the container class.
151        @type name: L{str}
152
153        @param bases: A tuple of the base classes for the new container class.
154        @type bases: L{tuple} of L{_ConstantsContainerType} instances
155
156        @param attributes: The attributes of the new container class, including
157            any constants it is to contain.
158        @type attributes: L{dict}
159        """
160        cls = super(_ConstantsContainerType, self).__new__(
161            self, name, bases, attributes)
162
163        # Only realize constants in concrete _ConstantsContainer subclasses.
164        # Ignore intermediate base classes.
165        constantType = getattr(cls, '_constantType', None)
166        if constantType is None:
167            return cls
168
169        constants = []
170        for (name, descriptor) in attributes.items():
171            if isinstance(descriptor, cls._constantType):
172                if descriptor._container is not None:
173                    raise ValueError(
174                        "Cannot use %s as the value of an attribute on %s" % (
175                            descriptor, cls.__name__))
176                constants.append((descriptor._index, name, descriptor))
177
178        enumerants = {}
179        for (index, enumerant, descriptor) in sorted(constants):
180            value = cls._constantFactory(enumerant, descriptor)
181            descriptor._realize(cls, enumerant, value)
182            enumerants[enumerant] = descriptor
183
184        # Save the dictionary which contains *only* constants (distinct from
185        # any other attributes the application may have given the container)
186        # where the class can use it later (eg for lookupByName).
187        cls._enumerants = enumerants
188
189        return cls
190
191
192
193# In Python3 metaclasses are defined using a C{metaclass} keyword argument in
194# the class definition. This would cause a syntax error in Python2.
195# So we use L{type} to introduce an intermediate base class with the desired
196# metaclass.
197# See:
198# * http://docs.python.org/2/library/functions.html#type
199# * http://docs.python.org/3/reference/datamodel.html#customizing-class-creation
200class _ConstantsContainer(_ConstantsContainerType('', (object,), {})):
201    """
202    L{_ConstantsContainer} is a class with attributes used as symbolic
203    constants.  It is up to subclasses to specify what kind of constants are
204    allowed.
205
206    @cvar _constantType: Specified by a L{_ConstantsContainer} subclass to
207        specify the type of constants allowed by that subclass.
208
209    @cvar _enumerants: A C{dict} mapping the names of constants (eg
210        L{NamedConstant} instances) found in the class definition to those
211        instances.
212    """
213
214    _constantType = None
215
216    def __new__(cls):
217        """
218        Classes representing constants containers are not intended to be
219        instantiated.
220
221        The class object itself is used directly.
222        """
223        raise TypeError("%s may not be instantiated." % (cls.__name__,))
224
225
226    @classmethod
227    def _constantFactory(cls, name, descriptor):
228        """
229        Construct the value for a new constant to add to this container.
230
231        @param name: The name of the constant to create.
232
233        @param descriptor: An instance of a L{_Constant} subclass (eg
234            L{NamedConstant}) which is assigned to C{name}.
235
236        @return: L{NamedConstant} instances have no value apart from identity,
237            so return a meaningless dummy value.
238        """
239        return _unspecified
240
241
242    @classmethod
243    def lookupByName(cls, name):
244        """
245        Retrieve a constant by its name or raise a C{ValueError} if there is no
246        constant associated with that name.
247
248        @param name: A C{str} giving the name of one of the constants defined
249            by C{cls}.
250
251        @raise ValueError: If C{name} is not the name of one of the constants
252            defined by C{cls}.
253
254        @return: The L{NamedConstant} associated with C{name}.
255        """
256        if name in cls._enumerants:
257            return getattr(cls, name)
258        raise ValueError(name)
259
260
261    @classmethod
262    def iterconstants(cls):
263        """
264        Iteration over a L{Names} subclass results in all of the constants it
265        contains.
266
267        @return: an iterator the elements of which are the L{NamedConstant}
268            instances defined in the body of this L{Names} subclass.
269        """
270        constants = cls._enumerants.values()
271
272        return iter(
273            sorted(constants, key=lambda descriptor: descriptor._index))
274
275
276
277class NamedConstant(_Constant):
278    """
279    L{NamedConstant} defines an attribute to be a named constant within a
280    collection defined by a L{Names} subclass.
281
282    L{NamedConstant} is only for use in the definition of L{Names}
283    subclasses.  Do not instantiate L{NamedConstant} elsewhere and do not
284    subclass it.
285    """
286
287
288
289class Names(_ConstantsContainer):
290    """
291    A L{Names} subclass contains constants which differ only in their names and
292    identities.
293    """
294    _constantType = NamedConstant
295
296
297
298class ValueConstant(_Constant):
299    """
300    L{ValueConstant} defines an attribute to be a named constant within a
301    collection defined by a L{Values} subclass.
302
303    L{ValueConstant} is only for use in the definition of L{Values} subclasses.
304    Do not instantiate L{ValueConstant} elsewhere and do not subclass it.
305    """
306    def __init__(self, value):
307        _Constant.__init__(self)
308        self.value = value
309
310
311
312class Values(_ConstantsContainer):
313    """
314    A L{Values} subclass contains constants which are associated with arbitrary
315    values.
316    """
317    _constantType = ValueConstant
318
319    @classmethod
320    def lookupByValue(cls, value):
321        """
322        Retrieve a constant by its value or raise a C{ValueError} if there is
323        no constant associated with that value.
324
325        @param value: The value of one of the constants defined by C{cls}.
326
327        @raise ValueError: If C{value} is not the value of one of the constants
328            defined by C{cls}.
329
330        @return: The L{ValueConstant} associated with C{value}.
331        """
332        for constant in cls.iterconstants():
333            if constant.value == value:
334                return constant
335        raise ValueError(value)
336
337
338
339def _flagOp(op, left, right):
340    """
341    Implement a binary operator for a L{FlagConstant} instance.
342
343    @param op: A two-argument callable implementing the binary operation.  For
344        example, C{operator.or_}.
345
346    @param left: The left-hand L{FlagConstant} instance.
347    @param right: The right-hand L{FlagConstant} instance.
348
349    @return: A new L{FlagConstant} instance representing the result of the
350        operation.
351    """
352    value = op(left.value, right.value)
353    names = op(left.names, right.names)
354    result = FlagConstant()
355    result._realize(left._container, names, value)
356    return result
357
358
359
360class FlagConstant(_Constant):
361    """
362    L{FlagConstant} defines an attribute to be a flag constant within a
363    collection defined by a L{Flags} subclass.
364
365    L{FlagConstant} is only for use in the definition of L{Flags} subclasses.
366    Do not instantiate L{FlagConstant} elsewhere and do not subclass it.
367    """
368    def __init__(self, value=_unspecified):
369        _Constant.__init__(self)
370        self.value = value
371
372
373    def _realize(self, container, names, value):
374        """
375        Complete the initialization of this L{FlagConstant}.
376
377        This implementation differs from other C{_realize} implementations in
378        that a L{FlagConstant} may have several names which apply to it, due to
379        flags being combined with various operators.
380
381        @param container: The L{Flags} subclass this constant is part of.
382
383        @param names: When a single-flag value is being initialized, a C{str}
384            giving the name of that flag.  This is the case which happens when
385            a L{Flags} subclass is being initialized and L{FlagConstant}
386            instances from its body are being realized.  Otherwise, a C{set} of
387            C{str} giving names of all the flags set on this L{FlagConstant}
388            instance.  This is the case when two flags are combined using C{|},
389            for example.
390        """
391        if isinstance(names, str):
392            name = names
393            names = set([names])
394        elif len(names) == 1:
395            (name,) = names
396        else:
397            name = "{" + ",".join(sorted(names)) + "}"
398        _Constant._realize(self, container, name, value)
399        self.value = value
400        self.names = names
401
402
403    def __or__(self, other):
404        """
405        Define C{|} on two L{FlagConstant} instances to create a new
406        L{FlagConstant} instance with all flags set in either instance set.
407        """
408        return _flagOp(or_, self, other)
409
410
411    def __and__(self, other):
412        """
413        Define C{&} on two L{FlagConstant} instances to create a new
414        L{FlagConstant} instance with only flags set in both instances set.
415        """
416        return _flagOp(and_, self, other)
417
418
419    def __xor__(self, other):
420        """
421        Define C{^} on two L{FlagConstant} instances to create a new
422        L{FlagConstant} instance with only flags set on exactly one instance
423        set.
424        """
425        return _flagOp(xor, self, other)
426
427
428    def __invert__(self):
429        """
430        Define C{~} on a L{FlagConstant} instance to create a new
431        L{FlagConstant} instance with all flags not set on this instance set.
432        """
433        result = FlagConstant()
434        result._realize(self._container, set(), 0)
435        for flag in self._container.iterconstants():
436            if flag.value & self.value == 0:
437                result |= flag
438        return result
439
440
441    def __iter__(self):
442        """
443        @return: An iterator of flags set on this instance set.
444        """
445        return (self._container.lookupByName(name) for name in self.names)
446
447
448    def __contains__(self, flag):
449        """
450        @param flag: The flag to test for membership in this instance
451            set.
452
453        @return: C{True} if C{flag} is in this instance set, else
454            C{False}.
455        """
456        # Optimization for testing membership without iteration.
457        return bool(flag & self)
458
459
460    def __nonzero__(self):
461        """
462        @return: C{False} if this flag's value is 0, else C{True}.
463        """
464        return bool(self.value)
465    __bool__ = __nonzero__
466
467
468
469class Flags(Values):
470    """
471    A L{Flags} subclass contains constants which can be combined using the
472    common bitwise operators (C{|}, C{&}, etc) similar to a I{bitvector} from a
473    language like C.
474    """
475    _constantType = FlagConstant
476
477    _value = 1
478
479    @classmethod
480    def _constantFactory(cls, name, descriptor):
481        """
482        For L{FlagConstant} instances with no explicitly defined value, assign
483        the next power of two as its value.
484
485        @param name: The name of the constant to create.
486
487        @param descriptor: An instance of a L{FlagConstant} which is assigned
488            to C{name}.
489
490        @return: Either the value passed to the C{descriptor} constructor, or
491            the next power of 2 value which will be assigned to C{descriptor},
492            relative to the value of the last defined L{FlagConstant}.
493        """
494        if descriptor.value is _unspecified:
495            value = cls._value
496            cls._value <<= 1
497        else:
498            value = descriptor.value
499            cls._value = value << 1
500        return value
501