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