From a06fa2de677afad437622216fac7971b727ac569 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 26 Jul 2012 23:04:16 -0700 Subject: Enhance plugin config with attribute accessors. This makes updating the config after plugin initialization much easier. --- .../features/feature_mechanisms/mechanisms.py | 16 ++++--- sleekxmpp/plugins/base.py | 36 ++++++++++++++- sleekxmpp/plugins/xep_0027/gpg.py | 13 +++--- sleekxmpp/plugins/xep_0030/disco.py | 7 +-- sleekxmpp/plugins/xep_0047/ibb.py | 13 +++--- sleekxmpp/plugins/xep_0050/adhoc.py | 10 +++- sleekxmpp/plugins/xep_0077/register.py | 10 ++-- sleekxmpp/plugins/xep_0078/legacyauth.py | 7 ++- sleekxmpp/plugins/xep_0086/legacy_error.py | 5 +- sleekxmpp/plugins/xep_0092/version.py | 17 ++++--- sleekxmpp/plugins/xep_0115/caps.py | 9 ++-- sleekxmpp/plugins/xep_0184/receipt.py | 7 +-- sleekxmpp/plugins/xep_0198/stream_management.py | 53 ++++++++++++---------- sleekxmpp/plugins/xep_0199/ping.py | 9 ++-- sleekxmpp/plugins/xep_0202/time.py | 21 +++++---- sleekxmpp/plugins/xep_0256.py | 5 +- 16 files changed, 153 insertions(+), 85 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) -- cgit v1.2.3 From 5867f08bf1c6bd9866321e148bf941c0ae0dbfa0 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 26 Jul 2012 23:35:23 -0700 Subject: Improve docs and fix typo in stringprep profiles. --- sleekxmpp/util/stringprep_profiles.py | 41 ++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) 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) -- cgit v1.2.3 From e3fab66dfb27abdd8aa28a8d15367a490d4b42dd Mon Sep 17 00:00:00 2001 From: Jonas Wielicki Date: Fri, 27 Jul 2012 13:14:20 +0200 Subject: Allow tasks to remove themselves during execution The scheduler class is now capable with dealing with tasks which remove themselves from the scheduler during execution. Additionally, some optimizations were applied by use of iterators and some functions better suited for the purpose. Please peer-review, all tests pass. --- sleekxmpp/xmlstream/scheduler.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) 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 -- cgit v1.2.3