diff options
Diffstat (limited to 'deluge/core')
-rw-r--r-- | deluge/core/alertmanager.py | 75 | ||||
-rw-r--r-- | deluge/core/core.py | 3 | ||||
-rw-r--r-- | deluge/core/rpcserver.py | 4 | ||||
-rw-r--r-- | deluge/core/torrentmanager.py | 76 |
4 files changed, 81 insertions, 77 deletions
diff --git a/deluge/core/alertmanager.py b/deluge/core/alertmanager.py index 9a1ded5..51a7f29 100644 --- a/deluge/core/alertmanager.py +++ b/deluge/core/alertmanager.py @@ -14,8 +14,11 @@ This should typically only be used by the Core. Plugins should utilize the `:mod:EventManager` for similar functionality. """ +import contextlib import logging +from collections import defaultdict from types import SimpleNamespace +from typing import Any, Callable from twisted.internet import reactor @@ -52,7 +55,7 @@ class AlertManager(component.Component): self.session.apply_settings({'alert_mask': alert_mask}) # handlers is a dictionary of lists {"alert_type": [handler1,h2,..]} - self.handlers = {} + self.handlers = defaultdict(list) self.delayed_calls = [] def update(self): @@ -65,35 +68,33 @@ class AlertManager(component.Component): delayed_call.cancel() self.delayed_calls = [] - def register_handler(self, alert_type, handler): + def register_handler(self, alert_type: str, handler: Callable[[Any], None]) -> None: """ Registers a function that will be called when 'alert_type' is pop'd in handle_alerts. The handler function should look like: handler(alert) Where 'alert' is the actual alert object from libtorrent. - :param alert_type: str, this is string representation of the alert name - :param handler: func(alert), the function to be called when the alert is raised + Args: + alert_type: String representation of the libtorrent alert name. + Can be supplied with or without `_alert` suffix. + handler: Callback function when the alert is raised. """ - if alert_type not in self.handlers: - # There is no entry for this alert type yet, so lets make it with an - # empty list. - self.handlers[alert_type] = [] + if alert_type and alert_type.endswith('_alert'): + alert_type = alert_type[: -len('_alert')] - # Append the handler to the list in the handlers dictionary self.handlers[alert_type].append(handler) log.debug('Registered handler for alert %s', alert_type) - def deregister_handler(self, handler): + def deregister_handler(self, handler: Callable[[Any], None]): """ - De-registers the `:param:handler` function from all alert types. + De-registers the `handler` function from all alert types. - :param handler: func, the handler function to deregister + Args: + handler: The handler function to deregister. """ - # Iterate through all handlers and remove 'handler' where found - for (dummy_key, value) in self.handlers.items(): - if handler in value: - # Handler is in this alert type list - value.remove(handler) + for alert_type_handlers in self.handlers.values(): + with contextlib.suppress(ValueError): + alert_type_handlers.remove(handler) def handle_alerts(self): """ @@ -112,26 +113,36 @@ class AlertManager(component.Component): num_alerts, ) - # Loop through all alerts in the queue for alert in alerts: - alert_type = type(alert).__name__ + alert_type = alert.what() + # Display the alert message if log.isEnabledFor(logging.DEBUG): log.debug('%s: %s', alert_type, decode_bytes(alert.message())) + + if alert_type not in self.handlers: + continue + # Call any handlers for this alert type - if alert_type in self.handlers: - for handler in self.handlers[alert_type]: - if log.isEnabledFor(logging.DEBUG): - log.debug('Handling alert: %s', alert_type) - # Copy alert attributes - alert_copy = SimpleNamespace( - **{ - attr: getattr(alert, attr) - for attr in dir(alert) - if not attr.startswith('__') - } - ) - self.delayed_calls.append(reactor.callLater(0, handler, alert_copy)) + for handler in self.handlers[alert_type]: + if log.isEnabledFor(logging.DEBUG): + log.debug('Handling alert: %s', alert_type) + + alert_copy = self.create_alert_copy(alert) + self.delayed_calls.append(reactor.callLater(0, handler, alert_copy)) + + @staticmethod + def create_alert_copy(alert): + """Create a Python copy of libtorrent alert + + Avoid segfault if an alert is handled after next pop_alert call""" + return SimpleNamespace( + **{ + attr: getattr(alert, attr) + for attr in dir(alert) + if not attr.startswith('__') + } + ) def set_alert_queue_size(self, queue_size): """Sets the maximum size of the libtorrent alert queue""" diff --git a/deluge/core/core.py b/deluge/core/core.py index 35cf019..198410e 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -199,7 +199,7 @@ class Core(component.Component): self.session_status_timer_interval = 0.5 self.session_status_timer = task.LoopingCall(self.session.post_session_stats) self.alertmanager.register_handler( - 'session_stats_alert', self._on_alert_session_stats + 'session_stats', self._on_alert_session_stats ) self.session_rates_timer_interval = 2 self.session_rates_timer = task.LoopingCall(self._update_session_rates) @@ -1000,7 +1000,6 @@ class Core(component.Component): trackers, add_to_session, ): - log.debug('creating torrent..') threading.Thread( target=self._create_torrent_thread, diff --git a/deluge/core/rpcserver.py b/deluge/core/rpcserver.py index d4ca5d1..81ab2e0 100644 --- a/deluge/core/rpcserver.py +++ b/deluge/core/rpcserver.py @@ -545,8 +545,8 @@ class RPCServer(component.Component): :type event: :class:`deluge.event.DelugeEvent` """ log.debug('intevents: %s', self.factory.interested_events) - # Find sessions interested in this event - for session_id, interest in self.factory.interested_events.items(): + # Use copy of `interested_events` since it can mutate while iterating. + for session_id, interest in self.factory.interested_events.copy().items(): if event.name in interest: log.debug('Emit Event: %s %s', event.name, event.args) # This session is interested so send a RPC_EVENT diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index 5609df4..a758d5c 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -50,10 +50,10 @@ from deluge.event import ( log = logging.getLogger(__name__) LT_DEFAULT_ADD_TORRENT_FLAGS = ( - lt.add_torrent_params_flags_t.flag_paused - | lt.add_torrent_params_flags_t.flag_auto_managed - | lt.add_torrent_params_flags_t.flag_update_subscribe - | lt.add_torrent_params_flags_t.flag_apply_ip_filter + lt.torrent_flags.paused + | lt.torrent_flags.auto_managed + | lt.torrent_flags.update_subscribe + | lt.torrent_flags.apply_ip_filter ) @@ -202,34 +202,32 @@ class TorrentManager(component.Component): # Register alert functions alert_handles = [ - 'external_ip_alert', - 'performance_alert', - 'add_torrent_alert', - 'metadata_received_alert', - 'torrent_finished_alert', - 'torrent_paused_alert', - 'torrent_checked_alert', - 'torrent_resumed_alert', - 'tracker_reply_alert', - 'tracker_announce_alert', - 'tracker_warning_alert', - 'tracker_error_alert', - 'file_renamed_alert', - 'file_error_alert', - 'file_completed_alert', - 'storage_moved_alert', - 'storage_moved_failed_alert', - 'state_update_alert', - 'state_changed_alert', - 'save_resume_data_alert', - 'save_resume_data_failed_alert', - 'fastresume_rejected_alert', + 'external_ip', + 'performance', + 'add_torrent', + 'metadata_received', + 'torrent_finished', + 'torrent_paused', + 'torrent_checked', + 'torrent_resumed', + 'tracker_reply', + 'tracker_announce', + 'tracker_warning', + 'tracker_error', + 'file_renamed', + 'file_error', + 'file_completed', + 'storage_moved', + 'storage_moved_failed', + 'state_update', + 'state_changed', + 'save_resume_data', + 'save_resume_data_failed', + 'fastresume_rejected', ] for alert_handle in alert_handles: - on_alert_func = getattr( - self, ''.join(['on_alert_', alert_handle.replace('_alert', '')]) - ) + on_alert_func = getattr(self, ''.join(['on_alert_', alert_handle])) self.alerts.register_handler(alert_handle, on_alert_func) # Define timers @@ -369,11 +367,11 @@ class TorrentManager(component.Component): add_torrent_params.flags = ( ( LT_DEFAULT_ADD_TORRENT_FLAGS - | lt.add_torrent_params_flags_t.flag_duplicate_is_error - | lt.add_torrent_params_flags_t.flag_upload_mode + | lt.torrent_flags.duplicate_is_error + | lt.torrent_flags.upload_mode ) - ^ lt.add_torrent_params_flags_t.flag_auto_managed - ^ lt.add_torrent_params_flags_t.flag_paused + ^ lt.torrent_flags.auto_managed + ^ lt.torrent_flags.paused ) torrent_handle = self.session.add_torrent(add_torrent_params) @@ -481,16 +479,12 @@ class TorrentManager(component.Component): # Set flags: enable duplicate_is_error & override_resume_data, disable auto_managed. add_torrent_params['flags'] = ( - LT_DEFAULT_ADD_TORRENT_FLAGS - | lt.add_torrent_params_flags_t.flag_duplicate_is_error - | lt.add_torrent_params_flags_t.flag_override_resume_data - ) ^ lt.add_torrent_params_flags_t.flag_auto_managed + LT_DEFAULT_ADD_TORRENT_FLAGS | lt.torrent_flags.duplicate_is_error + ) ^ lt.torrent_flags.auto_managed if options['seed_mode']: - add_torrent_params['flags'] |= lt.add_torrent_params_flags_t.flag_seed_mode + add_torrent_params['flags'] |= lt.torrent_flags.seed_mode if options['super_seeding']: - add_torrent_params[ - 'flags' - ] |= lt.add_torrent_params_flags_t.flag_super_seeding + add_torrent_params['flags'] |= lt.torrent_flags.super_seeding return torrent_id, add_torrent_params |