summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--deluge/core/alertmanager.py59
-rw-r--r--deluge/core/torrentmanager.py4
-rw-r--r--deluge/tests/test_common.py21
-rw-r--r--deluge/ui/gtk3/menubar.py44
-rw-r--r--deluge/ui/gtk3/preferences.py1
-rw-r--r--deluge/ui/web/js/deluge-all/TorrentGrid.js2
-rw-r--r--deluge/ui/web/json_api.py5
-rw-r--r--docs/requirements.txt10
8 files changed, 66 insertions, 80 deletions
diff --git a/deluge/core/alertmanager.py b/deluge/core/alertmanager.py
index 71045b0..cf541f0 100644
--- a/deluge/core/alertmanager.py
+++ b/deluge/core/alertmanager.py
@@ -17,10 +17,12 @@ This should typically only be used by the Core. Plugins should utilize the
import contextlib
import logging
import threading
+import time
from collections import defaultdict
+from functools import partial
from typing import Any, Callable
-from twisted.internet import reactor, threads
+from twisted.internet import reactor, task, threads
import deluge.component as component
from deluge._libtorrent import lt
@@ -56,8 +58,7 @@ class AlertManager(component.Component):
# handlers is a dictionary of lists {"alert_type": [handler1,h2,..]}
self.handlers = defaultdict(list)
- self.handlers_retry_timeout = 0.3
- self.handlers_retry_count = 6
+ self.handlers_timeout_secs = 2
self.delayed_calls = []
self._event = threading.Event()
@@ -82,45 +83,33 @@ class AlertManager(component.Component):
def wait_for_alert_in_thread(self):
while self._component_state not in ('Stopping', 'Stopped'):
+ if self.check_delayed_calls():
+ time.sleep(0.05)
+ continue
+
if self.session.wait_for_alert(1000) is None:
continue
if self._event.wait():
threads.blockingCallFromThread(reactor, self.maybe_handle_alerts)
+ def on_delayed_call_timeout(self, result, timeout, **kwargs):
+ log.warning('Alert handler was timed-out before being called %s', kwargs)
+
def cancel_delayed_calls(self):
"""Cancel all delayed handlers."""
for delayed_call in self.delayed_calls:
- if delayed_call.active():
- delayed_call.cancel()
+ delayed_call.cancel()
self.delayed_calls = []
- def check_delayed_calls(self, retries: int = 0) -> bool:
- """Returns True if any handler calls are delayed (upto retry limit)."""
- self.delayed_calls = [dc for dc in self.delayed_calls if dc.active()]
- if not self.delayed_calls:
- return False
-
- if retries > self.handlers_retry_count:
- log.warning(
- 'Alert handlers timeout reached, cancelling: %s', self.delayed_calls
- )
- self.cancel_delayed_calls()
- return False
+ def check_delayed_calls(self) -> bool:
+ """Returns True if any handler calls are delayed."""
+ self.delayed_calls = [dc for dc in self.delayed_calls if not dc.called]
+ return len(self.delayed_calls) > 0
- return True
-
- def maybe_handle_alerts(self, retries: int = 0) -> None:
+ def maybe_handle_alerts(self) -> None:
if self._component_state != 'Started':
return
- if self.check_delayed_calls(retries):
- log.debug('Waiting for delayed alerts: %s', self.delayed_calls)
- retries += 1
- reactor.callLater(
- self.handlers_retry_timeout, self.maybe_handle_alerts, retries
- )
- return
-
self.handle_alerts()
def register_handler(self, alert_type: str, handler: Callable[[Any], None]) -> None:
@@ -182,8 +171,18 @@ class AlertManager(component.Component):
for handler in self.handlers[alert_type]:
if log.isEnabledFor(logging.DEBUG):
log.debug('Handling alert: %s', alert_type)
-
- self.delayed_calls.append(reactor.callLater(0, handler, alert))
+ d = task.deferLater(reactor, 0, handler, alert)
+ on_handler_timeout = partial(
+ self.on_delayed_call_timeout,
+ handler=handler.__qualname__,
+ alert_type=alert_type,
+ )
+ d.addTimeout(
+ self.handlers_timeout_secs,
+ reactor,
+ onTimeoutCancel=on_handler_timeout,
+ )
+ self.delayed_calls.append(d)
def set_alert_queue_size(self, queue_size):
"""Sets the maximum size of the libtorrent alert queue"""
diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py
index a758d5c..c43a7a2 100644
--- a/deluge/core/torrentmanager.py
+++ b/deluge/core/torrentmanager.py
@@ -290,8 +290,8 @@ class TorrentManager(component.Component):
if torrent.options['remove_at_ratio']:
self.remove(torrent_id)
break
- if not torrent.status.paused:
- torrent.pause()
+
+ torrent.pause()
def __getitem__(self, torrent_id):
"""Return the Torrent with torrent_id.
diff --git a/deluge/tests/test_common.py b/deluge/tests/test_common.py
index 780d368..a1af6cc 100644
--- a/deluge/tests/test_common.py
+++ b/deluge/tests/test_common.py
@@ -30,6 +30,7 @@ from deluge.common import (
is_ipv6,
is_magnet,
is_url,
+ parse_human_size,
windows_check,
)
@@ -150,26 +151,26 @@ class TestCommon:
assert VersionSplit('1.4.0.dev1') < VersionSplit('1.4.0')
assert VersionSplit('1.4.0a1') < VersionSplit('1.4.0')
- def test_parse_human_size(self):
- from deluge.common import parse_human_size
-
- sizes = [
+ @pytest.mark.parametrize(
+ ('human_size', 'expected'),
+ [
('1', 1),
('10 bytes', 10),
('2048 bytes', 2048),
('1MiB', 2 ** (10 * 2)),
('1 MiB', 2 ** (10 * 2)),
('1 GiB', 2 ** (10 * 3)),
- ('1 GiB', 2 ** (10 * 3)),
+ ('1 TiB', 2 ** (10 * 4)),
('1M', 10**6),
+ ('1p', 10**15),
('1MB', 10**6),
('1 GB', 10**9),
('1 TB', 10**12),
- ]
-
- for human_size, byte_size in sizes:
- parsed = parse_human_size(human_size)
- assert parsed == byte_size, 'Mismatch when converting: %s' % human_size
+ ],
+ )
+ def test_parse_human_size(self, human_size, expected):
+ parsed = parse_human_size(human_size)
+ assert parsed == expected, 'Mismatch when converting: %s' % human_size
def test_archive_files(self):
arc_filelist = [
diff --git a/deluge/ui/gtk3/menubar.py b/deluge/ui/gtk3/menubar.py
index 328cb7e..9165320 100644
--- a/deluge/ui/gtk3/menubar.py
+++ b/deluge/ui/gtk3/menubar.py
@@ -578,42 +578,26 @@ class MenuBar(component.Component):
component.get('FilterTreeView').update()
def _on_known_accounts(self, known_accounts):
- known_accounts_to_log = []
- for account in known_accounts:
- account_to_log = {}
- for key, value in account.copy().items():
- if key == 'password':
- value = '*' * 10
- account_to_log[key] = value
- known_accounts_to_log.append(account_to_log)
- log.debug('_on_known_accounts: %s', known_accounts_to_log)
+ menuitem_change_owner = self.builder.get_object('menuitem_change_owner')
if len(known_accounts) <= 1:
+ menuitem_change_owner.set_visible(False)
return
- self.builder.get_object('menuitem_change_owner').set_visible(True)
-
- self.change_owner_submenu = Gtk.Menu()
- self.change_owner_submenu_items = {}
- maingroup = Gtk.RadioMenuItem()
-
- self.change_owner_submenu_items[None] = Gtk.RadioMenuItem(maingroup)
+ self.users_menu = Gtk.Menu()
+ self.users_menu_items = {}
+ menu_group = None
for account in known_accounts:
username = account['username']
- item = Gtk.RadioMenuItem.new_with_label(maingroup, username)
- self.change_owner_submenu_items[username] = item
- self.change_owner_submenu.append(item)
+ item = Gtk.RadioMenuItem.new_with_label(menu_group, username)
+ menu_group = item.get_group()
item.connect('toggled', self._on_change_owner_toggled, username)
+ self.users_menu_items[username] = item
+ self.users_menu.append(item)
- self.change_owner_submenu.show_all()
- self.change_owner_submenu_items[None].set_active(True)
- self.change_owner_submenu_items[None].hide()
- self.builder.get_object('menuitem_change_owner').connect(
- 'activate', self._on_change_owner_submenu_active
- )
- self.builder.get_object('menuitem_change_owner').set_submenu(
- self.change_owner_submenu
- )
+ self.users_menu.show_all()
+ menuitem_change_owner.set_submenu(self.users_menu)
+ menuitem_change_owner.set_visible(True)
def _on_known_accounts_fail(self, reason):
self.builder.get_object('menuitem_change_owner').set_visible(False)
@@ -622,13 +606,13 @@ class MenuBar(component.Component):
log.debug('_on_change_owner_submenu_active')
selected = component.get('TorrentView').get_selected_torrents()
if len(selected) > 1:
- self.change_owner_submenu_items[None].set_active(True)
+ self.users_menu_items[None].set_active(True)
return
torrent_owner = component.get('TorrentView').get_torrent_status(selected[0])[
'owner'
]
- for username, item in self.change_owner_submenu_items.items():
+ for username, item in self.users_menu_items.items():
item.set_active(username == torrent_owner)
def _on_change_owner_toggled(self, widget, username):
diff --git a/deluge/ui/gtk3/preferences.py b/deluge/ui/gtk3/preferences.py
index 3463b70..a024a59 100644
--- a/deluge/ui/gtk3/preferences.py
+++ b/deluge/ui/gtk3/preferences.py
@@ -117,7 +117,6 @@ class Preferences(component.Component):
# Setup accounts tab lisview
self.accounts_levels_mapping = None
- self.accounts_authlevel = self.builder.get_object('accounts_authlevel')
self.accounts_liststore = Gtk.ListStore(str, str, str, int)
self.accounts_liststore.set_sort_column_id(
ACCOUNTS_USERNAME, Gtk.SortType.ASCENDING
diff --git a/deluge/ui/web/js/deluge-all/TorrentGrid.js b/deluge/ui/web/js/deluge-all/TorrentGrid.js
index 333d133..5db7e9f 100644
--- a/deluge/ui/web/js/deluge-all/TorrentGrid.js
+++ b/deluge/ui/web/js/deluge-all/TorrentGrid.js
@@ -61,7 +61,7 @@
return String.format(
'<div style="background: url(' +
deluge.config.base +
- 'tracker/{0}) no-repeat; padding-left: 20px;">{0}</div>',
+ 'tracker/{0}) no-repeat; background-size: contain; padding-left: 20px;">{0}</div>',
Ext.util.Format.htmlEncode(value)
);
}
diff --git a/deluge/ui/web/json_api.py b/deluge/ui/web/json_api.py
index 3f25614..ea8105d 100644
--- a/deluge/ui/web/json_api.py
+++ b/deluge/ui/web/json_api.py
@@ -600,7 +600,10 @@ class WebApi(JSONComponent):
progresses = dirinfo.setdefault('progresses', [])
progresses.append(torrent_file['size'] * torrent_file['progress'] / 100)
- dirinfo['progress'] = sum(progresses) / dirinfo['size'] * 100
+ if dirinfo['size'] > 0:
+ dirinfo['progress'] = sum(progresses) / dirinfo['size'] * 100
+ else:
+ dirinfo['progress'] = 100
dirinfo['path'] = dirname
dirname = os.path.dirname(dirname)
diff --git a/docs/requirements.txt b/docs/requirements.txt
index de42439..3da1967 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,5 +1,5 @@
-sphinx==4.*
-myst-parser
-sphinx_rtd_theme==1.0.*
-sphinxcontrib-spelling==7.3.0
-sphinx-autodoc-typehints
+sphinx==7.2.*
+myst-parser==2.0.*
+sphinx_rtd_theme==2.0.*
+sphinxcontrib-spelling==8.0.*
+sphinx-autodoc-typehints==1.25.*