summaryrefslogtreecommitdiff
path: root/sleekxmpp/stanza/iq.py
diff options
context:
space:
mode:
authorLance Stout <lancestout@gmail.com>2010-07-29 23:58:25 -0400
committerLance Stout <lancestout@gmail.com>2010-07-29 23:58:25 -0400
commitcbed8029bad691f8353854dc264f83303a196a09 (patch)
treef1046af3647f7aa9f6c806af9022c9c51f93836c /sleekxmpp/stanza/iq.py
parent1da3e5b35eb59909d4d6903b1c0190a7aad98a30 (diff)
downloadslixmpp-cbed8029bad691f8353854dc264f83303a196a09.tar.gz
slixmpp-cbed8029bad691f8353854dc264f83303a196a09.tar.bz2
slixmpp-cbed8029bad691f8353854dc264f83303a196a09.tar.xz
slixmpp-cbed8029bad691f8353854dc264f83303a196a09.zip
Updated, cleaned, and documented Iq stanza class. Also added unit tests.
Diffstat (limited to 'sleekxmpp/stanza/iq.py')
-rw-r--r--sleekxmpp/stanza/iq.py233
1 files changed, 165 insertions, 68 deletions
diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py
index daf05b43..c735ae64 100644
--- a/sleekxmpp/stanza/iq.py
+++ b/sleekxmpp/stanza/iq.py
@@ -5,73 +5,170 @@
See the file LICENSE for copying permission.
"""
-from .. xmlstream.stanzabase import StanzaBase
-from xml.etree import cElementTree as ET
-from . error import Error
-from .. xmlstream.handler.waiter import Waiter
-from .. xmlstream.matcher.id import MatcherId
-from . rootstanza import RootStanza
+
+from sleekxmpp.stanza import Error
+from sleekxmpp.stanza.rootstanza import RootStanza
+from sleekxmpp.xmlstream import RESPONSE_TIMEOUT
+from sleekxmpp.xmlstream.stanzabase import StanzaBase, ET
+from sleekxmpp.xmlstream.handler import Waiter
+from sleekxmpp.xmlstream.matcher import MatcherId
+
class Iq(RootStanza):
- interfaces = set(('type', 'to', 'from', 'id','query'))
- types = set(('get', 'result', 'set', 'error'))
- name = 'iq'
- plugin_attrib = name
- namespace = 'jabber:client'
-
- def __init__(self, *args, **kwargs):
- StanzaBase.__init__(self, *args, **kwargs)
- if self['id'] == '':
- if self.stream is not None:
- self['id'] = self.stream.getNewId()
- else:
- self['id'] = '0'
-
- def unhandled(self):
- if self['type'] in ('get', 'set'):
- self.reply()
- self['error']['condition'] = 'feature-not-implemented'
- self['error']['text'] = 'No handlers registered for this request.'
- self.send()
-
- def setPayload(self, value):
- self.clear()
- StanzaBase.setPayload(self, value)
- return self
-
- def setQuery(self, value):
- query = self.xml.find("{%s}query" % value)
- if query is None and value:
- self.clear()
- query = ET.Element("{%s}query" % value)
- self.xml.append(query)
- return self
-
- def getQuery(self):
- for child in self.xml.getchildren():
- if child.tag.endswith('query'):
- ns =child.tag.split('}')[0]
- if '{' in ns:
- ns = ns[1:]
- return ns
- return ''
-
- def reply(self):
- self['type'] = 'result'
- StanzaBase.reply(self)
- return self
-
- def delQuery(self):
- for child in self.getchildren():
- if child.tag.endswith('query'):
- self.xml.remove(child)
- return self
-
- def send(self, block=True, timeout=10):
- if block and self['type'] in ('get', 'set'):
- waitfor = Waiter('IqWait_%s' % self['id'], MatcherId(self['id']))
- self.stream.registerHandler(waitfor)
- StanzaBase.send(self)
- return waitfor.wait(timeout)
- else:
- return StanzaBase.send(self)
+
+ """
+ XMPP <iq> stanzas, or info/query stanzas, are XMPP's method of
+ requesting and modifying information, similar to HTTP's GET and
+ POST methods.
+
+ Each <iq> stanza must have an 'id' value which associates the
+ stanza with the response stanza. XMPP entities must always
+ be given a response <iq> stanza with a type of 'result' after
+ sending a stanza of type 'get' or 'set'.
+
+ Most uses cases for <iq> stanzas will involve adding a <query>
+ element whose namespace indicates the type of information
+ desired. However, some custom XMPP applications use <iq> stanzas
+ as a carrier stanza for an application-specific protocol instead.
+
+ Example <iq> Stanzas:
+ <iq to="user@example.com" type="get" id="314">
+ <query xmlns="http://jabber.org/protocol/disco#items" />
+ </iq>
+
+ <iq to="user@localhost" type="result" id="17">
+ <query xmlns='jabber:iq:roster'>
+ <item jid='otheruser@example.net'
+ name='John Doe'
+ subscription='both'>
+ <group>Friends</group>
+ </item>
+ </query>
+ </iq>
+
+ Stanza Interface:
+ query -- The namespace of the <query> element if one exists.
+
+ Methods:
+ __init__ -- Overrides StanzaBase.__init__.
+ unhandled -- Send error if there are no handlers.
+ setPayload -- Overrides StanzaBase.setPayload.
+ setQuery -- Add or modify a <query> element.
+ getQuery -- Return the namespace of the <query> element.
+ delQuery -- Remove the <query> element.
+ reply -- Overrides StanzaBase.reply
+ send -- Overrides StanzaBase.send
+ """
+
+ namespace = 'jabber:client'
+ name = 'iq'
+ interfaces = set(('type', 'to', 'from', 'id', 'query'))
+ types = set(('get', 'result', 'set', 'error'))
+ plugin_attrib = name
+
+ def __init__(self, *args, **kwargs):
+ """
+ Initialize a new <iq> stanza with an 'id' value.
+
+ Overrides StanzaBase.__init__.
+ """
+ StanzaBase.__init__(self, *args, **kwargs)
+ if self['id'] == '':
+ if self.stream is not None:
+ self['id'] = self.stream.getNewId()
+ else:
+ self['id'] = '0'
+
+ def unhandled(self):
+ """
+ Send a feature-not-implemented error if the stanza is not handled.
+
+ Overrides StanzaBase.unhandled.
+ """
+ if self['type'] in ('get', 'set'):
+ self.reply()
+ self['error']['condition'] = 'feature-not-implemented'
+ self['error']['text'] = 'No handlers registered for this request.'
+ self.send()
+
+ def setPayload(self, value):
+ """
+ Set the XML contents of the <iq> stanza.
+
+ Arguments:
+ value -- An XML object to use as the <iq> stanza's contents
+ """
+ self.clear()
+ StanzaBase.setPayload(self, value)
+ return self
+
+ def setQuery(self, value):
+ """
+ Add or modify a <query> element.
+
+ Query elements are differentiated by their namespace.
+
+ Arguments:
+ value -- The namespace of the <query> element.
+ """
+ query = self.xml.find("{%s}query" % value)
+ if query is None and value:
+ self.clear()
+ query = ET.Element("{%s}query" % value)
+ self.xml.append(query)
+ return self
+
+ def getQuery(self):
+ """Return the namespace of the <query> element."""
+ for child in self.xml.getchildren():
+ if child.tag.endswith('query'):
+ ns = child.tag.split('}')[0]
+ if '{' in ns:
+ ns = ns[1:]
+ return ns
+ return ''
+
+ def delQuery(self):
+ """Remove the <query> element."""
+ for child in self.xml.getchildren():
+ if child.tag.endswith('query'):
+ self.xml.remove(child)
+ return self
+
+ def reply(self):
+ """
+ Send a reply <iq> stanza.
+
+ Overrides StanzaBase.reply
+
+ Sets the 'type' to 'result' in addition to the default
+ StanzaBase.reply behavior.
+ """
+ self['type'] = 'result'
+ StanzaBase.reply(self)
+ return self
+
+ def send(self, block=True, timeout=RESPONSE_TIMEOUT):
+ """
+ Send an <iq> stanza over the XML stream.
+
+ The send call can optionally block until a response is received or
+ a timeout occurs. Be aware that using blocking in non-threaded event
+ handlers can drastically impact performance.
+
+ Overrides StanzaBase.send
+
+ Arguments:
+ block -- Specify if the send call will block until a response
+ is received, or a timeout occurs. Defaults to True.
+ timeout -- The length of time (in seconds) to wait for a response
+ before exiting the send call if blocking is used.
+ Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
+ """
+ if block and self['type'] in ('get', 'set'):
+ waitfor = Waiter('IqWait_%s' % self['id'], MatcherId(self['id']))
+ self.stream.registerHandler(waitfor)
+ StanzaBase.send(self)
+ return waitfor.wait(timeout)
+ else:
+ return StanzaBase.send(self)