diff options
-rw-r--r-- | sleekxmpp/features/feature_mechanisms/mechanisms.py | 16 | ||||
-rw-r--r-- | sleekxmpp/plugins/base.py | 36 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0027/gpg.py | 13 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0030/disco.py | 7 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0047/ibb.py | 13 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0050/adhoc.py | 10 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0077/register.py | 10 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0078/legacyauth.py | 7 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0086/legacy_error.py | 5 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0092/version.py | 17 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0115/caps.py | 9 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0184/receipt.py | 7 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0198/stream_management.py | 53 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0199/ping.py | 9 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0202/time.py | 21 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0256.py | 5 | ||||
-rw-r--r-- | sleekxmpp/util/stringprep_profiles.py | 41 | ||||
-rw-r--r-- | sleekxmpp/xmlstream/scheduler.py | 28 |
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 |