[8] | 1 | # -*- test-case-name: wokkel.test.test_compat -*- |
---|
| 2 | # |
---|
[96] | 3 | # Copyright (c) Twisted Matrix Laboratories. |
---|
[8] | 4 | # See LICENSE for details. |
---|
| 5 | |
---|
[160] | 6 | """ |
---|
| 7 | Compatibility module to provide backwards compatibility with Twisted features. |
---|
| 8 | """ |
---|
| 9 | |
---|
| 10 | __all__ = ['BootstrapMixin', 'XmlStreamServerFactory', 'IQ', |
---|
| 11 | 'NamedConstant', 'ValueConstant', 'Names', 'Values'] |
---|
| 12 | |
---|
| 13 | from itertools import count |
---|
| 14 | |
---|
[165] | 15 | from twisted.python.deprecate import deprecatedModuleAttribute |
---|
| 16 | from twisted.python.versions import Version |
---|
[34] | 17 | from twisted.words.protocols.jabber import xmlstream |
---|
[165] | 18 | from twisted.words.protocols.jabber.xmlstream import XmlStreamServerFactory |
---|
| 19 | from twisted.words.xish.xmlstream import BootstrapMixin |
---|
[8] | 20 | |
---|
[165] | 21 | deprecatedModuleAttribute( |
---|
| 22 | Version("Wokkel", 0, 7, 0), |
---|
| 23 | "Use twisted.words.xish.xmlstream.BootstrapMixin instead.", |
---|
| 24 | __name__, |
---|
| 25 | "BootstrapMixin") |
---|
[8] | 26 | |
---|
[165] | 27 | deprecatedModuleAttribute( |
---|
| 28 | Version("Wokkel", 0, 7, 0), |
---|
| 29 | "Use twisted.words.protocols.jabber.xmlstream.XmlStreamServerFactory " |
---|
| 30 | "instead.", |
---|
| 31 | __name__, |
---|
| 32 | "XmlStreamServerFactory") |
---|
[63] | 33 | |
---|
| 34 | class IQ(xmlstream.IQ): |
---|
| 35 | def __init__(self, *args, **kwargs): |
---|
| 36 | # Make sure we have a reactor parameter |
---|
| 37 | try: |
---|
| 38 | reactor = kwargs['reactor'] |
---|
| 39 | except KeyError: |
---|
| 40 | from twisted.internet import reactor |
---|
| 41 | kwargs['reactor'] = reactor |
---|
| 42 | |
---|
| 43 | # Check if IQ's init accepts the reactor parameter |
---|
| 44 | try: |
---|
| 45 | xmlstream.IQ.__init__(self, *args, **kwargs) |
---|
| 46 | except TypeError: |
---|
| 47 | # Guess not. Remove the reactor parameter and try again. |
---|
| 48 | del kwargs['reactor'] |
---|
| 49 | xmlstream.IQ.__init__(self, *args, **kwargs) |
---|
| 50 | |
---|
| 51 | # Patch the XmlStream instance so that it has a _callLater |
---|
| 52 | self._xmlstream._callLater = reactor.callLater |
---|
[160] | 53 | |
---|
| 54 | |
---|
| 55 | |
---|
| 56 | _unspecified = object() |
---|
| 57 | _constantOrder = count().next |
---|
| 58 | |
---|
| 59 | |
---|
| 60 | class _Constant(object): |
---|
| 61 | """ |
---|
| 62 | @ivar _index: A C{int} allocated from a shared counter in order to keep |
---|
| 63 | track of the order in which L{_Constant}s are instantiated. |
---|
| 64 | |
---|
| 65 | @ivar name: A C{str} giving the name of this constant; only set once the |
---|
| 66 | constant is initialized by L{_ConstantsContainer}. |
---|
| 67 | |
---|
| 68 | @ivar _container: The L{_ConstantsContainer} subclass this constant belongs |
---|
| 69 | to; only set once the constant is initialized by that subclass. |
---|
[165] | 70 | |
---|
| 71 | @since: Twisted 12.0.0. |
---|
[160] | 72 | """ |
---|
| 73 | def __init__(self): |
---|
| 74 | self._index = _constantOrder() |
---|
| 75 | |
---|
| 76 | |
---|
| 77 | def __get__(self, oself, cls): |
---|
| 78 | """ |
---|
| 79 | Ensure this constant has been initialized before returning it. |
---|
| 80 | """ |
---|
| 81 | cls._initializeEnumerants() |
---|
| 82 | return self |
---|
| 83 | |
---|
| 84 | |
---|
| 85 | def __repr__(self): |
---|
| 86 | """ |
---|
| 87 | Return text identifying both which constant this is and which collection |
---|
| 88 | it belongs to. |
---|
| 89 | """ |
---|
| 90 | return "<%s=%s>" % (self._container.__name__, self.name) |
---|
| 91 | |
---|
| 92 | |
---|
| 93 | def _realize(self, container, name, value): |
---|
| 94 | """ |
---|
| 95 | Complete the initialization of this L{_Constant}. |
---|
| 96 | |
---|
| 97 | @param container: The L{_ConstantsContainer} subclass this constant is |
---|
| 98 | part of. |
---|
| 99 | |
---|
| 100 | @param name: The name of this constant in its container. |
---|
| 101 | |
---|
| 102 | @param value: The value of this constant; not used, as named constants |
---|
| 103 | have no value apart from their identity. |
---|
| 104 | """ |
---|
| 105 | self._container = container |
---|
| 106 | self.name = name |
---|
| 107 | |
---|
| 108 | |
---|
| 109 | |
---|
| 110 | class _EnumerantsInitializer(object): |
---|
| 111 | """ |
---|
| 112 | L{_EnumerantsInitializer} is a descriptor used to initialize a cache of |
---|
| 113 | objects representing named constants for a particular L{_ConstantsContainer} |
---|
| 114 | subclass. |
---|
[165] | 115 | |
---|
| 116 | @since: Twisted 12.0.0. |
---|
[160] | 117 | """ |
---|
| 118 | def __get__(self, oself, cls): |
---|
| 119 | """ |
---|
| 120 | Trigger the initialization of the enumerants cache on C{cls} and then |
---|
| 121 | return it. |
---|
| 122 | """ |
---|
| 123 | cls._initializeEnumerants() |
---|
| 124 | return cls._enumerants |
---|
| 125 | |
---|
| 126 | |
---|
| 127 | |
---|
| 128 | class _ConstantsContainer(object): |
---|
| 129 | """ |
---|
| 130 | L{_ConstantsContainer} is a class with attributes used as symbolic |
---|
| 131 | constants. It is up to subclasses to specify what kind of constants are |
---|
| 132 | allowed. |
---|
| 133 | |
---|
| 134 | @cvar _constantType: Specified by a L{_ConstantsContainer} subclass to |
---|
| 135 | specify the type of constants allowed by that subclass. |
---|
| 136 | |
---|
| 137 | @cvar _enumerantsInitialized: A C{bool} tracking whether C{_enumerants} has |
---|
| 138 | been initialized yet or not. |
---|
| 139 | |
---|
| 140 | @cvar _enumerants: A C{dict} mapping the names of constants (eg |
---|
| 141 | L{NamedConstant} instances) found in the class definition to those |
---|
| 142 | instances. This is initialized via the L{_EnumerantsInitializer} |
---|
| 143 | descriptor the first time it is accessed. |
---|
[165] | 144 | |
---|
| 145 | @since: Twisted 12.0.0. |
---|
[160] | 146 | """ |
---|
| 147 | _constantType = None |
---|
| 148 | |
---|
| 149 | _enumerantsInitialized = False |
---|
| 150 | _enumerants = _EnumerantsInitializer() |
---|
| 151 | |
---|
| 152 | def __new__(cls): |
---|
| 153 | """ |
---|
| 154 | Classes representing constants containers are not intended to be |
---|
| 155 | instantiated. |
---|
| 156 | |
---|
| 157 | The class object itself is used directly. |
---|
| 158 | """ |
---|
| 159 | raise TypeError("%s may not be instantiated." % (cls.__name__,)) |
---|
| 160 | |
---|
| 161 | |
---|
| 162 | def _initializeEnumerants(cls): |
---|
| 163 | """ |
---|
| 164 | Find all of the L{NamedConstant} instances in the definition of C{cls}, |
---|
| 165 | initialize them with constant values, and build a mapping from their |
---|
| 166 | names to them to attach to C{cls}. |
---|
| 167 | """ |
---|
| 168 | if not cls._enumerantsInitialized: |
---|
| 169 | constants = [] |
---|
| 170 | for (name, descriptor) in cls.__dict__.iteritems(): |
---|
| 171 | if isinstance(descriptor, cls._constantType): |
---|
| 172 | constants.append((descriptor._index, name, descriptor)) |
---|
| 173 | enumerants = {} |
---|
| 174 | for (index, enumerant, descriptor) in constants: |
---|
| 175 | value = cls._constantFactory(enumerant) |
---|
| 176 | descriptor._realize(cls, enumerant, value) |
---|
| 177 | enumerants[enumerant] = descriptor |
---|
| 178 | # Replace the _enumerants descriptor with the result so future |
---|
| 179 | # access will go directly to the values. The _enumerantsInitialized |
---|
| 180 | # flag is still necessary because NamedConstant.__get__ may also |
---|
| 181 | # call this method. |
---|
| 182 | cls._enumerants = enumerants |
---|
| 183 | cls._enumerantsInitialized = True |
---|
| 184 | _initializeEnumerants = classmethod(_initializeEnumerants) |
---|
| 185 | |
---|
| 186 | |
---|
| 187 | def _constantFactory(cls, name): |
---|
| 188 | """ |
---|
| 189 | Construct the value for a new constant to add to this container. |
---|
| 190 | |
---|
| 191 | @param name: The name of the constant to create. |
---|
| 192 | |
---|
| 193 | @return: L{NamedConstant} instances have no value apart from identity, |
---|
| 194 | so return a meaningless dummy value. |
---|
| 195 | """ |
---|
| 196 | return _unspecified |
---|
| 197 | _constantFactory = classmethod(_constantFactory) |
---|
| 198 | |
---|
| 199 | |
---|
| 200 | def lookupByName(cls, name): |
---|
| 201 | """ |
---|
| 202 | Retrieve a constant by its name or raise a C{ValueError} if there is no |
---|
| 203 | constant associated with that name. |
---|
| 204 | |
---|
| 205 | @param name: A C{str} giving the name of one of the constants defined by |
---|
| 206 | C{cls}. |
---|
| 207 | |
---|
| 208 | @raise ValueError: If C{name} is not the name of one of the constants |
---|
| 209 | defined by C{cls}. |
---|
| 210 | |
---|
| 211 | @return: The L{NamedConstant} associated with C{name}. |
---|
| 212 | """ |
---|
| 213 | if name in cls._enumerants: |
---|
| 214 | return getattr(cls, name) |
---|
| 215 | raise ValueError(name) |
---|
| 216 | lookupByName = classmethod(lookupByName) |
---|
| 217 | |
---|
| 218 | |
---|
| 219 | def iterconstants(cls): |
---|
| 220 | """ |
---|
| 221 | Iteration over a L{Names} subclass results in all of the constants it |
---|
| 222 | contains. |
---|
| 223 | |
---|
| 224 | @return: an iterator the elements of which are the L{NamedConstant} |
---|
| 225 | instances defined in the body of this L{Names} subclass. |
---|
| 226 | """ |
---|
| 227 | constants = cls._enumerants.values() |
---|
| 228 | constants.sort(key=lambda descriptor: descriptor._index) |
---|
| 229 | return iter(constants) |
---|
| 230 | iterconstants = classmethod(iterconstants) |
---|
| 231 | |
---|
| 232 | |
---|
| 233 | |
---|
| 234 | class NamedConstant(_Constant): |
---|
| 235 | """ |
---|
| 236 | L{NamedConstant} defines an attribute to be a named constant within a |
---|
| 237 | collection defined by a L{Names} subclass. |
---|
| 238 | |
---|
| 239 | L{NamedConstant} is only for use in the definition of L{Names} |
---|
| 240 | subclasses. Do not instantiate L{NamedConstant} elsewhere and do not |
---|
| 241 | subclass it. |
---|
[165] | 242 | |
---|
| 243 | @since: Twisted 12.0.0. |
---|
[160] | 244 | """ |
---|
| 245 | |
---|
| 246 | |
---|
| 247 | |
---|
| 248 | class Names(_ConstantsContainer): |
---|
| 249 | """ |
---|
| 250 | A L{Names} subclass contains constants which differ only in their names and |
---|
| 251 | identities. |
---|
[165] | 252 | |
---|
| 253 | @since: Twisted 12.0.0. |
---|
[160] | 254 | """ |
---|
| 255 | _constantType = NamedConstant |
---|
| 256 | |
---|
| 257 | |
---|
| 258 | |
---|
| 259 | class ValueConstant(_Constant): |
---|
| 260 | """ |
---|
| 261 | L{ValueConstant} defines an attribute to be a named constant within a |
---|
| 262 | collection defined by a L{Values} subclass. |
---|
| 263 | |
---|
| 264 | L{ValueConstant} is only for use in the definition of L{Values} subclasses. |
---|
| 265 | Do not instantiate L{ValueConstant} elsewhere and do not subclass it. |
---|
[165] | 266 | |
---|
| 267 | @since: Twisted 12.0.0. |
---|
[160] | 268 | """ |
---|
| 269 | def __init__(self, value): |
---|
| 270 | _Constant.__init__(self) |
---|
| 271 | self.value = value |
---|
| 272 | |
---|
| 273 | |
---|
| 274 | |
---|
| 275 | class Values(_ConstantsContainer): |
---|
| 276 | """ |
---|
| 277 | A L{Values} subclass contains constants which are associated with arbitrary |
---|
| 278 | values. |
---|
[165] | 279 | |
---|
| 280 | @since: Twisted 12.0.0. |
---|
[160] | 281 | """ |
---|
| 282 | _constantType = ValueConstant |
---|
| 283 | |
---|
| 284 | def lookupByValue(cls, value): |
---|
| 285 | """ |
---|
| 286 | Retrieve a constant by its value or raise a C{ValueError} if there is no |
---|
| 287 | constant associated with that value. |
---|
| 288 | |
---|
| 289 | @param value: The value of one of the constants defined by C{cls}. |
---|
| 290 | |
---|
| 291 | @raise ValueError: If C{value} is not the value of one of the constants |
---|
| 292 | defined by C{cls}. |
---|
| 293 | |
---|
| 294 | @return: The L{ValueConstant} associated with C{value}. |
---|
| 295 | """ |
---|
| 296 | for constant in cls.iterconstants(): |
---|
| 297 | if constant.value == value: |
---|
| 298 | return constant |
---|
| 299 | raise ValueError(value) |
---|
| 300 | lookupByValue = classmethod(lookupByValue) |
---|