summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sleekxmpp/features/feature_mechanisms/mechanisms.py16
-rw-r--r--sleekxmpp/plugins/base.py36
-rw-r--r--sleekxmpp/plugins/xep_0027/gpg.py13
-rw-r--r--sleekxmpp/plugins/xep_0030/disco.py7
-rw-r--r--sleekxmpp/plugins/xep_0047/ibb.py13
-rw-r--r--sleekxmpp/plugins/xep_0050/adhoc.py10
-rw-r--r--sleekxmpp/plugins/xep_0077/register.py10
-rw-r--r--sleekxmpp/plugins/xep_0078/legacyauth.py7
-rw-r--r--sleekxmpp/plugins/xep_0086/legacy_error.py5
-rw-r--r--sleekxmpp/plugins/xep_0092/version.py17
-rw-r--r--sleekxmpp/plugins/xep_0115/caps.py9
-rw-r--r--sleekxmpp/plugins/xep_0184/receipt.py7
-rw-r--r--sleekxmpp/plugins/xep_0198/stream_management.py53
-rw-r--r--sleekxmpp/plugins/xep_0199/ping.py9
-rw-r--r--sleekxmpp/plugins/xep_0202/time.py21
-rw-r--r--sleekxmpp/plugins/xep_0256.py5
-rw-r--r--sleekxmpp/util/stringprep_profiles.py41
-rw-r--r--sleekxmpp/xmlstream/scheduler.py28
18 files changed, 210 insertions, 97 deletions
diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py
index 930aa8fe..2ab7b0a4 100644
--- a/sleekxmpp/features/feature_mechanisms/mechanisms.py
+++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py
@@ -29,10 +29,13 @@ class FeatureMechanisms(BasePlugin):
description = 'RFC 6120: Stream Feature: SASL'
dependencies = set()
stanza = stanza
+ default_config = {
+ 'use_mech': None,
+ 'sasl_callback': None,
+ 'order': 100
+ }
def plugin_init(self):
- self.use_mech = self.config.get('use_mech', None)
-
if not self.use_mech and not self.xmpp.boundjid.user:
self.use_mech = 'ANONYMOUS'
@@ -53,15 +56,14 @@ class FeatureMechanisms(BasePlugin):
values[value] = creds[value]
mech.fulfill(values)
- sasl_callback = self.config.get('sasl_callback', None)
- if sasl_callback is None:
- sasl_callback = basic_callback
+ if self.sasl_callback is None:
+ self.sasl_callback = basic_callback
self.mech = None
self.sasl = suelta.SASL(self.xmpp.boundjid.domain, 'xmpp',
username=self.xmpp.boundjid.user,
sec_query=suelta.sec_query_allow,
- request_values=sasl_callback,
+ request_values=self.sasl_callback,
tls_active=tls_active,
mech=self.use_mech)
@@ -95,7 +97,7 @@ class FeatureMechanisms(BasePlugin):
self.xmpp.register_feature('mechanisms',
self._handle_sasl_auth,
restart=True,
- order=self.config.get('order', 100))
+ order=self.order)
def _handle_sasl_auth(self, features):
"""
diff --git a/sleekxmpp/plugins/base.py b/sleekxmpp/plugins/base.py
index 26f0c827..67675908 100644
--- a/sleekxmpp/plugins/base.py
+++ b/sleekxmpp/plugins/base.py
@@ -14,6 +14,7 @@
"""
import sys
+import copy
import logging
import threading
@@ -272,6 +273,14 @@ class BasePlugin(object):
#: be initialized as needed if this plugin is enabled.
dependencies = set()
+ #: The basic, standard configuration for the plugin, which may
+ #: be overridden when initializing the plugin. The configuration
+ #: fields included here may be accessed directly as attributes of
+ #: the plugin. For example, including the configuration field 'foo'
+ #: would mean accessing `plugin.foo` returns the current value of
+ #: `plugin.config['foo']`.
+ default_config = {}
+
def __init__(self, xmpp, config=None):
self.xmpp = xmpp
if self.xmpp:
@@ -279,7 +288,32 @@ class BasePlugin(object):
#: A plugin's behaviour may be configurable, in which case those
#: configuration settings will be provided as a dictionary.
- self.config = config if config is not None else {}
+ self.config = copy.copy(self.default_config)
+ if config:
+ self.config.update(config)
+
+ def __getattr__(self, key):
+ """Provide direct access to configuration fields.
+
+ If the standard configuration includes the option `'foo'`, then
+ accessing `self.foo` should be the same as `self.config['foo']`.
+ """
+ if key in self.default_config:
+ return self.config.get(key, None)
+ else:
+ return object.__getattribute__(self, key)
+
+ def __setattr__(self, key, value):
+ """Provide direct assignment to configuration fields.
+
+ If the standard configuration includes the option `'foo'`, then
+ assigning to `self.foo` should be the same as assigning to
+ `self.config['foo']`.
+ """
+ if key in self.default_config:
+ self.config[key] = value
+ else:
+ super(BasePlugin, self).__setattr__(key, value)
def _init(self):
"""Initialize plugin state, such as registering event handlers.
diff --git a/sleekxmpp/plugins/xep_0027/gpg.py b/sleekxmpp/plugins/xep_0027/gpg.py
index 3ca9c36d..2aa6e5a0 100644
--- a/sleekxmpp/plugins/xep_0027/gpg.py
+++ b/sleekxmpp/plugins/xep_0027/gpg.py
@@ -40,14 +40,15 @@ class XEP_0027(BasePlugin):
description = 'XEP-0027: Current Jabber OpenPGP Usage'
dependencies = set()
stanza = stanza
+ default_config = {
+ 'gpg_binary': 'gpg',
+ 'gpg_home': '',
+ 'use_agent': True,
+ 'keyring': None,
+ 'key_server': 'pgp.mit.edu'
+ }
def plugin_init(self):
- self.gpg_binary = self.config.get('gpg_binary', 'gpg')
- self.gpg_home = self.config.get('gpg_home', '')
- self.use_agent = self.config.get('use_agent', True)
- self.keyring = self.config.get('keyring', None)
- self.key_server = self.config.get('key_server', 'pgp.mit.edu')
-
self.gpg = GPG(gnupghome=self.gpg_home,
gpgbinary=self.gpg_binary,
use_agent=self.use_agent,
diff --git a/sleekxmpp/plugins/xep_0030/disco.py b/sleekxmpp/plugins/xep_0030/disco.py
index eeb977b1..be66b6fd 100644
--- a/sleekxmpp/plugins/xep_0030/disco.py
+++ b/sleekxmpp/plugins/xep_0030/disco.py
@@ -88,6 +88,10 @@ class XEP_0030(BasePlugin):
description = 'XEP-0030: Service Discovery'
dependencies = set()
stanza = stanza
+ default_config = {
+ 'use_cache': True,
+ 'wrap_results': False
+ }
def plugin_init(self):
"""
@@ -108,9 +112,6 @@ class XEP_0030(BasePlugin):
self.static = StaticDisco(self.xmpp, self)
- self.use_cache = self.config.get('use_cache', True)
- self.wrap_results = self.config.get('wrap_results', False)
-
self._disco_ops = [
'get_info', 'set_info', 'set_identities', 'set_features',
'get_items', 'set_items', 'del_items', 'add_identity',
diff --git a/sleekxmpp/plugins/xep_0047/ibb.py b/sleekxmpp/plugins/xep_0047/ibb.py
index 2b8c57d4..fb48a9b9 100644
--- a/sleekxmpp/plugins/xep_0047/ibb.py
+++ b/sleekxmpp/plugins/xep_0047/ibb.py
@@ -20,18 +20,19 @@ class XEP_0047(BasePlugin):
description = 'XEP-0047: In-band Bytestreams'
dependencies = set(['xep_0030'])
stanza = stanza
+ default_config = {
+ 'max_block_size': 8192,
+ 'window_size': 1,
+ 'auto_accept': True,
+ 'accept_stream': None
+ }
def plugin_init(self):
self.streams = {}
- self.pending_streams = {3: 5}
+ self.pending_streams = {}
self.pending_close_streams = {}
self._stream_lock = threading.Lock()
- self.max_block_size = self.config.get('max_block_size', 8192)
- self.window_size = self.config.get('window_size', 1)
- self.auto_accept = self.config.get('auto_accept', True)
- self.accept_stream = self.config.get('accept_stream', None)
-
register_stanza_plugin(Iq, Open)
register_stanza_plugin(Iq, Close)
register_stanza_plugin(Iq, Data)
diff --git a/sleekxmpp/plugins/xep_0050/adhoc.py b/sleekxmpp/plugins/xep_0050/adhoc.py
index a833221a..032b987a 100644
--- a/sleekxmpp/plugins/xep_0050/adhoc.py
+++ b/sleekxmpp/plugins/xep_0050/adhoc.py
@@ -82,12 +82,18 @@ class XEP_0050(BasePlugin):
description = 'XEP-0050: Ad-Hoc Commands'
dependencies = set(['xep_0030', 'xep_0004'])
stanza = stanza
+ default_config = {
+ 'threaded': True,
+ 'session_db': None
+ }
def plugin_init(self):
"""Start the XEP-0050 plugin."""
- self.threaded = self.config.get('threaded', True)
+ self.sessions = self.session_db
+ if self.sessions is None:
+ self.sessions = {}
+
self.commands = {}
- self.sessions = self.config.get('session_db', {})
self.xmpp.register_handler(
Callback("Ad-Hoc Execute",
diff --git a/sleekxmpp/plugins/xep_0077/register.py b/sleekxmpp/plugins/xep_0077/register.py
index 7f00354b..d4da21a5 100644
--- a/sleekxmpp/plugins/xep_0077/register.py
+++ b/sleekxmpp/plugins/xep_0077/register.py
@@ -27,10 +27,12 @@ class XEP_0077(BasePlugin):
description = 'XEP-0077: In-Band Registration'
dependencies = set(['xep_0004', 'xep_0066'])
stanza = stanza
+ default_config = {
+ 'create_account': True,
+ 'order': 50
+ }
def plugin_init(self):
- self.create_account = self.config.get('create_account', True)
-
register_stanza_plugin(StreamFeatures, RegisterFeature)
register_stanza_plugin(Iq, Register)
@@ -38,14 +40,14 @@ class XEP_0077(BasePlugin):
self.xmpp.register_feature('register',
self._handle_register_feature,
restart=False,
- order=self.config.get('order', 50))
+ order=self.order)
register_stanza_plugin(Register, self.xmpp['xep_0004'].stanza.Form)
register_stanza_plugin(Register, self.xmpp['xep_0066'].stanza.OOB)
def plugin_end(self):
if not self.xmpp.is_component:
- self.xmpp.unregister_feature('register', self.config.get('order', 50))
+ self.xmpp.unregister_feature('register', self.order)
def _handle_register_feature(self, features):
if 'mechanisms' in self.xmpp.features:
diff --git a/sleekxmpp/plugins/xep_0078/legacyauth.py b/sleekxmpp/plugins/xep_0078/legacyauth.py
index 8ea78fba..5c4045d8 100644
--- a/sleekxmpp/plugins/xep_0078/legacyauth.py
+++ b/sleekxmpp/plugins/xep_0078/legacyauth.py
@@ -34,18 +34,21 @@ class XEP_0078(BasePlugin):
description = 'XEP-0078: Non-SASL Authentication'
dependencies = set()
stanza = stanza
+ default_config = {
+ 'order': 15
+ }
def plugin_init(self):
self.xmpp.register_feature('auth',
self._handle_auth,
restart=False,
- order=self.config.get('order', 15))
+ order=self.order)
register_stanza_plugin(Iq, stanza.IqAuth)
register_stanza_plugin(StreamFeatures, stanza.AuthFeature)
def plugin_end(self):
- self.xmpp.unregister_feature('auth', self.config.get('order', 15))
+ self.xmpp.unregister_feature('auth', self.order)
def _handle_auth(self, features):
# If we can or have already authenticated with SASL, do nothing.
diff --git a/sleekxmpp/plugins/xep_0086/legacy_error.py b/sleekxmpp/plugins/xep_0086/legacy_error.py
index bed22ee2..f7d0ac9c 100644
--- a/sleekxmpp/plugins/xep_0086/legacy_error.py
+++ b/sleekxmpp/plugins/xep_0086/legacy_error.py
@@ -37,7 +37,10 @@ class XEP_0086(BasePlugin):
description = 'XEP-0086: Error Condition Mappings'
dependencies = set()
stanza = stanza
+ default_config = {
+ 'override': True
+ }
def plugin_init(self):
register_stanza_plugin(Error, LegacyError,
- overrides=self.config.get('override', True))
+ overrides=self.override)
diff --git a/sleekxmpp/plugins/xep_0092/version.py b/sleekxmpp/plugins/xep_0092/version.py
index 463da158..35813e1d 100644
--- a/sleekxmpp/plugins/xep_0092/version.py
+++ b/sleekxmpp/plugins/xep_0092/version.py
@@ -30,16 +30,18 @@ class XEP_0092(BasePlugin):
description = 'XEP-0092: Software Version'
dependencies = set(['xep_0030'])
stanza = stanza
+ default_config = {
+ 'software_name': 'SleekXMPP',
+ 'version': sleekxmpp.__version__,
+ 'os': ''
+ }
def plugin_init(self):
"""
Start the XEP-0092 plugin.
"""
- self.name = self.config.get('name', 'SleekXMPP')
- self.version = self.config.get('version', sleekxmpp.__version__)
- self.os = self.config.get('os', '')
-
- self.getVersion = self.get_version
+ if 'name' in self.config:
+ self.software_name = self.config['name']
self.xmpp.register_handler(
Callback('Software Version',
@@ -63,7 +65,7 @@ class XEP_0092(BasePlugin):
iq -- The Iq stanza containing the software version query.
"""
iq.reply()
- iq['software_version']['name'] = self.name
+ iq['software_version']['name'] = self.software_name
iq['software_version']['version'] = self.version
iq['software_version']['os'] = self.os
iq.send()
@@ -88,3 +90,6 @@ class XEP_0092(BasePlugin):
del values['lang']
return values
return False
+
+
+XEP_0092.getVersion = XEP_0092.get_version
diff --git a/sleekxmpp/plugins/xep_0115/caps.py b/sleekxmpp/plugins/xep_0115/caps.py
index 8ce10edb..15ddb283 100644
--- a/sleekxmpp/plugins/xep_0115/caps.py
+++ b/sleekxmpp/plugins/xep_0115/caps.py
@@ -33,16 +33,17 @@ class XEP_0115(BasePlugin):
description = 'XEP-0115: Entity Capabilities'
dependencies = set(['xep_0030', 'xep_0128', 'xep_0004'])
stanza = stanza
+ default_config = {
+ 'hash': 'sha-1',
+ 'caps_node': None,
+ 'broadcast': True
+ }
def plugin_init(self):
self.hashes = {'sha-1': hashlib.sha1,
'sha1': hashlib.sha1,
'md5': hashlib.md5}
- self.hash = self.config.get('hash', 'sha-1')
- self.caps_node = self.config.get('caps_node', None)
- self.broadcast = self.config.get('broadcast', True)
-
if self.caps_node is None:
ver = sleekxmpp.__version__
self.caps_node = 'http://sleekxmpp.com/ver/%s' % ver
diff --git a/sleekxmpp/plugins/xep_0184/receipt.py b/sleekxmpp/plugins/xep_0184/receipt.py
index 2b939321..1fda2066 100644
--- a/sleekxmpp/plugins/xep_0184/receipt.py
+++ b/sleekxmpp/plugins/xep_0184/receipt.py
@@ -26,13 +26,14 @@ class XEP_0184(BasePlugin):
description = 'XEP-0184: Message Delivery Receipts'
dependencies = set(['xep_0030'])
stanza = stanza
+ default_config = {
+ 'auto_ack': True,
+ 'auto_request': False
+ }
ack_types = ('normal', 'chat', 'headline')
def plugin_init(self):
- self.auto_ack = self.config.get('auto_ack', True)
- self.auto_request = self.config.get('auto_request', False)
-
register_stanza_plugin(Message, Request)
register_stanza_plugin(Message, Received)
diff --git a/sleekxmpp/plugins/xep_0198/stream_management.py b/sleekxmpp/plugins/xep_0198/stream_management.py
index a150ad39..48029913 100644
--- a/sleekxmpp/plugins/xep_0198/stream_management.py
+++ b/sleekxmpp/plugins/xep_0198/stream_management.py
@@ -34,39 +34,44 @@ class XEP_0198(BasePlugin):
description = 'XEP-0198: Stream Management'
dependencies = set()
stanza = stanza
+ default_config = {
+ #: The last ack number received from the server.
+ 'last_ack': 0,
- def plugin_init(self):
- """Start the XEP-0198 plugin."""
-
- # Only enable stream management for non-components,
- # since components do not yet perform feature negotiation.
- if self.xmpp.is_component:
- return
+ #: The number of stanzas to wait between sending ack requests to
+ #: the server. Setting this to ``1`` will send an ack request after
+ #: every sent stanza. Defaults to ``5``.
+ 'window': 5,
#: The stream management ID for the stream. Knowing this value is
#: required in order to do stream resumption.
- self.sm_id = self.config.get('sm_id', None)
+ 'sm_id': None,
#: A counter of handled incoming stanzas, mod 2^32.
- self.handled = self.config.get('handled', 0)
+ 'handled': 0,
#: A counter of unacked outgoing stanzas, mod 2^32.
- self.seq = self.config.get('seq', 0)
+ 'seq': 0,
- #: The last ack number received from the server.
- self.last_ack = self.config.get('last_ack', 0)
+ #: Control whether or not the ability to resume the stream will be
+ #: requested when enabling stream management. Defaults to ``True``.
+ 'allow_resume': True,
+
+ 'order': 10100,
+ 'resume_order': 9000
+ }
+
+ def plugin_init(self):
+ """Start the XEP-0198 plugin."""
+
+ # Only enable stream management for non-components,
+ # since components do not yet perform feature negotiation.
+ if self.xmpp.is_component:
+ return
- #: The number of stanzas to wait between sending ack requests to
- #: the server. Setting this to ``1`` will send an ack request after
- #: every sent stanza. Defaults to ``5``.
- self.window = self.config.get('window', 5)
self.window_counter = self.window
self.window_counter_lock = threading.Lock()
- #: Control whether or not the ability to resume the stream will be
- #: requested when enabling stream management. Defaults to ``True``.
- self.allow_resume = self.config.get('allow_resume', True)
-
self.enabled = threading.Event()
self.unacked_queue = collections.deque()
@@ -92,11 +97,11 @@ class XEP_0198(BasePlugin):
self.xmpp.register_feature('sm',
self._handle_sm_feature,
restart=True,
- order=self.config.get('order', 10100))
+ order=self.order)
self.xmpp.register_feature('sm',
self._handle_sm_feature,
restart=True,
- order=self.config.get('resume_order', 9000))
+ order=self.resume_order)
self.xmpp.register_handler(
Callback('Stream Management Enabled',
@@ -137,8 +142,8 @@ class XEP_0198(BasePlugin):
if self.xmpp.is_component:
return
- self.xmpp.unregister_feature('sm', self.config.get('order', 10100))
- self.xmpp.unregister_feature('sm', self.config.get('resume_order', 9000))
+ self.xmpp.unregister_feature('sm', self.order)
+ self.xmpp.unregister_feature('sm', self.resume_order)
self.xmpp.del_event_handler('session_end', self.session_end)
self.xmpp.del_filter('in', self._handle_incoming)
self.xmpp.del_filter('out_sync', self._handle_outgoing)
diff --git a/sleekxmpp/plugins/xep_0199/ping.py b/sleekxmpp/plugins/xep_0199/ping.py
index b9d145aa..0bdeabf3 100644
--- a/sleekxmpp/plugins/xep_0199/ping.py
+++ b/sleekxmpp/plugins/xep_0199/ping.py
@@ -51,15 +51,16 @@ class XEP_0199(BasePlugin):
description = 'XEP-0199: XMPP Ping'
dependencies = set(['xep_0030'])
stanza = stanza
+ default_config = {
+ 'keepalive': False,
+ 'frequency': 300,
+ 'timeout': 30
+ }
def plugin_init(self):
"""
Start the XEP-0199 plugin.
"""
- self.keepalive = self.config.get('keepalive', False)
- self.frequency = float(self.config.get('frequency', 300))
- self.timeout = self.config.get('timeout', 30)
-
register_stanza_plugin(Iq, Ping)
self.xmpp.register_handler(
diff --git a/sleekxmpp/plugins/xep_0202/time.py b/sleekxmpp/plugins/xep_0202/time.py
index 50af4730..fe20449d 100644
--- a/sleekxmpp/plugins/xep_0202/time.py
+++ b/sleekxmpp/plugins/xep_0202/time.py
@@ -30,21 +30,22 @@ class XEP_0202(BasePlugin):
description = 'XEP-0202: Entity Time'
dependencies = set(['xep_0030', 'xep_0082'])
stanza = stanza
+ default_config = {
+ #: As a default, respond to time requests with the
+ #: local time returned by XEP-0082. However, a
+ #: custom function can be supplied which accepts
+ #: the JID of the entity to query for the time.
+ 'local_time': None,
+ 'tz_offset': 0
+ }
def plugin_init(self):
"""Start the XEP-0203 plugin."""
- self.tz_offset = self.config.get('tz_offset', 0)
-
- # As a default, respond to time requests with the
- # local time returned by XEP-0082. However, a
- # custom function can be supplied which accepts
- # the JID of the entity to query for the time.
- self.local_time = self.config.get('local_time', None)
-
- def default_local_time(jid):
- return xep_0082.datetime(offset=self.tz_offset)
if not self.local_time:
+ def default_local_time(jid):
+ return xep_0082.datetime(offset=self.tz_offset)
+
self.local_time = default_local_time
self.xmpp.registerHandler(
diff --git a/sleekxmpp/plugins/xep_0256.py b/sleekxmpp/plugins/xep_0256.py
index dd407fff..0db8ea3b 100644
--- a/sleekxmpp/plugins/xep_0256.py
+++ b/sleekxmpp/plugins/xep_0256.py
@@ -25,10 +25,11 @@ class XEP_0256(BasePlugin):
description = 'XEP-0256: Last Activity in Presence'
dependencies = set(['xep_0012'])
stanza = stanza
+ default_config = {
+ 'auto_last_activity': False
+ }
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)
diff --git a/sleekxmpp/util/stringprep_profiles.py b/sleekxmpp/util/stringprep_profiles.py
index 6844c9ac..08278d6c 100644
--- a/sleekxmpp/util/stringprep_profiles.py
+++ b/sleekxmpp/util/stringprep_profiles.py
@@ -1,3 +1,19 @@
+# -*- coding: utf-8 -*-
+"""
+ sleekxmpp.util.stringprep_profiles
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ This module makes it easier to define profiles of stringprep,
+ such as nodeprep and resourceprep for JID validation, and
+ SASLprep for SASL.
+
+ Part of SleekXMPP: The Sleek XMPP Library
+
+ :copyright: (c) 2012 Nathanael C. Fritz, Lance J.T. Stout
+ :license: MIT, see LICENSE for more details
+"""
+
+
from __future__ import unicode_literals
import sys
@@ -10,6 +26,7 @@ class StringPrepError(UnicodeError):
def to_unicode(data):
+ """Ensure that a given string is Unicode, regardless of Python version."""
if sys.version_info < (3, 0):
return unicode(data)
else:
@@ -17,10 +34,12 @@ def to_unicode(data):
def b1_mapping(char):
- return '' if stringprep.in_table_c12(char) else None
+ """Map characters that are commonly mapped to nothing."""
+ return '' if stringprep.in_table_b1(char) else None
def c12_mapping(char):
+ """Map non-ASCII whitespace to spaces."""
return ' ' if stringprep.in_table_c12(char) else None
@@ -102,6 +121,26 @@ def check_bidi(data):
def create(nfkc=True, bidi=True, mappings=None,
prohibited=None, unassigned=None):
+ """Create a profile of stringprep.
+
+ :param bool nfkc:
+ If `True`, perform NFKC Unicode normalization. Defaults to `True`.
+ :param bool bidi:
+ If `True`, perform bidirectional text checks. Defaults to `True`.
+ :param list mappings:
+ Optional list of functions for mapping characters to
+ suitable replacements.
+ :param list prohibited:
+ Optional list of functions which check for the presence of
+ prohibited characters.
+ :param list unassigned:
+ Optional list of functions for detecting the use of unassigned
+ code points.
+
+ :raises: StringPrepError
+ :return: Unicode string of the resulting text passing the
+ profile's requirements.
+ """
def profile(data, query=False):
try:
data = to_unicode(data)
diff --git a/sleekxmpp/xmlstream/scheduler.py b/sleekxmpp/xmlstream/scheduler.py
index d98dc6c8..b3e50983 100644
--- a/sleekxmpp/xmlstream/scheduler.py
+++ b/sleekxmpp/xmlstream/scheduler.py
@@ -15,6 +15,7 @@
import time
import threading
import logging
+import itertools
from sleekxmpp.util import Queue, QueueEmpty
@@ -156,17 +157,23 @@ class Scheduler(object):
newtask = self.addq.get(True, 0.1)
elapsed += 0.1
except QueueEmpty:
- cleanup = []
self.schedule_lock.acquire()
- for task in self.schedule:
- if time.time() >= task.next:
- updated = True
- if not task.run():
- cleanup.append(task)
+ # select only those tasks which are to be executed now
+ relevant = itertools.takewhile(
+ lambda task: time.time() >= task.next, self.schedule)
+ # run the tasks and keep the return value in a tuple
+ status = map(lambda task: (task, task.run()), relevant)
+ # remove non-repeating tasks
+ for task, doRepeat in status:
+ if not doRepeat:
+ try:
+ self.schedule.remove(task)
+ except ValueError:
+ pass
else:
- break
- for task in cleanup:
- self.schedule.pop(self.schedule.index(task))
+ # only need to resort tasks if a repeated task has
+ # been kept in the list.
+ updated = True
else:
updated = True
self.schedule_lock.acquire()
@@ -174,8 +181,7 @@ class Scheduler(object):
self.schedule.append(newtask)
finally:
if updated:
- self.schedule = sorted(self.schedule,
- key=lambda task: task.next)
+ self.schedule.sort(key=lambda task: task.next)
self.schedule_lock.release()
except KeyboardInterrupt:
self.run = False