Fix the iq.send() function, and a bunch of places where it is called
[slixmpp.git] / slixmpp / xmlstream / stanzabase.py
blob8634250fa0a8414686cd293d0e901e3392e482af
1 # -*- coding: utf-8 -*-
2 """
3 slixmpp.xmlstream.stanzabase
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6 module implements a wrapper layer for XML objects
7 that allows them to be treated like dictionaries.
9 Part of Slixmpp: The Slick XMPP Library
11 :copyright: (c) 2011 Nathanael C. Fritz
12 :license: MIT, see LICENSE for more details
13 """
15 from __future__ import with_statement, unicode_literals
17 import copy
18 import logging
19 import weakref
20 from xml.etree import cElementTree as ET
22 from slixmpp.xmlstream import JID
23 from slixmpp.xmlstream.tostring import tostring
24 from slixmpp.thirdparty import OrderedDict
27 log = logging.getLogger(__name__)
30 # Used to check if an argument is an XML object.
31 XML_TYPE = type(ET.Element('xml'))
34 XML_NS = 'http://www.w3.org/XML/1998/namespace'
37 def register_stanza_plugin(stanza, plugin, iterable=False, overrides=False):
38 """
39 Associate a stanza object as a plugin for another stanza.
41 >>> from slixmpp.xmlstream import register_stanza_plugin
42 >>> register_stanza_plugin(Iq, CustomStanza)
44 Plugin stanzas marked as iterable will be included in the list of
45 substanzas for the parent, using ``parent['substanzas']``. If the
46 attribute ``plugin_multi_attrib`` was defined for the plugin, then
47 the substanza set can be filtered to only instances of the plugin
48 class. For example, given a plugin class ``Foo`` with
49 ``plugin_multi_attrib = 'foos'`` then::
51 parent['foos']
53 would return a collection of all ``Foo`` substanzas.
55 :param class stanza: The class of the parent stanza.
56 :param class plugin: The class of the plugin stanza.
57 :param bool iterable: Indicates if the plugin stanza should be
58 included in the parent stanza's iterable
59 ``'substanzas'`` interface results.
60 :param bool overrides: Indicates if the plugin should be allowed
61 to override the interface handlers for
62 the parent stanza, based on the plugin's
63 ``overrides`` field.
65 .. versionadded:: 1.0-Beta1
66 Made ``register_stanza_plugin`` the default name. The prior
67 ``registerStanzaPlugin`` function name remains as an alias.
68 """
69 tag = "{%s}%s" % (plugin.namespace, plugin.name)
71 # Prevent weird memory reference gotchas by ensuring
72 # that the parent stanza class has its own set of
73 # plugin info maps and is not using the mappings from
74 # an ancestor class (like ElementBase).
75 plugin_info = ('plugin_attrib_map', 'plugin_tag_map',
76 'plugin_iterables', 'plugin_overrides')
77 for attr in plugin_info:
78 info = getattr(stanza, attr)
79 setattr(stanza, attr, info.copy())
81 stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
82 stanza.plugin_tag_map[tag] = plugin
84 if iterable:
85 stanza.plugin_iterables.add(plugin)
86 if plugin.plugin_multi_attrib:
87 multiplugin = multifactory(plugin, plugin.plugin_multi_attrib)
88 register_stanza_plugin(stanza, multiplugin)
89 if overrides:
90 for interface in plugin.overrides:
91 stanza.plugin_overrides[interface] = plugin.plugin_attrib
94 # To maintain backwards compatibility for now, preserve the camel case name.
95 registerStanzaPlugin = register_stanza_plugin
98 def multifactory(stanza, plugin_attrib):
99 """
100 Returns a ElementBase class for handling reoccuring child stanzas
103 def plugin_filter(self):
104 return lambda x: isinstance(x, self._multistanza)
106 def plugin_lang_filter(self, lang):
107 return lambda x: isinstance(x, self._multistanza) and \
108 x['lang'] == lang
110 class Multi(ElementBase):
112 Template class for multifactory
114 def setup(self, xml=None):
115 self.xml = ET.Element('')
117 def get_multi(self, lang=None):
118 parent = self.parent()
119 if not lang or lang == '*':
120 res = filter(plugin_filter(self), parent)
121 else:
122 res = filter(plugin_filter(self, lang), parent)
123 return list(res)
125 def set_multi(self, val, lang=None):
126 parent = self.parent()
127 del_multi = getattr(self, 'del_%s' % plugin_attrib)
128 del_multi(lang)
129 for sub in val:
130 parent.append(sub)
132 def del_multi(self, lang=None):
133 parent = self.parent()
134 if not lang or lang == '*':
135 res = filter(plugin_filter(self), parent)
136 else:
137 res = filter(plugin_filter(self, lang), parent)
138 res = list(res)
139 if not res:
140 del parent.plugins[(plugin_attrib, None)]
141 parent.loaded_plugins.remove(plugin_attrib)
142 try:
143 parent.xml.remove(self.xml)
144 except ValueError:
145 pass
146 else:
147 for stanza in list(res):
148 parent.iterables.remove(stanza)
149 parent.xml.remove(stanza.xml)
151 Multi.is_extension = True
152 Multi.plugin_attrib = plugin_attrib
153 Multi._multistanza = stanza
154 Multi.interfaces = set([plugin_attrib])
155 Multi.lang_interfaces = set([plugin_attrib])
156 setattr(Multi, "get_%s" % plugin_attrib, get_multi)
157 setattr(Multi, "set_%s" % plugin_attrib, set_multi)
158 setattr(Multi, "del_%s" % plugin_attrib, del_multi)
159 return Multi
162 def fix_ns(xpath, split=False, propagate_ns=True, default_ns=''):
163 """Apply the stanza's namespace to elements in an XPath expression.
165 :param string xpath: The XPath expression to fix with namespaces.
166 :param bool split: Indicates if the fixed XPath should be left as a
167 list of element names with namespaces. Defaults to
168 False, which returns a flat string path.
169 :param bool propagate_ns: Overrides propagating parent element
170 namespaces to child elements. Useful if
171 you wish to simply split an XPath that has
172 non-specified namespaces, and child and
173 parent namespaces are known not to always
174 match. Defaults to True.
176 fixed = []
177 # Split the XPath into a series of blocks, where a block
178 # is started by an element with a namespace.
179 ns_blocks = xpath.split('{')
180 for ns_block in ns_blocks:
181 if '}' in ns_block:
182 # Apply the found namespace to following elements
183 # that do not have namespaces.
184 namespace = ns_block.split('}')[0]
185 elements = ns_block.split('}')[1].split('/')
186 else:
187 # Apply the stanza's namespace to the following
188 # elements since no namespace was provided.
189 namespace = default_ns
190 elements = ns_block.split('/')
192 for element in elements:
193 if element:
194 # Skip empty entry artifacts from splitting.
195 if propagate_ns and element[0] != '*':
196 tag = '{%s}%s' % (namespace, element)
197 else:
198 tag = element
199 fixed.append(tag)
200 if split:
201 return fixed
202 return '/'.join(fixed)
205 class ElementBase(object):
208 The core of Slixmpp's stanza XML manipulation and handling is provided
209 by ElementBase. ElementBase wraps XML cElementTree objects and enables
210 access to the XML contents through dictionary syntax, similar in style
211 to the Ruby XMPP library Blather's stanza implementation.
213 Stanzas are defined by their name, namespace, and interfaces. For
214 example, a simplistic Message stanza could be defined as::
216 >>> class Message(ElementBase):
217 ... name = "message"
218 ... namespace = "jabber:client"
219 ... interfaces = set(('to', 'from', 'type', 'body'))
220 ... sub_interfaces = set(('body',))
222 The resulting Message stanza's contents may be accessed as so::
224 >>> message['to'] = "user@example.com"
225 >>> message['body'] = "Hi!"
226 >>> message['body']
227 "Hi!"
228 >>> del message['body']
229 >>> message['body']
232 The interface values map to either custom access methods, stanza
233 XML attributes, or (if the interface is also in sub_interfaces) the
234 text contents of a stanza's subelement.
236 Custom access methods may be created by adding methods of the
237 form "getInterface", "setInterface", or "delInterface", where
238 "Interface" is the titlecase version of the interface name.
240 Stanzas may be extended through the use of plugins. A plugin
241 is simply a stanza that has a plugin_attrib value. For example::
243 >>> class MessagePlugin(ElementBase):
244 ... name = "custom_plugin"
245 ... namespace = "custom"
246 ... interfaces = set(('useful_thing', 'custom'))
247 ... plugin_attrib = "custom"
249 The plugin stanza class must be associated with its intended
250 container stanza by using register_stanza_plugin as so::
252 >>> register_stanza_plugin(Message, MessagePlugin)
254 The plugin may then be accessed as if it were built-in to the parent
255 stanza::
257 >>> message['custom']['useful_thing'] = 'foo'
259 If a plugin provides an interface that is the same as the plugin's
260 plugin_attrib value, then the plugin's interface may be assigned
261 directly from the parent stanza, as shown below, but retrieving
262 information will require all interfaces to be used, as so::
264 >>> # Same as using message['custom']['custom']
265 >>> message['custom'] = 'bar'
266 >>> # Must use all interfaces
267 >>> message['custom']['custom']
268 'bar'
270 If the plugin sets :attr:`is_extension` to ``True``, then both setting
271 and getting an interface value that is the same as the plugin's
272 plugin_attrib value will work, as so::
274 >>> message['custom'] = 'bar' # Using is_extension=True
275 >>> message['custom']
276 'bar'
279 :param xml: Initialize the stanza object with an existing XML object.
280 :param parent: Optionally specify a parent stanza object will
281 contain this substanza.
284 #: The XML tag name of the element, not including any namespace
285 #: prefixes. For example, an :class:`ElementBase` object for
286 #: ``<message />`` would use ``name = 'message'``.
287 name = 'stanza'
289 #: The XML namespace for the element. Given ``<foo xmlns="bar" />``,
290 #: then ``namespace = "bar"`` should be used. The default namespace
291 #: is ``jabber:client`` since this is being used in an XMPP library.
292 namespace = 'jabber:client'
294 #: For :class:`ElementBase` subclasses which are intended to be used
295 #: as plugins, the ``plugin_attrib`` value defines the plugin name.
296 #: Plugins may be accessed by using the ``plugin_attrib`` value as
297 #: the interface. An example using ``plugin_attrib = 'foo'``::
299 #: register_stanza_plugin(Message, FooPlugin)
300 #: msg = Message()
301 #: msg['foo']['an_interface_from_the_foo_plugin']
302 plugin_attrib = 'plugin'
304 #: For :class:`ElementBase` subclasses that are intended to be an
305 #: iterable group of items, the ``plugin_multi_attrib`` value defines
306 #: an interface for the parent stanza which returns the entire group
307 #: of matching substanzas. So the following are equivalent::
309 #: # Given stanza class Foo, with plugin_multi_attrib = 'foos'
310 #: parent['foos']
311 #: filter(isinstance(item, Foo), parent['substanzas'])
312 plugin_multi_attrib = ''
314 #: The set of keys that the stanza provides for accessing and
315 #: manipulating the underlying XML object. This set may be augmented
316 #: with the :attr:`plugin_attrib` value of any registered
317 #: stanza plugins.
318 interfaces = set(('type', 'to', 'from', 'id', 'payload'))
320 #: A subset of :attr:`interfaces` which maps interfaces to direct
321 #: subelements of the underlying XML object. Using this set, the text
322 #: of these subelements may be set, retrieved, or removed without
323 #: needing to define custom methods.
324 sub_interfaces = set()
326 #: A subset of :attr:`interfaces` which maps the presence of
327 #: subelements to boolean values. Using this set allows for quickly
328 #: checking for the existence of empty subelements like ``<required />``.
330 #: .. versionadded:: 1.1
331 bool_interfaces = set()
333 #: .. versionadded:: 1.1.2
334 lang_interfaces = set()
336 #: In some cases you may wish to override the behaviour of one of the
337 #: parent stanza's interfaces. The ``overrides`` list specifies the
338 #: interface name and access method to be overridden. For example,
339 #: to override setting the parent's ``'condition'`` interface you
340 #: would use::
342 #: overrides = ['set_condition']
344 #: Getting and deleting the ``'condition'`` interface would not
345 #: be affected.
347 #: .. versionadded:: 1.0-Beta5
348 overrides = []
350 #: If you need to add a new interface to an existing stanza, you
351 #: can create a plugin and set ``is_extension = True``. Be sure
352 #: to set the :attr:`plugin_attrib` value to the desired interface
353 #: name, and that it is the only interface listed in
354 #: :attr:`interfaces`. Requests for the new interface from the
355 #: parent stanza will be passed to the plugin directly.
357 #: .. versionadded:: 1.0-Beta5
358 is_extension = False
360 #: A map of interface operations to the overriding functions.
361 #: For example, after overriding the ``set`` operation for
362 #: the interface ``body``, :attr:`plugin_overrides` would be::
364 #: {'set_body': <some function>}
366 #: .. versionadded: 1.0-Beta5
367 plugin_overrides = {}
369 #: A mapping of the :attr:`plugin_attrib` values of registered
370 #: plugins to their respective classes.
371 plugin_attrib_map = {}
373 #: A mapping of root element tag names (in ``'{namespace}elementname'``
374 #: format) to the plugin classes responsible for them.
375 plugin_tag_map = {}
377 #: The set of stanza classes that can be iterated over using
378 #: the 'substanzas' interface. Classes are added to this set
379 #: when registering a plugin with ``iterable=True``::
381 #: register_stanza_plugin(DiscoInfo, DiscoItem, iterable=True)
383 #: .. versionadded:: 1.0-Beta5
384 plugin_iterables = set()
386 #: A deprecated version of :attr:`plugin_iterables` that remains
387 #: for backward compatibility. It required a parent stanza to
388 #: know beforehand what stanza classes would be iterable::
390 #: class DiscoItem(ElementBase):
391 #: ...
393 #: class DiscoInfo(ElementBase):
394 #: subitem = (DiscoItem, )
395 #: ...
397 #: .. deprecated:: 1.0-Beta5
398 subitem = set()
400 #: The default XML namespace: ``http://www.w3.org/XML/1998/namespace``.
401 xml_ns = XML_NS
403 def __init__(self, xml=None, parent=None):
404 self._index = 0
406 #: The underlying XML object for the stanza. It is a standard
407 #: :class:`xml.etree.cElementTree` object.
408 self.xml = xml
410 #: An ordered dictionary of plugin stanzas, mapped by their
411 #: :attr:`plugin_attrib` value.
412 self.plugins = OrderedDict()
413 self.loaded_plugins = set()
415 #: A list of child stanzas whose class is included in
416 #: :attr:`plugin_iterables`.
417 self.iterables = []
419 #: The name of the tag for the stanza's root element. It is the
420 #: same as calling :meth:`tag_name()` and is formatted as
421 #: ``'{namespace}elementname'``.
422 self.tag = self.tag_name()
424 #: A :class:`weakref.weakref` to the parent stanza, if there is one.
425 #: If not, then :attr:`parent` is ``None``.
426 self.parent = None
427 if parent is not None:
428 if not isinstance(parent, weakref.ReferenceType):
429 self.parent = weakref.ref(parent)
430 else:
431 self.parent = parent
433 if self.subitem is not None:
434 for sub in self.subitem:
435 self.plugin_iterables.add(sub)
437 if self.setup(xml):
438 # If we generated our own XML, then everything is ready.
439 return
441 # Initialize values using provided XML
442 for child in self.xml:
443 if child.tag in self.plugin_tag_map:
444 plugin_class = self.plugin_tag_map[child.tag]
445 self.init_plugin(plugin_class.plugin_attrib,
446 existing_xml=child,
447 reuse=False)
449 def setup(self, xml=None):
450 """Initialize the stanza's XML contents.
452 Will return ``True`` if XML was generated according to the stanza's
453 definition instead of building a stanza object from an existing
454 XML object.
456 :param xml: An existing XML object to use for the stanza's content
457 instead of generating new XML.
459 if self.xml is None:
460 self.xml = xml
462 last_xml = self.xml
463 if self.xml is None:
464 # Generate XML from the stanza definition
465 for ename in self.name.split('/'):
466 new = ET.Element("{%s}%s" % (self.namespace, ename))
467 if self.xml is None:
468 self.xml = new
469 else:
470 last_xml.append(new)
471 last_xml = new
472 if self.parent is not None:
473 self.parent().xml.append(self.xml)
475 # We had to generate XML
476 return True
477 else:
478 # We did not generate XML
479 return False
481 def enable(self, attrib, lang=None):
482 """Enable and initialize a stanza plugin.
484 Alias for :meth:`init_plugin`.
486 :param string attrib: The :attr:`plugin_attrib` value of the
487 plugin to enable.
489 return self.init_plugin(attrib, lang)
491 def _get_plugin(self, name, lang=None, check=False):
492 if lang is None:
493 lang = self.get_lang()
495 if name not in self.plugin_attrib_map:
496 return None
498 plugin_class = self.plugin_attrib_map[name]
500 if plugin_class.is_extension:
501 if (name, None) in self.plugins:
502 return self.plugins[(name, None)]
503 else:
504 return None if check else self.init_plugin(name, lang)
505 else:
506 if (name, lang) in self.plugins:
507 return self.plugins[(name, lang)]
508 else:
509 return None if check else self.init_plugin(name, lang)
511 def init_plugin(self, attrib, lang=None, existing_xml=None, reuse=True):
512 """Enable and initialize a stanza plugin.
514 :param string attrib: The :attr:`plugin_attrib` value of the
515 plugin to enable.
517 default_lang = self.get_lang()
518 if not lang:
519 lang = default_lang
521 plugin_class = self.plugin_attrib_map[attrib]
523 if plugin_class.is_extension and (attrib, None) in self.plugins:
524 return self.plugins[(attrib, None)]
525 if reuse and (attrib, lang) in self.plugins:
526 return self.plugins[(attrib, lang)]
528 plugin = plugin_class(parent=self, xml=existing_xml)
530 if plugin.is_extension:
531 self.plugins[(attrib, None)] = plugin
532 else:
533 if lang != default_lang:
534 plugin['lang'] = lang
535 self.plugins[(attrib, lang)] = plugin
537 if plugin_class in self.plugin_iterables:
538 self.iterables.append(plugin)
539 if plugin_class.plugin_multi_attrib:
540 self.init_plugin(plugin_class.plugin_multi_attrib)
542 self.loaded_plugins.add(attrib)
544 return plugin
546 def _get_stanza_values(self):
547 """Return A JSON/dictionary version of the XML content
548 exposed through the stanza's interfaces::
550 >>> msg = Message()
551 >>> msg.values
552 {'body': '', 'from': , 'mucnick': '', 'mucroom': '',
553 'to': , 'type': 'normal', 'id': '', 'subject': ''}
555 Likewise, assigning to :attr:`values` will change the XML
556 content::
558 >>> msg = Message()
559 >>> msg.values = {'body': 'Hi!', 'to': 'user@example.com'}
560 >>> msg
561 '<message to="user@example.com"><body>Hi!</body></message>'
563 .. versionadded:: 1.0-Beta1
565 values = {}
566 values['lang'] = self['lang']
567 for interface in self.interfaces:
568 values[interface] = self[interface]
569 if interface in self.lang_interfaces:
570 values['%s|*' % interface] = self['%s|*' % interface]
571 for plugin, stanza in self.plugins.items():
572 lang = stanza['lang']
573 if lang:
574 values['%s|%s' % (plugin[0], lang)] = stanza.values
575 else:
576 values[plugin[0]] = stanza.values
577 if self.iterables:
578 iterables = []
579 for stanza in self.iterables:
580 iterables.append(stanza.values)
581 iterables[-1]['__childtag__'] = stanza.tag
582 values['substanzas'] = iterables
583 return values
585 def _set_stanza_values(self, values):
586 """Set multiple stanza interface values using a dictionary.
588 Stanza plugin values may be set using nested dictionaries.
590 :param values: A dictionary mapping stanza interface with values.
591 Plugin interfaces may accept a nested dictionary that
592 will be used recursively.
594 .. versionadded:: 1.0-Beta1
596 iterable_interfaces = [p.plugin_attrib for \
597 p in self.plugin_iterables]
599 if 'lang' in values:
600 self['lang'] = values['lang']
602 if 'substanzas' in values:
603 # Remove existing substanzas
604 for stanza in self.iterables:
605 try:
606 self.xml.remove(stanza.xml)
607 except ValueError:
608 pass
609 self.iterables = []
611 # Add new substanzas
612 for subdict in values['substanzas']:
613 if '__childtag__' in subdict:
614 for subclass in self.plugin_iterables:
615 child_tag = "{%s}%s" % (subclass.namespace,
616 subclass.name)
617 if subdict['__childtag__'] == child_tag:
618 sub = subclass(parent=self)
619 sub.values = subdict
620 self.iterables.append(sub)
622 for interface, value in values.items():
623 full_interface = interface
624 interface_lang = ('%s|' % interface).split('|')
625 interface = interface_lang[0]
626 lang = interface_lang[1] or self.get_lang()
628 if interface == 'lang':
629 continue
630 elif interface == 'substanzas':
631 continue
632 elif interface in self.interfaces:
633 self[full_interface] = value
634 elif interface in self.plugin_attrib_map:
635 if interface not in iterable_interfaces:
636 plugin = self._get_plugin(interface, lang)
637 if plugin:
638 plugin.values = value
639 return self
641 def __getitem__(self, attrib):
642 """Return the value of a stanza interface using dict-like syntax.
644 Example::
646 >>> msg['body']
647 'Message contents'
649 Stanza interfaces are typically mapped directly to the underlying XML
650 object, but can be overridden by the presence of a ``get_attrib``
651 method (or ``get_foo`` where the interface is named ``'foo'``, etc).
653 The search order for interface value retrieval for an interface
654 named ``'foo'`` is:
656 1. The list of substanzas (``'substanzas'``)
657 2. The result of calling the ``get_foo`` override handler.
658 3. The result of calling ``get_foo``.
659 4. The result of calling ``getFoo``.
660 5. The contents of the ``foo`` subelement, if ``foo`` is listed
661 in :attr:`sub_interfaces`.
662 6. True or False depending on the existence of a ``foo``
663 subelement and ``foo`` is in :attr:`bool_interfaces`.
664 7. The value of the ``foo`` attribute of the XML object.
665 8. The plugin named ``'foo'``
666 9. An empty string.
668 :param string attrib: The name of the requested stanza interface.
670 full_attrib = attrib
671 attrib_lang = ('%s|' % attrib).split('|')
672 attrib = attrib_lang[0]
673 lang = attrib_lang[1] or None
675 kwargs = {}
676 if lang and attrib in self.lang_interfaces:
677 kwargs['lang'] = lang
679 if attrib == 'substanzas':
680 return self.iterables
681 elif attrib in self.interfaces or attrib == 'lang':
682 get_method = "get_%s" % attrib.lower()
683 get_method2 = "get%s" % attrib.title()
685 if self.plugin_overrides:
686 name = self.plugin_overrides.get(get_method, None)
687 if name:
688 plugin = self._get_plugin(name, lang)
689 if plugin:
690 handler = getattr(plugin, get_method, None)
691 if handler:
692 return handler(**kwargs)
694 if hasattr(self, get_method):
695 return getattr(self, get_method)(**kwargs)
696 elif hasattr(self, get_method2):
697 return getattr(self, get_method2)(**kwargs)
698 else:
699 if attrib in self.sub_interfaces:
700 return self._get_sub_text(attrib, lang=lang)
701 elif attrib in self.bool_interfaces:
702 elem = self.xml.find('{%s}%s' % (self.namespace, attrib))
703 return elem is not None
704 else:
705 return self._get_attr(attrib)
706 elif attrib in self.plugin_attrib_map:
707 plugin = self._get_plugin(attrib, lang)
708 if plugin and plugin.is_extension:
709 return plugin[full_attrib]
710 return plugin
711 else:
712 return ''
714 def __setitem__(self, attrib, value):
715 """Set the value of a stanza interface using dictionary-like syntax.
717 Example::
719 >>> msg['body'] = "Hi!"
720 >>> msg['body']
721 'Hi!'
723 Stanza interfaces are typically mapped directly to the underlying XML
724 object, but can be overridden by the presence of a ``set_attrib``
725 method (or ``set_foo`` where the interface is named ``'foo'``, etc).
727 The effect of interface value assignment for an interface
728 named ``'foo'`` will be one of:
730 1. Delete the interface's contents if the value is None.
731 2. Call the ``set_foo`` override handler, if it exists.
732 3. Call ``set_foo``, if it exists.
733 4. Call ``setFoo``, if it exists.
734 5. Set the text of a ``foo`` element, if ``'foo'`` is
735 in :attr:`sub_interfaces`.
736 6. Add or remove an empty subelement ``foo``
737 if ``foo`` is in :attr:`bool_interfaces`.
738 7. Set the value of a top level XML attribute named ``foo``.
739 8. Attempt to pass the value to a plugin named ``'foo'`` using
740 the plugin's ``'foo'`` interface.
741 9. Do nothing.
743 :param string attrib: The name of the stanza interface to modify.
744 :param value: The new value of the stanza interface.
746 full_attrib = attrib
747 attrib_lang = ('%s|' % attrib).split('|')
748 attrib = attrib_lang[0]
749 lang = attrib_lang[1] or None
751 kwargs = {}
752 if lang and attrib in self.lang_interfaces:
753 kwargs['lang'] = lang
755 if attrib in self.interfaces or attrib == 'lang':
756 if value is not None:
757 set_method = "set_%s" % attrib.lower()
758 set_method2 = "set%s" % attrib.title()
760 if self.plugin_overrides:
761 name = self.plugin_overrides.get(set_method, None)
762 if name:
763 plugin = self._get_plugin(name, lang)
764 if plugin:
765 handler = getattr(plugin, set_method, None)
766 if handler:
767 return handler(value, **kwargs)
769 if hasattr(self, set_method):
770 getattr(self, set_method)(value, **kwargs)
771 elif hasattr(self, set_method2):
772 getattr(self, set_method2)(value, **kwargs)
773 else:
774 if attrib in self.sub_interfaces:
775 if lang == '*':
776 return self._set_all_sub_text(attrib,
777 value,
778 lang='*')
779 return self._set_sub_text(attrib, text=value,
780 lang=lang)
781 elif attrib in self.bool_interfaces:
782 if value:
783 return self._set_sub_text(attrib, '',
784 keep=True,
785 lang=lang)
786 else:
787 return self._set_sub_text(attrib, '',
788 keep=False,
789 lang=lang)
790 else:
791 self._set_attr(attrib, value)
792 else:
793 self.__delitem__(attrib)
794 elif attrib in self.plugin_attrib_map:
795 plugin = self._get_plugin(attrib, lang)
796 if plugin:
797 plugin[full_attrib] = value
798 return self
800 def __delitem__(self, attrib):
801 """Delete the value of a stanza interface using dict-like syntax.
803 Example::
805 >>> msg['body'] = "Hi!"
806 >>> msg['body']
807 'Hi!'
808 >>> del msg['body']
809 >>> msg['body']
812 Stanza interfaces are typically mapped directly to the underlyig XML
813 object, but can be overridden by the presence of a ``del_attrib``
814 method (or ``del_foo`` where the interface is named ``'foo'``, etc).
816 The effect of deleting a stanza interface value named ``foo`` will be
817 one of:
819 1. Call ``del_foo`` override handler, if it exists.
820 2. Call ``del_foo``, if it exists.
821 3. Call ``delFoo``, if it exists.
822 4. Delete ``foo`` element, if ``'foo'`` is in
823 :attr:`sub_interfaces`.
824 5. Remove ``foo`` element if ``'foo'`` is in
825 :attr:`bool_interfaces`.
826 6. Delete top level XML attribute named ``foo``.
827 7. Remove the ``foo`` plugin, if it was loaded.
828 8. Do nothing.
830 :param attrib: The name of the affected stanza interface.
832 full_attrib = attrib
833 attrib_lang = ('%s|' % attrib).split('|')
834 attrib = attrib_lang[0]
835 lang = attrib_lang[1] or None
837 kwargs = {}
838 if lang and attrib in self.lang_interfaces:
839 kwargs['lang'] = lang
841 if attrib in self.interfaces or attrib == 'lang':
842 del_method = "del_%s" % attrib.lower()
843 del_method2 = "del%s" % attrib.title()
845 if self.plugin_overrides:
846 name = self.plugin_overrides.get(del_method, None)
847 if name:
848 plugin = self._get_plugin(attrib, lang)
849 if plugin:
850 handler = getattr(plugin, del_method, None)
851 if handler:
852 return handler(**kwargs)
854 if hasattr(self, del_method):
855 getattr(self, del_method)(**kwargs)
856 elif hasattr(self, del_method2):
857 getattr(self, del_method2)(**kwargs)
858 else:
859 if attrib in self.sub_interfaces:
860 return self._del_sub(attrib, lang=lang)
861 elif attrib in self.bool_interfaces:
862 return self._del_sub(attrib, lang=lang)
863 else:
864 self._del_attr(attrib)
865 elif attrib in self.plugin_attrib_map:
866 plugin = self._get_plugin(attrib, lang, check=True)
867 if not plugin:
868 return self
869 if plugin.is_extension:
870 del plugin[full_attrib]
871 del self.plugins[(attrib, None)]
872 else:
873 del self.plugins[(attrib, plugin['lang'])]
874 self.loaded_plugins.remove(attrib)
875 try:
876 self.xml.remove(plugin.xml)
877 except ValueError:
878 pass
879 return self
881 def _set_attr(self, name, value):
882 """Set the value of a top level attribute of the XML object.
884 If the new value is None or an empty string, then the attribute will
885 be removed.
887 :param name: The name of the attribute.
888 :param value: The new value of the attribute, or None or '' to
889 remove it.
891 if value is None or value == '':
892 self.__delitem__(name)
893 else:
894 self.xml.attrib[name] = value
896 def _del_attr(self, name):
897 """Remove a top level attribute of the XML object.
899 :param name: The name of the attribute.
901 if name in self.xml.attrib:
902 del self.xml.attrib[name]
904 def _get_attr(self, name, default=''):
905 """Return the value of a top level attribute of the XML object.
907 In case the attribute has not been set, a default value can be
908 returned instead. An empty string is returned if no other default
909 is supplied.
911 :param name: The name of the attribute.
912 :param default: Optional value to return if the attribute has not
913 been set. An empty string is returned otherwise.
915 return self.xml.attrib.get(name, default)
917 def _get_sub_text(self, name, default='', lang=None):
918 """Return the text contents of a sub element.
920 In case the element does not exist, or it has no textual content,
921 a default value can be returned instead. An empty string is returned
922 if no other default is supplied.
924 :param name: The name or XPath expression of the element.
925 :param default: Optional default to return if the element does
926 not exists. An empty string is returned otherwise.
928 name = self._fix_ns(name)
929 if lang == '*':
930 return self._get_all_sub_text(name, default, None)
932 default_lang = self.get_lang()
933 if not lang:
934 lang = default_lang
936 stanzas = self.xml.findall(name)
937 if not stanzas:
938 return default
939 for stanza in stanzas:
940 if stanza.attrib.get('{%s}lang' % XML_NS, default_lang) == lang:
941 if stanza.text is None:
942 return default
943 return stanza.text
944 return default
946 def _get_all_sub_text(self, name, default='', lang=None):
947 name = self._fix_ns(name)
949 default_lang = self.get_lang()
950 results = OrderedDict()
951 stanzas = self.xml.findall(name)
952 if stanzas:
953 for stanza in stanzas:
954 stanza_lang = stanza.attrib.get('{%s}lang' % XML_NS,
955 default_lang)
956 if not lang or lang == '*' or stanza_lang == lang:
957 results[stanza_lang] = stanza.text
958 return results
960 def _set_sub_text(self, name, text=None, keep=False, lang=None):
961 """Set the text contents of a sub element.
963 In case the element does not exist, a element will be created,
964 and its text contents will be set.
966 If the text is set to an empty string, or None, then the
967 element will be removed, unless keep is set to True.
969 :param name: The name or XPath expression of the element.
970 :param text: The new textual content of the element. If the text
971 is an empty string or None, the element will be removed
972 unless the parameter keep is True.
973 :param keep: Indicates if the element should be kept if its text is
974 removed. Defaults to False.
976 default_lang = self.get_lang()
977 if lang is None:
978 lang = default_lang
980 if not text and not keep:
981 return self._del_sub(name, lang=lang)
983 path = self._fix_ns(name, split=True)
984 name = path[-1]
985 parent = self.xml
987 # The first goal is to find the parent of the subelement, or, if
988 # we can't find that, the closest grandparent element.
989 missing_path = []
990 search_order = path[:-1]
991 while search_order:
992 parent = self.xml.find('/'.join(search_order))
993 ename = search_order.pop()
994 if parent is not None:
995 break
996 else:
997 missing_path.append(ename)
998 missing_path.reverse()
1000 # Find all existing elements that match the desired
1001 # element path (there may be multiples due to different
1002 # languages values).
1003 if parent is not None:
1004 elements = self.xml.findall('/'.join(path))
1005 else:
1006 parent = self.xml
1007 elements = []
1009 # Insert the remaining grandparent elements that don't exist yet.
1010 for ename in missing_path:
1011 element = ET.Element(ename)
1012 parent.append(element)
1013 parent = element
1015 # Re-use an existing element with the proper language, if one exists.
1016 for element in elements:
1017 elang = element.attrib.get('{%s}lang' % XML_NS, default_lang)
1018 if not lang and elang == default_lang or lang and lang == elang:
1019 element.text = text
1020 return element
1022 # No useable element exists, so create a new one.
1023 element = ET.Element(name)
1024 element.text = text
1025 if lang and lang != default_lang:
1026 element.attrib['{%s}lang' % XML_NS] = lang
1027 parent.append(element)
1028 return element
1030 def _set_all_sub_text(self, name, values, keep=False, lang=None):
1031 self._del_sub(name, lang)
1032 for value_lang, value in values.items():
1033 if not lang or lang == '*' or value_lang == lang:
1034 self._set_sub_text(name, text=value,
1035 keep=keep,
1036 lang=value_lang)
1038 def _del_sub(self, name, all=False, lang=None):
1039 """Remove sub elements that match the given name or XPath.
1041 If the element is in a path, then any parent elements that become
1042 empty after deleting the element may also be deleted if requested
1043 by setting all=True.
1045 :param name: The name or XPath expression for the element(s) to remove.
1046 :param bool all: If True, remove all empty elements in the path to the
1047 deleted element. Defaults to False.
1049 path = self._fix_ns(name, split=True)
1050 original_target = path[-1]
1052 default_lang = self.get_lang()
1053 if not lang:
1054 lang = default_lang
1056 for level, _ in enumerate(path):
1057 # Generate the paths to the target elements and their parent.
1058 element_path = "/".join(path[:len(path) - level])
1059 parent_path = "/".join(path[:len(path) - level - 1])
1061 elements = self.xml.findall(element_path)
1062 parent = self.xml.find(parent_path)
1064 if elements:
1065 if parent is None:
1066 parent = self.xml
1067 for element in elements:
1068 if element.tag == original_target or not list(element):
1069 # Only delete the originally requested elements, and
1070 # any parent elements that have become empty.
1071 elem_lang = element.attrib.get('{%s}lang' % XML_NS,
1072 default_lang)
1073 if lang == '*' or elem_lang == lang:
1074 parent.remove(element)
1075 if not all:
1076 # If we don't want to delete elements up the tree, stop
1077 # after deleting the first level of elements.
1078 return
1080 def match(self, xpath):
1081 """Compare a stanza object with an XPath-like expression.
1083 If the XPath matches the contents of the stanza object, the match
1084 is successful.
1086 The XPath expression may include checks for stanza attributes.
1087 For example::
1089 'presence@show=xa@priority=2/status'
1091 Would match a presence stanza whose show value is set to ``'xa'``,
1092 has a priority value of ``'2'``, and has a status element.
1094 :param string xpath: The XPath expression to check against. It
1095 may be either a string or a list of element
1096 names with attribute checks.
1098 if not isinstance(xpath, list):
1099 xpath = self._fix_ns(xpath, split=True, propagate_ns=False)
1101 # Extract the tag name and attribute checks for the first XPath node.
1102 components = xpath[0].split('@')
1103 tag = components[0]
1104 attributes = components[1:]
1106 if tag not in (self.name, "{%s}%s" % (self.namespace, self.name)) and \
1107 tag not in self.loaded_plugins and tag not in self.plugin_attrib:
1108 # The requested tag is not in this stanza, so no match.
1109 return False
1111 # Check the rest of the XPath against any substanzas.
1112 matched_substanzas = False
1113 for substanza in self.iterables:
1114 if xpath[1:] == []:
1115 break
1116 matched_substanzas = substanza.match(xpath[1:])
1117 if matched_substanzas:
1118 break
1120 # Check attribute values.
1121 for attribute in attributes:
1122 name, value = attribute.split('=')
1123 if self[name] != value:
1124 return False
1126 # Check sub interfaces.
1127 if len(xpath) > 1:
1128 next_tag = xpath[1]
1129 if next_tag in self.sub_interfaces and self[next_tag]:
1130 return True
1132 # Attempt to continue matching the XPath using the stanza's plugins.
1133 if not matched_substanzas and len(xpath) > 1:
1134 # Convert {namespace}tag@attribs to just tag
1135 next_tag = xpath[1].split('@')[0].split('}')[-1]
1136 langs = [name[1] for name in self.plugins if name[0] == next_tag]
1137 for lang in langs:
1138 plugin = self._get_plugin(next_tag, lang)
1139 if plugin and plugin.match(xpath[1:]):
1140 return True
1141 return False
1143 # Everything matched.
1144 return True
1146 def find(self, xpath):
1147 """Find an XML object in this stanza given an XPath expression.
1149 Exposes ElementTree interface for backwards compatibility.
1151 .. note::
1153 Matching on attribute values is not supported in Python 2.6
1154 or Python 3.1
1156 :param string xpath: An XPath expression matching a single
1157 desired element.
1159 return self.xml.find(xpath)
1161 def findall(self, xpath):
1162 """Find multiple XML objects in this stanza given an XPath expression.
1164 Exposes ElementTree interface for backwards compatibility.
1166 .. note::
1168 Matching on attribute values is not supported in Python 2.6
1169 or Python 3.1.
1171 :param string xpath: An XPath expression matching multiple
1172 desired elements.
1174 return self.xml.findall(xpath)
1176 def get(self, key, default=None):
1177 """Return the value of a stanza interface.
1179 If the found value is None or an empty string, return the supplied
1180 default value.
1182 Allows stanza objects to be used like dictionaries.
1184 :param string key: The name of the stanza interface to check.
1185 :param default: Value to return if the stanza interface has a value
1186 of ``None`` or ``""``. Will default to returning None.
1188 value = self[key]
1189 if value is None or value == '':
1190 return default
1191 return value
1193 def keys(self):
1194 """Return the names of all stanza interfaces provided by the
1195 stanza object.
1197 Allows stanza objects to be used like dictionaries.
1199 out = []
1200 out += [x for x in self.interfaces]
1201 out += [x for x in self.loaded_plugins]
1202 out.append('lang')
1203 if self.iterables:
1204 out.append('substanzas')
1205 return out
1207 def append(self, item):
1208 """Append either an XML object or a substanza to this stanza object.
1210 If a substanza object is appended, it will be added to the list
1211 of iterable stanzas.
1213 Allows stanza objects to be used like lists.
1215 :param item: Either an XML object or a stanza object to add to
1216 this stanza's contents.
1218 if not isinstance(item, ElementBase):
1219 if type(item) == XML_TYPE:
1220 return self.appendxml(item)
1221 else:
1222 raise TypeError
1223 self.xml.append(item.xml)
1224 self.iterables.append(item)
1225 if item.__class__ in self.plugin_iterables:
1226 if item.__class__.plugin_multi_attrib:
1227 self.init_plugin(item.__class__.plugin_multi_attrib)
1228 elif item.__class__ == self.plugin_tag_map.get(item.tag_name(), None):
1229 self.init_plugin(item.plugin_attrib,
1230 existing_xml=item.xml,
1231 reuse=False)
1232 return self
1234 def appendxml(self, xml):
1235 """Append an XML object to the stanza's XML.
1237 The added XML will not be included in the list of
1238 iterable substanzas.
1240 :param XML xml: The XML object to add to the stanza.
1242 self.xml.append(xml)
1243 return self
1245 def pop(self, index=0):
1246 """Remove and return the last substanza in the list of
1247 iterable substanzas.
1249 Allows stanza objects to be used like lists.
1251 :param int index: The index of the substanza to remove.
1253 substanza = self.iterables.pop(index)
1254 self.xml.remove(substanza.xml)
1255 return substanza
1257 def next(self):
1258 """Return the next iterable substanza."""
1259 return self.__next__()
1261 def clear(self):
1262 """Remove all XML element contents and plugins.
1264 Any attribute values will be preserved.
1266 for child in list(self.xml):
1267 self.xml.remove(child)
1269 for plugin in list(self.plugins.keys()):
1270 del self.plugins[plugin]
1271 return self
1273 @classmethod
1274 def tag_name(cls):
1275 """Return the namespaced name of the stanza's root element.
1277 The format for the tag name is::
1279 '{namespace}elementname'
1281 For example, for the stanza ``<foo xmlns="bar" />``,
1282 ``stanza.tag_name()`` would return ``"{bar}foo"``.
1284 return "{%s}%s" % (cls.namespace, cls.name)
1286 def get_lang(self, lang=None):
1287 result = self.xml.attrib.get('{%s}lang' % XML_NS, '')
1288 if not result and self.parent and self.parent():
1289 return self.parent()['lang']
1290 return result
1292 def set_lang(self, lang):
1293 self.del_lang()
1294 attr = '{%s}lang' % XML_NS
1295 if lang:
1296 self.xml.attrib[attr] = lang
1298 def del_lang(self):
1299 attr = '{%s}lang' % XML_NS
1300 if attr in self.xml.attrib:
1301 del self.xml.attrib[attr]
1303 @property
1304 def attrib(self):
1305 """Return the stanza object itself.
1307 Older implementations of stanza objects used XML objects directly,
1308 requiring the use of ``.attrib`` to access attribute values.
1310 Use of the dictionary syntax with the stanza object itself for
1311 accessing stanza interfaces is preferred.
1313 .. deprecated:: 1.0
1315 return self
1317 def _fix_ns(self, xpath, split=False, propagate_ns=True):
1318 return fix_ns(xpath, split=split,
1319 propagate_ns=propagate_ns,
1320 default_ns=self.namespace)
1322 def __eq__(self, other):
1323 """Compare the stanza object with another to test for equality.
1325 Stanzas are equal if their interfaces return the same values,
1326 and if they are both instances of ElementBase.
1328 :param ElementBase other: The stanza object to compare against.
1330 if not isinstance(other, ElementBase):
1331 return False
1333 # Check that this stanza is a superset of the other stanza.
1334 values = self.values
1335 for key in other.keys():
1336 if key not in values or values[key] != other[key]:
1337 return False
1339 # Check that the other stanza is a superset of this stanza.
1340 values = other.values
1341 for key in self.keys():
1342 if key not in values or values[key] != self[key]:
1343 return False
1345 # Both stanzas are supersets of each other, therefore they
1346 # must be equal.
1347 return True
1349 def __ne__(self, other):
1350 """Compare the stanza object with another to test for inequality.
1352 Stanzas are not equal if their interfaces return different values,
1353 or if they are not both instances of ElementBase.
1355 :param ElementBase other: The stanza object to compare against.
1357 return not self.__eq__(other)
1359 def __bool__(self):
1360 """Stanza objects should be treated as True in boolean contexts.
1362 Python 3.x version.
1364 return True
1366 def __nonzero__(self):
1367 """Stanza objects should be treated as True in boolean contexts.
1369 Python 2.x version.
1371 return True
1373 def __len__(self):
1374 """Return the number of iterable substanzas in this stanza."""
1375 return len(self.iterables)
1377 def __iter__(self):
1378 """Return an iterator object for the stanza's substanzas.
1380 The iterator is the stanza object itself. Attempting to use two
1381 iterators on the same stanza at the same time is discouraged.
1383 self._index = 0
1384 return self
1386 def __next__(self):
1387 """Return the next iterable substanza."""
1388 self._index += 1
1389 if self._index > len(self.iterables):
1390 self._index = 0
1391 raise StopIteration
1392 return self.iterables[self._index - 1]
1394 def __copy__(self):
1395 """Return a copy of the stanza object that does not share the same
1396 underlying XML object.
1398 return self.__class__(xml=copy.deepcopy(self.xml), parent=self.parent)
1400 def __str__(self, top_level_ns=True):
1401 """Return a string serialization of the underlying XML object.
1403 .. seealso:: :ref:`tostring`
1405 :param bool top_level_ns: Display the top-most namespace.
1406 Defaults to True.
1408 return tostring(self.xml, xmlns='',
1409 top_level=True)
1411 def __repr__(self):
1412 """Use the stanza's serialized XML as its representation."""
1413 return self.__str__()
1416 class StanzaBase(ElementBase):
1419 StanzaBase provides the foundation for all other stanza objects used
1420 by Slixmpp, and defines a basic set of interfaces common to nearly
1421 all stanzas. These interfaces are the ``'id'``, ``'type'``, ``'to'``,
1422 and ``'from'`` attributes. An additional interface, ``'payload'``, is
1423 available to access the XML contents of the stanza. Most stanza objects
1424 will provided more specific interfaces, however.
1426 **Stanza Interfaces:**
1428 :id: An optional id value that can be used to associate stanzas
1429 :to: A JID object representing the recipient's JID.
1430 :from: A JID object representing the sender's JID.
1431 with their replies.
1432 :type: The type of stanza, typically will be ``'normal'``,
1433 ``'error'``, ``'get'``, or ``'set'``, etc.
1434 :payload: The XML contents of the stanza.
1436 :param XMLStream stream: Optional :class:`slixmpp.xmlstream.XMLStream`
1437 object responsible for sending this stanza.
1438 :param XML xml: Optional XML contents to initialize stanza values.
1439 :param string stype: Optional stanza type value.
1440 :param sto: Optional string or :class:`slixmpp.xmlstream.JID`
1441 object of the recipient's JID.
1442 :param sfrom: Optional string or :class:`slixmpp.xmlstream.JID`
1443 object of the sender's JID.
1444 :param string sid: Optional ID value for the stanza.
1445 :param parent: Optionally specify a parent stanza object will
1446 contain this substanza.
1449 #: The default XMPP client namespace
1450 namespace = 'jabber:client'
1452 #: There is a small set of attributes which apply to all XMPP stanzas:
1453 #: the stanza type, the to and from JIDs, the stanza ID, and, especially
1454 #: in the case of an Iq stanza, a payload.
1455 interfaces = set(('type', 'to', 'from', 'id', 'payload'))
1457 #: A basic set of allowed values for the ``'type'`` interface.
1458 types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
1460 def __init__(self, stream=None, xml=None, stype=None,
1461 sto=None, sfrom=None, sid=None, parent=None):
1462 self.stream = stream
1463 if stream is not None:
1464 self.namespace = stream.default_ns
1465 ElementBase.__init__(self, xml, parent)
1466 if stype is not None:
1467 self['type'] = stype
1468 if sto is not None:
1469 self['to'] = sto
1470 if sfrom is not None:
1471 self['from'] = sfrom
1472 if sid is not None:
1473 self['id'] = sid
1474 self.tag = "{%s}%s" % (self.namespace, self.name)
1476 def set_type(self, value):
1477 """Set the stanza's ``'type'`` attribute.
1479 Only type values contained in :attr:`types` are accepted.
1481 :param string value: One of the values contained in :attr:`types`
1483 if value in self.types:
1484 self.xml.attrib['type'] = value
1485 return self
1487 def get_to(self):
1488 """Return the value of the stanza's ``'to'`` attribute."""
1489 return JID(self._get_attr('to'))
1491 def set_to(self, value):
1492 """Set the ``'to'`` attribute of the stanza.
1494 :param value: A string or :class:`slixmpp.xmlstream.JID` object
1495 representing the recipient's JID.
1497 return self._set_attr('to', str(value))
1499 def get_from(self):
1500 """Return the value of the stanza's ``'from'`` attribute."""
1501 return JID(self._get_attr('from'))
1503 def set_from(self, value):
1504 """Set the 'from' attribute of the stanza.
1506 Arguments:
1507 from -- A string or JID object representing the sender's JID.
1509 return self._set_attr('from', str(value))
1511 def get_payload(self):
1512 """Return a list of XML objects contained in the stanza."""
1513 return list(self.xml)
1515 def set_payload(self, value):
1516 """Add XML content to the stanza.
1518 :param value: Either an XML or a stanza object, or a list
1519 of XML or stanza objects.
1521 if not isinstance(value, list):
1522 value = [value]
1523 for val in value:
1524 self.append(val)
1525 return self
1527 def del_payload(self):
1528 """Remove the XML contents of the stanza."""
1529 self.clear()
1530 return self
1532 def reply(self, clear=True):
1533 """Prepare the stanza for sending a reply.
1535 Swaps the ``'from'`` and ``'to'`` attributes.
1537 If ``clear=True``, then also remove the stanza's
1538 contents to make room for the reply content.
1540 For client streams, the ``'from'`` attribute is removed.
1542 :param bool clear: Indicates if the stanza's contents should be
1543 removed. Defaults to ``True``.
1545 # if it's a component, use from
1546 if self.stream and hasattr(self.stream, "is_component") and \
1547 self.stream.is_component:
1548 self['from'], self['to'] = self['to'], self['from']
1549 else:
1550 self['to'] = self['from']
1551 del self['from']
1552 if clear:
1553 self.clear()
1554 return self
1556 def error(self):
1557 """Set the stanza's type to ``'error'``."""
1558 self['type'] = 'error'
1559 return self
1561 def unhandled(self):
1562 """Called if no handlers have been registered to process this stanza.
1564 Meant to be overridden.
1566 pass
1568 def exception(self, e):
1569 """Handle exceptions raised during stanza processing.
1571 Meant to be overridden.
1573 log.exception('Error handling {%s}%s stanza', self.namespace,
1574 self.name)
1576 def send(self):
1577 """Queue the stanza to be sent on the XML stream.
1579 :param bool now: Indicates if the queue should be skipped and the
1580 stanza sent immediately. Useful for stream
1581 initialization. Defaults to ``False``.
1583 self.stream.send(self)
1585 def __copy__(self):
1586 """Return a copy of the stanza object that does not share the
1587 same underlying XML object, but does share the same XML stream.
1589 return self.__class__(xml=copy.deepcopy(self.xml),
1590 stream=self.stream)
1592 def __str__(self, top_level_ns=False):
1593 """Serialize the stanza's XML to a string.
1595 :param bool top_level_ns: Display the top-most namespace.
1596 Defaults to ``False``.
1598 xmlns = self.stream.default_ns if self.stream else ''
1599 return tostring(self.xml, xmlns=xmlns,
1600 stream=self.stream,
1601 top_level=(self.stream is None))
1604 #: A JSON/dictionary version of the XML content exposed through
1605 #: the stanza interfaces::
1607 #: >>> msg = Message()
1608 #: >>> msg.values
1609 #: {'body': '', 'from': , 'mucnick': '', 'mucroom': '',
1610 #: 'to': , 'type': 'normal', 'id': '', 'subject': ''}
1612 #: Likewise, assigning to the :attr:`values` will change the XML
1613 #: content::
1615 #: >>> msg = Message()
1616 #: >>> msg.values = {'body': 'Hi!', 'to': 'user@example.com'}
1617 #: >>> msg
1618 #: '<message to="user@example.com"><body>Hi!</body></message>'
1620 #: Child stanzas are exposed as nested dictionaries.
1621 ElementBase.values = property(ElementBase._get_stanza_values,
1622 ElementBase._set_stanza_values)
1625 # To comply with PEP8, method names now use underscores.
1626 # Deprecated method names are re-mapped for backwards compatibility.
1627 ElementBase.initPlugin = ElementBase.init_plugin
1628 ElementBase._getAttr = ElementBase._get_attr
1629 ElementBase._setAttr = ElementBase._set_attr
1630 ElementBase._delAttr = ElementBase._del_attr
1631 ElementBase._getSubText = ElementBase._get_sub_text
1632 ElementBase._setSubText = ElementBase._set_sub_text
1633 ElementBase._delSub = ElementBase._del_sub
1634 ElementBase.getStanzaValues = ElementBase._get_stanza_values
1635 ElementBase.setStanzaValues = ElementBase._set_stanza_values
1637 StanzaBase.setType = StanzaBase.set_type
1638 StanzaBase.getTo = StanzaBase.get_to
1639 StanzaBase.setTo = StanzaBase.set_to
1640 StanzaBase.getFrom = StanzaBase.get_from
1641 StanzaBase.setFrom = StanzaBase.set_from
1642 StanzaBase.getPayload = StanzaBase.get_payload
1643 StanzaBase.setPayload = StanzaBase.set_payload
1644 StanzaBase.delPayload = StanzaBase.del_payload