Instead of the actual callback object, return just the name of
the callback object created when using iq.send(callback=..).
This will help prevent memory leaks by not keeping an additional
reference to the object, but still allows for the callback to be
canceled by using self.remove_handler("handler_name").
Waiting until the actual run step means that the handler is not
marked for deletion when checked in the __spawn_event() thread,
causing the callback to stay in the handler list.
This allows exceptions to include the original
content of a stanza in the error response by including
the parameter clear=False when raising the exception.
Use stanza.values instead of _get/set_stanza_values where used.
ElementBase stanzas can now use .tag
May use class method tag_name() for stanza classes.
ElementBase now has .clear() method.
May now use register_stanza_plugin(Foo, Bar, iterable=True)
to add to the set of stanza classes used for iterable
substanzas. It is no longer necessary to manually specify
the contents of subitem if the new method is used.
A stanza object may add is_extension = True to its class definition
to provide a single new interface to a parent stanza.
For example:
import sleekxmpp
from sleekxmpp import Iq
from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin, ET
class Foo(ElementBase):
"""
Test adding just an attribute to a parent stanza.
Adding subelements works as expected.
"""
is_extension = True
interfaces = set(('foo',))
plugin_attrib = 'foo'
def setup(self, xml):
# Don't include an XML element in the parent stanza
# since we're adding just an attribute.
# If adding a regular subelement, no need to do this.
self.xml = ET.Element('')
def set_foo(self, val):
self.parent()._set_attr('foo', val)
def get_foo(self):
return self.parent()._get_attr('foo')
def del_foo(self):
self.parent()._del_attr('foo')
register_stanza_plugin(Iq, Foo)
i1 = Iq()
i2 = Iq(xml=ET.fromstring("<iq xmlns='jabber:client' foo='bar' />"))
>>> i1['foo'] = '3'
>>> i1
'3'
>>> i1
'<iq id="0" foo="3" />'
>>> i2
'<iq id="0" foo="bar" />'
>>> i2['foo']
'bar'
>>> del i2['foo']
>>> i2
'<iq id="0" />'
We now raise the unexpected exceptions instead of sending
them on the network.
- avoids flood (sending a traceback on a MUC, for example…) and
maybe some security issues.
- lets you handle the traceback (catch it to handle
it properly, or with except_hook, etc)
- an exception cannot be raised without you knowing
Daemonized threads exit once the main program has quit,
and the only threads left running are all daemon threads.
Should fix hanging clients while not trampling over anyone
else's signal handlers.
Methods now accept either an ifrom or mfrom parameter
to specify a 'from' value. Client connections should not
need to use these, but component connections must use them.
May include a to and from JID in make_iq_* calls.
May pass an existing iq stanza to most of them instead of generating
a new stanza.
make_iq now accepts a 'to' value, 'type' value, and 'query' value to
simplify things a bit more.
Now with dynamic node handling goodness.
Some things are not quite working yet, in particular:
set_items
set_info
set_identities
set_features
And still need more unit tests to round things out.
Support is only for adding literal XML content
to stanzas. Full support for things like multiple
message bodies with different xml:lang values is
still in the works.
The callback will be a stream level handler, and will not
execute in its own thread. If you must have a thread, have the
callback function raise a custom event, which can be processed
by another event handler, which may run in an individual thread,
like so:
def handle_reply(self, iq):
self.event('custom_event', iq)
def do_long_operation_in_thread(self, iq):
...
self.add_event_handler('custom_event', self.do_long_operation_in_thread)
...take out already prepared iq stanza...
iq.send(callback=self.handle_reply)