Compare commits
17 Commits
1.1.6
...
sleek-1.1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a26a8bd79c | ||
|
|
9307a6915f | ||
|
|
85ef2d8d0b | ||
|
|
c2c7cc032b | ||
|
|
e4911e9391 | ||
|
|
b11e1ee92d | ||
|
|
5027d00c10 | ||
|
|
69ddeceb49 | ||
|
|
82698672bb | ||
|
|
9cec284947 | ||
|
|
dc501d1902 | ||
|
|
100e504b7f | ||
|
|
8a745c5e81 | ||
|
|
bf0a157c5d | ||
|
|
f49818be06 | ||
|
|
1ad171dfe5 | ||
|
|
2a78570d65 |
@@ -45,7 +45,7 @@ The latest source code for SleekXMPP may be found on `Github
|
||||
``develop`` branch.
|
||||
|
||||
**Latest Release**
|
||||
- `1.1.6 <http://github.com/fritzy/SleekXMPP/zipball/1.1.6>`_
|
||||
- `1.1.8 <http://github.com/fritzy/SleekXMPP/zipball/1.1.8>`_
|
||||
|
||||
**Develop Releases**
|
||||
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
|
||||
|
||||
@@ -122,6 +122,19 @@ if __name__ == '__main__':
|
||||
xmpp.register_plugin('xep_0060') # PubSub
|
||||
xmpp.register_plugin('xep_0199') # XMPP Ping
|
||||
|
||||
# If you are connecting to Facebook and wish to use the
|
||||
# X-FACEBOOK-PLATFORM authentication mechanism, you will need
|
||||
# your API key and an access token. Then you'll set:
|
||||
# xmpp.credentials['api_key'] = 'THE_API_KEY'
|
||||
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
|
||||
|
||||
# If you are connecting to MSN, then you will need an
|
||||
# access token, and it does not matter what JID you
|
||||
# specify other than that the domain is 'messenger.live.com',
|
||||
# so '_@messenger.live.com' will work. You can specify
|
||||
# the access token as so:
|
||||
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
|
||||
|
||||
# If you are working with an OpenFire server, you may need
|
||||
# to adjust the SSL version used:
|
||||
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
|
||||
|
||||
2
setup.py
2
setup.py
@@ -84,10 +84,12 @@ packages = [ 'sleekxmpp',
|
||||
'sleekxmpp/plugins/xep_0153',
|
||||
'sleekxmpp/plugins/xep_0172',
|
||||
'sleekxmpp/plugins/xep_0184',
|
||||
'sleekxmpp/plugins/xep_0186',
|
||||
'sleekxmpp/plugins/xep_0198',
|
||||
'sleekxmpp/plugins/xep_0199',
|
||||
'sleekxmpp/plugins/xep_0202',
|
||||
'sleekxmpp/plugins/xep_0203',
|
||||
'sleekxmpp/plugins/xep_0221',
|
||||
'sleekxmpp/plugins/xep_0224',
|
||||
'sleekxmpp/plugins/xep_0231',
|
||||
'sleekxmpp/plugins/xep_0249',
|
||||
|
||||
@@ -134,6 +134,7 @@ class BaseXMPP(XMLStream):
|
||||
Callback('Presence',
|
||||
MatchXPath("{%s}presence" % self.default_ns),
|
||||
self._handle_presence))
|
||||
|
||||
self.register_handler(
|
||||
Callback('Stream Error',
|
||||
MatchXPath("{%s}error" % self.stream_ns),
|
||||
@@ -658,6 +659,27 @@ class BaseXMPP(XMLStream):
|
||||
def _handle_stream_error(self, 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):
|
||||
"""Process incoming message stanzas."""
|
||||
if not self.is_component and not msg['to'].bare:
|
||||
|
||||
@@ -45,14 +45,19 @@ __all__ = [
|
||||
'xep_0163', # Personal Eventing Protocol
|
||||
'xep_0172', # User Nickname
|
||||
'xep_0184', # Message Receipts
|
||||
'xep_0186', # Invisible Command
|
||||
'xep_0198', # Stream Management
|
||||
'xep_0199', # Ping
|
||||
'xep_0202', # Entity Time
|
||||
'xep_0203', # Delayed Delivery
|
||||
'xep_0221', # Data Forms Media Element
|
||||
'xep_0222', # Persistent Storage of Public Data via Pubsub
|
||||
'xep_0223', # Persistent Storage of Private Data via Pubsub
|
||||
'xep_0224', # Attention
|
||||
'xep_0231', # Bits of Binary
|
||||
'xep_0249', # Direct MUC Invitations
|
||||
'xep_0256', # Last Activity in Presence
|
||||
'xep_0258', # Security Labels in XMPP
|
||||
'xep_0270', # XMPP Compliance Suites 2010
|
||||
'xep_0302', # XMPP Compliance Suites 2012
|
||||
]
|
||||
|
||||
16
sleekxmpp/plugins/xep_0186/__init__.py
Normal file
16
sleekxmpp/plugins/xep_0186/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
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.base import register_plugin
|
||||
|
||||
from sleekxmpp.plugins.xep_0186 import stanza
|
||||
from sleekxmpp.plugins.xep_0186.stanza import Invisible, Visible
|
||||
from sleekxmpp.plugins.xep_0186.invisible_command import XEP_0186
|
||||
|
||||
|
||||
register_plugin(XEP_0186)
|
||||
44
sleekxmpp/plugins/xep_0186/invisible_command.py
Normal file
44
sleekxmpp/plugins/xep_0186/invisible_command.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
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 Iq
|
||||
from sleekxmpp.plugins import BasePlugin
|
||||
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||
from sleekxmpp.plugins.xep_0186 import stanza, Visible, Invisible
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XEP_0186(BasePlugin):
|
||||
|
||||
name = 'xep_0186'
|
||||
description = 'XEP-0186: Invisible Command'
|
||||
dependencies = set(['xep_0030'])
|
||||
|
||||
def plugin_init(self):
|
||||
register_stanza_plugin(Iq, Visible)
|
||||
register_stanza_plugin(Iq, Invisible)
|
||||
|
||||
def set_invisible(self, ifrom=None, block=True, callback=None,
|
||||
timeout=None):
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'set'
|
||||
iq['from'] = ifrom
|
||||
iq.enable('invisible')
|
||||
iq.send(block=block, callback=callback, timeout=timeout)
|
||||
|
||||
def set_visible(self, ifrom=None, block=True, callback=None,
|
||||
timeout=None):
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'set'
|
||||
iq['from'] = ifrom
|
||||
iq.enable('visible')
|
||||
iq.send(block=block, callback=callback, timeout=timeout)
|
||||
23
sleekxmpp/plugins/xep_0186/stanza.py
Normal file
23
sleekxmpp/plugins/xep_0186/stanza.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
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.xmlstream import ElementBase, ET
|
||||
|
||||
|
||||
class Invisible(ElementBase):
|
||||
name = 'invisible'
|
||||
namespace = 'urn:xmpp:invisible:0'
|
||||
plugin_attrib = 'invisible'
|
||||
interfaces = set()
|
||||
|
||||
|
||||
class Visible(ElementBase):
|
||||
name = 'visible'
|
||||
namespace = 'urn:xmpp:visible:0'
|
||||
plugin_attrib = 'visible'
|
||||
interfaces = set()
|
||||
16
sleekxmpp/plugins/xep_0221/__init__.py
Normal file
16
sleekxmpp/plugins/xep_0221/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
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.base import register_plugin
|
||||
|
||||
from sleekxmpp.plugins.xep_0221 import stanza
|
||||
from sleekxmpp.plugins.xep_0221.stanza import Media, URI
|
||||
from sleekxmpp.plugins.xep_0221.media import XEP_0221
|
||||
|
||||
|
||||
register_plugin(XEP_0221)
|
||||
27
sleekxmpp/plugins/xep_0221/media.py
Normal file
27
sleekxmpp/plugins/xep_0221/media.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""
|
||||
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.plugins import BasePlugin
|
||||
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||
from sleekxmpp.plugins.xep_0221 import stanza, Media, URI
|
||||
from sleekxmpp.plugins.xep_0004 import FormField
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XEP_0221(BasePlugin):
|
||||
|
||||
name = 'xep_0221'
|
||||
description = 'XEP-0221: Data Forms Media Element'
|
||||
dependencies = set(['xep_0004'])
|
||||
|
||||
def plugin_init(self):
|
||||
register_stanza_plugin(FormField, Media)
|
||||
42
sleekxmpp/plugins/xep_0221/stanza.py
Normal file
42
sleekxmpp/plugins/xep_0221/stanza.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
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.xmlstream import ElementBase, register_stanza_plugin
|
||||
|
||||
|
||||
class Media(ElementBase):
|
||||
name = 'media'
|
||||
namespace = 'urn:xmpp:media-element'
|
||||
plugin_attrib = 'media'
|
||||
interfaces = set(['height', 'width', 'alt'])
|
||||
|
||||
def add_uri(self, value, itype):
|
||||
uri = URI()
|
||||
uri['value'] = value
|
||||
uri['type'] = itype
|
||||
self.append(uri)
|
||||
|
||||
|
||||
class URI(ElementBase):
|
||||
name = 'uri'
|
||||
namespace = 'urn:xmpp:media-element'
|
||||
plugin_attrib = 'uri'
|
||||
plugin_multi_attrib = 'uris'
|
||||
interfaces = set(['type', 'value'])
|
||||
|
||||
def get_value(self):
|
||||
return self.xml.text
|
||||
|
||||
def set_value(self, value):
|
||||
self.xml.text = value
|
||||
|
||||
def del_value(self):
|
||||
sel.xml.text = ''
|
||||
|
||||
|
||||
register_stanza_plugin(Media, URI, iterable=True)
|
||||
67
sleekxmpp/plugins/xep_0256.py
Normal file
67
sleekxmpp/plugins/xep_0256.py
Normal 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)
|
||||
20
sleekxmpp/plugins/xep_0270.py
Normal file
20
sleekxmpp/plugins/xep_0270.py
Normal 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)
|
||||
21
sleekxmpp/plugins/xep_0302.py
Normal file
21
sleekxmpp/plugins/xep_0302.py
Normal 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)
|
||||
@@ -7,7 +7,7 @@
|
||||
"""
|
||||
|
||||
from sleekxmpp.stanza.rootstanza import RootStanza
|
||||
from sleekxmpp.xmlstream import StanzaBase
|
||||
from sleekxmpp.xmlstream import StanzaBase, ET
|
||||
|
||||
|
||||
class Message(RootStanza):
|
||||
@@ -54,13 +54,14 @@ class Message(RootStanza):
|
||||
del_mucnick -- Dummy method to prevent deletion.
|
||||
"""
|
||||
|
||||
namespace = 'jabber:client'
|
||||
name = 'message'
|
||||
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject',
|
||||
'mucroom', 'mucnick'))
|
||||
sub_interfaces = set(('body', 'subject'))
|
||||
namespace = 'jabber:client'
|
||||
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):
|
||||
"""
|
||||
@@ -72,6 +73,31 @@ class Message(RootStanza):
|
||||
"""
|
||||
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):
|
||||
"""Set the message type to 'chat'."""
|
||||
self['type'] = 'chat'
|
||||
@@ -96,10 +122,16 @@ class Message(RootStanza):
|
||||
clear -- Indicates if existing content should be removed
|
||||
before replying. Defaults to True.
|
||||
"""
|
||||
thread = self['thread']
|
||||
parent = self['parent_thread']
|
||||
|
||||
StanzaBase.reply(self, clear)
|
||||
if self['type'] == 'groupchat':
|
||||
self['to'] = self['to'].bare
|
||||
|
||||
self['thread'] = thread
|
||||
self['parent_thread'] = parent
|
||||
|
||||
del self['id']
|
||||
|
||||
if body is not None:
|
||||
|
||||
@@ -60,16 +60,17 @@ class Presence(RootStanza):
|
||||
set_priority -- Set the value of the <priority> element.
|
||||
"""
|
||||
|
||||
namespace = 'jabber:client'
|
||||
name = 'presence'
|
||||
interfaces = set(('type', 'to', 'from', 'id', 'show',
|
||||
'status', 'priority'))
|
||||
sub_interfaces = set(('show', 'status', 'priority'))
|
||||
namespace = 'jabber:client'
|
||||
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',
|
||||
'subscribed', 'unsubscribe', 'unsubscribed'))
|
||||
showtypes = set(('dnd', 'chat', 'xa', 'away'))
|
||||
types = set(['available', 'unavailable', 'error', 'probe', 'subscribe',
|
||||
'subscribed', 'unsubscribe', 'unsubscribed'])
|
||||
showtypes = set(['dnd', 'chat', 'xa', 'away'])
|
||||
|
||||
def exception(self, e):
|
||||
"""
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
# We don't want to have to import the entire library
|
||||
# just to get the version info for setup.py
|
||||
|
||||
__version__ = '1.1.6'
|
||||
__version_info__ = (1, 1, 6, '', 0)
|
||||
__version__ = '1.1.8'
|
||||
__version_info__ = (1, 1, 8, '', 0)
|
||||
|
||||
@@ -421,12 +421,6 @@ class ElementBase(object):
|
||||
#: ``'{namespace}elementname'``.
|
||||
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.
|
||||
#: If not, then :attr:`parent` is ``None``.
|
||||
self.parent = None
|
||||
@@ -574,6 +568,7 @@ class ElementBase(object):
|
||||
.. versionadded:: 1.0-Beta1
|
||||
"""
|
||||
values = {}
|
||||
values['lang'] = self['lang']
|
||||
for interface in self.interfaces:
|
||||
values[interface] = self[interface]
|
||||
if interface in self.lang_interfaces:
|
||||
@@ -629,6 +624,8 @@ class ElementBase(object):
|
||||
sub.values = subdict
|
||||
self.iterables.append(sub)
|
||||
break
|
||||
elif interface == 'lang':
|
||||
self[interface] = value
|
||||
elif interface in self.interfaces:
|
||||
self[full_interface] = value
|
||||
elif interface in self.plugin_attrib_map:
|
||||
@@ -678,7 +675,7 @@ class ElementBase(object):
|
||||
|
||||
if attrib == 'substanzas':
|
||||
return self.iterables
|
||||
elif attrib in self.interfaces:
|
||||
elif attrib in self.interfaces or attrib == 'lang':
|
||||
get_method = "get_%s" % attrib.lower()
|
||||
get_method2 = "get%s" % attrib.title()
|
||||
|
||||
@@ -752,7 +749,7 @@ class ElementBase(object):
|
||||
if lang and attrib in self.lang_interfaces:
|
||||
kwargs['lang'] = lang
|
||||
|
||||
if attrib in self.interfaces:
|
||||
if attrib in self.interfaces or attrib == 'lang':
|
||||
if value is not None:
|
||||
set_method = "set_%s" % attrib.lower()
|
||||
set_method2 = "set%s" % attrib.title()
|
||||
@@ -838,7 +835,7 @@ class ElementBase(object):
|
||||
if lang and attrib in self.lang_interfaces:
|
||||
kwargs['lang'] = lang
|
||||
|
||||
if attrib in self.interfaces:
|
||||
if attrib in self.interfaces or attrib == 'lang':
|
||||
del_method = "del_%s" % attrib.lower()
|
||||
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
|
||||
removed. Defaults to False.
|
||||
"""
|
||||
path = self._fix_ns(name, split=True)
|
||||
element = self.xml.find(name)
|
||||
parent = self.xml
|
||||
|
||||
default_lang = self.get_lang()
|
||||
if lang is None:
|
||||
lang = default_lang
|
||||
@@ -984,32 +977,51 @@ class ElementBase(object):
|
||||
if not text and not keep:
|
||||
return self._del_sub(name, lang=lang)
|
||||
|
||||
if element is None:
|
||||
# We need to add the element. If the provided name was
|
||||
# an XPath expression, some of the intermediate elements
|
||||
# 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
|
||||
path = self._fix_ns(name, split=True)
|
||||
name = path[-1]
|
||||
parent = self.xml
|
||||
|
||||
if element.attrib.get('{%s}lang' % XML_NS, default_lang) != lang:
|
||||
# The first goal is to find the parent of the subelement, or, if
|
||||
# we can't find that, the closest grandparent element.
|
||||
missing_path = []
|
||||
search_order = path[:-1]
|
||||
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)
|
||||
if lang:
|
||||
element.attrib['{%s}lang' % XML_NS] = lang
|
||||
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
|
||||
if lang and lang != default_lang:
|
||||
element.attrib['{%s}lang' % XML_NS] = lang
|
||||
parent.append(element)
|
||||
return element
|
||||
|
||||
def _set_all_sub_text(self, name, values, keep=False, lang=None):
|
||||
@@ -1184,6 +1196,7 @@ class ElementBase(object):
|
||||
out = []
|
||||
out += [x for x in self.interfaces]
|
||||
out += [x for x in self.loaded_plugins]
|
||||
out.append('lang')
|
||||
if self.iterables:
|
||||
out.append('substanzas')
|
||||
return out
|
||||
@@ -1263,7 +1276,7 @@ class ElementBase(object):
|
||||
"""
|
||||
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, '')
|
||||
if not result and self.parent and self.parent():
|
||||
return self.parent()['lang']
|
||||
|
||||
@@ -212,6 +212,9 @@ class XMLStream(object):
|
||||
#: proxy based on the settings in :attr:`proxy_config`.
|
||||
self.use_proxy = False
|
||||
|
||||
#: If set to ``True``, attempt to use IPv6.
|
||||
self.use_ipv6 = True
|
||||
|
||||
#: An optional dictionary of proxy settings. It may provide:
|
||||
#: :host: The host offering proxy services.
|
||||
#: :port: The port for the proxy service.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from sleekxmpp.test import *
|
||||
from sleekxmpp.xmlstream.stanzabase import ElementBase
|
||||
from sleekxmpp.thirdparty import OrderedDict
|
||||
|
||||
|
||||
class TestElementBase(SleekTest):
|
||||
@@ -760,7 +761,7 @@ class TestElementBase(SleekTest):
|
||||
<foo xmlns="foo" />
|
||||
""")
|
||||
|
||||
self.assertFalse(stanza['bar'],
|
||||
self.assertFalse(stanza['bar'],
|
||||
"Returned True for missing bool interface element.")
|
||||
|
||||
stanza['bar'] = True
|
||||
@@ -797,7 +798,7 @@ class TestElementBase(SleekTest):
|
||||
namespace = 'baz'
|
||||
plugin_attrib = name
|
||||
plugin_multi_attrib = 'bazs'
|
||||
|
||||
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True)
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True)
|
||||
|
||||
@@ -829,9 +830,9 @@ class TestElementBase(SleekTest):
|
||||
<baz xmlns="baz" />
|
||||
""")
|
||||
|
||||
self.assertEqual(len(bars), 2,
|
||||
self.assertEqual(len(bars), 2,
|
||||
"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))
|
||||
|
||||
def testSetMultiAttrib(self):
|
||||
@@ -853,7 +854,7 @@ class TestElementBase(SleekTest):
|
||||
namespace = 'baz'
|
||||
plugin_attrib = name
|
||||
plugin_multi_attrib = 'bazs'
|
||||
|
||||
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True)
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True)
|
||||
|
||||
@@ -906,7 +907,7 @@ class TestElementBase(SleekTest):
|
||||
namespace = 'baz'
|
||||
plugin_attrib = name
|
||||
plugin_multi_attrib = 'bazs'
|
||||
|
||||
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza1, iterable=True)
|
||||
register_stanza_plugin(TestStanza, TestMultiStanza2, iterable=True)
|
||||
|
||||
@@ -938,5 +939,313 @@ class TestElementBase(SleekTest):
|
||||
self.assertEqual(len(stanza['substanzas']), 2,
|
||||
"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)
|
||||
|
||||
Reference in New Issue
Block a user