Merge branch 'master' into develop

This commit is contained in:
Lance Stout 2012-06-22 23:17:15 -07:00
commit 5d6019a962
9 changed files with 544 additions and 56 deletions

View File

@ -134,6 +134,7 @@ class BaseXMPP(XMLStream):
Callback('Presence', Callback('Presence',
MatchXPath("{%s}presence" % self.default_ns), MatchXPath("{%s}presence" % self.default_ns),
self._handle_presence)) self._handle_presence))
self.register_handler( self.register_handler(
Callback('Stream Error', Callback('Stream Error',
MatchXPath("{%s}error" % self.stream_ns), MatchXPath("{%s}error" % self.stream_ns),
@ -658,6 +659,27 @@ class BaseXMPP(XMLStream):
def _handle_stream_error(self, error): def _handle_stream_error(self, error):
self.event('stream_error', error) self.event('stream_error', error)
if error['condition'] == 'see-other-host':
other_host = error['see_other_host']
host = other_host
port = 5222
if '[' in other_host and ']' in other_host:
host = other_host.split(']')[0][1:]
elif ':' in other_host:
host = other_host.split(':')[0]
port_sec = other_host.split(']')[-1]
if ':' in port_sec:
port = int(port_sec.split(':')[1])
self.address = (host, port)
self.default_domain = host
self.dns_records = None
self.reconnect_delay = None
self.reconnect()
def _handle_message(self, msg): def _handle_message(self, msg):
"""Process incoming message stanzas.""" """Process incoming message stanzas."""
if not self.is_component and not msg['to'].bare: if not self.is_component and not msg['to'].bare:

View File

@ -57,5 +57,8 @@ __all__ = [
'xep_0224', # Attention 'xep_0224', # Attention
'xep_0231', # Bits of Binary 'xep_0231', # Bits of Binary
'xep_0249', # Direct MUC Invitations 'xep_0249', # Direct MUC Invitations
'xep_0256', # Last Activity in Presence
'xep_0258', # Security Labels in XMPP 'xep_0258', # Security Labels in XMPP
'xep_0270', # XMPP Compliance Suites 2010
'xep_0302', # XMPP Compliance Suites 2012
] ]

View File

@ -0,0 +1,67 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import logging
from sleekxmpp import Presence
from sleekxmpp.plugins import BasePlugin, register_plugin
from sleekxmpp.xmlstream import register_stanza_plugin
from sleekxmpp.plugins.xep_0012 import stanza, LastActivity
log = logging.getLogger(__name__)
class XEP_0256(BasePlugin):
name = 'xep_0256'
description = 'XEP-0256: Last Activity in Presence'
dependencies = set(['xep_0012'])
stanza = stanza
def plugin_init(self):
self.auto_last_activity = self.config.get('auto_last_activity', False)
register_stanza_plugin(Presence, LastActivity)
self.xmpp.add_filter('out', self._initial_presence_activity)
self.xmpp.add_event_handler('connected', self._reset_presence_activity)
self._initial_presence = set()
def _reset_presence_activity(self, e):
self._initial_presence = set()
def _initial_presence_activity(self, stanza):
if isinstance(stanza, Presence):
use_last_activity = False
if self.auto_last_activity and stanza['show'] in ('xa', 'away'):
use_last_activity = True
if stanza['from'] not in self._initial_presence:
self._initial_presence.add(stanza['from'])
use_last_activity = True
if use_last_activity:
plugin = self.xmpp['xep_0012']
try:
result = plugin.api['get_last_activity'](stanza['from'],
None,
stanza['to'])
seconds = result['last_activity']['seconds']
except XMPPError:
seconds = None
if seconds is not None:
stanza['last_activity']['seconds'] = seconds
return stanza
register_plugin(XEP_0256)

View File

@ -0,0 +1,20 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
from sleekxmpp.plugins import BasePlugin, register_plugin
class XEP_0270(BasePlugin):
name = 'xep_0270'
description = 'XEP-0270: XMPP Compliance Suites 2010'
dependencies = set(['xep_0030', 'xep_0115', 'xep_0054',
'xep_0163', 'xep_0045', 'xep_0085'])
register_plugin(XEP_0270)

View File

@ -0,0 +1,21 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
from sleekxmpp.plugins import BasePlugin, register_plugin
class XEP_0302(BasePlugin):
name = 'xep_0302'
description = 'XEP-0302: XMPP Compliance Suites 2012'
dependencies = set(['xep_0030', 'xep_0115', 'xep_0054',
'xep_0163', 'xep_0045', 'xep_0085',
'xep_0184', 'xep_0198'])
register_plugin(XEP_0302)

View File

@ -7,7 +7,7 @@
""" """
from sleekxmpp.stanza.rootstanza import RootStanza from sleekxmpp.stanza.rootstanza import RootStanza
from sleekxmpp.xmlstream import StanzaBase from sleekxmpp.xmlstream import StanzaBase, ET
class Message(RootStanza): class Message(RootStanza):
@ -54,13 +54,14 @@ class Message(RootStanza):
del_mucnick -- Dummy method to prevent deletion. del_mucnick -- Dummy method to prevent deletion.
""" """
namespace = 'jabber:client'
name = 'message' name = 'message'
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject', namespace = 'jabber:client'
'mucroom', 'mucnick'))
sub_interfaces = set(('body', 'subject'))
plugin_attrib = name plugin_attrib = name
types = set((None, 'normal', 'chat', 'headline', 'error', 'groupchat')) interfaces = set(['type', 'to', 'from', 'id', 'body', 'subject',
'thread', 'parent_thread', 'mucroom', 'mucnick'])
sub_interfaces = set(['body', 'subject', 'thread'])
lang_interfaces = sub_interfaces
types = set(['normal', 'chat', 'headline', 'error', 'groupchat'])
def get_type(self): def get_type(self):
""" """
@ -72,6 +73,31 @@ class Message(RootStanza):
""" """
return self._get_attr('type', 'normal') return self._get_attr('type', 'normal')
def get_parent_thread(self):
"""Return the message thread's parent thread."""
thread = self.xml.find('{%s}thread' % self.namespace)
if thread is not None:
return thread.attrib.get('parent', '')
return ''
def set_parent_thread(self, value):
"""Add or change the message thread's parent thread."""
thread = self.xml.find('{%s}thread' % self.namespace)
if value:
if thread is None:
thread = ET.Element('{%s}thread' % self.namespace)
self.xml.append(thread)
thread.attrib['parent'] = value
else:
if thread is not None and 'parent' in thread.attrib:
del thread.attrib['parent']
def del_parent_thread(self):
"""Delete the message thread's parent reference."""
thread = self.xml.find('{%s}thread' % self.namespace)
if thread is not None and 'parent' in thread.attrib:
del thread.attrib['parent']
def chat(self): def chat(self):
"""Set the message type to 'chat'.""" """Set the message type to 'chat'."""
self['type'] = 'chat' self['type'] = 'chat'
@ -96,10 +122,16 @@ class Message(RootStanza):
clear -- Indicates if existing content should be removed clear -- Indicates if existing content should be removed
before replying. Defaults to True. before replying. Defaults to True.
""" """
thread = self['thread']
parent = self['parent_thread']
StanzaBase.reply(self, clear) StanzaBase.reply(self, clear)
if self['type'] == 'groupchat': if self['type'] == 'groupchat':
self['to'] = self['to'].bare self['to'] = self['to'].bare
self['thread'] = thread
self['parent_thread'] = parent
del self['id'] del self['id']
if body is not None: if body is not None:

View File

@ -60,16 +60,17 @@ class Presence(RootStanza):
set_priority -- Set the value of the <priority> element. set_priority -- Set the value of the <priority> element.
""" """
namespace = 'jabber:client'
name = 'presence' name = 'presence'
interfaces = set(('type', 'to', 'from', 'id', 'show', namespace = 'jabber:client'
'status', 'priority'))
sub_interfaces = set(('show', 'status', 'priority'))
plugin_attrib = name plugin_attrib = name
interfaces = set(['type', 'to', 'from', 'id', 'show',
'status', 'priority'])
sub_interfaces = set(['show', 'status', 'priority'])
lang_interfaces = set(['status'])
types = set(('available', 'unavailable', 'error', 'probe', 'subscribe', types = set(['available', 'unavailable', 'error', 'probe', 'subscribe',
'subscribed', 'unsubscribe', 'unsubscribed')) 'subscribed', 'unsubscribe', 'unsubscribed'])
showtypes = set(('dnd', 'chat', 'xa', 'away')) showtypes = set(['dnd', 'chat', 'xa', 'away'])
def exception(self, e): def exception(self, e):
""" """

View File

@ -421,12 +421,6 @@ class ElementBase(object):
#: ``'{namespace}elementname'``. #: ``'{namespace}elementname'``.
self.tag = self.tag_name() self.tag = self.tag_name()
if 'lang' not in self.interfaces:
if isinstance(self.interfaces, tuple):
self.interfaces += ('lang',)
else:
self.interfaces.add('lang')
#: A :class:`weakref.weakref` to the parent stanza, if there is one. #: A :class:`weakref.weakref` to the parent stanza, if there is one.
#: If not, then :attr:`parent` is ``None``. #: If not, then :attr:`parent` is ``None``.
self.parent = None self.parent = None
@ -574,6 +568,7 @@ class ElementBase(object):
.. versionadded:: 1.0-Beta1 .. versionadded:: 1.0-Beta1
""" """
values = {} values = {}
values['lang'] = self['lang']
for interface in self.interfaces: for interface in self.interfaces:
values[interface] = self[interface] values[interface] = self[interface]
if interface in self.lang_interfaces: if interface in self.lang_interfaces:
@ -629,6 +624,8 @@ class ElementBase(object):
sub.values = subdict sub.values = subdict
self.iterables.append(sub) self.iterables.append(sub)
break break
elif interface == 'lang':
self[interface] = value
elif interface in self.interfaces: elif interface in self.interfaces:
self[full_interface] = value self[full_interface] = value
elif interface in self.plugin_attrib_map: elif interface in self.plugin_attrib_map:
@ -678,7 +675,7 @@ class ElementBase(object):
if attrib == 'substanzas': if attrib == 'substanzas':
return self.iterables return self.iterables
elif attrib in self.interfaces: elif attrib in self.interfaces or attrib == 'lang':
get_method = "get_%s" % attrib.lower() get_method = "get_%s" % attrib.lower()
get_method2 = "get%s" % attrib.title() get_method2 = "get%s" % attrib.title()
@ -752,7 +749,7 @@ class ElementBase(object):
if lang and attrib in self.lang_interfaces: if lang and attrib in self.lang_interfaces:
kwargs['lang'] = lang kwargs['lang'] = lang
if attrib in self.interfaces: if attrib in self.interfaces or attrib == 'lang':
if value is not None: if value is not None:
set_method = "set_%s" % attrib.lower() set_method = "set_%s" % attrib.lower()
set_method2 = "set%s" % attrib.title() set_method2 = "set%s" % attrib.title()
@ -838,7 +835,7 @@ class ElementBase(object):
if lang and attrib in self.lang_interfaces: if lang and attrib in self.lang_interfaces:
kwargs['lang'] = lang kwargs['lang'] = lang
if attrib in self.interfaces: if attrib in self.interfaces or attrib == 'lang':
del_method = "del_%s" % attrib.lower() del_method = "del_%s" % attrib.lower()
del_method2 = "del%s" % attrib.title() del_method2 = "del%s" % attrib.title()
@ -973,10 +970,6 @@ class ElementBase(object):
:param keep: Indicates if the element should be kept if its text is :param keep: Indicates if the element should be kept if its text is
removed. Defaults to False. removed. Defaults to False.
""" """
path = self._fix_ns(name, split=True)
element = self.xml.find(name)
parent = self.xml
default_lang = self.get_lang() default_lang = self.get_lang()
if lang is None: if lang is None:
lang = default_lang lang = default_lang
@ -984,32 +977,51 @@ class ElementBase(object):
if not text and not keep: if not text and not keep:
return self._del_sub(name, lang=lang) return self._del_sub(name, lang=lang)
if element is None: path = self._fix_ns(name, split=True)
# We need to add the element. If the provided name was name = path[-1]
# an XPath expression, some of the intermediate elements parent = self.xml
# may already exist. If so, we want to use those instead
# of generating new elements.
last_xml = self.xml
walked = []
for ename in path:
walked.append(ename)
element = self.xml.find("/".join(walked))
if element is None:
element = ET.Element(ename)
if lang:
element.attrib['{%s}lang' % XML_NS] = lang
last_xml.append(element)
parent = last_xml
last_xml = element
element = last_xml
if lang: # The first goal is to find the parent of the subelement, or, if
if element.attrib.get('{%s}lang' % XML_NS, default_lang) != lang: # we can't find that, the closest grandparent element.
element = ET.Element(ename) missing_path = []
element.attrib['{%s}lang' % XML_NS] = lang search_order = path[:-1]
parent.append(element) while search_order:
parent = self.xml.find('/'.join(search_order))
ename = search_order.pop()
if parent is not None:
break
else:
missing_path.append(ename)
missing_path.reverse()
# Find all existing elements that match the desired
# element path (there may be multiples due to different
# languages values).
if parent is not None:
elements = self.xml.findall('/'.join(path))
else:
parent = self.xml
elements = []
# Insert the remaining grandparent elements that don't exist yet.
for ename in missing_path:
element = ET.Element(ename)
parent.append(element)
parent = element
# Re-use an existing element with the proper language, if one exists.
for element in elements:
elang = element.attrib.get('{%s}lang' % XML_NS, default_lang)
if not lang and elang == default_lang or lang and lang == elang:
element.text = text
return element
# No useable element exists, so create a new one.
element = ET.Element(name)
element.text = text element.text = text
if lang and lang != default_lang:
element.attrib['{%s}lang' % XML_NS] = lang
parent.append(element)
return element return element
def _set_all_sub_text(self, name, values, keep=False, lang=None): def _set_all_sub_text(self, name, values, keep=False, lang=None):
@ -1184,6 +1196,7 @@ class ElementBase(object):
out = [] out = []
out += [x for x in self.interfaces] out += [x for x in self.interfaces]
out += [x for x in self.loaded_plugins] out += [x for x in self.loaded_plugins]
out.append('lang')
if self.iterables: if self.iterables:
out.append('substanzas') out.append('substanzas')
return out return out
@ -1263,7 +1276,7 @@ class ElementBase(object):
""" """
return "{%s}%s" % (cls.namespace, cls.name) return "{%s}%s" % (cls.namespace, cls.name)
def get_lang(self): def get_lang(self, lang=None):
result = self.xml.attrib.get('{%s}lang' % XML_NS, '') result = self.xml.attrib.get('{%s}lang' % XML_NS, '')
if not result and self.parent and self.parent(): if not result and self.parent and self.parent():
return self.parent()['lang'] return self.parent()['lang']

View File

@ -1,5 +1,6 @@
from sleekxmpp.test import * from sleekxmpp.test import *
from sleekxmpp.xmlstream.stanzabase import ElementBase from sleekxmpp.xmlstream.stanzabase import ElementBase
from sleekxmpp.thirdparty import OrderedDict
class TestElementBase(SleekTest): class TestElementBase(SleekTest):
@ -760,7 +761,7 @@ class TestElementBase(SleekTest):
<foo xmlns="foo" /> <foo xmlns="foo" />
""") """)
self.assertFalse(stanza['bar'], self.assertFalse(stanza['bar'],
"Returned True for missing bool interface element.") "Returned True for missing bool interface element.")
stanza['bar'] = True stanza['bar'] = True
@ -797,7 +798,7 @@ class TestElementBase(SleekTest):
namespace = 'baz' namespace = 'baz'
plugin_attrib = name plugin_attrib = name
plugin_multi_attrib = 'bazs' plugin_multi_attrib = 'bazs'
register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True) register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True)
register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True) register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True)
@ -829,9 +830,9 @@ class TestElementBase(SleekTest):
<baz xmlns="baz" /> <baz xmlns="baz" />
""") """)
self.assertEqual(len(bars), 2, self.assertEqual(len(bars), 2,
"Wrong number of <bar /> stanzas: %s" % len(bars)) "Wrong number of <bar /> stanzas: %s" % len(bars))
self.assertEqual(len(bazs), 2, self.assertEqual(len(bazs), 2,
"Wrong number of <baz /> stanzas: %s" % len(bazs)) "Wrong number of <baz /> stanzas: %s" % len(bazs))
def testSetMultiAttrib(self): def testSetMultiAttrib(self):
@ -853,7 +854,7 @@ class TestElementBase(SleekTest):
namespace = 'baz' namespace = 'baz'
plugin_attrib = name plugin_attrib = name
plugin_multi_attrib = 'bazs' plugin_multi_attrib = 'bazs'
register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True) register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True)
register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True) register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True)
@ -906,7 +907,7 @@ class TestElementBase(SleekTest):
namespace = 'baz' namespace = 'baz'
plugin_attrib = name plugin_attrib = name
plugin_multi_attrib = 'bazs' plugin_multi_attrib = 'bazs'
register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True) register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True)
register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True) register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True)
@ -938,5 +939,313 @@ class TestElementBase(SleekTest):
self.assertEqual(len(stanza['substanzas']), 2, self.assertEqual(len(stanza['substanzas']), 2,
"Wrong number of substanzas: %s" % len(stanza['substanzas'])) "Wrong number of substanzas: %s" % len(stanza['substanzas']))
def testDefaultLang(self):
"""Test setting a normal subinterface when a default language is set"""
class TestStanza(ElementBase):
name = 'foo'
namespace = 'test'
interfaces = set(['test'])
sub_interfaces = interfaces
lang_interfaces = interfaces
stanza = TestStanza()
stanza['lang'] = 'sv'
stanza['test'] = 'hej'
self.check(stanza, """
<foo xmlns="test" xml:lang="sv">
<test>hej</test>
</foo>
""")
self.assertEqual(stanza['test'], 'hej',
"Incorrect subinterface value: %s" % stanza['test'])
self.assertEqual(stanza['test|sv'], 'hej',
"Incorrect subinterface value: %s" % stanza['test|sv'])
def testSpecifyLangWithDefault(self):
"""Test specifying various languages."""
class TestStanza(ElementBase):
name = 'foo'
namespace = 'test'
interfaces = set(['test'])
sub_interfaces = interfaces
lang_interfaces = interfaces
stanza = TestStanza()
stanza['lang'] = 'sv'
stanza['test'] = 'hej'
stanza['test|en'] = 'hi'
stanza['test|es'] = 'hola'
self.check(stanza, """
<foo xmlns="test" xml:lang="sv">
<test>hej</test>
<test xml:lang="en">hi</test>
<test xml:lang="es">hola</test>
</foo>
""")
self.assertEqual(stanza['test'], 'hej',
"Incorrect subinterface value: %s" % stanza['test'])
self.assertEqual(stanza['test|sv'], 'hej',
"Incorrect subinterface value: %s" % stanza['test|sv'])
self.assertEqual(stanza['test|en'], 'hi',
"Incorrect subinterface value: %s" % stanza['test|en'])
self.assertEqual(stanza['test|es'], 'hola',
"Incorrect subinterface value: %s" % stanza['test|es'])
def testSpecifyLangWithNoDefault(self):
"""Test specifying various languages."""
class TestStanza(ElementBase):
name = 'foo'
namespace = 'test'
interfaces = set(['test'])
sub_interfaces = interfaces
lang_interfaces = interfaces
stanza = TestStanza()
stanza['test'] = 'hej'
stanza['test|en'] = 'hi'
stanza['test|es'] = 'hola'
self.check(stanza, """
<foo xmlns="test">
<test>hej</test>
<test xml:lang="en">hi</test>
<test xml:lang="es">hola</test>
</foo>
""")
self.assertEqual(stanza['test'], 'hej',
"Incorrect subinterface value: %s" % stanza['test'])
self.assertEqual(stanza['test|en'], 'hi',
"Incorrect subinterface value: %s" % stanza['test|en'])
self.assertEqual(stanza['test|es'], 'hola',
"Incorrect subinterface value: %s" % stanza['test|es'])
def testModifyLangInterfaceWithDefault(self):
"""Test resetting an interface when a default lang is used."""
class TestStanza(ElementBase):
name = 'foo'
namespace = 'test'
interfaces = set(['test'])
sub_interfaces = interfaces
lang_interfaces = interfaces
stanza = TestStanza()
stanza['lang'] = 'es'
stanza['test'] = 'hola'
stanza['test|en'] = 'hi'
self.check(stanza, """
<foo xmlns="test" xml:lang="es">
<test>hola</test>
<test xml:lang="en">hi</test>
</foo>
""")
stanza['test'] = 'adios'
stanza['test|en'] = 'bye'
self.check(stanza, """
<foo xmlns="test" xml:lang="es">
<test>adios</test>
<test xml:lang="en">bye</test>
</foo>
""")
self.assertEqual(stanza['test'], 'adios',
"Incorrect subinterface value: %s" % stanza['test'])
self.assertEqual(stanza['test|es'], 'adios',
"Incorrect subinterface value: %s" % stanza['test|es'])
self.assertEqual(stanza['test|en'], 'bye',
"Incorrect subinterface value: %s" % stanza['test|en'])
stanza['test|es'] = 'hola'
self.check(stanza, """
<foo xmlns="test" xml:lang="es">
<test>hola</test>
<test xml:lang="en">bye</test>
</foo>
""")
self.assertEqual(stanza['test'], 'hola',
"Incorrect subinterface value: %s" % stanza['test'])
self.assertEqual(stanza['test|es'], 'hola',
"Incorrect subinterface value: %s" % stanza['test|es'])
def testModifyLangInterfaceWithNoDefault(self):
"""Test resetting an interface when no default lang is used."""
class TestStanza(ElementBase):
name = 'foo'
namespace = 'test'
interfaces = set(['test'])
sub_interfaces = interfaces
lang_interfaces = interfaces
stanza = TestStanza()
stanza['test'] = 'hola'
stanza['test|en'] = 'hi'
self.check(stanza, """
<foo xmlns="test">
<test>hola</test>
<test xml:lang="en">hi</test>
</foo>
""")
stanza['test'] = 'adios'
stanza['test|en'] = 'bye'
self.check(stanza, """
<foo xmlns="test">
<test>adios</test>
<test xml:lang="en">bye</test>
</foo>
""")
self.assertEqual(stanza['test'], 'adios',
"Incorrect subinterface value: %s" % stanza['test'])
self.assertEqual(stanza['test'], 'adios',
"Incorrect subinterface value: %s" % stanza['test|es'])
self.assertEqual(stanza['test|en'], 'bye',
"Incorrect subinterface value: %s" % stanza['test|en'])
def testDelInterfacesWithDefaultLang(self):
"""Test deleting interfaces with a default lang set."""
class TestStanza(ElementBase):
name = 'foo'
namespace = 'test'
interfaces = set(['test'])
sub_interfaces = interfaces
lang_interfaces = interfaces
stanza = TestStanza()
stanza['lang'] = 'en'
stanza['test'] = 'hi'
stanza['test|no'] = 'hej'
stanza['test|fr'] = 'bonjour'
self.check(stanza, """
<foo xmlns="test" xml:lang="en">
<test>hi</test>
<test xml:lang="no">hej</test>
<test xml:lang="fr">bonjour</test>
</foo>
""")
del stanza['test']
self.check(stanza, """
<foo xmlns="test" xml:lang="en">
<test xml:lang="no">hej</test>
<test xml:lang="fr">bonjour</test>
</foo>
""")
del stanza['test|no']
self.check(stanza, """
<foo xmlns="test" xml:lang="en">
<test xml:lang="fr">bonjour</test>
</foo>
""")
def testDelInterfacesWithNoDefaultLang(self):
"""Test deleting interfaces with no default lang set."""
class TestStanza(ElementBase):
name = 'foo'
namespace = 'test'
interfaces = set(['test'])
sub_interfaces = interfaces
lang_interfaces = interfaces
stanza = TestStanza()
stanza['test'] = 'hi'
stanza['test|no'] = 'hej'
stanza['test|fr'] = 'bonjour'
self.check(stanza, """
<foo xmlns="test">
<test>hi</test>
<test xml:lang="no">hej</test>
<test xml:lang="fr">bonjour</test>
</foo>
""")
del stanza['test']
self.check(stanza, """
<foo xmlns="test">
<test xml:lang="no">hej</test>
<test xml:lang="fr">bonjour</test>
</foo>
""")
del stanza['test|no']
self.check(stanza, """
<foo xmlns="test">
<test xml:lang="fr">bonjour</test>
</foo>
""")
def testStarLang(self):
"""Test using interface|*."""
class TestStanza(ElementBase):
name = 'foo'
namespace = 'test'
interfaces = set(['test'])
sub_interfaces = interfaces
lang_interfaces = interfaces
data = OrderedDict()
data['en'] = 'hi'
data['fr'] = 'bonjour'
data['no'] = 'hej'
stanza = TestStanza()
stanza['test|*'] = data
self.check(stanza, """
<foo xmlns="test">
<test xml:lang="en">hi</test>
<test xml:lang="fr">bonjour</test>
<test xml:lang="no">hej</test>
</foo>
""")
data2 = stanza['test|*']
self.assertEqual(data, data2,
"Did not extract expected language data: %s" % data2)
del stanza['test|*']
self.check(stanza, """
<foo xmlns="test" />
""")
suite = unittest.TestLoader().loadTestsFromTestCase(TestElementBase) suite = unittest.TestLoader().loadTestsFromTestCase(TestElementBase)