diff options
Diffstat (limited to 'deluge')
221 files changed, 9167 insertions, 3993 deletions
diff --git a/deluge/__rpcapi.py b/deluge/__rpcapi.py index 5f2934a32..b279d23ea 100644 --- a/deluge/__rpcapi.py +++ b/deluge/__rpcapi.py @@ -18,9 +18,7 @@ class RpcApi(object): def scan_for_methods(obj): - methods = { - '__doc__': 'Methods available in %s' % obj.__name__.lower(), - } + methods = {'__doc__': 'Methods available in %s' % obj.__name__.lower()} for d in dir(obj): if not hasattr(getattr(obj, d), '_rpcserver_export'): continue diff --git a/deluge/_libtorrent.py b/deluge/_libtorrent.py index 02edf187c..f155feedb 100644 --- a/deluge/_libtorrent.py +++ b/deluge/_libtorrent.py @@ -28,4 +28,6 @@ REQUIRED_VERSION = '1.1.2.0' LT_VERSION = lt.__version__ if VersionSplit(LT_VERSION) < VersionSplit(REQUIRED_VERSION): - raise ImportError('Deluge %s requires libtorrent >= %s' % (get_version(), REQUIRED_VERSION)) + raise ImportError( + 'Deluge %s requires libtorrent >= %s' % (get_version(), REQUIRED_VERSION) + ) diff --git a/deluge/bencode.py b/deluge/bencode.py index 997f2f2c0..2cb30f5a1 100644 --- a/deluge/bencode.py +++ b/deluge/bencode.py @@ -33,9 +33,9 @@ def decode_int(x, f): f += 1 newf = x.index(END_DELIM, f) n = int(x[f:newf]) - if x[f:f + 1] == b'-' and x[f + 1:f + 2] == b'0': + if x[f : f + 1] == b'-' and x[f + 1 : f + 2] == b'0': raise ValueError - elif x[f:f + 1] == b'0' and newf != f + 1: + elif x[f : f + 1] == b'0' and newf != f + 1: raise ValueError return (n, newf + 1) @@ -43,25 +43,25 @@ def decode_int(x, f): def decode_string(x, f): colon = x.index(BYTE_SEP, f) n = int(x[f:colon]) - if x[f:f + 1] == b'0' and colon != f + 1: + if x[f : f + 1] == b'0' and colon != f + 1: raise ValueError colon += 1 - return (x[colon:colon + n], colon + n) + return (x[colon : colon + n], colon + n) def decode_list(x, f): r, f = [], f + 1 - while x[f:f + 1] != END_DELIM: - v, f = decode_func[x[f:f + 1]](x, f) + while x[f : f + 1] != END_DELIM: + v, f = decode_func[x[f : f + 1]](x, f) r.append(v) return (r, f + 1) def decode_dict(x, f): r, f = {}, f + 1 - while x[f:f + 1] != END_DELIM: + while x[f : f + 1] != END_DELIM: k, f = decode_string(x, f) - r[k], f = decode_func[x[f:f + 1]](x, f) + r[k], f = decode_func[x[f : f + 1]](x, f) return (r, f + 1) diff --git a/deluge/common.py b/deluge/common.py index 6de58a9c5..6bb2fb043 100644 --- a/deluge/common.py +++ b/deluge/common.py @@ -44,6 +44,7 @@ except ImportError: # see: https://twistedmatrix.com/trac/ticket/9209 if platform.system() in ('Windows', 'Microsoft'): from certifi import where + os.environ['SSL_CERT_FILE'] = where() DBUS_FILEMAN = None @@ -56,7 +57,9 @@ if platform.system() not in ('Windows', 'Microsoft', 'Darwin'): else: try: bus = dbus.SessionBus() - DBUS_FILEMAN = bus.get_object('org.freedesktop.FileManager1', '/org/freedesktop/FileManager1') + DBUS_FILEMAN = bus.get_object( + 'org.freedesktop.FileManager1', '/org/freedesktop/FileManager1' + ) except dbus.DBusException: pass @@ -101,6 +104,7 @@ def get_default_config_dir(filename=None): """ if windows_check(): + def save_config_path(resource): app_data_path = os.environ.get('APPDATA') if not app_data_path: @@ -116,6 +120,7 @@ def get_default_config_dir(filename=None): app_data_path = app_data_reg[0] winreg.CloseKey(hkey) return os.path.join(app_data_path, resource) + else: from xdg.BaseDirectory import save_config_path if not filename: @@ -136,11 +141,14 @@ def get_default_download_dir(): download_dir = '' if not windows_check(): from xdg.BaseDirectory import xdg_config_home + try: with open(os.path.join(xdg_config_home, 'user-dirs.dirs'), 'r') as _file: for line in _file: if not line.startswith('#') and line.startswith('XDG_DOWNLOAD_DIR'): - download_dir = os.path.expandvars(line.partition('=')[2].rstrip().strip('"')) + download_dir = os.path.expandvars( + line.partition('=')[2].rstrip().strip('"') + ) break except IOError: pass @@ -151,46 +159,50 @@ def get_default_download_dir(): def archive_files(arc_name, filepaths): - """Compress a list of filepaths into timestamped tarball in config dir. + """Compress a list of filepaths into timestamped tarball in config dir. - The archiving config directory is 'archive'. + The archiving config directory is 'archive'. - Args: - arc_name (str): The archive output filename (appended with timestamp). - filepaths (list): A list of the files to be archived into tarball. + Args: + arc_name (str): The archive output filename (appended with timestamp). + filepaths (list): A list of the files to be archived into tarball. - Returns: - str: The full archive filepath. + Returns: + str: The full archive filepath. - """ + """ - from deluge.configmanager import get_config_dir + from deluge.configmanager import get_config_dir - # Set archive compression to lzma with bz2 fallback. - arc_comp = 'xz' if not PY2 else 'bz2' + # Set archive compression to lzma with bz2 fallback. + arc_comp = 'xz' if not PY2 else 'bz2' - archive_dir = os.path.join(get_config_dir(), 'archive') - timestamp = datetime.datetime.now().replace(microsecond=0).isoformat().replace(':', '-') - arc_filepath = os.path.join(archive_dir, arc_name + '-' + timestamp + '.tar.' + arc_comp) - max_num_arcs = 20 + archive_dir = os.path.join(get_config_dir(), 'archive') + timestamp = ( + datetime.datetime.now().replace(microsecond=0).isoformat().replace(':', '-') + ) + arc_filepath = os.path.join( + archive_dir, arc_name + '-' + timestamp + '.tar.' + arc_comp + ) + max_num_arcs = 20 - if not os.path.exists(archive_dir): - os.makedirs(archive_dir) - else: - old_arcs = glob.glob(os.path.join(archive_dir, arc_name) + '*') - if len(old_arcs) > max_num_arcs: - # TODO: Remove oldest timestamped archives. - log.warning('More than %s tarballs in config archive', max_num_arcs) + if not os.path.exists(archive_dir): + os.makedirs(archive_dir) + else: + old_arcs = glob.glob(os.path.join(archive_dir, arc_name) + '*') + if len(old_arcs) > max_num_arcs: + # TODO: Remove oldest timestamped archives. + log.warning('More than %s tarballs in config archive', max_num_arcs) - try: - with tarfile.open(arc_filepath, 'w:' + arc_comp) as tf: - for filepath in filepaths: - tf.add(filepath, arcname=os.path.basename(filepath)) - except OSError: - log.error('Problem occurred archiving filepaths: %s', filepaths) - return False - else: - return arc_filepath + try: + with tarfile.open(arc_filepath, 'w:' + arc_comp) as tf: + for filepath in filepaths: + tf.add(filepath, arcname=os.path.basename(filepath)) + except OSError: + log.error('Problem occurred archiving filepaths: %s', filepaths) + return False + else: + return arc_filepath def windows_check(): @@ -254,7 +266,7 @@ def get_os_version(): elif linux_check(): os_version = platform.linux_distribution() else: - os_version = (platform.release(), ) + os_version = (platform.release(),) return ' '.join(filter(None, os_version)) @@ -281,7 +293,7 @@ def resource_filename(module, path): This is a work-around that. """ return pkg_resources.require('Deluge>=%s' % get_version())[0].get_resource_filename( - pkg_resources._manager, os.path.join(*(module.split('.') + [path])), + pkg_resources._manager, os.path.join(*(module.split('.') + [path])) ) @@ -301,8 +313,12 @@ def open_file(path, timestamp=None): if timestamp is None: timestamp = int(time.time()) env = os.environ.copy() - env['DESKTOP_STARTUP_ID'] = '%s-%u-%s-xdg_open_TIME%d' % \ - (os.path.basename(sys.argv[0]), os.getpid(), os.uname()[1], timestamp) + env['DESKTOP_STARTUP_ID'] = '%s-%u-%s-xdg_open_TIME%d' % ( + os.path.basename(sys.argv[0]), + os.getpid(), + os.uname()[1], + timestamp, + ) subprocess.Popen(['xdg-open', '%s' % path], env=env) @@ -321,10 +337,17 @@ def show_file(path, timestamp=None): else: if timestamp is None: timestamp = int(time.time()) - startup_id = '%s_%u_%s-dbus_TIME%d' % (os.path.basename(sys.argv[0]), os.getpid(), os.uname()[1], timestamp) + startup_id = '%s_%u_%s-dbus_TIME%d' % ( + os.path.basename(sys.argv[0]), + os.getpid(), + os.uname()[1], + timestamp, + ) if DBUS_FILEMAN: paths = [urljoin('file:', pathname2url(path))] - DBUS_FILEMAN.ShowItems(paths, startup_id, dbus_interface='org.freedesktop.FileManager1') + DBUS_FILEMAN.ShowItems( + paths, startup_id, dbus_interface='org.freedesktop.FileManager1' + ) else: env = os.environ.copy() env['DESKTOP_STARTUP_ID'] = startup_id.replace('dbus', 'xdg-open') @@ -341,6 +364,7 @@ def open_url_in_browser(url): """ import webbrowser + webbrowser.open(url) @@ -396,13 +420,29 @@ def fsize(fsize_b, precision=1, shortform=False): """ if fsize_b >= 1024 ** 4: - return '%.*f %s' % (precision, fsize_b / 1024 ** 4, tib_txt_short if shortform else tib_txt) + return '%.*f %s' % ( + precision, + fsize_b / 1024 ** 4, + tib_txt_short if shortform else tib_txt, + ) elif fsize_b >= 1024 ** 3: - return '%.*f %s' % (precision, fsize_b / 1024 ** 3, gib_txt_short if shortform else gib_txt) + return '%.*f %s' % ( + precision, + fsize_b / 1024 ** 3, + gib_txt_short if shortform else gib_txt, + ) elif fsize_b >= 1024 ** 2: - return '%.*f %s' % (precision, fsize_b / 1024 ** 2, mib_txt_short if shortform else mib_txt) + return '%.*f %s' % ( + precision, + fsize_b / 1024 ** 2, + mib_txt_short if shortform else mib_txt, + ) elif fsize_b >= 1024: - return '%.*f %s' % (precision, fsize_b / 1024, kib_txt_short if shortform else kib_txt) + return '%.*f %s' % ( + precision, + fsize_b / 1024, + kib_txt_short if shortform else kib_txt, + ) else: return '%d %s' % (fsize_b, byte_txt) @@ -425,7 +465,7 @@ def fpcnt(dec, precision=2): """ - pcnt = (dec * 100) + pcnt = dec * 100 if pcnt == 0 or pcnt == 100: precision = 0 return '%.*f%%' % (precision, pcnt) @@ -447,13 +487,29 @@ def fspeed(bps, precision=1, shortform=False): """ if bps < 1024 ** 2: - return '%.*f %s' % (precision, bps / 1024, _('K/s') if shortform else _('KiB/s')) + return '%.*f %s' % ( + precision, + bps / 1024, + _('K/s') if shortform else _('KiB/s'), + ) elif bps < 1024 ** 3: - return '%.*f %s' % (precision, bps / 1024 ** 2, _('M/s') if shortform else _('MiB/s')) + return '%.*f %s' % ( + precision, + bps / 1024 ** 2, + _('M/s') if shortform else _('MiB/s'), + ) elif bps < 1024 ** 4: - return '%.*f %s' % (precision, bps / 1024 ** 3, _('G/s') if shortform else _('GiB/s')) + return '%.*f %s' % ( + precision, + bps / 1024 ** 3, + _('G/s') if shortform else _('GiB/s'), + ) else: - return '%.*f %s' % (precision, bps / 1024 ** 4, _('T/s') if shortform else _('TiB/s')) + return '%.*f %s' % ( + precision, + bps / 1024 ** 4, + _('T/s') if shortform else _('TiB/s'), + ) def fpeer(num_peers, total_peers): @@ -566,17 +622,17 @@ def tokenize(text): size_units = [ {'prefix': 'b', 'divider': 1, 'singular': 'byte', 'plural': 'bytes'}, - {'prefix': 'KiB', 'divider': 1024**1}, - {'prefix': 'MiB', 'divider': 1024**2}, - {'prefix': 'GiB', 'divider': 1024**3}, - {'prefix': 'TiB', 'divider': 1024**4}, - {'prefix': 'PiB', 'divider': 1024**5}, - {'prefix': 'KB', 'divider': 1000**1}, - {'prefix': 'MB', 'divider': 1000**2}, - {'prefix': 'GB', 'divider': 1000**3}, - {'prefix': 'TB', 'divider': 1000**4}, - {'prefix': 'PB', 'divider': 1000**5}, - {'prefix': 'm', 'divider': 1000**2}, + {'prefix': 'KiB', 'divider': 1024 ** 1}, + {'prefix': 'MiB', 'divider': 1024 ** 2}, + {'prefix': 'GiB', 'divider': 1024 ** 3}, + {'prefix': 'TiB', 'divider': 1024 ** 4}, + {'prefix': 'PiB', 'divider': 1024 ** 5}, + {'prefix': 'KB', 'divider': 1000 ** 1}, + {'prefix': 'MB', 'divider': 1000 ** 2}, + {'prefix': 'GB', 'divider': 1000 ** 3}, + {'prefix': 'TB', 'divider': 1000 ** 4}, + {'prefix': 'PB', 'divider': 1000 ** 5}, + {'prefix': 'm', 'divider': 1000 ** 2}, ] @@ -706,9 +762,9 @@ def get_magnet_info(uri): info_hash = None trackers = {} tier = 0 - for param in uri[len(MAGNET_SCHEME):].split('&'): + for param in uri[len(MAGNET_SCHEME) :].split('&'): if param.startswith(XT_BTIH_PARAM): - xt_hash = param[len(XT_BTIH_PARAM):] + xt_hash = param[len(XT_BTIH_PARAM) :] if len(xt_hash) == 32: try: infohash_str = base64.b32decode(xt_hash.upper()) @@ -721,9 +777,9 @@ def get_magnet_info(uri): else: break elif param.startswith(DN_PARAM): - name = unquote_plus(param[len(DN_PARAM):]) + name = unquote_plus(param[len(DN_PARAM) :]) elif param.startswith(TR_PARAM): - tracker = unquote_plus(param[len(TR_PARAM):]) + tracker = unquote_plus(param[len(TR_PARAM) :]) trackers[tracker] = tier tier += 1 elif param.startswith(tr0_param): @@ -736,7 +792,12 @@ def get_magnet_info(uri): if info_hash: if not name: name = info_hash - return {'name': name, 'info_hash': info_hash, 'files_tree': '', 'trackers': trackers} + return { + 'name': name, + 'info_hash': info_hash, + 'files_tree': '', + 'trackers': trackers, + } else: return {} @@ -758,11 +819,7 @@ def create_magnet_uri(infohash, name=None, trackers=None): except TypeError: infohash.encode('utf-8') - uri = [ - MAGNET_SCHEME, - XT_BTIH_PARAM, - base64.b32encode(infohash).decode('utf-8'), - ] + uri = [MAGNET_SCHEME, XT_BTIH_PARAM, base64.b32encode(infohash).decode('utf-8')] if name: uri.extend(['&', DN_PARAM, name]) if trackers: @@ -817,6 +874,7 @@ def free_space(path): if windows_check(): from win32file import GetDiskFreeSpaceEx + return GetDiskFreeSpaceEx(path)[0] else: disk_data = os.statvfs(path.encode('utf8')) @@ -860,6 +918,7 @@ def is_ipv4(ip): """ import socket + try: if windows_check(): return socket.inet_aton(ip) @@ -888,6 +947,7 @@ def is_ipv6(ip): import ipaddress except ImportError: import socket + try: return socket.inet_pton(socket.AF_INET6, ip) except (socket.error, AttributeError): @@ -969,8 +1029,7 @@ def utf8_encode_structure(data): return type(data)([utf8_encode_structure(d) for d in data]) elif isinstance(data, dict): return { - utf8_encode_structure(k): utf8_encode_structure(v) - for k, v in data.items() + utf8_encode_structure(k): utf8_encode_structure(v) for k, v in data.items() } elif not isinstance(data, bytes): try: @@ -989,6 +1048,7 @@ class VersionSplit(object): :type ver: string """ + def __init__(self, ver): version_re = re.compile( r""" @@ -1001,7 +1061,8 @@ class VersionSplit(object): (?P<prerelversion>\d+(?:\.\d+)*) )? (?P<postdev>(\.post(?P<post>\d+))?(\.dev(?P<dev>\d+))?)? - $""", re.VERBOSE, + $""", + re.VERBOSE, ) # Check for PEP 386 compliant version @@ -1086,48 +1147,52 @@ def create_localclient_account(append=False): with open(auth_file, 'a' if append else 'w') as _file: _file.write( - ':'.join([ - 'localclient', - sha(str(random.random()).encode('utf8')).hexdigest(), - str(AUTH_LEVEL_ADMIN), - ]) + '\n', + ':'.join( + [ + 'localclient', + sha(str(random.random()).encode('utf8')).hexdigest(), + str(AUTH_LEVEL_ADMIN), + ] + ) + + '\n' ) _file.flush() os.fsync(_file.fileno()) def get_localhost_auth(): - """Grabs the localclient auth line from the 'auth' file and creates a localhost uri. + """Grabs the localclient auth line from the 'auth' file and creates a localhost uri. - Returns: - tuple: With the username and password to login as. + Returns: + tuple: With the username and password to login as. + """ + from deluge.configmanager import get_config_dir - """ - from deluge.configmanager import get_config_dir - auth_file = get_config_dir('auth') - if not os.path.exists(auth_file): - from deluge.common import create_localclient_account - create_localclient_account() + auth_file = get_config_dir('auth') + if not os.path.exists(auth_file): + from deluge.common import create_localclient_account - with open(auth_file) as auth: - for line in auth: - line = line.strip() - if line.startswith('#') or not line: - # This is a comment or blank line - continue + create_localclient_account() - lsplit = line.split(':') + with open(auth_file) as auth: + for line in auth: + line = line.strip() + if line.startswith('#') or not line: + # This is a comment or blank line + continue - if len(lsplit) == 2: - username, password = lsplit - elif len(lsplit) == 3: - username, password, level = lsplit - else: - log.error('Your auth file is malformed: Incorrect number of fields!') - continue + lsplit = line.split(':') - if username == 'localclient': - return (username, password) + if len(lsplit) == 2: + username, password = lsplit + elif len(lsplit) == 3: + username, password, level = lsplit + else: + log.error('Your auth file is malformed: Incorrect number of fields!') + continue + + if username == 'localclient': + return (username, password) def set_env_variable(name, value): @@ -1169,9 +1234,16 @@ def set_env_variable(name, value): if result == 0: raise Warning except Exception: - log.warning('Failed to set Env Var \'%s\' (\'kernel32.SetEnvironmentVariableW\')', name) + log.warning( + 'Failed to set Env Var \'%s\' (\'kernel32.SetEnvironmentVariableW\')', + name, + ) else: - log.debug('Set Env Var \'%s\' to \'%s\' (\'kernel32.SetEnvironmentVariableW\')', name, value) + log.debug( + 'Set Env Var \'%s\' to \'%s\' (\'kernel32.SetEnvironmentVariableW\')', + name, + value, + ) # Update the copy maintained by msvcrt (used by gtk+ runtime) try: @@ -1191,9 +1263,13 @@ def set_env_variable(name, value): if result != 0: raise Warning except Exception: - log.warning('Failed to set Env Var \'%s\' (\'%s._putenv\')', name, msvcrtname) + log.warning( + 'Failed to set Env Var \'%s\' (\'%s._putenv\')', name, msvcrtname + ) else: - log.debug('Set Env Var \'%s\' to \'%s\' (\'%s._putenv\')', name, value, msvcrtname) + log.debug( + 'Set Env Var \'%s\' to \'%s\' (\'%s._putenv\')', name, value, msvcrtname + ) def unicode_argv(): @@ -1219,8 +1295,7 @@ def unicode_argv(): if argc.value > 0: # Remove Python executable and commands if present start = argc.value - len(sys.argv) - return [argv[i] for i in - range(start, argc.value)] + return [argv[i] for i in range(start, argc.value)] else: # On other platforms, we have to find the likely encoding of the args and decode # First check if sys.stdout or stdin have encoding set @@ -1253,6 +1328,7 @@ def run_profiled(func, *args, **kwargs): """ if kwargs.get('do_profile', True) is not False: import cProfile + profiler = cProfile.Profile() def on_shutdown(): @@ -1264,6 +1340,7 @@ def run_profiled(func, *args, **kwargs): else: import pstats from io import StringIO + strio = StringIO() ps = pstats.Stats(profiler, stream=strio).sort_stats('cumulative') ps.print_stats() @@ -1291,6 +1368,7 @@ def is_process_running(pid): if windows_check(): from win32process import EnumProcesses + return pid in EnumProcesses() else: try: diff --git a/deluge/component.py b/deluge/component.py index 8264a5e64..72aa3246a 100644 --- a/deluge/component.py +++ b/deluge/component.py @@ -27,7 +27,6 @@ class ComponentAlreadyRegistered(Exception): class ComponentException(Exception): - def __init__(self, message, tb): super(ComponentException, self).__init__(message) self.message = message @@ -93,6 +92,7 @@ class Component(object): still be considered in a Started state. """ + def __init__(self, name, interval=1, depend=None): """Initialize component. @@ -146,12 +146,14 @@ class Component(object): elif self._component_state == 'Started': d = succeed(True) else: - d = fail(ComponentException( - 'Trying to start component "%s" but it is ' - 'not in a stopped state. Current state: %s' % - (self._component_name, self._component_state), - traceback.format_stack(limit=4), - )) + d = fail( + ComponentException( + 'Trying to start component "%s" but it is ' + 'not in a stopped state. Current state: %s' + % (self._component_name, self._component_state), + traceback.format_stack(limit=4), + ) + ) return d def _component_stop(self): @@ -195,12 +197,14 @@ class Component(object): elif self._component_state == 'Paused': d = succeed(None) else: - d = fail(ComponentException( - 'Trying to pause component "%s" but it is ' - 'not in a started state. Current state: %s' % - (self._component_name, self._component_state), - traceback.format_stack(limit=4), - )) + d = fail( + ComponentException( + 'Trying to pause component "%s" but it is ' + 'not in a started state. Current state: %s' + % (self._component_name, self._component_state), + traceback.format_stack(limit=4), + ) + ) return d def _component_resume(self): @@ -211,12 +215,14 @@ class Component(object): d = maybeDeferred(self._component_start_timer) d.addCallback(on_resume) else: - d = fail(ComponentException( - 'Trying to resume component "%s" but it is ' - 'not in a paused state. Current state: %s' % - (self._component_name, self._component_state), - traceback.format_stack(limit=4), - )) + d = fail( + ComponentException( + 'Trying to resume component "%s" but it is ' + 'not in a paused state. Current state: %s' + % (self._component_name, self._component_state), + traceback.format_stack(limit=4), + ) + ) return d def _component_shutdown(self): @@ -250,6 +256,7 @@ class ComponentRegistry(object): It is used to manage the Components by starting, stopping, pausing and shutting them down. """ + def __init__(self): self.components = {} # Stores all of the components that are dependent on a particular component @@ -270,7 +277,9 @@ class ComponentRegistry(object): """ name = obj._component_name if name in self.components: - raise ComponentAlreadyRegistered('Component already registered with name %s' % name) + raise ComponentAlreadyRegistered( + 'Component already registered with name %s' % name + ) self.components[obj._component_name] = obj if obj._component_depend: @@ -295,6 +304,7 @@ class ComponentRegistry(object): def on_stop(result, name): # Component may have been removed, so pop to ensure it doesn't fail self.components.pop(name, None) + return d.addCallback(on_stop, obj._component_name) else: return succeed(None) @@ -364,7 +374,9 @@ class ComponentRegistry(object): if name in self.components: if name in self.dependents: # If other components depend on this component, stop them first - d = self.stop(self.dependents[name]).addCallback(on_dependents_stopped, name) + d = self.stop(self.dependents[name]).addCallback( + on_dependents_stopped, name + ) deferreds.append(d) stopped_in_deferred.update(self.dependents[name]) else: @@ -434,8 +446,11 @@ class ComponentRegistry(object): Deferred: Fired once all Components have been successfully shut down. """ + def on_stopped(result): - return DeferredList([comp._component_shutdown() for comp in self.components.values()]) + return DeferredList( + [comp._component_shutdown() for comp in self.components.values()] + ) return self.stop(list(self.components)).addCallback(on_stopped) diff --git a/deluge/config.py b/deluge/config.py index 45a440d1a..3061bf947 100644 --- a/deluge/config.py +++ b/deluge/config.py @@ -121,16 +121,14 @@ class Config(object): setup to convert old config files. (default: 1) """ + def __init__(self, filename, defaults=None, config_dir=None, file_version=1): self.__config = {} self.__set_functions = {} self.__change_callbacks = [] # These hold the version numbers and they will be set when loaded - self.__version = { - 'format': 1, - 'file': file_version, - } + self.__version = {'format': 1, 'file': file_version} # This will get set with a reactor.callLater whenever a config option # is set. @@ -210,7 +208,9 @@ class Config(object): global callLater if callLater is None: # Must import here and not at the top or it will throw ReactorAlreadyInstalledError - from twisted.internet.reactor import callLater # pylint: disable=redefined-outer-name + from twisted.internet.reactor import ( + callLater, + ) # pylint: disable=redefined-outer-name # Run the set_function for this key if any try: for func in self.__set_functions[key]: @@ -218,9 +218,11 @@ class Config(object): except KeyError: pass try: + def do_change_callbacks(key, value): for func in self.__change_callbacks: func(key, value) + callLater(0, do_change_callbacks, key, value) except Exception: pass @@ -306,7 +308,9 @@ class Config(object): global callLater if callLater is None: # Must import here and not at the top or it will throw ReactorAlreadyInstalledError - from twisted.internet.reactor import callLater # pylint: disable=redefined-outer-name + from twisted.internet.reactor import ( + callLater, + ) # pylint: disable=redefined-outer-name # We set the save_timer for 5 seconds if not already set if not self._save_timer or not self._save_timer.active(): @@ -432,8 +436,11 @@ class Config(object): log.warning('Unable to load config file: %s', filename) log.debug( - 'Config %s version: %s.%s loaded: %s', filename, - self.__version['format'], self.__version['file'], self.__config, + 'Config %s version: %s.%s loaded: %s', + filename, + self.__version['format'], + self.__version['file'], + self.__config, ) def save(self, filename=None): @@ -518,7 +525,8 @@ class Config(object): if self.__version['file'] not in input_range: log.debug( 'File version %s is not in input_range %s, ignoring converter function..', - self.__version['file'], input_range, + self.__version['file'], + input_range, ) return @@ -528,7 +536,9 @@ class Config(object): log.exception(ex) log.error( 'There was an exception try to convert config file %s %s to %s', - self.__config_file, self.__version['file'], output_version, + self.__config_file, + self.__version['file'], + output_version, ) raise ex else: @@ -542,9 +552,11 @@ class Config(object): @prop def config(): # pylint: disable=no-method-argument """The config dictionary""" + def fget(self): return self.__config def fdel(self): return self.save() + return locals() diff --git a/deluge/configmanager.py b/deluge/configmanager.py index db4de51f6..bbb0389a5 100644 --- a/deluge/configmanager.py +++ b/deluge/configmanager.py @@ -95,7 +95,8 @@ class _ConfigManager(object): # Create the config object if not already created if config_file not in self.config_files: self.config_files[config_file] = Config( - config_file, defaults, + config_file, + defaults, config_dir=self.config_directory, file_version=file_version, ) @@ -108,7 +109,9 @@ _configmanager = _ConfigManager() def ConfigManager(config, defaults=None, file_version=1): # NOQA: N802 - return _configmanager.get_config(config, defaults=defaults, file_version=file_version) + return _configmanager.get_config( + config, defaults=defaults, file_version=file_version + ) def set_config_dir(directory): diff --git a/deluge/core/alertmanager.py b/deluge/core/alertmanager.py index af4fa4287..04e429ea5 100644 --- a/deluge/core/alertmanager.py +++ b/deluge/core/alertmanager.py @@ -30,6 +30,7 @@ log = logging.getLogger(__name__) class AlertManager(component.Component): """AlertManager fetches and processes libtorrent alerts""" + def __init__(self): log.debug('AlertManager init...') component.Component.__init__(self, 'AlertManager', interval=0.3) @@ -40,13 +41,13 @@ class AlertManager(component.Component): self.set_alert_queue_size(self.alert_queue_size) alert_mask = ( - lt.alert.category_t.error_notification | - lt.alert.category_t.port_mapping_notification | - lt.alert.category_t.storage_notification | - lt.alert.category_t.tracker_notification | - lt.alert.category_t.status_notification | - lt.alert.category_t.ip_block_notification | - lt.alert.category_t.performance_warning + lt.alert.category_t.error_notification + | lt.alert.category_t.port_mapping_notification + | lt.alert.category_t.storage_notification + | lt.alert.category_t.tracker_notification + | lt.alert.category_t.status_notification + | lt.alert.category_t.ip_block_notification + | lt.alert.category_t.performance_warning ) self.session.apply_settings({'alert_mask': alert_mask}) @@ -107,7 +108,10 @@ class AlertManager(component.Component): if log.isEnabledFor(logging.DEBUG): log.debug('Alerts queued: %s', num_alerts) if num_alerts > 0.9 * self.alert_queue_size: - log.warning('Warning total alerts queued, %s, passes 90%% of queue size.', num_alerts) + log.warning( + 'Warning total alerts queued, %s, passes 90%% of queue size.', + num_alerts, + ) # Loop through all alerts in the queue for alert in alerts: @@ -126,4 +130,6 @@ class AlertManager(component.Component): """Sets the maximum size of the libtorrent alert queue""" log.info('Alert Queue Size set to %s', queue_size) self.alert_queue_size = queue_size - component.get('Core').apply_session_setting('alert_queue_size', self.alert_queue_size) + component.get('Core').apply_session_setting( + 'alert_queue_size', self.alert_queue_size + ) diff --git a/deluge/core/authmanager.py b/deluge/core/authmanager.py index b241d9a75..0d997c10c 100644 --- a/deluge/core/authmanager.py +++ b/deluge/core/authmanager.py @@ -17,8 +17,14 @@ from io import open import deluge.component as component import deluge.configmanager as configmanager -from deluge.common import (AUTH_LEVEL_ADMIN, AUTH_LEVEL_DEFAULT, AUTH_LEVEL_NONE, AUTH_LEVEL_NORMAL, - AUTH_LEVEL_READONLY, create_localclient_account) +from deluge.common import ( + AUTH_LEVEL_ADMIN, + AUTH_LEVEL_DEFAULT, + AUTH_LEVEL_NONE, + AUTH_LEVEL_NORMAL, + AUTH_LEVEL_READONLY, + create_localclient_account, +) from deluge.error import AuthenticationRequired, AuthManagerError, BadLoginError log = logging.getLogger(__name__) @@ -50,8 +56,10 @@ class Account(object): } def __repr__(self): - return ('<Account username="%(username)s" authlevel=%(authlevel)s>' % - {'username': self.username, 'authlevel': self.authlevel}) + return '<Account username="%(username)s" authlevel=%(authlevel)s>' % { + 'username': self.username, + 'authlevel': self.authlevel, + } class AuthManager(component.Component): @@ -99,7 +107,7 @@ class AuthManager(component.Component): """ if not username: raise AuthenticationRequired( - 'Username and Password are required.', username, + 'Username and Password are required.', username ) if username not in self.__auth: @@ -131,8 +139,7 @@ class AuthManager(component.Component): raise AuthManagerError('Invalid auth level: %s' % authlevel) try: self.__auth[username] = Account( - username, password, - AUTH_LEVELS_MAPPING[authlevel], + username, password, AUTH_LEVELS_MAPPING[authlevel] ) self.write_auth_file() return True @@ -160,7 +167,7 @@ class AuthManager(component.Component): raise AuthManagerError('Username not known', username) elif username == component.get('RPCServer').get_session_user(): raise AuthManagerError( - 'You cannot delete your own account while logged in!', username, + 'You cannot delete your own account while logged in!', username ) del self.__auth[username] @@ -184,7 +191,10 @@ class AuthManager(component.Component): try: with open(filepath_tmp, 'w', encoding='utf8') as _file: for account in self.__auth.values(): - _file.write('%(username)s:%(password)s:%(authlevel_int)s\n' % account.data()) + _file.write( + '%(username)s:%(password)s:%(authlevel_int)s\n' + % account.data() + ) _file.flush() os.fsync(_file.fileno()) shutil.move(filepath_tmp, filepath) @@ -237,7 +247,9 @@ class AuthManager(component.Component): username, password = lsplit log.warning( 'Your auth entry for %s contains no auth level, ' - 'using AUTH_LEVEL_DEFAULT(%s)..', username, AUTH_LEVEL_DEFAULT, + 'using AUTH_LEVEL_DEFAULT(%s)..', + username, + AUTH_LEVEL_DEFAULT, ) if username == 'localclient': authlevel = AUTH_LEVEL_ADMIN @@ -259,7 +271,10 @@ class AuthManager(component.Component): try: authlevel = AUTH_LEVELS_MAPPING[authlevel] except KeyError: - log.error('Your auth file is malformed: %r is not a valid auth level', authlevel) + log.error( + 'Your auth file is malformed: %r is not a valid auth level', + authlevel, + ) continue self.__auth[username] = Account(username, password, authlevel) diff --git a/deluge/core/core.py b/deluge/core/core.py index 362bf1dfb..a1571a25e 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -28,8 +28,13 @@ from deluge._libtorrent import LT_VERSION, lt from deluge.common import PY2 from deluge.configmanager import ConfigManager, get_config_dir from deluge.core.alertmanager import AlertManager -from deluge.core.authmanager import (AUTH_LEVEL_ADMIN, AUTH_LEVEL_NONE, AUTH_LEVELS_MAPPING, - AUTH_LEVELS_MAPPING_REVERSE, AuthManager) +from deluge.core.authmanager import ( + AUTH_LEVEL_ADMIN, + AUTH_LEVEL_NONE, + AUTH_LEVELS_MAPPING, + AUTH_LEVELS_MAPPING_REVERSE, + AuthManager, +) from deluge.core.eventmanager import EventManager from deluge.core.filtermanager import FilterManager from deluge.core.pluginmanager import PluginManager @@ -37,8 +42,18 @@ from deluge.core.preferencesmanager import PreferencesManager from deluge.core.rpcserver import export from deluge.core.torrentmanager import TorrentManager from deluge.decorators import deprecated -from deluge.error import AddTorrentError, DelugeError, InvalidPathError, InvalidTorrentError -from deluge.event import NewVersionAvailableEvent, SessionPausedEvent, SessionResumedEvent, TorrentQueueChangedEvent +from deluge.error import ( + AddTorrentError, + DelugeError, + InvalidPathError, + InvalidTorrentError, +) +from deluge.event import ( + NewVersionAvailableEvent, + SessionPausedEvent, + SessionResumedEvent, + TorrentQueueChangedEvent, +) from deluge.httpdownloader import download_file try: @@ -99,16 +114,15 @@ DELUGE_VER = deluge.common.get_version() class Core(component.Component): - def __init__(self, listen_interface=None, outgoing_interface=None, read_only_config_keys=None): + def __init__( + self, listen_interface=None, outgoing_interface=None, read_only_config_keys=None + ): component.Component.__init__(self, 'Core') # Start the libtorrent session. user_agent = 'Deluge/{} libtorrent/{}'.format(DELUGE_VER, LT_VERSION) peer_id = self._create_peer_id(DELUGE_VER) - log.debug( - 'Starting session (peer_id: %s, user_agent: %s)', - peer_id, user_agent, - ) + log.debug('Starting session (peer_id: %s, user_agent: %s)', peer_id, user_agent) settings_pack = { 'peer_fingerprint': peer_id, 'user_agent': user_agent, @@ -139,7 +153,9 @@ class Core(component.Component): # External IP Address from libtorrent self.external_ip = None - self.eventmanager.register_event_handler('ExternalIPEvent', self._on_external_ip_event) + self.eventmanager.register_event_handler( + 'ExternalIPEvent', self._on_external_ip_event + ) # GeoIP instance with db loaded self.geoip_instance = None @@ -161,7 +177,10 @@ class Core(component.Component): self._old_listen_interface = self.config['listen_interface'] self.config['listen_interface'] = listen_interface else: - log.error('Invalid listen interface (must be IP Address): %s', listen_interface) + log.error( + 'Invalid listen interface (must be IP Address): %s', + listen_interface, + ) self._old_outgoing_interface = None if outgoing_interface: @@ -186,10 +205,10 @@ class Core(component.Component): self.session_status.update({k: 0.0 for k in hit_ratio_keys}) self.session_status_timer_interval = 0.5 - self.session_status_timer = task.LoopingCall( - self.session.post_session_stats) + 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_alert', self._on_alert_session_stats + ) self.session_rates_timer_interval = 2 self.session_rates_timer = task.LoopingCall(self._update_session_rates) @@ -264,7 +283,7 @@ class Core(component.Component): def substitute_chr(string, idx, char): """Fast substitute single char in string.""" - return string[:idx] + char + string[idx + 1:] + return string[:idx] + char + string[idx + 1 :] if split.dev: release_chr = 'D' @@ -358,16 +377,20 @@ class Core(component.Component): for rate_key, prev_bytes in list(self._session_prev_bytes.items()): new_bytes = self.session_status[SESSION_RATES_MAPPING[rate_key]] self.session_status[rate_key] = ( - new_bytes - prev_bytes) / self.session_rates_timer_interval + new_bytes - prev_bytes + ) / self.session_rates_timer_interval # Store current value for next update. self._session_prev_bytes[rate_key] = new_bytes def get_new_release(self): log.debug('get_new_release') try: - self.new_release = urlopen( - 'http://download.deluge-torrent.org/version-2.0', - ).read().decode().strip() + self.new_release = ( + urlopen('http://download.deluge-torrent.org/version-2.0') + .read() + .decode() + .strip() + ) except URLError as ex: log.debug('Unable to get release info from website: %s', ex) else: @@ -376,8 +399,12 @@ class Core(component.Component): def check_new_release(self): if self.new_release: log.debug('new_release: %s', self.new_release) - if deluge.common.VersionSplit(self.new_release) > deluge.common.VersionSplit(deluge.common.get_version()): - component.get('EventManager').emit(NewVersionAvailableEvent(self.new_release)) + if deluge.common.VersionSplit( + self.new_release + ) > deluge.common.VersionSplit(deluge.common.get_version()): + component.get('EventManager').emit( + NewVersionAvailableEvent(self.new_release) + ) return self.new_release return False @@ -403,7 +430,10 @@ class Core(component.Component): try: d = self.torrentmanager.add_async( - filedump=filedump, options=options, filename=filename, save_state=save_state, + filedump=filedump, + options=options, + filename=filename, + save_state=save_state, ) except RuntimeError as ex: log.error('There was an error adding the torrent file %s: %s', filename, ex) @@ -425,6 +455,7 @@ class Core(component.Component): The metadata is base64 encoded. """ + def on_metadata(result, result_d): torrent_id, metadata = result result_d.callback((torrent_id, b64encode(metadata))) @@ -456,7 +487,7 @@ class Core(component.Component): try: return self.torrentmanager.add( - filedump=filedump, options=options, filename=filename, + filedump=filedump, options=options, filename=filename ) except RuntimeError as ex: log.error('There was an error adding the torrent file %s: %s', filename, ex) @@ -473,6 +504,7 @@ class Core(component.Component): Deferred """ + @defer.inlineCallbacks def add_torrents(): errors = [] @@ -480,12 +512,13 @@ class Core(component.Component): for idx, torrent in enumerate(torrent_files): try: yield self.add_torrent_file_async( - torrent[0], torrent[1], torrent[2], save_state=idx == last_index, + torrent[0], torrent[1], torrent[2], save_state=idx == last_index ) except AddTorrentError as ex: log.warning('Error when adding torrent: %s', ex) errors.append(ex) defer.returnValue(errors) + return task.deferLater(reactor, 0, add_torrents) @export @@ -583,14 +616,19 @@ class Core(component.Component): errors = [] for torrent_id in torrent_ids: try: - self.torrentmanager.remove(torrent_id, remove_data=remove_data, save_state=False) + self.torrentmanager.remove( + torrent_id, remove_data=remove_data, save_state=False + ) except InvalidTorrentError as ex: errors.append((torrent_id, str(ex))) # Save the session state self.torrentmanager.save_state() if errors: - log.warning('Failed to remove %d of %d torrents.', len(errors), len(torrent_ids)) + log.warning( + 'Failed to remove %d of %d torrents.', len(errors), len(torrent_ids) + ) return errors + return task.deferLater(reactor, 0, do_remove_torrents) @export @@ -617,9 +655,7 @@ class Core(component.Component): if key in DEPR_SESSION_STATUS_KEYS: new_key = DEPR_SESSION_STATUS_KEYS[key] log.debug( - 'Deprecated session status key %s, please use %s', - key, - new_key, + 'Deprecated session status key %s, please use %s', key, new_key ) status[key] = self.session_status[new_key] else: @@ -700,11 +736,22 @@ class Core(component.Component): for torrent_id in torrent_ids: self.resume_torrent(torrent_id) - def create_torrent_status(self, torrent_id, torrent_keys, plugin_keys, diff=False, update=False, all_keys=False): + def create_torrent_status( + self, + torrent_id, + torrent_keys, + plugin_keys, + diff=False, + update=False, + all_keys=False, + ): try: - status = self.torrentmanager[torrent_id].get_status(torrent_keys, diff, update=update, all_keys=all_keys) + status = self.torrentmanager[torrent_id].get_status( + torrent_keys, diff, update=update, all_keys=all_keys + ) except KeyError: import traceback + traceback.print_exc() # Torrent was probaly removed meanwhile return {} @@ -716,9 +763,15 @@ class Core(component.Component): @export def get_torrent_status(self, torrent_id, keys, diff=False): - torrent_keys, plugin_keys = self.torrentmanager.separate_keys(keys, [torrent_id]) + torrent_keys, plugin_keys = self.torrentmanager.separate_keys( + keys, [torrent_id] + ) return self.create_torrent_status( - torrent_id, torrent_keys, plugin_keys, diff=diff, update=True, + torrent_id, + torrent_keys, + plugin_keys, + diff=diff, + update=True, all_keys=not keys, ) @@ -735,8 +788,11 @@ class Core(component.Component): # Ask the plugin manager to fill in the plugin keys if len(plugin_keys) > 0: for key in status_dict: - status_dict[key].update(self.pluginmanager.get_status(key, plugin_keys)) + status_dict[key].update( + self.pluginmanager.get_status(key, plugin_keys) + ) return status_dict + d.addCallback(add_plugin_fields) return d @@ -798,7 +854,9 @@ class Core(component.Component): settings = self.session.get_settings() proxy_type = settings['proxy_type'] - proxy_hostname = settings['i2p_hostname'] if proxy_type == 6 else settings['proxy_hostname'] + proxy_hostname = ( + settings['i2p_hostname'] if proxy_type == 6 else settings['proxy_hostname'] + ) proxy_port = settings['i2p_port'] if proxy_type == 6 else settings['proxy_port'] proxy_dict = { 'type': proxy_type, @@ -939,8 +997,17 @@ class Core(component.Component): @export def create_torrent( - self, path, tracker, piece_length, comment, target, - webseeds, private, created_by, trackers, add_to_session, + self, + path, + tracker, + piece_length, + comment, + target, + webseeds, + private, + created_by, + trackers, + add_to_session, ): log.debug('creating torrent..') @@ -961,10 +1028,20 @@ class Core(component.Component): ).start() def _create_torrent_thread( - self, path, tracker, piece_length, comment, target, - webseeds, private, created_by, trackers, add_to_session, + self, + path, + tracker, + piece_length, + comment, + target, + webseeds, + private, + created_by, + trackers, + add_to_session, ): from deluge import metafile + metafile.make_meta_file( path, tracker, @@ -1058,7 +1135,9 @@ class Core(component.Component): def queue_top(self, torrent_ids): log.debug('Attempting to queue %s to top', torrent_ids) # torrent_ids must be sorted in reverse before moving to preserve order - for torrent_id in sorted(torrent_ids, key=self.torrentmanager.get_queue_position, reverse=True): + for torrent_id in sorted( + torrent_ids, key=self.torrentmanager.get_queue_position, reverse=True + ): try: # If the queue method returns True, then we should emit a signal if self.torrentmanager.queue_top(torrent_id): @@ -1069,7 +1148,10 @@ class Core(component.Component): @export def queue_up(self, torrent_ids): log.debug('Attempting to queue %s to up', torrent_ids) - torrents = ((self.torrentmanager.get_queue_position(torrent_id), torrent_id) for torrent_id in torrent_ids) + torrents = ( + (self.torrentmanager.get_queue_position(torrent_id), torrent_id) + for torrent_id in torrent_ids + ) torrent_moved = True prev_queue_position = None # torrent_ids must be sorted before moving. @@ -1079,7 +1161,9 @@ class Core(component.Component): try: torrent_moved = self.torrentmanager.queue_up(torrent_id) except KeyError: - log.warning('torrent_id: %s does not exist in the queue', torrent_id) + log.warning( + 'torrent_id: %s does not exist in the queue', torrent_id + ) # If the torrent moved, then we should emit a signal if torrent_moved: component.get('EventManager').emit(TorrentQueueChangedEvent()) @@ -1089,7 +1173,10 @@ class Core(component.Component): @export def queue_down(self, torrent_ids): log.debug('Attempting to queue %s to down', torrent_ids) - torrents = ((self.torrentmanager.get_queue_position(torrent_id), torrent_id) for torrent_id in torrent_ids) + torrents = ( + (self.torrentmanager.get_queue_position(torrent_id), torrent_id) + for torrent_id in torrent_ids + ) torrent_moved = True prev_queue_position = None # torrent_ids must be sorted before moving. @@ -1099,7 +1186,9 @@ class Core(component.Component): try: torrent_moved = self.torrentmanager.queue_down(torrent_id) except KeyError: - log.warning('torrent_id: %s does not exist in the queue', torrent_id) + log.warning( + 'torrent_id: %s does not exist in the queue', torrent_id + ) # If the torrent moved, then we should emit a signal if torrent_moved: component.get('EventManager').emit(TorrentQueueChangedEvent()) @@ -1110,7 +1199,9 @@ class Core(component.Component): def queue_bottom(self, torrent_ids): log.debug('Attempting to queue %s to bottom', torrent_ids) # torrent_ids must be sorted before moving to preserve order - for torrent_id in sorted(torrent_ids, key=self.torrentmanager.get_queue_position): + for torrent_id in sorted( + torrent_ids, key=self.torrentmanager.get_queue_position + ): try: # If the queue method returns True, then we should emit a signal if self.torrentmanager.queue_bottom(torrent_id): diff --git a/deluge/core/daemon.py b/deluge/core/daemon.py index bd203d940..42f39a23c 100644 --- a/deluge/core/daemon.py +++ b/deluge/core/daemon.py @@ -66,7 +66,12 @@ class Daemon(object): """The Deluge Daemon class""" def __init__( - self, listen_interface=None, outgoing_interface=None, interface=None, port=None, standalone=False, + self, + listen_interface=None, + outgoing_interface=None, + interface=None, + port=None, + standalone=False, read_only_config_keys=None, ): """ @@ -84,19 +89,23 @@ class Daemon(object): self.pid_file = get_config_dir('deluged.pid') log.info('Deluge daemon %s', get_version()) if is_daemon_running(self.pid_file): - raise DaemonRunningError('Deluge daemon already running with this config directory!') + raise DaemonRunningError( + 'Deluge daemon already running with this config directory!' + ) # Twisted catches signals to terminate, so just have it call the shutdown method. reactor.addSystemEventTrigger('before', 'shutdown', self._shutdown) # Catch some Windows specific signals if windows_check(): + def win_handler(ctrl_type): """Handle the Windows shutdown or close events.""" log.debug('windows handler ctrl_type: %s', ctrl_type) if ctrl_type == CTRL_CLOSE_EVENT or ctrl_type == CTRL_SHUTDOWN_EVENT: self._shutdown() return 1 + SetConsoleCtrlHandler(win_handler) # Start the core as a thread and join it until it's done @@ -123,7 +132,10 @@ class Daemon(object): log.debug( 'Listening to UI on: %s:%s and bittorrent on: %s Making connections out on: %s', - interface, port, listen_interface, outgoing_interface, + interface, + port, + listen_interface, + outgoing_interface, ) def start(self): @@ -179,4 +191,6 @@ class Daemon(object): if rpc not in self.get_method_list(): return False - return self.rpcserver.get_session_auth_level() >= self.rpcserver.get_rpc_auth_level(rpc) + return self.rpcserver.get_session_auth_level() >= self.rpcserver.get_rpc_auth_level( + rpc + ) diff --git a/deluge/core/daemon_entry.py b/deluge/core/daemon_entry.py index eee14b32e..fffc8c8c2 100644 --- a/deluge/core/daemon_entry.py +++ b/deluge/core/daemon_entry.py @@ -24,15 +24,26 @@ from deluge.ui.translations_util import set_dummy_trans def add_daemon_options(parser): group = parser.add_argument_group(_('Daemon Options')) group.add_argument( - '-u', '--ui-interface', metavar='<ip-addr>', action='store', + '-u', + '--ui-interface', + metavar='<ip-addr>', + action='store', help=_('IP address to listen for UI connections'), ) group.add_argument( - '-p', '--port', metavar='<port>', action='store', type=int, + '-p', + '--port', + metavar='<port>', + action='store', + type=int, help=_('Port to listen for UI connections on'), ) group.add_argument( - '-i', '--interface', metavar='<ip-addr>', dest='listen_interface', action='store', + '-i', + '--interface', + metavar='<ip-addr>', + dest='listen_interface', + action='store', help=_('IP address to listen for BitTorrent connections'), ) group.add_argument( @@ -44,8 +55,12 @@ def add_daemon_options(parser): help=_('The interface adapter name for outgoing BitTorrent connections.'), ) group.add_argument( - '--read-only-config-keys', metavar='<comma-separated-keys>', action='store', - help=_('Config keys to be unmodified by `set_config` RPC'), type=str, default='', + '--read-only-config-keys', + metavar='<comma-separated-keys>', + action='store', + help=_('Config keys to be unmodified by `set_config` RPC'), + type=str, + default='', ) parser.add_process_arg_group() @@ -71,11 +86,12 @@ def start_daemon(skip_start=False): # Check for any daemons running with this same config from deluge.core.daemon import is_daemon_running + pid_file = get_config_dir('deluged.pid') if is_daemon_running(pid_file): print( 'Cannot run multiple daemons with same config directory.\n' - 'If you believe this is an error, force starting by deleting: %s' % pid_file, + 'If you believe this is an error, force starting by deleting: %s' % pid_file ) sys.exit(1) @@ -90,6 +106,7 @@ def start_daemon(skip_start=False): def run_daemon(options): try: from deluge.core.daemon import Daemon + daemon = Daemon( listen_interface=options.listen_interface, outgoing_interface=options.outgoing_interface, @@ -105,7 +122,8 @@ def start_daemon(skip_start=False): log.error( 'Cannot start deluged, listen port in use.\n' ' Check for other running daemons or services using this port: %s:%s', - ex.interface, ex.port, + ex.interface, + ex.port, ) sys.exit(1) except Exception as ex: @@ -118,4 +136,6 @@ def start_daemon(skip_start=False): if options.pidfile: os.remove(options.pidfile) - return run_profiled(run_daemon, options, output_file=options.profile, do_profile=options.profile) + return run_profiled( + run_daemon, options, output_file=options.profile, do_profile=options.profile + ) diff --git a/deluge/core/eventmanager.py b/deluge/core/eventmanager.py index 03efb5abb..5ba2989f9 100644 --- a/deluge/core/eventmanager.py +++ b/deluge/core/eventmanager.py @@ -36,7 +36,12 @@ class EventManager(component.Component): try: handler(*event.args) except Exception as ex: - log.error('Event handler %s failed in %s with exception %s', event.name, handler, ex) + log.error( + 'Event handler %s failed in %s with exception %s', + event.name, + handler, + ex, + ) def register_event_handler(self, event, handler): """ diff --git a/deluge/core/filtermanager.py b/deluge/core/filtermanager.py index 34ba1d35f..82ded3268 100644 --- a/deluge/core/filtermanager.py +++ b/deluge/core/filtermanager.py @@ -101,6 +101,7 @@ class FilterManager(component.Component): """FilterManager """ + def __init__(self, core): component.Component.__init__(self, 'FilterManager') log.debug('FilterManager init..') @@ -115,12 +116,14 @@ class FilterManager(component.Component): def _init_tracker_tree(): return {'Error': 0} + self.register_tree_field('tracker_host', _init_tracker_tree) self.register_filter('tracker_host', tracker_error_filter) def _init_users_tree(): return {'': 0} + self.register_tree_field('owner', _init_users_tree) def filter_torrent_ids(self, filter_dict): @@ -165,16 +168,22 @@ class FilterManager(component.Component): for field, values in list(filter_dict.items()): if field in self.registered_filters: # Filters out doubles - torrent_ids = list(set(self.registered_filters[field](torrent_ids, values))) + torrent_ids = list( + set(self.registered_filters[field](torrent_ids, values)) + ) del filter_dict[field] if not filter_dict: return torrent_ids - torrent_keys, plugin_keys = self.torrents.separate_keys(list(filter_dict), torrent_ids) + torrent_keys, plugin_keys = self.torrents.separate_keys( + list(filter_dict), torrent_ids + ) # Leftover filter arguments, default filter on status fields. for torrent_id in list(torrent_ids): - status = self.core.create_torrent_status(torrent_id, torrent_keys, plugin_keys) + status = self.core.create_torrent_status( + torrent_id, torrent_keys, plugin_keys + ) for field, values in filter_dict.items(): if field in status and status[field] in values: continue @@ -197,14 +206,18 @@ class FilterManager(component.Component): items = {field: self.tree_fields[field]() for field in tree_keys} for torrent_id in list(torrent_ids): - status = self.core.create_torrent_status(torrent_id, torrent_keys, plugin_keys) # status={key:value} + status = self.core.create_torrent_status( + torrent_id, torrent_keys, plugin_keys + ) # status={key:value} for field in tree_keys: value = status[field] items[field][value] = items[field].get(value, 0) + 1 if 'tracker_host' in items: items['tracker_host']['All'] = len(torrent_ids) - items['tracker_host']['Error'] = len(tracker_error_filter(torrent_ids, ('Error',))) + items['tracker_host']['Error'] = len( + tracker_error_filter(torrent_ids, ('Error',)) + ) if not show_zero_hits: for cat in ['state', 'owner', 'tracker_host']: @@ -224,7 +237,9 @@ class FilterManager(component.Component): init_state['All'] = len(self.torrents.get_torrent_list()) for state in TORRENT_STATE: init_state[state] = 0 - init_state['Active'] = len(self.filter_state_active(self.torrents.get_torrent_list())) + init_state['Active'] = len( + self.filter_state_active(self.torrents.get_torrent_list()) + ) return init_state def register_filter(self, filter_id, filter_func, filter_value=None): @@ -242,7 +257,9 @@ class FilterManager(component.Component): def filter_state_active(self, torrent_ids): for torrent_id in list(torrent_ids): - status = self.torrents[torrent_id].get_status(['download_payload_rate', 'upload_payload_rate']) + status = self.torrents[torrent_id].get_status( + ['download_payload_rate', 'upload_payload_rate'] + ) if status['download_payload_rate'] or status['upload_payload_rate']: pass else: diff --git a/deluge/core/pluginmanager.py b/deluge/core/pluginmanager.py index 0f5f14ec8..7d2f3a1e8 100644 --- a/deluge/core/pluginmanager.py +++ b/deluge/core/pluginmanager.py @@ -33,7 +33,7 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, component.Compon # Call the PluginManagerBase constructor deluge.pluginmanagerbase.PluginManagerBase.__init__( - self, 'core.conf', 'deluge.plugin.core', + self, 'core.conf', 'deluge.plugin.core' ) def start(self): @@ -77,6 +77,7 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, component.Compon if name not in self.plugins: component.get('EventManager').emit(PluginDisabledEvent(name)) return result + d.addBoth(on_disable_plugin) return d diff --git a/deluge/core/preferencesmanager.py b/deluge/core/preferencesmanager.py index 101360059..5d10b31ec 100644 --- a/deluge/core/preferencesmanager.py +++ b/deluge/core/preferencesmanager.py @@ -73,8 +73,9 @@ DEFAULT_PREFS = { 'max_download_speed': -1.0, 'max_upload_slots_global': 4, 'max_half_open_connections': ( - lambda: deluge.common.windows_check() and - (lambda: deluge.common.vista_check() and 4 or 8)() or 50 + lambda: deluge.common.windows_check() + and (lambda: deluge.common.vista_check() and 4 or 8)() + or 50 )(), 'max_connections_per_second': 20, 'ignore_limits_on_local_network': True, @@ -134,7 +135,9 @@ class PreferencesManager(component.Component): component.Component.__init__(self, 'PreferencesManager') self.config = deluge.configmanager.ConfigManager('core.conf', DEFAULT_PREFS) if 'proxies' in self.config: - log.warning('Updating config file for proxy, using "peer" values to fill new "proxy" setting') + log.warning( + 'Updating config file for proxy, using "peer" values to fill new "proxy" setting' + ) self.config['proxy'].update(self.config['proxies']['peer']) log.warning('New proxy config is: %s', self.config['proxy']) del self.config['proxies'] @@ -205,7 +208,9 @@ class PreferencesManager(component.Component): if self.config['random_port']: if not self.config['listen_random_port']: self.config['listen_random_port'] = random.randrange(49152, 65525) - listen_ports = [self.config['listen_random_port']] * 2 # use single port range + listen_ports = [ + self.config['listen_random_port'] + ] * 2 # use single port range else: self.config['listen_random_port'] = None listen_ports = self.config['listen_ports'] @@ -217,7 +222,9 @@ class PreferencesManager(component.Component): log.debug( 'Listen Interface: %s, Ports: %s with use_sys_port: %s', - interface, listen_ports, self.config['listen_use_sys_port'], + interface, + listen_ports, + self.config['listen_use_sys_port'], ) interfaces = [ '%s:%s' % (interface, port) @@ -227,7 +234,7 @@ class PreferencesManager(component.Component): { 'listen_system_port_fallback': self.config['listen_use_sys_port'], 'listen_interfaces': ''.join(interfaces), - }, + } ) def _on_set_outgoing_ports(self, key, value): @@ -237,14 +244,22 @@ class PreferencesManager(component.Component): self.__set_outgoing_ports() def __set_outgoing_ports(self): - port = 0 if self.config['random_outgoing_ports'] else self.config['outgoing_ports'][0] + port = ( + 0 + if self.config['random_outgoing_ports'] + else self.config['outgoing_ports'][0] + ) if port: - num_ports = self.config['outgoing_ports'][1] - self.config['outgoing_ports'][0] + num_ports = ( + self.config['outgoing_ports'][1] - self.config['outgoing_ports'][0] + ) num_ports = num_ports if num_ports > 1 else 5 else: num_ports = 0 log.debug('Outgoing port set to %s with range: %s', port, num_ports) - self.core.apply_session_settings({'outgoing_port': port, 'num_outgoing_ports': num_ports}) + self.core.apply_session_settings( + {'outgoing_port': port, 'num_outgoing_ports': num_ports} + ) def _on_set_peer_tos(self, key, value): try: @@ -263,12 +278,11 @@ class PreferencesManager(component.Component): 'router.bitcomet.com:6881', 'dht.transmissionbt.com:6881', 'dht.aelitis.com:6881', - ], + ] + ) + self.core.apply_session_settings( + {'dht_bootstrap_nodes': ','.join(dht_bootstraps), 'enable_dht': value} ) - self.core.apply_session_settings({ - 'dht_bootstrap_nodes': ','.join(dht_bootstraps), - 'enable_dht': value, - }) def _on_set_upnp(self, key, value): self.core.apply_session_setting('enable_upnp', value) @@ -294,14 +308,20 @@ class PreferencesManager(component.Component): def _on_set_encryption(self, key, value): # Convert Deluge enc_level values to libtorrent enc_level values. - pe_enc_level = {0: lt.enc_level.plaintext, 1: lt.enc_level.rc4, 2: lt.enc_level.both} + pe_enc_level = { + 0: lt.enc_level.plaintext, + 1: lt.enc_level.rc4, + 2: lt.enc_level.both, + } self.core.apply_session_settings( { 'out_enc_policy': lt.enc_policy(self.config['enc_out_policy']), 'in_enc_policy': lt.enc_policy(self.config['enc_in_policy']), - 'allowed_enc_level': lt.enc_level(pe_enc_level[self.config['enc_level']]), + 'allowed_enc_level': lt.enc_level( + pe_enc_level[self.config['enc_level']] + ), 'prefer_rc4': True, - }, + } ) def _on_set_max_connections_global(self, key, value): @@ -364,20 +384,29 @@ class PreferencesManager(component.Component): def run(self): import time + now = time.time() # check if we've done this within the last week or never if (now - self.config['info_sent']) >= (60 * 60 * 24 * 7): try: - url = 'http://deluge-torrent.org/stats_get.php?processor=' + \ - platform.machine() + '&python=' + platform.python_version() \ - + '&deluge=' + deluge.common.get_version() \ - + '&os=' + platform.system() \ - + '&plugins=' + quote_plus(':'.join(self.config['enabled_plugins'])) + url = ( + 'http://deluge-torrent.org/stats_get.php?processor=' + + platform.machine() + + '&python=' + + platform.python_version() + + '&deluge=' + + deluge.common.get_version() + + '&os=' + + platform.system() + + '&plugins=' + + quote_plus(':'.join(self.config['enabled_plugins'])) + ) urlopen(url) except IOError as ex: log.debug('Network error while trying to send info: %s', ex) else: self.config['info_sent'] = now + if value: SendInfoThread(self.config).start() @@ -389,7 +418,7 @@ class PreferencesManager(component.Component): self.new_release_timer.stop() # Set a timer to check for a new release every 3 days self.new_release_timer = LoopingCall( - self._on_set_new_release_check, 'new_release_check', True, + self._on_set_new_release_check, 'new_release_check', True ) self.new_release_timer.start(72 * 60 * 60, False) else: @@ -410,20 +439,23 @@ class PreferencesManager(component.Component): } if value['type'] == lt.proxy_type.i2p_proxy: - proxy_settings.update({ - 'proxy_type': lt.proxy_type.i2p_proxy, - 'i2p_hostname': value['hostname'], - 'i2p_port': value['port'], - }) + proxy_settings.update( + { + 'proxy_type': lt.proxy_type.i2p_proxy, + 'i2p_hostname': value['hostname'], + 'i2p_port': value['port'], + } + ) elif value['type'] != lt.proxy_type.none: - proxy_settings.update({ - 'proxy_type': value['type'], - 'proxy_hostname': value['hostname'], - 'proxy_port': value['port'], - 'proxy_username': value['username'], - 'proxy_password': value['password'], - - }) + proxy_settings.update( + { + 'proxy_type': value['type'], + 'proxy_hostname': value['hostname'], + 'proxy_port': value['port'], + 'proxy_username': value['username'], + 'proxy_password': value['password'], + } + ) self.core.apply_session_settings(proxy_settings) @@ -434,7 +466,9 @@ class PreferencesManager(component.Component): # Load the GeoIP DB for country look-ups if available if os.path.exists(geoipdb_path): try: - self.core.geoip_instance = GeoIP.open(geoipdb_path, GeoIP.GEOIP_STANDARD) + self.core.geoip_instance = GeoIP.open( + geoipdb_path, GeoIP.GEOIP_STANDARD + ) except AttributeError: log.warning('GeoIP Unavailable') else: diff --git a/deluge/core/rpcserver.py b/deluge/core/rpcserver.py index 146f39c3e..07e6f1531 100644 --- a/deluge/core/rpcserver.py +++ b/deluge/core/rpcserver.py @@ -24,9 +24,19 @@ from twisted.internet.protocol import Factory, connectionDone import deluge.component as component import deluge.configmanager -from deluge.core.authmanager import AUTH_LEVEL_ADMIN, AUTH_LEVEL_DEFAULT, AUTH_LEVEL_NONE +from deluge.core.authmanager import ( + AUTH_LEVEL_ADMIN, + AUTH_LEVEL_DEFAULT, + AUTH_LEVEL_NONE, +) from deluge.crypto_utils import get_context_factory -from deluge.error import DelugeError, IncompatibleClient, NotAuthorizedError, WrappedException, _ClientSideRecreateError +from deluge.error import ( + DelugeError, + IncompatibleClient, + NotAuthorizedError, + WrappedException, + _ClientSideRecreateError, +) from deluge.event import ClientDisconnectedEvent from deluge.transfer import DelugeTransferProtocol @@ -48,6 +58,7 @@ def export(auth_level=AUTH_LEVEL_DEFAULT): :type auth_level: int """ + def wrap(func, *args, **kwargs): func._rpcserver_export = True func._rpcserver_auth_level = auth_level @@ -120,8 +131,8 @@ class DelugeRPCProtocol(DelugeTransferProtocol): for call in request: if len(call) != 4: log.debug( - 'Received invalid rpc request: number of items ' - 'in request is %s', len(call), + 'Received invalid rpc request: number of items ' 'in request is %s', + len(call), ) continue # log.debug('RPCRequest: %s', format_request(call)) @@ -148,14 +159,11 @@ class DelugeRPCProtocol(DelugeTransferProtocol): This method is called when a new client connects. """ peer = self.transport.getPeer() - log.info( - 'Deluge Client connection made from: %s:%s', - peer.host, peer.port, - ) + log.info('Deluge Client connection made from: %s:%s', peer.host, peer.port) # Set the initial auth level of this session to AUTH_LEVEL_NONE - self.factory.authorized_sessions[ - self.transport.sessionno - ] = self.AuthLevel(AUTH_LEVEL_NONE, '') + self.factory.authorized_sessions[self.transport.sessionno] = self.AuthLevel( + AUTH_LEVEL_NONE, '' + ) def connectionLost(self, reason=connectionDone): # NOQA: N802 """ @@ -174,7 +182,9 @@ class DelugeRPCProtocol(DelugeTransferProtocol): del self.factory.interested_events[self.transport.sessionno] if self.factory.state == 'running': - component.get('EventManager').emit(ClientDisconnectedEvent(self.factory.session_id)) + component.get('EventManager').emit( + ClientDisconnectedEvent(self.factory.session_id) + ) log.info('Deluge client disconnected: %s', reason.value) def valid_session(self): @@ -196,6 +206,7 @@ class DelugeRPCProtocol(DelugeTransferProtocol): :type kwargs: dict """ + def send_error(): """ Sends an error response with the contents of the exception that was raised. @@ -203,29 +214,34 @@ class DelugeRPCProtocol(DelugeTransferProtocol): exc_type, exc_value, dummy_exc_trace = sys.exc_info() formated_tb = traceback.format_exc() try: - self.sendData(( - RPC_ERROR, - request_id, - exc_type.__name__, - exc_value._args, - exc_value._kwargs, - formated_tb, - )) + self.sendData( + ( + RPC_ERROR, + request_id, + exc_type.__name__, + exc_value._args, + exc_value._kwargs, + formated_tb, + ) + ) except AttributeError: # This is not a deluge exception (object has no attribute '_args), let's wrap it log.warning( 'An exception occurred while sending RPC_ERROR to ' 'client. Wrapping it and resending. Error to ' - 'send(causing exception goes next):\n%s', formated_tb, + 'send(causing exception goes next):\n%s', + formated_tb, ) try: raise WrappedException( - str(exc_value), exc_type.__name__, formated_tb, + str(exc_value), exc_type.__name__, formated_tb ) except WrappedException: send_error() except Exception as ex: - log.error('An exception occurred while sending RPC_ERROR to client: %s', ex) + log.error( + 'An exception occurred while sending RPC_ERROR to client: %s', ex + ) if method == 'daemon.info': # This is a special case and used in the initial connection process @@ -285,7 +301,9 @@ class DelugeRPCProtocol(DelugeTransferProtocol): log.debug('RPC dispatch %s', method) try: method_auth_requirement = self.factory.methods[method]._rpcserver_auth_level - auth_level = self.factory.authorized_sessions[self.transport.sessionno].auth_level + auth_level = self.factory.authorized_sessions[ + self.transport.sessionno + ].auth_level if auth_level < method_auth_requirement: # This session is not allowed to call this method log.debug( @@ -307,6 +325,7 @@ class DelugeRPCProtocol(DelugeTransferProtocol): # Check if the return value is a deferred, since we'll need to # wait for it to fire before sending the RPC_RESPONSE if isinstance(ret, defer.Deferred): + def on_success(result): try: self.sendData((RPC_RESPONSE, request_id, result)) @@ -380,7 +399,9 @@ class RPCServer(component.Component): pkey = os.path.join(deluge.configmanager.get_config_dir('ssl'), 'daemon.pkey') try: - reactor.listenSSL(port, self.factory, get_context_factory(cert, pkey), interface=hostname) + reactor.listenSSL( + port, self.factory, get_context_factory(cert, pkey), interface=hostname + ) except Exception as ex: log.debug('Daemon already running or port not available.: %s', ex) raise @@ -513,7 +534,7 @@ class RPCServer(component.Component): log.debug('Emit Event: %s %s', event.name, event.args) # This session is interested so send a RPC_EVENT self.factory.session_protocols[session_id].sendData( - (RPC_EVENT, event.name, event.args), + (RPC_EVENT, event.name, event.args) ) def emit_event_for_session_id(self, session_id, event): @@ -526,22 +547,35 @@ class RPCServer(component.Component): :type event: :class:`deluge.event.DelugeEvent` """ if not self.is_session_valid(session_id): - log.debug('Session ID %s is not valid. Not sending event "%s".', session_id, event.name) + log.debug( + 'Session ID %s is not valid. Not sending event "%s".', + session_id, + event.name, + ) return if session_id not in self.factory.interested_events: log.debug( 'Session ID %s is not interested in any events. Not sending event "%s".', - session_id, event.name, + session_id, + event.name, ) return if event.name not in self.factory.interested_events[session_id]: - log.debug('Session ID %s is not interested in event "%s". Not sending it.', session_id, event.name) + log.debug( + 'Session ID %s is not interested in event "%s". Not sending it.', + session_id, + event.name, + ) return log.debug( 'Sending event "%s" with args "%s" to session id "%s".', - event.name, event.args, session_id, + event.name, + event.args, + session_id, + ) + self.factory.session_protocols[session_id].sendData( + (RPC_EVENT, event.name, event.args) ) - self.factory.session_protocols[session_id].sendData((RPC_EVENT, event.name, event.args)) def stop(self): self.factory.state = 'stopping' @@ -568,6 +602,7 @@ def generate_ssl_keys(): This method generates a new SSL key/cert. """ from deluge.common import PY2 + digest = 'sha256' if not PY2 else b'sha256' # Generate key pair diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index 2a834c773..7ff0091ff 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -28,7 +28,11 @@ from deluge.common import decode_bytes from deluge.configmanager import ConfigManager, get_config_dir from deluge.core.authmanager import AUTH_LEVEL_ADMIN from deluge.decorators import deprecated -from deluge.event import TorrentFolderRenamedEvent, TorrentStateChangedEvent, TorrentTrackerStatusEvent +from deluge.event import ( + TorrentFolderRenamedEvent, + TorrentStateChangedEvent, + TorrentTrackerStatusEvent, +) try: from urllib.parse import urlparse @@ -65,6 +69,7 @@ def sanitize_filepath(filepath, folder=False): Args: folder (bool): A trailing slash is appended to the returned filepath. """ + def clean_filename(filename): """Strips whitespace and discards dotted filenames""" filename = filename.strip() @@ -110,12 +115,14 @@ def convert_lt_files(files): except AttributeError: file_path = _file.path - filelist.append({ - 'index': index, - 'path': file_path.replace('\\', '/'), - 'size': _file.size, - 'offset': _file.offset, - }) + filelist.append( + { + 'index': index, + 'path': file_path.replace('\\', '/'), + 'size': _file.size, + 'offset': _file.offset, + } + ) return filelist @@ -152,6 +159,7 @@ class TorrentOptions(dict): stop_ratio (float): The seeding ratio to stop (or remove) the torrent at. super_seeding (bool): Enable super seeding/initial seeding. """ + def __init__(self): super(TorrentOptions, self).__init__() config = ConfigManager('core.conf').config @@ -227,6 +235,7 @@ class Torrent(object): we can re-pause it after its done if necessary forced_error (TorrentError): Keep track if we have forced this torrent to be in Error state. """ + def __init__(self, handle, options, state=None, filename=None, magnet=None): self.torrent_id = str(handle.info_hash()) if log.isEnabledFor(logging.DEBUG): @@ -295,7 +304,9 @@ class Torrent(object): # Skip set_prioritize_first_last if set_file_priorities is in options as it also calls the method. if 'file_priorities' in options and 'prioritize_first_last_pieces' in options: - self.options['prioritize_first_last_pieces'] = options.pop('prioritize_first_last_pieces') + self.options['prioritize_first_last_pieces'] = options.pop( + 'prioritize_first_last_pieces' + ) for key, value in options.items(): if key in self.options: @@ -407,8 +418,12 @@ class Torrent(object): # Set the pieces in first and last ranges to priority 7 # if they are not marked as do not download - priorities[first_start:first_end] = [p and 7 for p in priorities[first_start:first_end]] - priorities[last_start:last_end] = [p and 7 for p in priorities[last_start:last_end]] + priorities[first_start:first_end] = [ + p and 7 for p in priorities[first_start:first_end] + ] + priorities[last_start:last_end] = [ + p and 7 for p in priorities[last_start:last_end] + ] # Setting the priorites for all the pieces of this torrent self.handle.prioritize_pieces(priorities) @@ -494,10 +509,15 @@ class Torrent(object): """ if log.isEnabledFor(logging.DEBUG): - log.debug('Setting %s file priorities to: %s', self.torrent_id, file_priorities) + log.debug( + 'Setting %s file priorities to: %s', self.torrent_id, file_priorities + ) - if (self.handle.has_metadata() and file_priorities and - len(file_priorities) == len(self.get_files())): + if ( + self.handle.has_metadata() + and file_priorities + and len(file_priorities) == len(self.get_files()) + ): self.handle.prioritize_files(file_priorities) else: log.debug('Unable to set new file priorities.') @@ -516,7 +536,9 @@ class Torrent(object): # Set the first/last priorities if needed. if self.options['prioritize_first_last_pieces']: - self.set_prioritize_first_last_pieces(self.options['prioritize_first_last_pieces']) + self.set_prioritize_first_last_pieces( + self.options['prioritize_first_last_pieces'] + ) @deprecated def set_save_path(self, download_location): @@ -592,11 +614,16 @@ class Torrent(object): if self.tracker_status != status: self.tracker_status = status - component.get('EventManager').emit(TorrentTrackerStatusEvent(self.torrent_id, self.tracker_status)) + component.get('EventManager').emit( + TorrentTrackerStatusEvent(self.torrent_id, self.tracker_status) + ) def merge_trackers(self, torrent_info): """Merges new trackers in torrent_info into torrent""" - log.info('Adding any new trackers to torrent (%s) already in session...', self.torrent_id) + log.info( + 'Adding any new trackers to torrent (%s) already in session...', + self.torrent_id, + ) if not torrent_info: return # Don't merge trackers if either torrent has private flag set. @@ -634,15 +661,23 @@ class Torrent(object): self.state = LT_TORRENT_STATE_MAP.get(str(status.state), str(status.state)) if self.state != old_state: - component.get('EventManager').emit(TorrentStateChangedEvent(self.torrent_id, self.state)) + component.get('EventManager').emit( + TorrentStateChangedEvent(self.torrent_id, self.state) + ) if log.isEnabledFor(logging.DEBUG): log.debug( 'State from lt was: %s | Session is paused: %s\nTorrent state set from "%s" to "%s" (%s)', - 'error' if status_error else status.state, session_paused, old_state, self.state, self.torrent_id, + 'error' if status_error else status.state, + session_paused, + old_state, + self.state, + self.torrent_id, ) if self.forced_error: - log.debug('Torrent Error state message: %s', self.forced_error.error_message) + log.debug( + 'Torrent Error state message: %s', self.forced_error.error_message + ) def set_status_message(self, message=None): """Sets the torrent status message. @@ -783,24 +818,30 @@ class Torrent(object): client = decode_bytes(peer.client) try: - country = component.get('Core').geoip_instance.country_code_by_addr(peer.ip[0]) + country = component.get('Core').geoip_instance.country_code_by_addr( + peer.ip[0] + ) except AttributeError: country = '' else: try: - country = ''.join([char if char.isalpha() else ' ' for char in country]) + country = ''.join( + [char if char.isalpha() else ' ' for char in country] + ) except TypeError: country = '' - ret.append({ - 'client': client, - 'country': country, - 'down_speed': peer.payload_down_speed, - 'ip': '%s:%s' % (peer.ip[0], peer.ip[1]), - 'progress': peer.progress, - 'seed': peer.flags & peer.seed, - 'up_speed': peer.payload_up_speed, - }) + ret.append( + { + 'client': client, + 'country': country, + 'down_speed': peer.payload_down_speed, + 'ip': '%s:%s' % (peer.ip[0], peer.ip[1]), + 'progress': peer.progress, + 'seed': peer.flags & peer.seed, + 'up_speed': peer.payload_up_speed, + } + ) return ret @@ -832,8 +873,10 @@ class Torrent(object): if not self.has_metadata: return [] return [ - progress / _file.size if _file.size else 0.0 for progress, _file in - zip(self.handle.file_progress(), self.torrent_info.files()) + progress / _file.size if _file.size else 0.0 + for progress, _file in zip( + self.handle.file_progress(), self.torrent_info.files() + ) ] def get_tracker_host(self): @@ -854,7 +897,7 @@ class Torrent(object): if tracker: url = urlparse(tracker.replace('udp://', 'http://')) if hasattr(url, 'hostname'): - host = (url.hostname or 'DHT') + host = url.hostname or 'DHT' # Check if hostname is an IP address and just return it if that's the case try: socket.inet_aton(host) @@ -995,7 +1038,9 @@ class Torrent(object): 'seeding_time': lambda: self.status.seeding_time, 'finished_time': lambda: self.status.finished_time, 'all_time_download': lambda: self.status.all_time_download, - 'storage_mode': lambda: self.status.storage_mode.name.split('_')[2], # sparse or allocate + 'storage_mode': lambda: self.status.storage_mode.name.split('_')[ + 2 + ], # sparse or allocate 'distributed_copies': lambda: max(0.0, self.status.distributed_copies), 'download_payload_rate': lambda: self.status.download_payload_rate, 'file_priorities': self.get_file_priorities, @@ -1008,8 +1053,12 @@ class Torrent(object): 'max_upload_slots': lambda: self.options['max_upload_slots'], 'max_upload_speed': lambda: self.options['max_upload_speed'], 'message': lambda: self.statusmsg, - 'move_on_completed_path': lambda: self.options['move_completed_path'], # Deprecated: move_completed_path - 'move_on_completed': lambda: self.options['move_completed'], # Deprecated: Use move_completed + 'move_on_completed_path': lambda: self.options[ + 'move_completed_path' + ], # Deprecated: move_completed_path + 'move_on_completed': lambda: self.options[ + 'move_completed' + ], # Deprecated: Use move_completed 'move_completed_path': lambda: self.options['move_completed_path'], 'move_completed': lambda: self.options['move_completed'], 'next_announce': lambda: self.status.next_announce.seconds, @@ -1017,16 +1066,24 @@ class Torrent(object): 'num_seeds': lambda: self.status.num_seeds, 'owner': lambda: self.options['owner'], 'paused': lambda: self.status.paused, - 'prioritize_first_last': lambda: self.options['prioritize_first_last_pieces'], + 'prioritize_first_last': lambda: self.options[ + 'prioritize_first_last_pieces' + ], # Deprecated: Use prioritize_first_last_pieces - 'prioritize_first_last_pieces': lambda: self.options['prioritize_first_last_pieces'], + 'prioritize_first_last_pieces': lambda: self.options[ + 'prioritize_first_last_pieces' + ], 'sequential_download': lambda: self.options['sequential_download'], 'progress': self.get_progress, 'shared': lambda: self.options['shared'], 'remove_at_ratio': lambda: self.options['remove_at_ratio'], - 'save_path': lambda: self.options['download_location'], # Deprecated: Use download_location + 'save_path': lambda: self.options[ + 'download_location' + ], # Deprecated: Use download_location 'download_location': lambda: self.options['download_location'], - 'seeds_peers_ratio': lambda: -1.0 if self.status.num_incomplete == 0 else ( # Use -1.0 to signify infinity + 'seeds_peers_ratio': lambda: -1.0 + if self.status.num_incomplete == 0 + else ( # Use -1.0 to signify infinity self.status.num_complete / self.status.num_incomplete ), 'seed_rank': lambda: self.status.seed_rank, @@ -1041,19 +1098,32 @@ class Torrent(object): 'total_seeds': lambda: self.status.num_complete, 'total_uploaded': lambda: self.status.all_time_upload, 'total_wanted': lambda: self.status.total_wanted, - 'total_remaining': lambda: self.status.total_wanted - self.status.total_wanted_done, + 'total_remaining': lambda: self.status.total_wanted + - self.status.total_wanted_done, 'tracker': lambda: self.status.current_tracker, 'tracker_host': self.get_tracker_host, 'trackers': lambda: self.trackers, 'tracker_status': lambda: self.tracker_status, 'upload_payload_rate': lambda: self.status.upload_payload_rate, - 'comment': lambda: decode_bytes(self.torrent_info.comment()) if self.has_metadata else '', - 'creator': lambda: decode_bytes(self.torrent_info.creator()) if self.has_metadata else '', - 'num_files': lambda: self.torrent_info.num_files() if self.has_metadata else 0, - 'num_pieces': lambda: self.torrent_info.num_pieces() if self.has_metadata else 0, - 'piece_length': lambda: self.torrent_info.piece_length() if self.has_metadata else 0, + 'comment': lambda: decode_bytes(self.torrent_info.comment()) + if self.has_metadata + else '', + 'creator': lambda: decode_bytes(self.torrent_info.creator()) + if self.has_metadata + else '', + 'num_files': lambda: self.torrent_info.num_files() + if self.has_metadata + else 0, + 'num_pieces': lambda: self.torrent_info.num_pieces() + if self.has_metadata + else 0, + 'piece_length': lambda: self.torrent_info.piece_length() + if self.has_metadata + else 0, 'private': lambda: self.torrent_info.priv() if self.has_metadata else False, - 'total_size': lambda: self.torrent_info.total_size() if self.has_metadata else 0, + 'total_size': lambda: self.torrent_info.total_size() + if self.has_metadata + else 0, 'eta': self.get_eta, 'file_progress': self.get_file_progress, 'files': self.get_files, @@ -1090,7 +1160,9 @@ class Torrent(object): # show it as 'Paused'. We need to emit a torrent_paused signal because # the torrent_paused alert from libtorrent will not be generated. self.update_state() - component.get('EventManager').emit(TorrentStateChangedEvent(self.torrent_id, 'Paused')) + component.get('EventManager').emit( + TorrentStateChangedEvent(self.torrent_id, 'Paused') + ) else: try: self.handle.pause() @@ -1102,9 +1174,14 @@ class Torrent(object): if self.status.paused and self.status.auto_managed: log.debug('Resume not possible for auto-managed torrent!') elif self.forced_error and self.forced_error.was_paused: - log.debug('Resume skipped for forced_error torrent as it was originally paused.') - elif (self.status.is_finished and self.options['stop_at_ratio'] and - self.get_ratio() >= self.options['stop_ratio']): + log.debug( + 'Resume skipped for forced_error torrent as it was originally paused.' + ) + elif ( + self.status.is_finished + and self.options['stop_at_ratio'] + and self.get_ratio() >= self.options['stop_ratio'] + ): log.debug('Resume skipped for torrent as it has reached "stop_seed_ratio".') else: # Check if torrent was originally being auto-managed. @@ -1157,7 +1234,9 @@ class Torrent(object): log.error( 'Could not move storage for torrent %s since %s does ' 'not exist and could not create the directory: %s', - self.torrent_id, dest, ex, + self.torrent_id, + dest, + ex, ) return False @@ -1191,9 +1270,9 @@ class Torrent(object): flags = lt.save_resume_flags_t.flush_disk_cache if flush_disk_cache else 0 # Don't generate fastresume data if torrent is in a Deluge Error state. if self.forced_error: - component.get('TorrentManager').waiting_on_resume_data[self.torrent_id].errback( - UserWarning('Skipped creating resume_data while in Error state'), - ) + component.get('TorrentManager').waiting_on_resume_data[ + self.torrent_id + ].errback(UserWarning('Skipped creating resume_data while in Error state')) else: self.handle.save_resume_data(flags) @@ -1232,9 +1311,13 @@ class Torrent(object): def delete_torrentfile(self, delete_copies=False): """Deletes the .torrent file in the state directory in config""" - torrent_files = [os.path.join(get_config_dir(), 'state', self.torrent_id + '.torrent')] + torrent_files = [ + os.path.join(get_config_dir(), 'state', self.torrent_id + '.torrent') + ] if delete_copies: - torrent_files.append(os.path.join(self.config['torrentfiles_location'], self.filename)) + torrent_files.append( + os.path.join(self.config['torrentfiles_location'], self.filename) + ) for torrent_file in torrent_files: log.debug('Deleting torrent file: %s', torrent_file) @@ -1325,7 +1408,7 @@ class Torrent(object): if _file['path'].startswith(folder): # Keep track of filerenames we're waiting on wait_on_folder[_file['index']] = Deferred().addBoth( - on_file_rename_complete, wait_on_folder, _file['index'], + on_file_rename_complete, wait_on_folder, _file['index'] ) new_path = _file['path'].replace(folder, new_folder, 1) try: @@ -1335,10 +1418,14 @@ class Torrent(object): def on_folder_rename_complete(dummy_result, torrent, folder, new_folder): """Folder rename complete""" - component.get('EventManager').emit(TorrentFolderRenamedEvent(torrent.torrent_id, folder, new_folder)) + component.get('EventManager').emit( + TorrentFolderRenamedEvent(torrent.torrent_id, folder, new_folder) + ) # Empty folders are removed after libtorrent folder renames self.remove_empty_folders(folder) - torrent.waiting_on_folder_rename = [_dir for _dir in torrent.waiting_on_folder_rename if _dir] + torrent.waiting_on_folder_rename = [ + _dir for _dir in torrent.waiting_on_folder_rename if _dir + ] component.get('TorrentManager').save_resume_data((self.torrent_id,)) d = DeferredList(list(wait_on_folder.values())) @@ -1355,7 +1442,9 @@ class Torrent(object): """ # Removes leading slashes that can cause join to ignore download_location download_location = self.options['download_location'] - folder_full_path = os.path.normpath(os.path.join(download_location, folder.lstrip('\\/'))) + folder_full_path = os.path.normpath( + os.path.join(download_location, folder.lstrip('\\/')) + ) try: if not os.listdir(folder_full_path): @@ -1366,7 +1455,9 @@ class Torrent(object): for name in dirs: try: os.removedirs(os.path.join(root, name)) - log.debug('Removed Empty Folder %s', os.path.join(root, name)) + log.debug( + 'Removed Empty Folder %s', os.path.join(root, name) + ) except OSError as ex: log.debug(ex) @@ -1389,16 +1480,24 @@ class Torrent(object): pieces = None else: pieces = [] - for piece, avail_piece in zip(self.status.pieces, self.handle.piece_availability()): + for piece, avail_piece in zip( + self.status.pieces, self.handle.piece_availability() + ): if piece: pieces.append(3) # Completed. elif avail_piece: - pieces.append(1) # Available, just not downloaded nor being downloaded. + pieces.append( + 1 + ) # Available, just not downloaded nor being downloaded. else: - pieces.append(0) # Missing, no known peer with piece, or not asked for yet. + pieces.append( + 0 + ) # Missing, no known peer with piece, or not asked for yet. for peer_info in self.handle.get_peer_info(): if peer_info.downloading_piece_index >= 0: - pieces[peer_info.downloading_piece_index] = 2 # Being downloaded from peer. + pieces[ + peer_info.downloading_piece_index + ] = 2 # Being downloaded from peer. return pieces diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index 8e221fe32..c3307e33f 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -29,17 +29,25 @@ from deluge.configmanager import ConfigManager, get_config_dir from deluge.core.authmanager import AUTH_LEVEL_ADMIN from deluge.core.torrent import Torrent, TorrentOptions, sanitize_filepath from deluge.error import AddTorrentError, InvalidTorrentError -from deluge.event import (ExternalIPEvent, PreTorrentRemovedEvent, SessionStartedEvent, TorrentAddedEvent, - TorrentFileCompletedEvent, TorrentFileRenamedEvent, TorrentFinishedEvent, TorrentRemovedEvent, - TorrentResumedEvent) +from deluge.event import ( + ExternalIPEvent, + PreTorrentRemovedEvent, + SessionStartedEvent, + TorrentAddedEvent, + TorrentFileCompletedEvent, + TorrentFileRenamedEvent, + TorrentFinishedEvent, + TorrentRemovedEvent, + TorrentResumedEvent, +) 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.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 ) @@ -50,6 +58,7 @@ class TorrentState: # pylint: disable=old-style-class This must be old style class to avoid breaking torrent.state file. """ + def __init__( self, torrent_id=None, @@ -99,11 +108,14 @@ class TorrentManagerState: # pylint: disable=old-style-class This must be old style class to avoid breaking torrent.state file. """ + def __init__(self): self.torrents = [] def __eq__(self, other): - return isinstance(other, TorrentManagerState) and self.torrents == other.torrents + return ( + isinstance(other, TorrentManagerState) and self.torrents == other.torrents + ) def __ne__(self, other): return not self == other @@ -115,11 +127,14 @@ class TorrentManager(component.Component): This object is also responsible for saving the state of the session for use on restart. """ + callLater = reactor.callLater def __init__(self): component.Component.__init__( - self, 'TorrentManager', interval=5, + self, + 'TorrentManager', + interval=5, depend=['CorePluginManager', 'AlertManager'], ) log.debug('TorrentManager init...') @@ -163,8 +178,10 @@ class TorrentManager(component.Component): # Register set functions set_config_keys = [ - 'max_connections_per_torrent', 'max_upload_slots_per_torrent', - 'max_upload_speed_per_torrent', 'max_download_speed_per_torrent', + 'max_connections_per_torrent', + 'max_upload_slots_per_torrent', + 'max_upload_speed_per_torrent', + 'max_download_speed_per_torrent', ] for config_key in set_config_keys: @@ -173,18 +190,34 @@ 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', + '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', ] 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.replace('_alert', '')]) + ) self.alerts.register_handler(alert_handle, on_alert_func) # Define timers @@ -195,7 +228,9 @@ class TorrentManager(component.Component): def start(self): # Check for old temp file to verify safe shutdown if os.path.isfile(self.temp_file): - log.warning('Potential bad shutdown of Deluge detected, archiving torrent state files...') + log.warning( + 'Potential bad shutdown of Deluge detected, archiving torrent state files...' + ) arc_filepaths = [] for filename in ('torrents.fastresume', 'torrents.state'): filepath = os.path.join(self.state_dir, filename) @@ -240,14 +275,20 @@ class TorrentManager(component.Component): for torrent_id, torrent in self.torrents.items(): # XXX: Should the state check be those that _can_ be stopped at ratio if torrent.options['stop_at_ratio'] and torrent.state not in ( - 'Checking', 'Allocating', 'Paused', 'Queued', + 'Checking', + 'Allocating', + 'Paused', + 'Queued', ): # If the global setting is set, but the per-torrent isn't... # Just skip to the next torrent. # This is so that a user can turn-off the stop at ratio option on a per-torrent basis if not torrent.options['stop_at_ratio']: continue - if torrent.get_ratio() >= torrent.options['stop_ratio'] and torrent.is_finished: + if ( + torrent.get_ratio() >= torrent.options['stop_ratio'] + and torrent.is_finished + ): if torrent.options['remove_at_ratio']: self.remove(torrent_id) break @@ -324,11 +365,12 @@ class TorrentManager(component.Component): add_torrent_params = {} add_torrent_params['save_path'] = gettempdir() add_torrent_params['url'] = magnet.strip().encode('utf8') - 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 - ) + 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.add_torrent_params_flags_t.flag_auto_managed ^ lt.add_torrent_params_flags_t.flag_paused ) @@ -379,7 +421,7 @@ class TorrentManager(component.Component): return options def _build_torrent_params( - self, torrent_info=None, magnet=None, options=None, resume_data=None, + self, torrent_info=None, magnet=None, options=None, resume_data=None ): """Create the add_torrent_params dict for adding torrent to libtorrent.""" add_torrent_params = {} @@ -387,7 +429,9 @@ class TorrentManager(component.Component): add_torrent_params['ti'] = torrent_info name = torrent_info.name() if not name: - name = torrent_info.file_at(0).path.replace('\\', '/', 1).split('/', 1)[0] + name = ( + torrent_info.file_at(0).path.replace('\\', '/', 1).split('/', 1)[0] + ) add_torrent_params['name'] = name torrent_id = str(torrent_info.info_hash()) elif magnet: @@ -397,7 +441,9 @@ class TorrentManager(component.Component): add_torrent_params['name'] = magnet_info['name'] torrent_id = magnet_info['info_hash'] else: - raise AddTorrentError('Unable to add magnet, invalid magnet info: %s' % magnet) + raise AddTorrentError( + 'Unable to add magnet, invalid magnet info: %s' % magnet + ) # Check for existing torrent in session. if torrent_id in self.get_torrent_list(): @@ -437,13 +483,10 @@ 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.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 if options['seed_mode']: add_torrent_params['flags'] |= lt.add_torrent_params_flags_t.flag_seed_mode @@ -480,17 +523,21 @@ class TorrentManager(component.Component): """ if not torrent_info and not filedump and not magnet: - raise AddTorrentError('You must specify a valid torrent_info, torrent state or magnet.') + raise AddTorrentError( + 'You must specify a valid torrent_info, torrent state or magnet.' + ) if filedump: try: torrent_info = lt.torrent_info(lt.bdecode(filedump)) except RuntimeError as ex: - raise AddTorrentError('Unable to add torrent, decoding filedump failed: %s' % ex) + raise AddTorrentError( + 'Unable to add torrent, decoding filedump failed: %s' % ex + ) options = self._build_torrent_options(options) __, add_torrent_params = self._build_torrent_params( - torrent_info, magnet, options, resume_data, + torrent_info, magnet, options, resume_data ) # We need to pause the AlertManager momentarily to prevent alerts @@ -506,7 +553,7 @@ class TorrentManager(component.Component): raise AddTorrentError('Unable to add torrent to session: %s' % ex) torrent = self._add_torrent_obj( - handle, options, state, filename, magnet, resume_data, filedump, save_state, + handle, options, state, filename, magnet, resume_data, filedump, save_state ) return torrent.torrent_id @@ -541,28 +588,51 @@ class TorrentManager(component.Component): """ if not torrent_info and not filedump and not magnet: - raise AddTorrentError('You must specify a valid torrent_info, torrent state or magnet.') + raise AddTorrentError( + 'You must specify a valid torrent_info, torrent state or magnet.' + ) if filedump: try: torrent_info = lt.torrent_info(lt.bdecode(filedump)) except RuntimeError as ex: - raise AddTorrentError('Unable to add torrent, decoding filedump failed: %s' % ex) + raise AddTorrentError( + 'Unable to add torrent, decoding filedump failed: %s' % ex + ) options = self._build_torrent_options(options) torrent_id, add_torrent_params = self._build_torrent_params( - torrent_info, magnet, options, resume_data, + torrent_info, magnet, options, resume_data ) d = Deferred() - self.torrents_loading[torrent_id] = (d, options, state, filename, magnet, resume_data, filedump, save_state) + self.torrents_loading[torrent_id] = ( + d, + options, + state, + filename, + magnet, + resume_data, + filedump, + save_state, + ) try: self.session.async_add_torrent(add_torrent_params) except RuntimeError as ex: raise AddTorrentError('Unable to add torrent to session: %s' % ex) return d - def _add_torrent_obj(self, handle, options, state, filename, magnet, resume_data, filedump, save_state): + def _add_torrent_obj( + self, + handle, + options, + state, + filename, + magnet, + resume_data, + filedump, + save_state, + ): # For magnets added with metadata, filename is used so set as magnet. if not magnet and is_magnet(filename): magnet = filename @@ -590,7 +660,9 @@ class TorrentManager(component.Component): # Emit torrent_added signal. from_state = state is not None - component.get('EventManager').emit(TorrentAddedEvent(torrent.torrent_id, from_state)) + component.get('EventManager').emit( + TorrentAddedEvent(torrent.torrent_id, from_state) + ) if log.isEnabledFor(logging.DEBUG): log.debug('Torrent added: %s', str(handle.info_hash())) @@ -614,10 +686,19 @@ class TorrentManager(component.Component): return torrent def add_async_callback( - self, handle, d, options, state, filename, magnet, resume_data, filedump, save_state, + self, + handle, + d, + options, + state, + filename, + magnet, + resume_data, + filedump, + save_state, ): torrent = self._add_torrent_obj( - handle, options, state, filename, magnet, resume_data, filedump, save_state, + handle, options, state, filename, magnet, resume_data, filedump, save_state ) d.callback(torrent.torrent_id) @@ -661,7 +742,9 @@ class TorrentManager(component.Component): self.resume_data.pop(torrent_id, None) # Remove the .torrent file in the state and copy location, if user requested. - delete_copies = self.config['copy_torrent_file'] and self.config['del_copy_torrent_file'] + delete_copies = ( + self.config['copy_torrent_file'] and self.config['del_copy_torrent_file'] + ) torrent.delete_torrentfile(delete_copies) # Remove from set if it wasn't finished @@ -670,7 +753,9 @@ class TorrentManager(component.Component): self.queued_torrents.remove(torrent_id) except KeyError: log.debug('%s is not in queued torrents set.', torrent_id) - raise InvalidTorrentError('%s is not in queued torrents set.' % torrent_id) + raise InvalidTorrentError( + '%s is not in queued torrents set.' % torrent_id + ) # Remove the torrent from deluge's session del self.torrents[torrent_id] @@ -680,7 +765,11 @@ class TorrentManager(component.Component): # Emit the signal to the clients component.get('EventManager').emit(TorrentRemovedEvent(torrent_id)) - log.info('Torrent %s removed by user: %s', torrent_name, component.get('RPCServer').get_session_user()) + log.info( + 'Torrent %s removed by user: %s', + torrent_name, + component.get('RPCServer').get_session_user(), + ) return True def fixup_state(self, state): @@ -701,7 +790,9 @@ class TorrentManager(component.Component): for t_state in state.torrents: setattr(t_state, attr, getattr(t_state_tmp, attr, None)) except AttributeError as ex: - log.error('Unable to update state file to a compatible version: %s', ex) + log.error( + 'Unable to update state file to a compatible version: %s', ex + ) return state def open_state(self): @@ -740,7 +831,9 @@ class TorrentManager(component.Component): state = self.fixup_state(state) # Reorder the state.torrents list to add torrents in the correct queue order. - state.torrents.sort(key=operator.attrgetter('queue'), reverse=self.config['queue_new_to_top']) + state.torrents.sort( + key=operator.attrgetter('queue'), reverse=self.config['queue_new_to_top'] + ) resume_data = self.load_resume_data_file() deferreds = [] @@ -760,7 +853,7 @@ class TorrentManager(component.Component): magnet = t_state.magnet torrent_info = self.get_torrent_info_from_file( - os.path.join(self.state_dir, t_state.torrent_id + '.torrent'), + os.path.join(self.state_dir, t_state.torrent_id + '.torrent') ) try: @@ -773,15 +866,24 @@ class TorrentManager(component.Component): resume_data=resume_data.get(t_state.torrent_id), ) except AddTorrentError as ex: - log.warning('Error when adding torrent "%s" to session: %s', t_state.torrent_id, ex) + log.warning( + 'Error when adding torrent "%s" to session: %s', + t_state.torrent_id, + ex, + ) else: deferreds.append(d) deferred_list = DeferredList(deferreds, consumeErrors=False) def on_complete(result): - log.info('Finished loading %d torrents in %s', len(state.torrents), str(datetime.datetime.now() - start)) + log.info( + 'Finished loading %d torrents in %s', + len(state.torrents), + str(datetime.datetime.now() - start), + ) component.get('EventManager').emit(SessionStartedEvent()) + deferred_list.addCallback(on_complete) def create_state(self): @@ -850,6 +952,7 @@ class TorrentManager(component.Component): self.is_saving_state = False if self.save_state_timer.running: self.save_state_timer.reset() + d.addBoth(on_state_saved) return d @@ -910,7 +1013,11 @@ class TorrentManager(component.Component): """ if torrent_ids is None: - torrent_ids = (tid for tid, t in self.torrents.items() if t.handle.need_save_resume_data()) + torrent_ids = ( + tid + for tid, t in self.torrents.items() + if t.handle.need_save_resume_data() + ) def on_torrent_resume_save(dummy_result, torrent_id): """Recieved torrent resume_data alert so remove from waiting list""" @@ -994,8 +1101,10 @@ class TorrentManager(component.Component): if self.save_resume_data_timer.running: self.save_resume_data_timer.reset() return arg + d.addBoth(on_resume_data_file_saved) return d + return self.save_resume_data_file_lock.run(on_lock_aquired) def _save_resume_data_file(self): @@ -1066,7 +1175,9 @@ class TorrentManager(component.Component): def queue_down(self, torrent_id): """Queue torrent down one position""" - if self.torrents[torrent_id].get_queue_position() == (len(self.queued_torrents) - 1): + if self.torrents[torrent_id].get_queue_position() == ( + len(self.queued_torrents) - 1 + ): return False self.torrents[torrent_id].handle.queue_position_down() @@ -1074,7 +1185,9 @@ class TorrentManager(component.Component): def queue_bottom(self, torrent_id): """Queue torrent to bottom""" - if self.torrents[torrent_id].get_queue_position() == (len(self.queued_torrents) - 1): + if self.torrents[torrent_id].get_queue_position() == ( + len(self.queued_torrents) - 1 + ): return False self.torrents[torrent_id].handle.queue_position_bottom() @@ -1140,21 +1253,28 @@ class TorrentManager(component.Component): # If total_download is 0, do not move, it's likely the torrent wasn't downloaded, but just added. # Get fresh data from libtorrent, the cache isn't always up to date - total_download = torrent.get_status(['total_payload_download'], update=True)['total_payload_download'] + total_download = torrent.get_status(['total_payload_download'], update=True)[ + 'total_payload_download' + ] if log.isEnabledFor(logging.DEBUG): log.debug('Finished %s ', torrent_id) log.debug( 'Torrent settings: is_finished: %s, total_download: %s, move_completed: %s, move_path: %s', - torrent.is_finished, total_download, torrent.options['move_completed'], + torrent.is_finished, + total_download, + torrent.options['move_completed'], torrent.options['move_completed_path'], ) torrent.update_state() if not torrent.is_finished and total_download: # Move completed download to completed folder if needed - if torrent.options['move_completed'] and \ - torrent.options['download_location'] != torrent.options['move_completed_path']: + if ( + torrent.options['move_completed'] + and torrent.options['download_location'] + != torrent.options['move_completed_path'] + ): self.waiting_on_finish_moving.append(torrent_id) torrent.move_storage(torrent.options['move_completed_path']) else: @@ -1177,7 +1297,7 @@ class TorrentManager(component.Component): # worth really to save in resume data, we just read it up in # self.load_state(). if total_download: - self.save_resume_data((torrent_id, )) + self.save_resume_data((torrent_id,)) def on_alert_torrent_paused(self, alert): """Alert handler for libtorrent torrent_paused_alert""" @@ -1217,7 +1337,10 @@ class TorrentManager(component.Component): torrent.set_tracker_status('Announce OK') # Check for peer information from the tracker, if none then send a scrape request. - if alert.handle.status().num_complete == -1 or alert.handle.status().num_incomplete == -1: + if ( + alert.handle.status().num_complete == -1 + or alert.handle.status().num_incomplete == -1 + ): torrent.scrape_tracker() def on_alert_tracker_announce(self, alert): @@ -1249,7 +1372,9 @@ class TorrentManager(component.Component): error_message = decode_bytes(alert.error_message()) if not error_message: error_message = decode_bytes(alert.error.message()) - log.debug('Tracker Error Alert: %s [%s]', decode_bytes(alert.message()), error_message) + log.debug( + 'Tracker Error Alert: %s [%s]', decode_bytes(alert.message()), error_message + ) torrent.set_tracker_status('Error: ' + error_message) def on_alert_storage_moved(self, alert): @@ -1337,7 +1462,9 @@ class TorrentManager(component.Component): return if torrent_id in self.waiting_on_resume_data: - self.waiting_on_resume_data[torrent_id].errback(Exception(decode_bytes(alert.message()))) + self.waiting_on_resume_data[torrent_id].errback( + Exception(decode_bytes(alert.message())) + ) def on_alert_fastresume_rejected(self, alert): """Alert handler for libtorrent fastresume_rejected_alert""" @@ -1355,7 +1482,9 @@ class TorrentManager(component.Component): else: error_msg = 'Missing or invalid torrent data!' else: - error_msg = 'Problem with resume data: %s' % alert_msg.split(':', 1)[1].strip() + error_msg = ( + 'Problem with resume data: %s' % alert_msg.split(':', 1)[1].strip() + ) torrent.force_error_state(error_msg, restart_to_resume=True) def on_alert_file_renamed(self, alert): @@ -1381,7 +1510,9 @@ class TorrentManager(component.Component): break else: # This is just a regular file rename so send the signal - component.get('EventManager').emit(TorrentFileRenamedEvent(torrent_id, alert.index, new_name)) + component.get('EventManager').emit( + TorrentFileRenamedEvent(torrent_id, alert.index, new_name) + ) self.save_resume_data((torrent_id,)) def on_alert_metadata_received(self, alert): @@ -1427,7 +1558,9 @@ class TorrentManager(component.Component): except RuntimeError: return if torrent_id in self.torrents: - component.get('EventManager').emit(TorrentFileCompletedEvent(torrent_id, alert.index)) + component.get('EventManager').emit( + TorrentFileCompletedEvent(torrent_id, alert.index) + ) def on_alert_state_update(self, alert): """Alert handler for libtorrent state_update_alert @@ -1464,7 +1597,11 @@ class TorrentManager(component.Component): def on_alert_performance(self, alert): """Alert handler for libtorrent performance_alert""" - log.warning('on_alert_performance: %s, %s', decode_bytes(alert.message()), alert.warning_code) + log.warning( + 'on_alert_performance: %s, %s', + decode_bytes(alert.message()), + alert.warning_code, + ) if alert.warning_code == lt.performance_warning_t.send_buffer_watermark_too_low: max_send_buffer_watermark = 3 * 1024 * 1024 # 3MiB settings = self.session.get_settings() @@ -1473,10 +1610,19 @@ class TorrentManager(component.Component): # If send buffer is too small, try increasing its size by 512KiB (up to max_send_buffer_watermark) if send_buffer_watermark < max_send_buffer_watermark: value = send_buffer_watermark + (500 * 1024) - log.info('Increasing send_buffer_watermark from %s to %s Bytes', send_buffer_watermark, value) - component.get('Core').apply_session_setting('send_buffer_watermark', value) + log.info( + 'Increasing send_buffer_watermark from %s to %s Bytes', + send_buffer_watermark, + value, + ) + component.get('Core').apply_session_setting( + 'send_buffer_watermark', value + ) else: - log.warning('send_buffer_watermark reached maximum value: %s Bytes', max_send_buffer_watermark) + log.warning( + 'send_buffer_watermark reached maximum value: %s Bytes', + max_send_buffer_watermark, + ) def separate_keys(self, keys, torrent_ids): """Separates the input keys into torrent class keys and plugins keys""" @@ -1502,7 +1648,9 @@ class TorrentManager(component.Component): # Could be the clients cache (sessionproxy) isn't up to speed. del status_dict[torrent_id] else: - status_dict[torrent_id] = self.torrents[torrent_id].get_status(torrent_keys, diff, all_keys=not keys) + status_dict[torrent_id] = self.torrents[torrent_id].get_status( + torrent_keys, diff, all_keys=not keys + ) self.status_dict = status_dict d.callback((status_dict, plugin_keys)) @@ -1527,7 +1675,9 @@ class TorrentManager(component.Component): now = time.time() # If last update was recent, use cached data instead of request updates from libtorrent if (now - self.last_state_update_alert_ts) < 1.5: - reactor.callLater(0, self.handle_torrents_status_callback, (d, torrent_ids, keys, diff)) + reactor.callLater( + 0, self.handle_torrents_status_callback, (d, torrent_ids, keys, diff) + ) else: # Ask libtorrent for status update self.torrents_status_requests.insert(0, (d, torrent_ids, keys, diff)) diff --git a/deluge/crypto_utils.py b/deluge/crypto_utils.py index 7993fb98f..7672efa71 100644 --- a/deluge/crypto_utils.py +++ b/deluge/crypto_utils.py @@ -10,7 +10,13 @@ from __future__ import division, print_function, unicode_literals from OpenSSL.crypto import FILETYPE_PEM -from twisted.internet.ssl import AcceptableCiphers, Certificate, CertificateOptions, KeyPair, TLSVersion +from twisted.internet.ssl import ( + AcceptableCiphers, + Certificate, + CertificateOptions, + KeyPair, + TLSVersion, +) # A TLS ciphers list. # Sources for more information on TLS ciphers: @@ -25,16 +31,17 @@ from twisted.internet.ssl import AcceptableCiphers, Certificate, CertificateOpti # - prefer cipher suites that offer perfect forward secrecy (ECDHE), # - prefer AES-GCM over ChaCha20 because hardware-accelerated AES is common, # - disable NULL authentication, MD5 MACs and DSS for security reasons. -TLS_CIPHERS = ':'.join([ - 'ECDH+AESGCM', - 'ECDH+CHACHA20', - 'AES256-GCM-SHA384', - 'AES128-GCM-SHA256', - '!DSS' - '!aNULL', - '!eNULL', - '!MD5' -]) +TLS_CIPHERS = ':'.join( + [ + 'ECDH+AESGCM', + 'ECDH+CHACHA20', + 'AES256-GCM-SHA384', + 'AES128-GCM-SHA256', + '!DSS' '!aNULL', + '!eNULL', + '!MD5', + ] +) # This value tells OpenSSL to disable all SSL/TLS renegotiation. SSL_OP_NO_RENEGOTIATION = 0x40000000 diff --git a/deluge/decorators.py b/deluge/decorators.py index bd0ca02d9..b101712d4 100644 --- a/deluge/decorators.py +++ b/deluge/decorators.py @@ -23,11 +23,14 @@ def proxy(proxy_func): :param proxy_func: the proxy function :type proxy_func: function """ + def decorator(func): @wraps(func) def wrapper(*args, **kwargs): return proxy_func(func, *args, **kwargs) + return wrapper + return decorator @@ -57,6 +60,7 @@ def overrides(*args): # called with the real function as argument def ret_func(func, **kwargs): return _overrides(stack, func, explicit_base_classes=args) + return ret_func @@ -75,7 +79,10 @@ def _overrides(stack, method, explicit_base_classes=None): check_classes = base_classes if not base_classes: - raise ValueError('overrides decorator: unable to determine base class of class "%s"' % class_name) + raise ValueError( + 'overrides decorator: unable to determine base class of class "%s"' + % class_name + ) def get_class(cls_name): if '.' not in cls_name: @@ -91,7 +98,9 @@ def _overrides(stack, method, explicit_base_classes=None): if explicit_base_classes: # One or more base classes are explicitly given, check only those classes - override_classes = re.search(r'\s*@overrides\((.+)\)\s*', stack[1][4][0]).group(1) + override_classes = re.search(r'\s*@overrides\((.+)\)\s*', stack[1][4][0]).group( + 1 + ) override_classes = [c.strip() for c in override_classes.split(',')] check_classes = override_classes @@ -101,21 +110,36 @@ def _overrides(stack, method, explicit_base_classes=None): # Verify that the excplicit override class is one of base classes if explicit_base_classes: from itertools import product + for bc, cc in product(base_classes, check_classes): if issubclass(classes[bc], classes[cc]): break else: - raise Exception('Excplicit override class "%s" is not a super class of: %s' - % (explicit_base_classes, class_name)) + raise Exception( + 'Excplicit override class "%s" is not a super class of: %s' + % (explicit_base_classes, class_name) + ) if not all(hasattr(classes[cls], method.__name__) for cls in check_classes): for cls in check_classes: if not hasattr(classes[cls], method.__name__): - raise Exception('Function override "%s" not found in superclass: %s\n%s' - % (method.__name__, cls, 'File: %s:%s' % (stack[1][1], stack[1][2]))) + raise Exception( + 'Function override "%s" not found in superclass: %s\n%s' + % ( + method.__name__, + cls, + 'File: %s:%s' % (stack[1][1], stack[1][2]), + ) + ) if not any(hasattr(classes[cls], method.__name__) for cls in check_classes): - raise Exception('Function override "%s" not found in any superclass: %s\n%s' - % (method.__name__, check_classes, 'File: %s:%s' % (stack[1][1], stack[1][2]))) + raise Exception( + 'Function override "%s" not found in any superclass: %s\n%s' + % ( + method.__name__, + check_classes, + 'File: %s:%s' % (stack[1][1], stack[1][2]), + ) + ) return method @@ -131,7 +155,8 @@ def deprecated(func): warnings.simplefilter('always', DeprecationWarning) # Turn off filter warnings.warn( 'Call to deprecated function {}.'.format(func.__name__), - category=DeprecationWarning, stacklevel=2, + category=DeprecationWarning, + stacklevel=2, ) warnings.simplefilter('default', DeprecationWarning) # Reset filter return func(*args, **kwargs) diff --git a/deluge/error.py b/deluge/error.py index 526168d46..8705fdfe6 100644 --- a/deluge/error.py +++ b/deluge/error.py @@ -13,7 +13,6 @@ from __future__ import unicode_literals class DelugeError(Exception): - def __new__(cls, *args, **kwargs): inst = super(DelugeError, cls).__new__(cls, *args, **kwargs) inst._args = args @@ -45,7 +44,6 @@ class InvalidPathError(DelugeError): class WrappedException(DelugeError): - def __init__(self, message, exception_type, traceback): super(WrappedException, self).__init__(message) self.type = exception_type @@ -60,7 +58,6 @@ class _ClientSideRecreateError(DelugeError): class IncompatibleClient(_ClientSideRecreateError): - def __init__(self, daemon_version): self.daemon_version = daemon_version msg = ( @@ -71,11 +68,8 @@ class IncompatibleClient(_ClientSideRecreateError): class NotAuthorizedError(_ClientSideRecreateError): - def __init__(self, current_level, required_level): - msg = ( - 'Auth level too low: %(current_level)s < %(required_level)s' - ) % { + msg = ('Auth level too low: %(current_level)s < %(required_level)s') % { 'current_level': current_level, 'required_level': required_level, } @@ -85,7 +79,6 @@ class NotAuthorizedError(_ClientSideRecreateError): class _UsernameBasedPasstroughError(_ClientSideRecreateError): - def __init__(self, message, username): super(_UsernameBasedPasstroughError, self).__init__(message) self.username = username diff --git a/deluge/event.py b/deluge/event.py index b6a48508b..c5d5ff910 100644 --- a/deluge/event.py +++ b/deluge/event.py @@ -25,6 +25,7 @@ class DelugeEventMetaClass(type): """ This metaclass simply keeps a list of all events classes created. """ + def __init__(cls, name, bases, dct): # pylint: disable=bad-mcs-method-argument super(DelugeEventMetaClass, cls).__init__(name, bases, dct) if name != 'DelugeEvent': @@ -58,6 +59,7 @@ class TorrentAddedEvent(DelugeEvent): """ Emitted when a new torrent is successfully added to the session. """ + def __init__(self, torrent_id, from_state): """ :param torrent_id: the torrent_id of the torrent that was added @@ -72,6 +74,7 @@ class TorrentRemovedEvent(DelugeEvent): """ Emitted when a torrent has been removed from the session. """ + def __init__(self, torrent_id): """ :param torrent_id: the torrent_id @@ -84,6 +87,7 @@ class PreTorrentRemovedEvent(DelugeEvent): """ Emitted when a torrent is about to be removed from the session. """ + def __init__(self, torrent_id): """ :param torrent_id: the torrent_id @@ -96,6 +100,7 @@ class TorrentStateChangedEvent(DelugeEvent): """ Emitted when a torrent changes state. """ + def __init__(self, torrent_id, state): """ :param torrent_id: the torrent_id @@ -110,6 +115,7 @@ class TorrentTrackerStatusEvent(DelugeEvent): """ Emitted when a torrents tracker status changes. """ + def __init__(self, torrent_id, status): """ Args: @@ -123,6 +129,7 @@ class TorrentQueueChangedEvent(DelugeEvent): """ Emitted when the queue order has changed. """ + pass @@ -130,6 +137,7 @@ class TorrentFolderRenamedEvent(DelugeEvent): """ Emitted when a folder within a torrent has been renamed. """ + def __init__(self, torrent_id, old, new): """ :param torrent_id: the torrent_id @@ -146,6 +154,7 @@ class TorrentFileRenamedEvent(DelugeEvent): """ Emitted when a file within a torrent has been renamed. """ + def __init__(self, torrent_id, index, name): """ :param torrent_id: the torrent_id @@ -162,6 +171,7 @@ class TorrentFinishedEvent(DelugeEvent): """ Emitted when a torrent finishes downloading. """ + def __init__(self, torrent_id): """ :param torrent_id: the torrent_id @@ -174,6 +184,7 @@ class TorrentResumedEvent(DelugeEvent): """ Emitted when a torrent resumes from a paused state. """ + def __init__(self, torrent_id): """ :param torrent_id: the torrent_id @@ -186,6 +197,7 @@ class TorrentFileCompletedEvent(DelugeEvent): """ Emitted when a file completes. """ + def __init__(self, torrent_id, index): """ :param torrent_id: the torrent_id @@ -200,6 +212,7 @@ class TorrentStorageMovedEvent(DelugeEvent): """ Emitted when the storage location for a torrent has been moved. """ + def __init__(self, torrent_id, path): """ :param torrent_id: the torrent_id @@ -214,6 +227,7 @@ class CreateTorrentProgressEvent(DelugeEvent): """ Emitted when creating a torrent file remotely. """ + def __init__(self, piece_count, num_pieces): self._args = [piece_count, num_pieces] @@ -222,6 +236,7 @@ class NewVersionAvailableEvent(DelugeEvent): """ Emitted when a more recent version of Deluge is available. """ + def __init__(self, new_release): """ :param new_release: the new version that is available @@ -235,6 +250,7 @@ class SessionStartedEvent(DelugeEvent): Emitted when a session has started. This typically only happens once when the daemon is initially started. """ + pass @@ -242,6 +258,7 @@ class SessionPausedEvent(DelugeEvent): """ Emitted when the session has been paused. """ + pass @@ -249,6 +266,7 @@ class SessionResumedEvent(DelugeEvent): """ Emitted when the session has been resumed. """ + pass @@ -256,6 +274,7 @@ class ConfigValueChangedEvent(DelugeEvent): """ Emitted when a config value changes in the Core. """ + def __init__(self, key, value): """ :param key: the key that changed @@ -269,6 +288,7 @@ class PluginEnabledEvent(DelugeEvent): """ Emitted when a plugin is enabled in the Core. """ + def __init__(self, plugin_name): self._args = [plugin_name] @@ -277,6 +297,7 @@ class PluginDisabledEvent(DelugeEvent): """ Emitted when a plugin is disabled in the Core. """ + def __init__(self, plugin_name): self._args = [plugin_name] @@ -285,6 +306,7 @@ class ClientDisconnectedEvent(DelugeEvent): """ Emitted when a client disconnects. """ + def __init__(self, session_id): self._args = [session_id] @@ -293,6 +315,7 @@ class ExternalIPEvent(DelugeEvent): """ Emitted when the external ip address is received from libtorrent. """ + def __init__(self, external_ip): """ Args: diff --git a/deluge/httpdownloader.py b/deluge/httpdownloader.py index ad0d2f80a..b4acd0784 100644 --- a/deluge/httpdownloader.py +++ b/deluge/httpdownloader.py @@ -31,13 +31,14 @@ log = logging.getLogger(__name__) class CompressionDecoder(client.GzipDecoder): """A compression decoder for gzip, x-gzip and deflate.""" + def deliverBody(self, protocol): # NOQA: N802 - self.original.deliverBody( - CompressionDecoderProtocol(protocol, self.original)) + self.original.deliverBody(CompressionDecoderProtocol(protocol, self.original)) class CompressionDecoderProtocol(client._GzipProtocol): """A compression decoder protocol for CompressionDecoder.""" + def __init__(self, protocol, response): super(CompressionDecoderProtocol, self).__init__(protocol, response) self._zlibDecompress = zlib.decompressobj(32 + zlib.MAX_WBITS) @@ -45,6 +46,7 @@ class CompressionDecoderProtocol(client._GzipProtocol): class BodyHandler(HTTPClientParser, object): """An HTTP parser that saves the response to a file.""" + def __init__(self, request, finished, length, agent, encoding=None): """BodyHandler init. @@ -66,8 +68,7 @@ class BodyHandler(HTTPClientParser, object): self.current_length += len(data) self.data += data if self.agent.part_callback: - self.agent.part_callback( - data, self.current_length, self.total_length) + self.agent.part_callback(data, self.current_length, self.total_length) def connectionLost(self, reason): # NOQA: N802 if self.encoding: @@ -82,6 +83,7 @@ class BodyHandler(HTTPClientParser, object): @implementer(IAgent) class HTTPDownloaderAgent(object): """A File Downloader Agent.""" + def __init__( self, agent, @@ -125,21 +127,19 @@ class HTTPDownloaderAgent(object): finished.errback(Failure(error)) else: headers = response.headers - body_length = int( - headers.getRawHeaders(b'content-length', default=[0])[0]) - - if ( - headers.hasHeader(b'content-disposition') - and not self.force_filename - ): - content_disp = headers.getRawHeaders( - b'content-disposition')[0].decode('utf-8') + body_length = int(headers.getRawHeaders(b'content-length', default=[0])[0]) + + if headers.hasHeader(b'content-disposition') and not self.force_filename: + content_disp = headers.getRawHeaders(b'content-disposition')[0].decode( + 'utf-8' + ) content_disp_params = cgi.parse_header(content_disp)[1] if 'filename' in content_disp_params: new_file_name = content_disp_params['filename'] new_file_name = sanitise_filename(new_file_name) new_file_name = os.path.join( - os.path.split(self.filename)[0], new_file_name) + os.path.split(self.filename)[0], new_file_name + ) count = 1 fileroot = os.path.splitext(new_file_name)[0] @@ -155,13 +155,8 @@ class HTTPDownloaderAgent(object): params = cgi.parse_header(cont_type)[1] encoding = params.get('charset', None) response.deliverBody( - BodyHandler( - response.request, - finished, - body_length, - self, - encoding, - )) + BodyHandler(response.request, finished, body_length, self, encoding) + ) return finished @@ -186,10 +181,7 @@ class HTTPDownloaderAgent(object): headers.addRawHeader('User-Agent', user_agent) d = self.agent.request( - method=method, - uri=uri, - headers=headers, - bodyProducer=body_producer, + method=method, uri=uri, headers=headers, bodyProducer=body_producer ) d.addCallback(self.request_callback) return d @@ -212,8 +204,7 @@ def sanitise_filename(filename): if os.path.basename(filename) != filename: # Dodgy server, log it log.warning( - 'Potentially malicious server: trying to write to file: %s', - filename, + 'Potentially malicious server: trying to write to file: %s', filename ) # Only use the basename filename = os.path.basename(filename) @@ -222,15 +213,15 @@ def sanitise_filename(filename): if filename.startswith('.') or ';' in filename or '|' in filename: # Dodgy server, log it log.warning( - 'Potentially malicious server: trying to write to file: %s', - filename, + 'Potentially malicious server: trying to write to file: %s', filename ) return filename def _download_file( - url, filename, + url, + filename, callback=None, headers=None, force_filename=False, @@ -269,12 +260,7 @@ def _download_file( agent = client.RedirectAgent(agent) agent = HTTPDownloaderAgent( - agent, - filename, - callback, - force_filename, - allow_compression, - handle_redirects, + agent, filename, callback, force_filename, allow_compression, handle_redirects ) # The Headers init expects dict values to be a list. @@ -317,6 +303,7 @@ def download_file( t.w.e.PageRedirect: If handle_redirects is False. t.w.e.Error: For all other HTTP response errors. """ + def on_download_success(result): log.debug('Download success!') return result @@ -324,14 +311,19 @@ def download_file( def on_download_fail(failure): log.warning( 'Error occurred downloading file from "%s": %s', - url, failure.getErrorMessage(), + url, + failure.getErrorMessage(), ) result = failure return result d = _download_file( - url, filename, callback=callback, headers=headers, - force_filename=force_filename, allow_compression=allow_compression, + url, + filename, + callback=callback, + headers=headers, + force_filename=force_filename, + allow_compression=allow_compression, handle_redirects=handle_redirects, ) d.addCallbacks(on_download_success, on_download_fail) diff --git a/deluge/log.py b/deluge/log.py index ba0af4c8b..e2e718e40 100644 --- a/deluge/log.py +++ b/deluge/log.py @@ -29,7 +29,9 @@ LoggingLoggerClass = logging.getLoggerClass() if 'dev' in common.get_version(): DEFAULT_LOGGING_FORMAT = '%%(asctime)s.%%(msecs)03.0f [%%(levelname)-8s][%%(name)-%ds:%%(lineno)-4d] %%(message)s' else: - DEFAULT_LOGGING_FORMAT = '%%(asctime)s [%%(levelname)-8s][%%(name)-%ds:%%(lineno)-4d] %%(message)s' + DEFAULT_LOGGING_FORMAT = ( + '%%(asctime)s [%%(levelname)-8s][%%(name)-%ds:%%(lineno)-4d] %%(message)s' + ) MAX_LOGGER_NAME_LENGTH = 10 @@ -43,10 +45,12 @@ class Logging(LoggingLoggerClass): if len(logger_name) > MAX_LOGGER_NAME_LENGTH: MAX_LOGGER_NAME_LENGTH = len(logger_name) for handler in logging.getLogger().handlers: - handler.setFormatter(logging.Formatter( - DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH, - datefmt='%H:%M:%S', - )) + handler.setFormatter( + logging.Formatter( + DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH, + datefmt='%H:%M:%S', + ) + ) @defer.inlineCallbacks def garbage(self, msg, *args, **kwargs): @@ -112,8 +116,12 @@ levels = { def setup_logger( - level='error', filename=None, filemode='w', logrotate=None, - output_stream=sys.stdout, twisted_observer=True, + level='error', + filename=None, + filemode='w', + logrotate=None, + output_stream=sys.stdout, + twisted_observer=True, ): """ Sets up the basic logger and if `:param:filename` is set, then it will log @@ -140,13 +148,14 @@ def setup_logger( if filename and logrotate: handler = logging.handlers.RotatingFileHandler( - filename, maxBytes=logrotate, - backupCount=5, encoding='utf-8', + filename, maxBytes=logrotate, backupCount=5, encoding='utf-8' ) elif filename and filemode == 'w': handler_cls = logging.FileHandler if not common.windows_check(): - handler_cls = getattr(logging.handlers, 'WatchedFileHandler', logging.FileHandler) + handler_cls = getattr( + logging.handlers, 'WatchedFileHandler', logging.FileHandler + ) handler = handler_cls(filename, mode=filemode, encoding='utf-8') else: handler = logging.StreamHandler(stream=output_stream) @@ -154,8 +163,7 @@ def setup_logger( handler.setLevel(level) formatter = logging.Formatter( - DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH, - datefmt='%H:%M:%S', + DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH, datefmt='%H:%M:%S' ) handler.setFormatter(formatter) @@ -190,7 +198,9 @@ class TwistedLoggingObserver(PythonLoggingObserver): log = logging.getLogger(__name__) if 'log_failure' in event_dict: fmt = '%(log_namespace)s \n%(log_failure)s' - getattr(LoggingLoggerClass, event_dict['log_level'].name)(log, fmt % (event_dict)) + getattr(LoggingLoggerClass, event_dict['log_level'].name)( + log, fmt % (event_dict) + ) else: PythonLoggingObserver.emit(self, event_dict) @@ -214,13 +224,13 @@ def tweak_logging_levels(): the command line. """ from deluge import configmanager + logging_config_file = os.path.join(configmanager.get_config_dir(), 'logging.conf') if not os.path.isfile(logging_config_file): return log = logging.getLogger(__name__) log.warning( - 'logging.conf found! tweaking logging levels from %s', - logging_config_file, + 'logging.conf found! tweaking logging levels from %s', logging_config_file ) with open(logging_config_file, 'r') as _file: for line in _file: @@ -249,15 +259,18 @@ def set_logger_level(level, logger_name=None): def get_plugin_logger(logger_name): import warnings + stack = inspect.stack() - stack.pop(0) # The logging call from this module + stack.pop(0) # The logging call from this module module_stack = stack.pop(0) # The module that called the log function caller_module = inspect.getmodule(module_stack[0]) # In some weird cases caller_module might be None, try to continue caller_module_name = getattr(caller_module, '__name__', '') warnings.warn_explicit( - DEPRECATION_WARNING, DeprecationWarning, - module_stack[1], module_stack[2], + DEPRECATION_WARNING, + DeprecationWarning, + module_stack[1], + module_stack[2], caller_module_name, ) @@ -292,16 +305,19 @@ Triggering code:""" class _BackwardsCompatibleLOG(object): def __getattribute__(self, name): import warnings + logger_name = 'deluge' stack = inspect.stack() - stack.pop(0) # The logging call from this module + stack.pop(0) # The logging call from this module module_stack = stack.pop(0) # The module that called the log function caller_module = inspect.getmodule(module_stack[0]) # In some weird cases caller_module might be None, try to continue caller_module_name = getattr(caller_module, '__name__', '') warnings.warn_explicit( - DEPRECATION_WARNING, DeprecationWarning, - module_stack[1], module_stack[2], + DEPRECATION_WARNING, + DeprecationWarning, + module_stack[1], + module_stack[2], caller_module_name, ) if caller_module: @@ -320,7 +336,7 @@ class _BackwardsCompatibleLOG(object): else: logging.getLogger(logger_name).warning( "Unable to monkey-patch the calling module's `log` attribute! " - 'You should really update and rebuild your plugins...', + 'You should really update and rebuild your plugins...' ) return getattr(logging.getLogger(logger_name), name) diff --git a/deluge/maketorrent.py b/deluge/maketorrent.py index 7ea64746c..528638e43 100644 --- a/deluge/maketorrent.py +++ b/deluge/maketorrent.py @@ -18,6 +18,7 @@ from deluge.common import get_path_size, utf8_encode_structure class InvalidPath(Exception): """Raised when an invalid path is supplied.""" + pass @@ -27,6 +28,7 @@ class InvalidPieceSize(Exception): Note: Piece sizes must be multiples of 16KiB. """ + pass @@ -42,6 +44,7 @@ class TorrentMetadata(object): >>> t.save('/tmp/test.torrent') """ + def __init__(self): self.__data_path = None self.__piece_size = 0 @@ -66,9 +69,7 @@ class TorrentMetadata(object): if not self.data_path: raise InvalidPath('Need to set a data_path!') - torrent = { - 'info': {}, - } + torrent = {'info': {}} if self.comment: torrent['comment'] = self.comment @@ -121,8 +122,10 @@ class TorrentMetadata(object): # Collect a list of file paths and add padding files if necessary for (dirpath, dirnames, filenames) in os.walk(self.data_path): for index, filename in enumerate(filenames): - size = get_path_size(os.path.join(self.data_path, dirpath, filename)) - p = dirpath[len(self.data_path):] + size = get_path_size( + os.path.join(self.data_path, dirpath, filename) + ) + p = dirpath[len(self.data_path) :] p = p.lstrip('/') p = p.split('/') if p[0]: @@ -156,7 +159,9 @@ class TorrentMetadata(object): buf = b'' fs[-1][b'attr'] = b'p' else: - with open(os.path.join(self.data_path.encode('utf8'), *path), 'rb') as _file: + with open( + os.path.join(self.data_path.encode('utf8'), *path), 'rb' + ) as _file: r = _file.read(piece_size - len(buf)) while r: buf += r diff --git a/deluge/metafile.py b/deluge/metafile.py index 29c020e9f..8c28c7e91 100644 --- a/deluge/metafile.py +++ b/deluge/metafile.py @@ -50,14 +50,25 @@ class RemoteFileProgress(object): def __call__(self, piece_count, num_pieces): component.get('RPCServer').emit_event_for_session_id( - self.session_id, CreateTorrentProgressEvent(piece_count, num_pieces), + self.session_id, CreateTorrentProgressEvent(piece_count, num_pieces) ) def make_meta_file( - path, url, piece_length, progress=None, title=None, comment=None, - safe=None, content_type=None, target=None, webseeds=None, name=None, - private=False, created_by=None, trackers=None, + path, + url, + piece_length, + progress=None, + title=None, + comment=None, + safe=None, + content_type=None, + target=None, + webseeds=None, + name=None, + private=False, + created_by=None, + trackers=None, ): data = {'creation date': int(gmtime())} if url: @@ -140,6 +151,7 @@ def makeinfo(path, piece_length, progress, name=None, content_type=None, private totalsize += os.path.getsize(f) if totalsize >= piece_length: import math + num_pieces = math.ceil(totalsize / piece_length) else: num_pieces = 1 @@ -149,10 +161,9 @@ def makeinfo(path, piece_length, progress, name=None, content_type=None, private size = os.path.getsize(f) p2 = [n.encode('utf8') for n in p] if content_type: - fs.append({ - 'length': size, 'path': p2, - 'content_type': content_type, - }) # HEREDAVE. bad for batch! + fs.append( + {'length': size, 'path': p2, 'content_type': content_type} + ) # HEREDAVE. bad for batch! else: fs.append({'length': size, 'path': p2}) with open(f, 'rb') as file_: @@ -206,14 +217,16 @@ def makeinfo(path, piece_length, progress, name=None, content_type=None, private if content_type is not None: return { 'pieces': b''.join(pieces), - 'piece length': piece_length, 'length': size, + 'piece length': piece_length, + 'length': size, 'name': name, 'content_type': content_type, 'private': private, } return { 'pieces': b''.join(pieces), - 'piece length': piece_length, 'length': size, + 'piece length': piece_length, + 'length': size, 'name': name, 'private': private, } diff --git a/deluge/path_chooser_common.py b/deluge/path_chooser_common.py index d6cd27c40..b84d78ef9 100644 --- a/deluge/path_chooser_common.py +++ b/deluge/path_chooser_common.py @@ -17,9 +17,12 @@ def is_hidden(filepath): def has_hidden_attribute(filepath): import win32api import win32con + try: attribute = win32api.GetFileAttributes(filepath) - return attribute & (win32con.FILE_ATTRIBUTE_HIDDEN | win32con.FILE_ATTRIBUTE_SYSTEM) + return attribute & ( + win32con.FILE_ATTRIBUTE_HIDDEN | win32con.FILE_ATTRIBUTE_SYSTEM + ) except (AttributeError, AssertionError): return False diff --git a/deluge/pluginmanagerbase.py b/deluge/pluginmanagerbase.py index 624b06b05..0a84c1b1c 100644 --- a/deluge/pluginmanagerbase.py +++ b/deluge/pluginmanagerbase.py @@ -56,7 +56,9 @@ class PluginManagerBase(object): self.config = deluge.configmanager.ConfigManager(config_file) # Create the plugins folder if it doesn't exist - if not os.path.exists(os.path.join(deluge.configmanager.get_config_dir(), 'plugins')): + if not os.path.exists( + os.path.join(deluge.configmanager.get_config_dir(), 'plugins') + ): os.mkdir(os.path.join(deluge.configmanager.get_config_dir(), 'plugins')) # This is the entry we want to load.. @@ -149,7 +151,9 @@ class PluginManagerBase(object): log.error(ex) return defer.succeed(False) except Exception as ex: - log.error('Unable to instantiate plugin %r from %r!', name, egg.location) + log.error( + 'Unable to instantiate plugin %r from %r!', name, egg.location + ) log.exception(ex) continue try: @@ -161,35 +165,47 @@ class PluginManagerBase(object): if not instance.__module__.startswith('deluge.plugins.'): import warnings + warnings.warn_explicit( DEPRECATION_WARNING % name, DeprecationWarning, - instance.__module__, 0, + instance.__module__, + 0, ) if self._component_state == 'Started': + def on_enabled(result, instance): return component.start([instance.plugin._component_name]) + return_d.addCallback(on_enabled, instance) def on_started(result, instance): plugin_name_space = plugin_name.replace('-', ' ') self.plugins[plugin_name_space] = instance if plugin_name_space not in self.config['enabled_plugins']: - log.debug('Adding %s to enabled_plugins list in config', plugin_name_space) + log.debug( + 'Adding %s to enabled_plugins list in config', plugin_name_space + ) self.config['enabled_plugins'].append(plugin_name_space) log.info('Plugin %s enabled...', plugin_name_space) return True def on_started_error(result, instance): log.error( - 'Failed to start plugin: %s\n%s', plugin_name, + 'Failed to start plugin: %s\n%s', + plugin_name, result.getTraceback(elideFrameworkCode=1, detail='brief'), ) self.plugins[plugin_name.replace('-', ' ')] = instance self.disable_plugin(plugin_name) return False - return_d.addCallbacks(on_started, on_started_error, callbackArgs=[instance], errbackArgs=[instance]) + return_d.addCallbacks( + on_started, + on_started_error, + callbackArgs=[instance], + errbackArgs=[instance], + ) return return_d return defer.succeed(False) @@ -219,7 +235,9 @@ class PluginManagerBase(object): def on_disabled(result): ret = True if isinstance(result, Failure): - log.debug('Error when disabling plugin %s: %s', name, result.getTraceback()) + log.debug( + 'Error when disabling plugin %s: %s', name, result.getTraceback() + ) ret = False try: component.deregister(self.plugins[name].plugin) @@ -250,7 +268,9 @@ class PluginManagerBase(object): for line in self.pkg_env[name][0].get_metadata('PKG-INFO').splitlines(): if not line: continue - if line[0] in ' \t' and (len(line.split(':', 1)) == 1 or line.split(':', 1)[0] not in info): + if line[0] in ' \t' and ( + len(line.split(':', 1)) == 1 or line.split(':', 1)[0] not in info + ): # This is a continuation cont_lines.append(line.strip()) else: diff --git a/deluge/plugins/AutoAdd/deluge/plugins/autoadd/__init__.py b/deluge/plugins/AutoAdd/deluge/plugins/autoadd/__init__.py index 74d8be491..b93d89a45 100644 --- a/deluge/plugins/AutoAdd/deluge/plugins/autoadd/__init__.py +++ b/deluge/plugins/AutoAdd/deluge/plugins/autoadd/__init__.py @@ -20,6 +20,7 @@ from deluge.plugins.init import PluginInitBase class CorePlugin(PluginInitBase): def __init__(self, plugin_name): from .core import Core as _pluginCls + self._plugin_cls = _pluginCls super(CorePlugin, self).__init__(plugin_name) @@ -27,6 +28,7 @@ class CorePlugin(PluginInitBase): class GtkUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .gtkui import GtkUI as _pluginCls + self._plugin_cls = _pluginCls super(GtkUIPlugin, self).__init__(plugin_name) @@ -34,5 +36,6 @@ class GtkUIPlugin(PluginInitBase): class WebUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .webui import WebUI as _pluginCls + self._plugin_cls = _pluginCls super(WebUIPlugin, self).__init__(plugin_name) diff --git a/deluge/plugins/AutoAdd/deluge/plugins/autoadd/core.py b/deluge/plugins/AutoAdd/deluge/plugins/autoadd/core.py index d23f94aae..658a163d5 100644 --- a/deluge/plugins/AutoAdd/deluge/plugins/autoadd/core.py +++ b/deluge/plugins/AutoAdd/deluge/plugins/autoadd/core.py @@ -35,10 +35,7 @@ from deluge.plugins.pluginbase import CorePluginBase log = logging.getLogger(__name__) -DEFAULT_PREFS = { - 'watchdirs': {}, - 'next_id': 1, -} +DEFAULT_PREFS = {'watchdirs': {}, 'next_id': 1} OPTIONS_AVAILABLE = { # option: builtin @@ -72,6 +69,7 @@ MAX_NUM_ATTEMPTS = 10 class AutoaddOptionsChangedEvent(DelugeEvent): """Emitted when the options for the plugin are changed.""" + def __init__(self): pass @@ -92,7 +90,7 @@ class Core(CorePluginBase): self.rpcserver = component.get('RPCServer') component.get('EventManager').register_event_handler( - 'PreTorrentRemovedEvent', self.__on_pre_torrent_removed, + 'PreTorrentRemovedEvent', self.__on_pre_torrent_removed ) # Dict of Filename:Attempts @@ -110,7 +108,7 @@ class Core(CorePluginBase): def disable(self): # disable all running looping calls component.get('EventManager').deregister_event_handler( - 'PreTorrentRemovedEvent', self.__on_pre_torrent_removed, + 'PreTorrentRemovedEvent', self.__on_pre_torrent_removed ) for loopingcall in self.update_timers.values(): loopingcall.stop() @@ -124,14 +122,10 @@ class Core(CorePluginBase): """Update the options for a watch folder.""" watchdir_id = str(watchdir_id) options = self._make_unicode(options) - check_input( - watchdir_id in self.watchdirs, _('Watch folder does not exist.'), - ) + check_input(watchdir_id in self.watchdirs, _('Watch folder does not exist.')) if 'path' in options: options['abspath'] = os.path.abspath(options['path']) - check_input( - os.path.isdir(options['abspath']), _('Path does not exist.'), - ) + check_input(os.path.isdir(options['abspath']), _('Path does not exist.')) for w_id, w in self.watchdirs.items(): if options['abspath'] == w['abspath'] and watchdir_id != w_id: raise Exception('Path is already being watched.') @@ -211,10 +205,7 @@ class Core(CorePluginBase): watchdir = self.watchdirs[watchdir_id] if not watchdir['enabled']: # We shouldn't be updating because this watchdir is not enabled - log.debug( - 'Watchdir id %s is not enabled. Disabling it.', - watchdir_id, - ) + log.debug('Watchdir id %s is not enabled. Disabling it.', watchdir_id) self.disable_watchdir(watchdir_id) return @@ -231,7 +222,10 @@ class Core(CorePluginBase): # without them is valid, and applies all its settings. for option, value in watchdir.items(): if OPTIONS_AVAILABLE.get(option): - if watchdir.get(option + '_toggle', True) or option in ['owner', 'seed_mode']: + if watchdir.get(option + '_toggle', True) or option in [ + 'owner', + 'seed_mode', + ]: options[option] = value # Check for .magnet files containing multiple magnet links and @@ -240,19 +234,27 @@ class Core(CorePluginBase): try: filepath = os.path.join(watchdir['abspath'], filename) except UnicodeDecodeError as ex: - log.error('Unable to auto add torrent due to improper filename encoding: %s', ex) + log.error( + 'Unable to auto add torrent due to improper filename encoding: %s', + ex, + ) continue if os.path.isdir(filepath): # Skip directories continue - elif os.path.splitext(filename)[1] == '.magnet' and self.split_magnets(filepath): + elif os.path.splitext(filename)[1] == '.magnet' and self.split_magnets( + filepath + ): os.remove(filepath) for filename in os.listdir(watchdir['abspath']): try: filepath = os.path.join(watchdir['abspath'], filename) except UnicodeDecodeError as ex: - log.error('Unable to auto add torrent due to improper filename encoding: %s', ex) + log.error( + 'Unable to auto add torrent due to improper filename encoding: %s', + ex, + ) continue if os.path.isdir(filepath): @@ -276,7 +278,8 @@ class Core(CorePluginBase): if self.invalid_torrents[filename] >= MAX_NUM_ATTEMPTS: log.warning( 'Maximum attempts reached while trying to add the ' - 'torrent file with the path %s', filepath, + 'torrent file with the path %s', + filepath, ) os.rename(filepath, filepath + '.invalid') del self.invalid_torrents[filename] @@ -296,7 +299,10 @@ class Core(CorePluginBase): except Exception as ex: log.error('Unable to set label: %s', ex) - if watchdir.get('queue_to_top_toggle', True) and 'queue_to_top' in watchdir: + if ( + watchdir.get('queue_to_top_toggle', True) + and 'queue_to_top' in watchdir + ): if watchdir['queue_to_top']: component.get('TorrentManager').queue_top(torrent_id) else: @@ -312,7 +318,8 @@ class Core(CorePluginBase): copy_torrent_file = os.path.join(copy_torrent_path, filename) log.debug( 'Moving added torrent file "%s" to "%s"', - os.path.basename(filepath), copy_torrent_path, + os.path.basename(filepath), + copy_torrent_path, ) shutil.move(filepath, copy_torrent_file) else: @@ -331,10 +338,12 @@ class Core(CorePluginBase): try: # The torrent looks good, so lets add it to the session. if magnet: - d = component.get('Core').add_torrent_magnet(filedump.strip(), options) + d = component.get('Core').add_torrent_magnet( + filedump.strip(), options + ) else: d = component.get('Core').add_torrent_file_async( - filename, b64encode(filedump), options, + filename, b64encode(filedump), options ) d.addCallback(on_torrent_added, filename, filepath) d.addErrback(fail_torrent_add, filepath, magnet) @@ -346,7 +355,8 @@ class Core(CorePluginBase): self.disable_watchdir(watchdir_id) log.error( 'Disabling "%s", error during update: %s', - self.watchdirs[watchdir_id]['path'], failure, + self.watchdirs[watchdir_id]['path'], + failure, ) @export @@ -356,7 +366,7 @@ class Core(CorePluginBase): if w_id not in self.update_timers or not self.update_timers[w_id].running: self.update_timers[w_id] = LoopingCall(self.update_watchdir, w_id) self.update_timers[w_id].start(5).addErrback( - self.on_update_watchdir_error, w_id, + self.on_update_watchdir_error, w_id ) # Update the config if not self.watchdirs[w_id]['enabled']: @@ -398,8 +408,8 @@ class Core(CorePluginBase): session_auth_level = self.rpcserver.get_session_auth_level() if session_auth_level == AUTH_LEVEL_ADMIN: log.debug( - 'Current logged in user %s is an ADMIN, send all ' - 'watchdirs', session_user, + 'Current logged in user %s is an ADMIN, send all ' 'watchdirs', + session_user, ) return self.watchdirs @@ -410,7 +420,9 @@ class Core(CorePluginBase): log.debug( 'Current logged in user %s is not an ADMIN, send only ' - 'their watchdirs: %s', session_user, list(watchdirs), + 'their watchdirs: %s', + session_user, + list(watchdirs), ) return watchdirs @@ -451,7 +463,9 @@ class Core(CorePluginBase): def remove(self, watchdir_id): """Remove a watch folder.""" watchdir_id = str(watchdir_id) - check_input(watchdir_id in self.watchdirs, 'Unknown Watchdir: %s' % self.watchdirs) + check_input( + watchdir_id in self.watchdirs, 'Unknown Watchdir: %s' % self.watchdirs + ) if self.watchdirs[watchdir_id]['enabled']: self.disable_watchdir(watchdir_id) del self.watchdirs[watchdir_id] @@ -488,13 +502,16 @@ class Core(CorePluginBase): os.remove(torrent_fname_path) log.info( 'Removed torrent file "%s" from "%s"', - torrent_fname, copy_torrent_path, + torrent_fname, + copy_torrent_path, ) break except OSError as ex: log.info( 'Failed to removed torrent file "%s" from "%s": %s', - torrent_fname, copy_torrent_path, ex, + torrent_fname, + copy_torrent_path, + ex, ) @export diff --git a/deluge/plugins/AutoAdd/deluge/plugins/autoadd/gtkui.py b/deluge/plugins/AutoAdd/deluge/plugins/autoadd/gtkui.py index 083a1bcb2..99a70c04f 100644 --- a/deluge/plugins/AutoAdd/deluge/plugins/autoadd/gtkui.py +++ b/deluge/plugins/AutoAdd/deluge/plugins/autoadd/gtkui.py @@ -38,8 +38,12 @@ class OptionsDialog(object): spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio'] spin_int_ids = ['max_upload_slots', 'max_connections'] chk_ids = [ - 'stop_at_ratio', 'remove_at_ratio', 'move_completed', - 'add_paused', 'auto_managed', 'queue_to_top', + 'stop_at_ratio', + 'remove_at_ratio', + 'move_completed', + 'add_paused', + 'auto_managed', + 'queue_to_top', ] def __init__(self): @@ -52,13 +56,15 @@ class OptionsDialog(object): options = {} self.builder = gtk.Builder() self.builder.add_from_file(get_resource('autoadd_options.ui')) - self.builder.connect_signals({ - 'on_opts_add': self.on_add, - 'on_opts_apply': self.on_apply, - 'on_opts_cancel': self.on_cancel, - 'on_options_dialog_close': self.on_cancel, - 'on_toggle_toggled': self.on_toggle_toggled, - }) + self.builder.connect_signals( + { + 'on_opts_add': self.on_add, + 'on_opts_apply': self.on_apply, + 'on_opts_cancel': self.on_cancel, + 'on_options_dialog_close': self.on_cancel, + 'on_toggle_toggled': self.on_toggle_toggled, + } + ) self.dialog = self.builder.get_object('options_dialog') self.dialog.set_transient_for(component.get('Preferences').pref_dialog) @@ -79,23 +85,21 @@ class OptionsDialog(object): def load_options(self, options): self.builder.get_object('enabled').set_active(options.get('enabled', True)) self.builder.get_object('append_extension_toggle').set_active( - options.get('append_extension_toggle', False), + options.get('append_extension_toggle', False) ) self.builder.get_object('append_extension').set_text( - options.get('append_extension', '.added'), + options.get('append_extension', '.added') ) self.builder.get_object('download_location_toggle').set_active( - options.get('download_location_toggle', False), + options.get('download_location_toggle', False) ) self.builder.get_object('copy_torrent_toggle').set_active( - options.get('copy_torrent_toggle', False), + options.get('copy_torrent_toggle', False) ) self.builder.get_object('delete_copy_torrent_toggle').set_active( - options.get('delete_copy_torrent_toggle', False), - ) - self.builder.get_object('seed_mode').set_active( - options.get('seed_mode', False), + options.get('delete_copy_torrent_toggle', False) ) + self.builder.get_object('seed_mode').set_active(options.get('seed_mode', False)) self.accounts.clear() self.labels.clear() combobox = self.builder.get_object('OwnerCombobox') @@ -108,14 +112,20 @@ class OptionsDialog(object): label_widget.child.set_text(options.get('label', '')) label_widget.set_model(self.labels) label_widget.set_entry_text_column(0) - self.builder.get_object('label_toggle').set_active(options.get('label_toggle', False)) + self.builder.get_object('label_toggle').set_active( + options.get('label_toggle', False) + ) for spin_id in self.spin_ids + self.spin_int_ids: self.builder.get_object(spin_id).set_value(options.get(spin_id, 0)) - self.builder.get_object(spin_id + '_toggle').set_active(options.get(spin_id + '_toggle', False)) + self.builder.get_object(spin_id + '_toggle').set_active( + options.get(spin_id + '_toggle', False) + ) for chk_id in self.chk_ids: self.builder.get_object(chk_id).set_active(bool(options.get(chk_id, True))) - self.builder.get_object(chk_id + '_toggle').set_active(options.get(chk_id + '_toggle', False)) + self.builder.get_object(chk_id + '_toggle').set_active( + options.get(chk_id + '_toggle', False) + ) if not options.get('add_paused', True): self.builder.get_object('isnt_add_paused').set_active(True) if not options.get('queue_to_top', True): @@ -123,18 +133,20 @@ class OptionsDialog(object): if not options.get('auto_managed', True): self.builder.get_object('isnt_auto_managed').set_active(True) for field in [ - 'move_completed_path', 'path', 'download_location', + 'move_completed_path', + 'path', + 'download_location', 'copy_torrent', ]: if client.is_localhost(): self.builder.get_object(field + '_chooser').set_current_folder( - options.get(field, os.path.expanduser('~')), + options.get(field, os.path.expanduser('~')) ) self.builder.get_object(field + '_chooser').show() self.builder.get_object(field + '_entry').hide() else: self.builder.get_object(field + '_entry').set_text( - options.get(field, ''), + options.get(field, '') ) self.builder.get_object(field + '_entry').show() self.builder.get_object(field + '_chooser').hide() @@ -143,36 +155,44 @@ class OptionsDialog(object): def on_core_config(config): if client.is_localhost(): self.builder.get_object('download_location_chooser').set_current_folder( - options.get('download_location', config['download_location']), + options.get('download_location', config['download_location']) ) if options.get('move_completed_toggle', config['move_completed']): self.builder.get_object('move_completed_toggle').set_active(True) - self.builder.get_object('move_completed_path_chooser').set_current_folder( - options.get('move_completed_path', config['move_completed_path']), + self.builder.get_object( + 'move_completed_path_chooser' + ).set_current_folder( + options.get( + 'move_completed_path', config['move_completed_path'] + ) ) if options.get('copy_torrent_toggle', config['copy_torrent_file']): self.builder.get_object('copy_torrent_toggle').set_active(True) self.builder.get_object('copy_torrent_chooser').set_current_folder( - options.get('copy_torrent', config['torrentfiles_location']), + options.get('copy_torrent', config['torrentfiles_location']) ) else: self.builder.get_object('download_location_entry').set_text( - options.get('download_location', config['download_location']), + options.get('download_location', config['download_location']) ) if options.get('move_completed_toggle', config['move_completed']): self.builder.get_object('move_completed_toggle').set_active( - options.get('move_completed_toggle', False), + options.get('move_completed_toggle', False) ) self.builder.get_object('move_completed_path_entry').set_text( - options.get('move_completed_path', config['move_completed_path']), + options.get( + 'move_completed_path', config['move_completed_path'] + ) ) if options.get('copy_torrent_toggle', config['copy_torrent_file']): self.builder.get_object('copy_torrent_toggle').set_active(True) self.builder.get_object('copy_torrent_entry').set_text( - options.get('copy_torrent', config['torrentfiles_location']), + options.get('copy_torrent', config['torrentfiles_location']) ) - if options.get('delete_copy_torrent_toggle', config['del_copy_torrent_file']): + if options.get( + 'delete_copy_torrent_toggle', config['del_copy_torrent_file'] + ): self.builder.get_object('delete_copy_torrent_toggle').set_active(True) if not options: @@ -183,9 +203,7 @@ class OptionsDialog(object): selected_iter = None for account in accounts: acc_iter = self.accounts.append() - self.accounts.set_value( - acc_iter, 0, account['username'], - ) + self.accounts.set_value(acc_iter, 0, account['username']) if account['username'] == owner: selected_iter = acc_iter self.builder.get_object('OwnerCombobox').set_active_iter(selected_iter) @@ -219,7 +237,7 @@ class OptionsDialog(object): client.core.get_enabled_plugins().addCallback(on_get_enabled_plugins) if client.get_auth_level() == deluge.common.AUTH_LEVEL_ADMIN: client.core.get_known_accounts().addCallback( - on_accounts, options.get('owner', client.get_auth_user()), + on_accounts, options.get('owner', client.get_auth_user()) ).addErrback(on_accounts_failure) else: acc_iter = self.accounts.append() @@ -229,11 +247,19 @@ class OptionsDialog(object): def set_sensitive(self): maintoggles = [ - 'download_location', 'append_extension', - 'move_completed', 'label', 'max_download_speed', - 'max_upload_speed', 'max_connections', - 'max_upload_slots', 'add_paused', 'auto_managed', - 'stop_at_ratio', 'queue_to_top', 'copy_torrent', + 'download_location', + 'append_extension', + 'move_completed', + 'label', + 'max_download_speed', + 'max_upload_speed', + 'max_connections', + 'max_upload_slots', + 'add_paused', + 'auto_managed', + 'stop_at_ratio', + 'queue_to_top', + 'copy_torrent', ] for maintoggle in maintoggles: self.on_toggle_toggled(self.builder.get_object(maintoggle + '_toggle')) @@ -249,9 +275,13 @@ class OptionsDialog(object): elif toggle == 'copy_torrent': self.builder.get_object('copy_torrent_entry').set_sensitive(isactive) self.builder.get_object('copy_torrent_chooser').set_sensitive(isactive) - self.builder.get_object('delete_copy_torrent_toggle').set_sensitive(isactive) + self.builder.get_object('delete_copy_torrent_toggle').set_sensitive( + isactive + ) elif toggle == 'move_completed': - self.builder.get_object('move_completed_path_chooser').set_sensitive(isactive) + self.builder.get_object('move_completed_path_chooser').set_sensitive( + isactive + ) self.builder.get_object('move_completed_path_entry').set_sensitive(isactive) self.builder.get_object('move_completed').set_active(isactive) elif toggle == 'label': @@ -283,9 +313,9 @@ class OptionsDialog(object): def on_apply(self, event=None): try: options = self.generate_opts() - client.autoadd.set_options( - str(self.watchdir_id), options, - ).addCallbacks(self.on_added, self.on_error_show) + client.autoadd.set_options(str(self.watchdir_id), options).addCallbacks( + self.on_added, self.on_error_show + ) except IncompatibleOption as ex: dialogs.ErrorDialog(_('Incompatible Option'), str(ex), self.dialog).run() @@ -314,54 +344,72 @@ class OptionsDialog(object): if client.is_localhost(): options['path'] = self.builder.get_object('path_chooser').get_filename() options['download_location'] = self.builder.get_object( - 'download_location_chooser', + 'download_location_chooser' ).get_filename() options['move_completed_path'] = self.builder.get_object( - 'move_completed_path_chooser', + 'move_completed_path_chooser' ).get_filename() options['copy_torrent'] = self.builder.get_object( - 'copy_torrent_chooser', + 'copy_torrent_chooser' ).get_filename() else: options['path'] = self.builder.get_object('path_entry').get_text() options['download_location'] = self.builder.get_object( - 'download_location_entry', + 'download_location_entry' ).get_text() options['move_completed_path'] = self.builder.get_object( - 'move_completed_path_entry', + 'move_completed_path_entry' ).get_text() options['copy_torrent'] = self.builder.get_object( - 'copy_torrent_entry', + 'copy_torrent_entry' ).get_text() options['label'] = self.builder.get_object('label').child.get_text().lower() - options['append_extension'] = self.builder.get_object('append_extension').get_text() + options['append_extension'] = self.builder.get_object( + 'append_extension' + ).get_text() options['owner'] = self.accounts[ self.builder.get_object('OwnerCombobox').get_active() ][0] for key in [ - 'append_extension_toggle', 'download_location_toggle', - 'label_toggle', 'copy_torrent_toggle', - 'delete_copy_torrent_toggle', 'seed_mode', + 'append_extension_toggle', + 'download_location_toggle', + 'label_toggle', + 'copy_torrent_toggle', + 'delete_copy_torrent_toggle', + 'seed_mode', ]: options[key] = self.builder.get_object(key).get_active() for spin_id in self.spin_ids: options[spin_id] = self.builder.get_object(spin_id).get_value() - options[spin_id + '_toggle'] = self.builder.get_object(spin_id + '_toggle').get_active() + options[spin_id + '_toggle'] = self.builder.get_object( + spin_id + '_toggle' + ).get_active() for spin_int_id in self.spin_int_ids: - options[spin_int_id] = self.builder.get_object(spin_int_id).get_value_as_int() - options[spin_int_id + '_toggle'] = self.builder.get_object(spin_int_id + '_toggle').get_active() + options[spin_int_id] = self.builder.get_object( + spin_int_id + ).get_value_as_int() + options[spin_int_id + '_toggle'] = self.builder.get_object( + spin_int_id + '_toggle' + ).get_active() for chk_id in self.chk_ids: options[chk_id] = self.builder.get_object(chk_id).get_active() - options[chk_id + '_toggle'] = self.builder.get_object(chk_id + '_toggle').get_active() - - if options['copy_torrent_toggle'] and options['path'] == options['copy_torrent']: - raise IncompatibleOption(_( - '"Watch Folder" directory and "Copy of .torrent' - ' files to" directory cannot be the same!', - )) + options[chk_id + '_toggle'] = self.builder.get_object( + chk_id + '_toggle' + ).get_active() + + if ( + options['copy_torrent_toggle'] + and options['path'] == options['copy_torrent'] + ): + raise IncompatibleOption( + _( + '"Watch Folder" directory and "Copy of .torrent' + ' files to" directory cannot be the same!' + ) + ) return options @@ -374,13 +422,13 @@ class GtkUI(GtkPluginBase): self.opts_dialog = OptionsDialog() component.get('PluginManager').register_hook( - 'on_apply_prefs', self.on_apply_prefs, + 'on_apply_prefs', self.on_apply_prefs ) component.get('PluginManager').register_hook( - 'on_show_prefs', self.on_show_prefs, + 'on_show_prefs', self.on_show_prefs ) client.register_event_handler( - 'AutoaddOptionsChangedEvent', self.on_options_changed_event, + 'AutoaddOptionsChangedEvent', self.on_options_changed_event ) self.watchdirs = {} @@ -403,31 +451,35 @@ class GtkUI(GtkPluginBase): sw.add(self.treeView) sw.show_all() component.get('Preferences').add_page( - _('AutoAdd'), self.builder.get_object('prefs_box'), + _('AutoAdd'), self.builder.get_object('prefs_box') ) def disable(self): component.get('Preferences').remove_page(_('AutoAdd')) component.get('PluginManager').deregister_hook( - 'on_apply_prefs', self.on_apply_prefs, + 'on_apply_prefs', self.on_apply_prefs ) component.get('PluginManager').deregister_hook( - 'on_show_prefs', self.on_show_prefs, + 'on_show_prefs', self.on_show_prefs ) def create_model(self): store = gtk.ListStore(str, bool, str, str) for watchdir_id, watchdir in self.watchdirs.items(): - store.append([ - watchdir_id, watchdir['enabled'], - watchdir.get('owner', 'localclient'), watchdir['path'], - ]) + store.append( + [ + watchdir_id, + watchdir['enabled'], + watchdir.get('owner', 'localclient'), + watchdir['path'], + ] + ) return store def create_columns(self, treeview): renderer_toggle = gtk.CellRendererToggle() column = gtk.TreeViewColumn( - _('Active'), renderer_toggle, activatable=1, active=1, + _('Active'), renderer_toggle, activatable=1, active=1 ) column.set_sort_column_id(1) treeview.append_column(column) @@ -505,10 +557,14 @@ class GtkUI(GtkPluginBase): self.watchdirs = watchdirs or {} self.store.clear() for watchdir_id, watchdir in self.watchdirs.items(): - self.store.append([ - watchdir_id, watchdir['enabled'], - watchdir.get('owner', 'localclient'), watchdir['path'], - ]) + self.store.append( + [ + watchdir_id, + watchdir['enabled'], + watchdir.get('owner', 'localclient'), + watchdir['path'], + ] + ) # Workaround for cached glade signal appearing when re-enabling plugin in same session if self.builder.get_object('edit_button'): # Disable the remove and edit buttons, because nothing in the store is selected diff --git a/deluge/plugins/AutoAdd/setup.py b/deluge/plugins/AutoAdd/setup.py index 77ca16267..206ca7fe4 100644 --- a/deluge/plugins/AutoAdd/setup.py +++ b/deluge/plugins/AutoAdd/setup.py @@ -37,7 +37,6 @@ setup( packages=find_packages(), namespace_packages=['deluge', 'deluge.plugins'], package_data=__pkg_data__, - entry_points=""" [deluge.plugin.core] %s = deluge.plugins.%s:CorePlugin @@ -45,5 +44,6 @@ setup( %s = deluge.plugins.%s:GtkUIPlugin [deluge.plugin.web] %s = deluge.plugins.%s:WebUIPlugin - """ % ((__plugin_name__, __plugin_name__.lower()) * 3), + """ + % ((__plugin_name__, __plugin_name__.lower()) * 3), ) diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/__init__.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/__init__.py index bc9a8d82a..96ccc02ae 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/__init__.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/__init__.py @@ -15,6 +15,7 @@ from deluge.plugins.init import PluginInitBase class CorePlugin(PluginInitBase): def __init__(self, plugin_name): from .core import Core as _pluginCls + self._plugin_cls = _pluginCls super(CorePlugin, self).__init__(plugin_name) @@ -22,6 +23,7 @@ class CorePlugin(PluginInitBase): class GtkUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .gtkui import GtkUI as _pluginCls + self._plugin_cls = _pluginCls super(GtkUIPlugin, self).__init__(plugin_name) @@ -29,5 +31,6 @@ class GtkUIPlugin(PluginInitBase): class WebUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .webui import WebUI as _pluginCls + self._plugin_cls = _pluginCls super(WebUIPlugin, self).__init__(plugin_name) diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/common.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/common.py index 1d4fe9ae6..d42d90d2f 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/common.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/common.py @@ -31,8 +31,10 @@ def raises_errors_as(error): function to raise all exceptions as the specified error type. """ + def decorator(func): """Returns a function which wraps the given func to raise all exceptions as error.""" + @wraps(func) def wrapper(self, *args, **kwargs): """Wraps the function in a try..except block and calls it with the specified args. @@ -46,7 +48,9 @@ def raises_errors_as(error): except Exception: (value, tb) = exc_info()[1:] six.reraise(error, value, tb) + return wrapper + return decorator @@ -117,37 +121,37 @@ class IP(object): def quadrants(self): return (self.q1, self.q2, self.q3, self.q4) -# def next_ip(self): -# (q1, q2, q3, q4) = self.quadrants() -# if q4 >= 255: -# if q3 >= 255: -# if q2 >= 255: -# if q1 >= 255: -# raise BadIP(_('There is not a next IP address')) -# q1 += 1 -# else: -# q2 += 1 -# else: -# q3 += 1 -# else: -# q4 += 1 -# return IP(q1, q2, q3, q4) -# -# def previous_ip(self): -# (q1, q2, q3, q4) = self.quadrants() -# if q4 <= 1: -# if q3 <= 1: -# if q2 <= 1: -# if q1 <= 1: -# raise BadIP(_('There is not a previous IP address')) -# q1 -= 1 -# else: -# q2 -= 1 -# else: -# q3 -= 1 -# else: -# q4 -= 1 -# return IP(q1, q2, q3, q4) + # def next_ip(self): + # (q1, q2, q3, q4) = self.quadrants() + # if q4 >= 255: + # if q3 >= 255: + # if q2 >= 255: + # if q1 >= 255: + # raise BadIP(_('There is not a next IP address')) + # q1 += 1 + # else: + # q2 += 1 + # else: + # q3 += 1 + # else: + # q4 += 1 + # return IP(q1, q2, q3, q4) + # + # def previous_ip(self): + # (q1, q2, q3, q4) = self.quadrants() + # if q4 <= 1: + # if q3 <= 1: + # if q2 <= 1: + # if q1 <= 1: + # raise BadIP(_('There is not a previous IP address')) + # q1 -= 1 + # else: + # q2 -= 1 + # else: + # q3 -= 1 + # else: + # q4 -= 1 + # return IP(q1, q2, q3, q4) def __lt__(self, other): if isinstance(other, ''.__class__): @@ -166,5 +170,7 @@ class IP(object): def __repr__(self): return '<%s long=%s address="%s">' % ( - self.__class__.__name__, self.long, self.address, + self.__class__.__name__, + self.long, + self.address, ) diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/core.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/core.py index 18d0d036b..a096b8ac9 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/core.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/core.py @@ -76,11 +76,15 @@ class Core(CorePluginBase): self.file_progress = 0.0 self.core = component.get('Core') - self.config = deluge.configmanager.ConfigManager('blocklist.conf', DEFAULT_PREFS) + self.config = deluge.configmanager.ConfigManager( + 'blocklist.conf', DEFAULT_PREFS + ) if 'whitelisted' not in self.config: self.config['whitelisted'] = [] - self.reader = create_reader(self.config['list_type'], self.config['list_compression']) + self.reader = create_reader( + self.config['list_type'], self.config['list_compression'] + ) if not isinstance(self.config['last_update'], float): self.config.config['last_update'] = 0.0 @@ -91,10 +95,15 @@ class Core(CorePluginBase): if self.config['last_update']: last_update = datetime.fromtimestamp(self.config['last_update']) check_period = timedelta(days=self.config['check_after_days']) - if not self.config['last_update'] or last_update + check_period < datetime.now(): + if ( + not self.config['last_update'] + or last_update + check_period < datetime.now() + ): update_now = True else: - d = self.import_list(deluge.configmanager.get_config_dir('blocklist.cache')) + d = self.import_list( + deluge.configmanager.get_config_dir('blocklist.cache') + ) d.addCallbacks(self.on_import_complete, self.on_import_error) if self.need_to_resume_session: d.addBoth(self.resume_session) @@ -104,14 +113,14 @@ class Core(CorePluginBase): self.update_timer = LoopingCall(self.check_import) if self.config['check_after_days'] > 0: self.update_timer.start( - self.config['check_after_days'] * 24 * 60 * 60, update_now, + self.config['check_after_days'] * 24 * 60 * 60, update_now ) def disable(self): self.config.save() log.debug('Reset IP filter') self.core.session.get_ip_filter().add_rule( - '0.0.0.0', '255.255.255.255', ALLOW_RANGE, + '0.0.0.0', '255.255.255.255', ALLOW_RANGE ) log.debug('Blocklist: Plugin disabled') @@ -189,7 +198,7 @@ class Core(CorePluginBase): try: ip = IP.parse(ip) self.blocklist.add_rule( - ip.address, ip.address, ALLOW_RANGE, + ip.address, ip.address, ALLOW_RANGE ) saved.add(ip.address) log.debug('Added %s to whitelisted', ip) @@ -217,13 +226,16 @@ class Core(CorePluginBase): if self.config['last_update']: last_update = datetime.fromtimestamp(self.config['last_update']) check_period = timedelta(days=self.config['check_after_days']) - if not self.config['last_update'] or last_update + check_period < datetime.now(): + if ( + not self.config['last_update'] + or last_update + check_period < datetime.now() + ): update_now = True if self.update_timer.running: self.update_timer.stop() if self.config['check_after_days'] > 0: self.update_timer.start( - self.config['check_after_days'] * 24 * 60 * 60, update_now, + self.config['check_after_days'] * 24 * 60 * 60, update_now ) continue self.config[key] = config[key] @@ -232,7 +244,7 @@ class Core(CorePluginBase): log.debug( 'IP addresses were removed from the whitelist. Since we ' 'do not know if they were blocked before. Re-import ' - 'current blocklist and re-add whitelisted.', + 'current blocklist and re-add whitelisted.' ) self.has_imported = False d = self.import_list(deluge.configmanager.get_config_dir('blocklist.cache')) @@ -295,6 +307,7 @@ class Core(CorePluginBase): Deferred: a Deferred which fires once the blocklist has been downloaded. """ + def on_retrieve_data(data, current_length, total_length): if total_length: fp = current_length / total_length @@ -306,6 +319,7 @@ class Core(CorePluginBase): self.file_progress = fp import socket + socket.setdefaulttimeout(self.config['timeout']) if not url: @@ -313,14 +327,18 @@ class Core(CorePluginBase): headers = {} if self.config['last_update'] and not self.force_download: - headers['If-Modified-Since'] = formatdate(self.config['last_update'], usegmt=True) + headers['If-Modified-Since'] = formatdate( + self.config['last_update'], usegmt=True + ) log.debug('Attempting to download blocklist %s', url) log.debug('Sending headers: %s', headers) self.is_downloading = True return download_file( - url, deluge.configmanager.get_config_dir('blocklist.download'), - on_retrieve_data, headers, + url, + deluge.configmanager.get_config_dir('blocklist.download'), + on_retrieve_data, + headers, ) def on_download_complete(self, blocklist): @@ -369,7 +387,8 @@ class Core(CorePluginBase): if self.failed_attempts < self.config['try_times']: log.debug( 'Try downloading blocklist again... (%s/%s)', - self.failed_attempts, self.config['try_times'], + self.failed_attempts, + self.config['try_times'], ) self.failed_attempts += 1 d = self.download_list() @@ -430,7 +449,11 @@ class Core(CorePluginBase): log.exception(failure) log.debug('Importing using reader: %s', self.reader) - log.debug('Reader type: %s compression: %s', self.config['list_type'], self.config['list_compression']) + log.debug( + 'Reader type: %s compression: %s', + self.config['list_type'], + self.config['list_compression'], + ) log.debug('Clearing current ip filtering') # self.blocklist.add_rule('0.0.0.0', '255.255.255.255', ALLOW_RANGE) d = threads.deferToThread(self.reader(blocklist).read, on_read_ip_range) @@ -508,13 +531,21 @@ class Core(CorePluginBase): """ self.config['list_compression'] = detect_compression(blocklist) - self.config['list_type'] = detect_format(blocklist, self.config['list_compression']) - log.debug('Auto-detected type: %s compression: %s', self.config['list_type'], self.config['list_compression']) + self.config['list_type'] = detect_format( + blocklist, self.config['list_compression'] + ) + log.debug( + 'Auto-detected type: %s compression: %s', + self.config['list_type'], + self.config['list_compression'], + ) if not self.config['list_type']: self.config['list_compression'] = '' raise UnknownFormatError else: - self.reader = create_reader(self.config['list_type'], self.config['list_compression']) + self.reader = create_reader( + self.config['list_type'], self.config['list_compression'] + ) def pause_session(self): self.need_to_resume_session = not self.core.session.is_paused() diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/decompressers.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/decompressers.py index 8b9a0f4fb..35211b706 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/decompressers.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/decompressers.py @@ -17,25 +17,31 @@ import zipfile def Zipped(reader): # NOQA: N802 """Blocklist reader for zipped blocklists""" + def _open(self): z = zipfile.ZipFile(self.file) f = z.open(z.namelist()[0]) return f + reader.open = _open return reader def GZipped(reader): # NOQA: N802 """Blocklist reader for gzipped blocklists""" + def _open(self): return gzip.open(self.file) + reader.open = _open return reader def BZipped2(reader): # NOQA: N802 """Blocklist reader for bzipped2 blocklists""" + def _open(self): return bz2.BZ2File(self.file) + reader.open = _open return reader diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/detect.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/detect.py index 1d8b5d864..9ed960adb 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/detect.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/detect.py @@ -12,17 +12,9 @@ from __future__ import unicode_literals from .decompressers import BZipped2, GZipped, Zipped from .readers import EmuleReader, PeerGuardianReader, SafePeerReader -COMPRESSION_TYPES = { - 'PK': 'Zip', - '\x1f\x8b': 'GZip', - 'BZ': 'BZip2', -} +COMPRESSION_TYPES = {'PK': 'Zip', '\x1f\x8b': 'GZip', 'BZ': 'BZip2'} -DECOMPRESSERS = { - 'Zip': Zipped, - 'GZip': GZipped, - 'BZip2': BZipped2, -} +DECOMPRESSERS = {'Zip': Zipped, 'GZip': GZipped, 'BZip2': BZipped2} READERS = { 'Emule': EmuleReader, diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/gtkui.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/gtkui.py index 3d5df6c41..ad73a45e6 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/gtkui.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/gtkui.py @@ -67,9 +67,11 @@ class GtkUI(GtkPluginBase): self.builder.get_object('image_up_to_date').hide() self.status_item.set_text( - 'Downloading %.2f%%' % (status['file_progress'] * 100), + 'Downloading %.2f%%' % (status['file_progress'] * 100) + ) + self.progress_bar.set_text( + 'Downloading %.2f%%' % (status['file_progress'] * 100) ) - self.progress_bar.set_text('Downloading %.2f%%' % (status['file_progress'] * 100)) self.progress_bar.set_fraction(status['file_progress']) self.progress_bar.show() @@ -79,9 +81,7 @@ class GtkUI(GtkPluginBase): self.builder.get_object('button_force_download').set_sensitive(False) self.builder.get_object('image_up_to_date').hide() - self.status_item.set_text( - 'Importing ' + str(status['num_blocked']), - ) + self.status_item.set_text('Importing ' + str(status['num_blocked'])) self.progress_bar.set_text('Importing %s' % (status['num_blocked'])) self.progress_bar.pulse() self.progress_bar.show() @@ -99,15 +99,13 @@ class GtkUI(GtkPluginBase): self.status_item.set_text('%(num_blocked)s/%(num_whited)s' % status) self.builder.get_object('label_filesize').set_text( - deluge.common.fsize(status['file_size']), + deluge.common.fsize(status['file_size']) ) self.builder.get_object('label_modified').set_text( - datetime.fromtimestamp(status['file_date']).strftime('%c'), + datetime.fromtimestamp(status['file_date']).strftime('%c') ) self.builder.get_object('label_type').set_text(status['file_type']) - self.builder.get_object('label_url').set_text( - status['file_url'], - ) + self.builder.get_object('label_url').set_text(status['file_url']) client.blocklist.get_status().addCallback(_on_get_status) @@ -115,8 +113,12 @@ class GtkUI(GtkPluginBase): def _on_get_config(config): log.trace('Loaded config: %s', config) self.builder.get_object('entry_url').set_text(config['url']) - self.builder.get_object('spin_check_days').set_value(config['check_after_days']) - self.builder.get_object('chk_import_on_start').set_active(config['load_on_start']) + self.builder.get_object('spin_check_days').set_value( + config['check_after_days'] + ) + self.builder.get_object('chk_import_on_start').set_active( + config['load_on_start'] + ) self.populate_whitelist(config['whitelisted']) client.blocklist.get_config().addCallback(_on_get_config) @@ -124,9 +126,15 @@ class GtkUI(GtkPluginBase): def _on_apply_prefs(self): config = {} config['url'] = self.builder.get_object('entry_url').get_text().strip() - config['check_after_days'] = self.builder.get_object('spin_check_days').get_value_as_int() - config['load_on_start'] = self.builder.get_object('chk_import_on_start').get_active() - config['whitelisted'] = [ip[0] for ip in self.whitelist_model if ip[0] != 'IP HERE'] + config['check_after_days'] = self.builder.get_object( + 'spin_check_days' + ).get_value_as_int() + config['load_on_start'] = self.builder.get_object( + 'chk_import_on_start' + ).get_active() + config['whitelisted'] = [ + ip[0] for ip in self.whitelist_model if ip[0] != 'IP HERE' + ] client.blocklist.set_config(config) def _on_button_check_download_clicked(self, widget): @@ -157,26 +165,28 @@ class GtkUI(GtkPluginBase): # Create the whitelisted model self.build_whitelist_model_treeview() - self.builder.connect_signals({ - 'on_button_check_download_clicked': self._on_button_check_download_clicked, - 'on_button_force_download_clicked': self._on_button_force_download_clicked, - 'on_whitelist_add_clicked': ( - self.on_add_button_clicked, - self.whitelist_treeview, - ), - 'on_whitelist_remove_clicked': ( - self.on_delete_button_clicked, - self.whitelist_treeview, - ), - }) + self.builder.connect_signals( + { + 'on_button_check_download_clicked': self._on_button_check_download_clicked, + 'on_button_force_download_clicked': self._on_button_force_download_clicked, + 'on_whitelist_add_clicked': ( + self.on_add_button_clicked, + self.whitelist_treeview, + ), + 'on_whitelist_remove_clicked': ( + self.on_delete_button_clicked, + self.whitelist_treeview, + ), + } + ) # Set button icons self.builder.get_object('image_download').set_from_file( - common.get_resource('blocklist_download24.png'), + common.get_resource('blocklist_download24.png') ) self.builder.get_object('image_import').set_from_file( - common.get_resource('blocklist_import24.png'), + common.get_resource('blocklist_import24.png') ) # Update the preferences page with config values from the core @@ -184,15 +194,14 @@ class GtkUI(GtkPluginBase): # Add the page to the preferences dialog self.plugin.add_preferences_page( - _('Blocklist'), - self.builder.get_object('blocklist_prefs_box'), + _('Blocklist'), self.builder.get_object('blocklist_prefs_box') ) def build_whitelist_model_treeview(self): self.whitelist_treeview = self.builder.get_object('whitelist_treeview') treeview_selection = self.whitelist_treeview.get_selection() treeview_selection.connect( - 'changed', self.on_whitelist_treeview_selection_changed, + 'changed', self.on_whitelist_treeview_selection_changed ) self.whitelist_model = gtk.ListStore(str, bool) renderer = gtk.CellRendererText() @@ -213,21 +222,16 @@ class GtkUI(GtkPluginBase): except common.BadIP as ex: model.remove(model.get_iter_from_string(path_string)) from deluge.ui.gtkui import dialogs + d = dialogs.ErrorDialog(_('Bad IP address'), ex.message) d.run() def on_whitelist_treeview_selection_changed(self, selection): model, selected_connection_iter = selection.get_selected() if selected_connection_iter: - self.builder.get_object('whitelist_delete').set_property( - 'sensitive', - True, - ) + self.builder.get_object('whitelist_delete').set_property('sensitive', True) else: - self.builder.get_object('whitelist_delete').set_property( - 'sensitive', - False, - ) + self.builder.get_object('whitelist_delete').set_property('sensitive', False) def on_add_button_clicked(self, widget, treeview): model = treeview.get_model() @@ -243,6 +247,4 @@ class GtkUI(GtkPluginBase): def populate_whitelist(self, whitelist): self.whitelist_model.clear() for ip in whitelist: - self.whitelist_model.set( - self.whitelist_model.append(), 0, ip, 1, True, - ) + self.whitelist_model.set(self.whitelist_model.append(), 0, ip, 1, True) diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/peerguardian.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/peerguardian.py index cf5675ca8..ba410c2cf 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/peerguardian.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/peerguardian.py @@ -24,7 +24,6 @@ class PGException(Exception): # Incrementally reads PeerGuardian blocklists v1 and v2. # See http://wiki.phoenixlabs.org/wiki/P2B_Format class PGReader(object): - def __init__(self, filename): log.debug('PGReader loading: %s', filename) diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/readers.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/readers.py index 2a57efff5..4079e849e 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/readers.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/readers.py @@ -25,6 +25,7 @@ class ReaderParseError(Exception): class BaseReader(object): """Base reader for blocklist files""" + def __init__(self, _file): """Creates a new BaseReader given a file""" self.file = _file @@ -60,8 +61,9 @@ class BaseReader(object): if not self.is_ignored(line): try: (start, end) = self.parse(line) - if not re.match(r'^(\d{1,3}\.){4}$', start + '.') or \ - not re.match(r'^(\d{1,3}\.){4}$', end + '.'): + if not re.match(r'^(\d{1,3}\.){4}$', start + '.') or not re.match( + r'^(\d{1,3}\.){4}$', end + '.' + ): valid = False except Exception: valid = False @@ -82,16 +84,19 @@ class BaseReader(object): class EmuleReader(BaseReader): """Blocklist reader for emule style blocklists""" + def parse(self, line): return line.strip().split(' , ')[0].split(' - ') class SafePeerReader(BaseReader): """Blocklist reader for SafePeer style blocklists""" + def parse(self, line): return line.strip().split(':')[-1].split('-') class PeerGuardianReader(SafePeerReader): """Blocklist reader for PeerGuardian style blocklists""" + pass diff --git a/deluge/plugins/Blocklist/setup.py b/deluge/plugins/Blocklist/setup.py index 03e2e189d..6d7f01253 100644 --- a/deluge/plugins/Blocklist/setup.py +++ b/deluge/plugins/Blocklist/setup.py @@ -39,5 +39,6 @@ setup( %s = deluge.plugins.%s:GtkUIPlugin [deluge.plugin.web] %s = deluge.plugins.%s:WebUIPlugin - """ % ((__plugin_name__, __plugin_name__.lower()) * 3), + """ + % ((__plugin_name__, __plugin_name__.lower()) * 3), ) diff --git a/deluge/plugins/Execute/deluge/plugins/execute/__init__.py b/deluge/plugins/Execute/deluge/plugins/execute/__init__.py index f7854eeb7..c6d55f4ec 100644 --- a/deluge/plugins/Execute/deluge/plugins/execute/__init__.py +++ b/deluge/plugins/Execute/deluge/plugins/execute/__init__.py @@ -15,6 +15,7 @@ from deluge.plugins.init import PluginInitBase class CorePlugin(PluginInitBase): def __init__(self, plugin_name): from .core import Core as _pluginCls + self._plugin_cls = _pluginCls super(CorePlugin, self).__init__(plugin_name) @@ -22,6 +23,7 @@ class CorePlugin(PluginInitBase): class GtkUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .gtkui import GtkUI as _pluginCls + self._plugin_cls = _pluginCls super(GtkUIPlugin, self).__init__(plugin_name) @@ -29,5 +31,6 @@ class GtkUIPlugin(PluginInitBase): class WebUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .webui import WebUI as _pluginCls + self._plugin_cls = _pluginCls super(WebUIPlugin, self).__init__(plugin_name) diff --git a/deluge/plugins/Execute/deluge/plugins/execute/core.py b/deluge/plugins/Execute/deluge/plugins/execute/core.py index ed35a32bf..c7824e5f0 100644 --- a/deluge/plugins/Execute/deluge/plugins/execute/core.py +++ b/deluge/plugins/Execute/deluge/plugins/execute/core.py @@ -25,9 +25,7 @@ from deluge.plugins.pluginbase import CorePluginBase log = logging.getLogger(__name__) -DEFAULT_CONFIG = { - 'commands': [], -} +DEFAULT_CONFIG = {'commands': []} EXECUTE_ID = 0 EXECUTE_EVENT = 1 @@ -44,6 +42,7 @@ class ExecuteCommandAddedEvent(DelugeEvent): """ Emitted when a new command is added. """ + def __init__(self, command_id, event, command): self._args = [command_id, event, command] @@ -52,6 +51,7 @@ class ExecuteCommandRemovedEvent(DelugeEvent): """ Emitted when a command is removed. """ + def __init__(self, command_id): self._args = [command_id] @@ -72,11 +72,15 @@ class Core(CorePluginBase): def create_event_handler(event): def event_handler(torrent_id, *arg): self.execute_commands(torrent_id, event, *arg) + return event_handler + event_handler = create_event_handler(event) event_manager.register_event_handler(EVENT_MAP[event], event_handler) if event == 'removed': - event_manager.register_event_handler('PreTorrentRemovedEvent', self.on_preremoved) + event_manager.register_event_handler( + 'PreTorrentRemovedEvent', self.on_preremoved + ) self.registered_events[event] = event_handler log.debug('Execute core plugin enabled!') @@ -85,14 +89,20 @@ class Core(CorePluginBase): # Get and store the torrent info before it is removed torrent = component.get('TorrentManager').torrents[torrent_id] info = torrent.get_status(['name', 'download_location']) - self.preremoved_cache[torrent_id] = [torrent_id, info['name'], info['download_location']] + self.preremoved_cache[torrent_id] = [ + torrent_id, + info['name'], + info['download_location'], + ] def execute_commands(self, torrent_id, event, *arg): if event == 'added' and arg[0]: # No futher action as from_state (arg[0]) is True return elif event == 'removed': - torrent_id, torrent_name, download_location = self.preremoved_cache.pop(torrent_id) + torrent_id, torrent_name, download_location = self.preremoved_cache.pop( + torrent_id + ) else: torrent = component.get('TorrentManager').torrents[torrent_id] info = torrent.get_status(['name', 'download_location']) @@ -119,7 +129,8 @@ class Core(CorePluginBase): command = os.path.expanduser(command) cmd_args = [ - torrent_id.encode('utf8'), torrent_name.encode('utf8'), + torrent_id.encode('utf8'), + torrent_name.encode('utf8'), download_location.encode('utf8'), ] if windows_check(): @@ -146,7 +157,9 @@ class Core(CorePluginBase): command_id = hashlib.sha1(str(time.time())).hexdigest() self.config['commands'].append((command_id, event, command)) self.config.save() - component.get('EventManager').emit(ExecuteCommandAddedEvent(command_id, event, command)) + component.get('EventManager').emit( + ExecuteCommandAddedEvent(command_id, event, command) + ) @export def get_commands(self): @@ -157,7 +170,9 @@ class Core(CorePluginBase): for command in self.config['commands']: if command[EXECUTE_ID] == command_id: self.config['commands'].remove(command) - component.get('EventManager').emit(ExecuteCommandRemovedEvent(command_id)) + component.get('EventManager').emit( + ExecuteCommandRemovedEvent(command_id) + ) break self.config.save() diff --git a/deluge/plugins/Execute/deluge/plugins/execute/gtkui.py b/deluge/plugins/Execute/deluge/plugins/execute/gtkui.py index f74e72007..a2b4f5f68 100644 --- a/deluge/plugins/Execute/deluge/plugins/execute/gtkui.py +++ b/deluge/plugins/Execute/deluge/plugins/execute/gtkui.py @@ -55,14 +55,20 @@ class ExecutePreferences(object): events.set_model(store) events.set_active(0) - self.plugin.add_preferences_page(_('Execute'), self.builder.get_object('execute_box')) + self.plugin.add_preferences_page( + _('Execute'), self.builder.get_object('execute_box') + ) self.plugin.register_hook('on_show_prefs', self.load_commands) self.plugin.register_hook('on_apply_prefs', self.on_apply_prefs) self.load_commands() - client.register_event_handler('ExecuteCommandAddedEvent', self.on_command_added_event) - client.register_event_handler('ExecuteCommandRemovedEvent', self.on_command_removed_event) + client.register_event_handler( + 'ExecuteCommandAddedEvent', self.on_command_added_event + ) + client.register_event_handler( + 'ExecuteCommandRemovedEvent', self.on_command_removed_event + ) def unload(self): self.plugin.remove_preferences_page(_('Execute')) @@ -145,7 +151,6 @@ class ExecutePreferences(object): class GtkUI(GtkPluginBase): - def enable(self): self.plugin = component.get('PluginManager') self.preferences = ExecutePreferences(self.plugin) diff --git a/deluge/plugins/Execute/setup.py b/deluge/plugins/Execute/setup.py index 55d0ed56b..5bee45635 100644 --- a/deluge/plugins/Execute/setup.py +++ b/deluge/plugins/Execute/setup.py @@ -28,11 +28,9 @@ setup( url=__url__, license=__license__, long_description=__long_description__, - packages=find_packages(), namespace_packages=['deluge', 'deluge.plugins'], package_data=__pkg_data__, - entry_points=""" [deluge.plugin.core] %s = deluge.plugins.%s:CorePlugin @@ -40,5 +38,6 @@ setup( %s = deluge.plugins.%s:GtkUIPlugin [deluge.plugin.web] %s = deluge.plugins.%s:WebUIPlugin - """ % ((__plugin_name__, __plugin_name__.lower()) * 3), + """ + % ((__plugin_name__, __plugin_name__.lower()) * 3), ) diff --git a/deluge/plugins/Extractor/deluge/plugins/extractor/__init__.py b/deluge/plugins/Extractor/deluge/plugins/extractor/__init__.py index 7d3492d2c..6db72b63b 100644 --- a/deluge/plugins/Extractor/deluge/plugins/extractor/__init__.py +++ b/deluge/plugins/Extractor/deluge/plugins/extractor/__init__.py @@ -19,6 +19,7 @@ from deluge.plugins.init import PluginInitBase class CorePlugin(PluginInitBase): def __init__(self, plugin_name): from .core import Core as _pluginCls + self._plugin_cls = _pluginCls super(CorePlugin, self).__init__(plugin_name) @@ -26,6 +27,7 @@ class CorePlugin(PluginInitBase): class GtkUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .gtkui import GtkUI as _pluginCls + self._plugin_cls = _pluginCls super(GtkUIPlugin, self).__init__(plugin_name) @@ -33,5 +35,6 @@ class GtkUIPlugin(PluginInitBase): class WebUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .webui import WebUI as _pluginCls + self._plugin_cls = _pluginCls super(WebUIPlugin, self).__init__(plugin_name) diff --git a/deluge/plugins/Extractor/deluge/plugins/extractor/core.py b/deluge/plugins/Extractor/deluge/plugins/extractor/core.py index dbedb23e8..99d1b083a 100644 --- a/deluge/plugins/Extractor/deluge/plugins/extractor/core.py +++ b/deluge/plugins/Extractor/deluge/plugins/extractor/core.py @@ -28,10 +28,7 @@ from deluge.plugins.pluginbase import CorePluginBase log = logging.getLogger(__name__) -DEFAULT_PREFS = { - 'extract_path': '', - 'use_name_folder': True, -} +DEFAULT_PREFS = {'extract_path': '', 'use_name_folder': True} if windows_check(): win_7z_exes = [ @@ -61,10 +58,7 @@ if windows_check(): # ".tar.bz2", ".tbz", # ".tar.lzma", ".tlz", # ".tar.xz", ".txz", - exts_7z = [ - '.rar', '.zip', '.tar', - '.7z', '.xz', '.lzma', - ] + exts_7z = ['.rar', '.zip', '.tar', '.7z', '.xz', '.lzma'] for win_7z_exe in win_7z_exes: if which(win_7z_exe): EXTRACT_COMMANDS = dict.fromkeys(exts_7z, [win_7z_exe, switch_7z]) @@ -82,10 +76,14 @@ else: '.rar': ['unrar', 'x -o+ -y'], '.tar': ['tar', '-xf'], '.zip': ['unzip', ''], - '.tar.gz': ['tar', '-xzf'], '.tgz': ['tar', '-xzf'], - '.tar.bz2': ['tar', '-xjf'], '.tbz': ['tar', '-xjf'], - '.tar.lzma': ['tar', '--lzma -xf'], '.tlz': ['tar', '--lzma -xf'], - '.tar.xz': ['tar', '--xz -xf'], '.txz': ['tar', '--xz -xf'], + '.tar.gz': ['tar', '-xzf'], + '.tgz': ['tar', '-xzf'], + '.tar.bz2': ['tar', '-xjf'], + '.tbz': ['tar', '-xjf'], + '.tar.lzma': ['tar', '--lzma -xf'], + '.tlz': ['tar', '--lzma -xf'], + '.tar.xz': ['tar', '--xz -xf'], + '.txz': ['tar', '--xz -xf'], '.7z': ['7zr', 'x'], } # Test command exists and if not, remove. @@ -102,13 +100,21 @@ if not EXTRACT_COMMANDS: class Core(CorePluginBase): def enable(self): - self.config = deluge.configmanager.ConfigManager('extractor.conf', DEFAULT_PREFS) + self.config = deluge.configmanager.ConfigManager( + 'extractor.conf', DEFAULT_PREFS + ) if not self.config['extract_path']: - self.config['extract_path'] = deluge.configmanager.ConfigManager('core.conf')['download_location'] - component.get('EventManager').register_event_handler('TorrentFinishedEvent', self._on_torrent_finished) + self.config['extract_path'] = deluge.configmanager.ConfigManager( + 'core.conf' + )['download_location'] + component.get('EventManager').register_event_handler( + 'TorrentFinishedEvent', self._on_torrent_finished + ) def disable(self): - component.get('EventManager').deregister_event_handler('TorrentFinishedEvent', self._on_torrent_finished) + component.get('EventManager').deregister_event_handler( + 'TorrentFinishedEvent', self._on_torrent_finished + ) def update(self): pass @@ -136,7 +142,9 @@ class Core(CorePluginBase): continue cmd = EXTRACT_COMMANDS[file_ext] - fpath = os.path.join(tid_status['download_location'], os.path.normpath(f['path'])) + fpath = os.path.join( + tid_status['download_location'], os.path.normpath(f['path']) + ) dest = os.path.normpath(self.config['extract_path']) if self.config['use_name_folder']: dest = os.path.join(dest, tid_status['name']) @@ -153,11 +161,22 @@ class Core(CorePluginBase): if not result[2]: log.info('Extract successful: %s (%s)', fpath, torrent_id) else: - log.error('Extract failed: %s (%s) %s', fpath, torrent_id, result[1]) + log.error( + 'Extract failed: %s (%s) %s', fpath, torrent_id, result[1] + ) # Run the command and add callback. - log.debug('Extracting %s from %s with %s %s to %s', fpath, torrent_id, cmd[0], cmd[1], dest) - d = getProcessOutputAndValue(cmd[0], cmd[1].split() + [str(fpath)], os.environ, str(dest)) + log.debug( + 'Extracting %s from %s with %s %s to %s', + fpath, + torrent_id, + cmd[0], + cmd[1], + dest, + ) + d = getProcessOutputAndValue( + cmd[0], cmd[1].split() + [str(fpath)], os.environ, str(dest) + ) d.addCallback(on_extract, torrent_id, fpath) @export diff --git a/deluge/plugins/Extractor/deluge/plugins/extractor/gtkui.py b/deluge/plugins/Extractor/deluge/plugins/extractor/gtkui.py index 2adea4368..aaae29a93 100644 --- a/deluge/plugins/Extractor/deluge/plugins/extractor/gtkui.py +++ b/deluge/plugins/Extractor/deluge/plugins/extractor/gtkui.py @@ -31,15 +31,25 @@ class GtkUI(GtkPluginBase): self.builder = gtk.Builder() self.builder.add_from_file(get_resource('extractor_prefs.ui')) - component.get('Preferences').add_page(_('Extractor'), self.builder.get_object('extractor_prefs_box')) - component.get('PluginManager').register_hook('on_apply_prefs', self.on_apply_prefs) - component.get('PluginManager').register_hook('on_show_prefs', self.on_show_prefs) + component.get('Preferences').add_page( + _('Extractor'), self.builder.get_object('extractor_prefs_box') + ) + component.get('PluginManager').register_hook( + 'on_apply_prefs', self.on_apply_prefs + ) + component.get('PluginManager').register_hook( + 'on_show_prefs', self.on_show_prefs + ) self.on_show_prefs() def disable(self): component.get('Preferences').remove_page(_('Extractor')) - component.get('PluginManager').deregister_hook('on_apply_prefs', self.on_apply_prefs) - component.get('PluginManager').deregister_hook('on_show_prefs', self.on_show_prefs) + component.get('PluginManager').deregister_hook( + 'on_apply_prefs', self.on_apply_prefs + ) + component.get('PluginManager').deregister_hook( + 'on_show_prefs', self.on_show_prefs + ) del self.builder def on_apply_prefs(self): @@ -66,10 +76,14 @@ class GtkUI(GtkPluginBase): def on_get_config(config): if client.is_localhost(): - self.builder.get_object('folderchooser_path').set_current_folder(config['extract_path']) + self.builder.get_object('folderchooser_path').set_current_folder( + config['extract_path'] + ) else: self.builder.get_object('entry_path').set_text(config['extract_path']) - self.builder.get_object('chk_use_name').set_active(config['use_name_folder']) + self.builder.get_object('chk_use_name').set_active( + config['use_name_folder'] + ) client.extractor.get_config().addCallback(on_get_config) diff --git a/deluge/plugins/Extractor/setup.py b/deluge/plugins/Extractor/setup.py index 9be0652a7..7426c690a 100644 --- a/deluge/plugins/Extractor/setup.py +++ b/deluge/plugins/Extractor/setup.py @@ -41,11 +41,9 @@ setup( url=__url__, license=__license__, long_description=__long_description__ if __long_description__ else __description__, - packages=find_packages(), namespace_packages=['deluge', 'deluge.plugins'], package_data=__pkg_data__, - entry_points=""" [deluge.plugin.core] %s = deluge.plugins.%s:CorePlugin @@ -53,5 +51,6 @@ setup( %s = deluge.plugins.%s:GtkUIPlugin [deluge.plugin.web] %s = deluge.plugins.%s:WebUIPlugin - """ % ((__plugin_name__, __plugin_name__.lower()) * 3), + """ + % ((__plugin_name__, __plugin_name__.lower()) * 3), ) diff --git a/deluge/plugins/Label/deluge/plugins/label/__init__.py b/deluge/plugins/Label/deluge/plugins/label/__init__.py index 363d49d42..bc0b0f243 100644 --- a/deluge/plugins/Label/deluge/plugins/label/__init__.py +++ b/deluge/plugins/Label/deluge/plugins/label/__init__.py @@ -19,6 +19,7 @@ from deluge.plugins.init import PluginInitBase class CorePlugin(PluginInitBase): def __init__(self, plugin_name): from .core import Core as _pluginCls + self._plugin_cls = _pluginCls super(CorePlugin, self).__init__(plugin_name) @@ -26,6 +27,7 @@ class CorePlugin(PluginInitBase): class GtkUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .gtkui import GtkUI as _pluginCls + self._plugin_cls = _pluginCls super(GtkUIPlugin, self).__init__(plugin_name) @@ -33,5 +35,6 @@ class GtkUIPlugin(PluginInitBase): class WebUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .webui import WebUI as _pluginCls + self._plugin_cls = _pluginCls super(WebUIPlugin, self).__init__(plugin_name) diff --git a/deluge/plugins/Label/deluge/plugins/label/core.py b/deluge/plugins/Label/deluge/plugins/label/core.py index 4a1976edd..b16156c91 100644 --- a/deluge/plugins/Label/deluge/plugins/label/core.py +++ b/deluge/plugins/Label/deluge/plugins/label/core.py @@ -73,6 +73,7 @@ class Core(CorePluginBase): self.labels = {label_id:label_options_dict} self.torrent_labels = {torrent_id:label_id} """ + def enable(self): log.info('*** Start Label plugin ***') self.plugin = component.get('CorePluginManager') @@ -90,19 +91,29 @@ class Core(CorePluginBase): self.clean_initial_config() - component.get('EventManager').register_event_handler('TorrentAddedEvent', self.post_torrent_add) - component.get('EventManager').register_event_handler('TorrentRemovedEvent', self.post_torrent_remove) + component.get('EventManager').register_event_handler( + 'TorrentAddedEvent', self.post_torrent_add + ) + component.get('EventManager').register_event_handler( + 'TorrentRemovedEvent', self.post_torrent_remove + ) # register tree: - component.get('FilterManager').register_tree_field('label', self.init_filter_dict) + component.get('FilterManager').register_tree_field( + 'label', self.init_filter_dict + ) log.debug('Label plugin enabled..') def disable(self): self.plugin.deregister_status_field('label') component.get('FilterManager').deregister_tree_field('label') - component.get('EventManager').deregister_event_handler('TorrentAddedEvent', self.post_torrent_add) - component.get('EventManager').deregister_event_handler('TorrentRemovedEvent', self.post_torrent_remove) + component.get('EventManager').deregister_event_handler( + 'TorrentAddedEvent', self.post_torrent_add + ) + component.get('EventManager').deregister_event_handler( + 'TorrentRemovedEvent', self.post_torrent_remove + ) def update(self): pass @@ -169,7 +180,9 @@ class Core(CorePluginBase): see label_set_options for more options. """ label_id = label_id.lower() - check_input(RE_VALID.match(label_id), _('Invalid label, valid characters:[a-z0-9_-]')) + check_input( + RE_VALID.match(label_id), _('Invalid label, valid characters:[a-z0-9_-]') + ) check_input(label_id, _('Empty Label')) check_input(not (label_id in self.labels), _('Label already exists')) @@ -209,7 +222,7 @@ class Core(CorePluginBase): { 'move_completed': options['move_completed'], 'move_completed_path': options['move_completed_path'], - }, + } ) def _unset_torrent_options(self, torrent_id, label_id): @@ -217,11 +230,21 @@ class Core(CorePluginBase): torrent = self.torrents[torrent_id] if options['apply_max']: - torrent.set_max_download_speed(self.core_cfg.config['max_download_speed_per_torrent']) - torrent.set_max_upload_speed(self.core_cfg.config['max_upload_speed_per_torrent']) - torrent.set_max_connections(self.core_cfg.config['max_connections_per_torrent']) - torrent.set_max_upload_slots(self.core_cfg.config['max_upload_slots_per_torrent']) - torrent.set_prioritize_first_last_pieces(self.core_cfg.config['prioritize_first_last_pieces']) + torrent.set_max_download_speed( + self.core_cfg.config['max_download_speed_per_torrent'] + ) + torrent.set_max_upload_speed( + self.core_cfg.config['max_upload_speed_per_torrent'] + ) + torrent.set_max_connections( + self.core_cfg.config['max_connections_per_torrent'] + ) + torrent.set_max_upload_slots( + self.core_cfg.config['max_upload_slots_per_torrent'] + ) + torrent.set_prioritize_first_last_pieces( + self.core_cfg.config['prioritize_first_last_pieces'] + ) if options['apply_queue']: torrent.set_auto_managed(self.core_cfg.config['auto_managed']) @@ -234,7 +257,7 @@ class Core(CorePluginBase): { 'move_completed': self.core_cfg.config['move_completed'], 'move_completed_path': self.core_cfg.config['move_completed_path'], - }, + } ) def _has_auto_match(self, torrent, label_options): @@ -311,8 +334,7 @@ class Core(CorePluginBase): def get_config(self): """see : label_set_config""" return { - key: self.config[key] - for key in CORE_OPTIONS if key in self.config.config + key: self.config[key] for key in CORE_OPTIONS if key in self.config.config } @export diff --git a/deluge/plugins/Label/deluge/plugins/label/gtkui/label_config.py b/deluge/plugins/Label/deluge/plugins/label/gtkui/label_config.py index f5a03d564..507be5afa 100644 --- a/deluge/plugins/Label/deluge/plugins/label/gtkui/label_config.py +++ b/deluge/plugins/Label/deluge/plugins/label/gtkui/label_config.py @@ -33,7 +33,9 @@ class LabelConfig(object): builder = Builder() builder.add_from_file(get_resource('label_pref.ui')) - self.plugin.add_preferences_page(_('Label'), builder.get_object('label_prefs_box')) + self.plugin.add_preferences_page( + _('Label'), builder.get_object('label_prefs_box') + ) self.plugin.register_hook('on_show_prefs', self.load_settings) self.plugin.register_hook('on_apply_prefs', self.on_apply_prefs) diff --git a/deluge/plugins/Label/deluge/plugins/label/gtkui/sidebar_menu.py b/deluge/plugins/Label/deluge/plugins/label/gtkui/sidebar_menu.py index cdb298ccb..d143c7a7e 100644 --- a/deluge/plugins/Label/deluge/plugins/label/gtkui/sidebar_menu.py +++ b/deluge/plugins/Label/deluge/plugins/label/gtkui/sidebar_menu.py @@ -80,7 +80,7 @@ class LabelSidebarMenu(object): for item in self.items: item.show() # default items - sensitive = ((label not in (NO_LABEL, None, '', 'All')) and (cat != 'cat')) + sensitive = (label not in (NO_LABEL, None, '', 'All')) and (cat != 'cat') for item in self.items: item.set_sensitive(sensitive) @@ -127,13 +127,28 @@ class OptionsDialog(object): spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio'] spin_int_ids = ['max_upload_slots', 'max_connections'] chk_ids = [ - 'apply_max', 'apply_queue', 'stop_at_ratio', 'apply_queue', 'remove_at_ratio', - 'apply_move_completed', 'move_completed', 'is_auto_managed', 'auto_add', + 'apply_max', + 'apply_queue', + 'stop_at_ratio', + 'apply_queue', + 'remove_at_ratio', + 'apply_move_completed', + 'move_completed', + 'is_auto_managed', + 'auto_add', ] # list of tuples, because order matters when nesting. sensitive_groups = [ - ('apply_max', ['max_download_speed', 'max_upload_speed', 'max_upload_slots', 'max_connections']), + ( + 'apply_max', + [ + 'max_download_speed', + 'max_upload_speed', + 'max_upload_slots', + 'max_connections', + ], + ), ('apply_queue', ['is_auto_managed', 'stop_at_ratio']), ('stop_at_ratio', ['remove_at_ratio', 'stop_ratio']), # nested ('apply_move_completed', ['move_completed']), @@ -152,7 +167,9 @@ class OptionsDialog(object): self.dialog.set_transient_for(component.get('MainWindow').window) self.builder.connect_signals(self) # Show the label name in the header label - self.builder.get_object('label_header').set_markup('<b>%s:</b> %s' % (_('Label Options'), self.label)) + self.builder.get_object('label_header').set_markup( + '<b>%s:</b> %s' % (_('Label Options'), self.label) + ) for chk_id, group in self.sensitive_groups: chk = self.builder.get_object(chk_id) @@ -171,15 +188,21 @@ class OptionsDialog(object): self.builder.get_object(chk_id).set_active(bool(options[chk_id])) if client.is_localhost(): - self.builder.get_object('move_completed_path').set_filename(options['move_completed_path']) + self.builder.get_object('move_completed_path').set_filename( + options['move_completed_path'] + ) self.builder.get_object('move_completed_path').show() self.builder.get_object('move_completed_path_entry').hide() else: - self.builder.get_object('move_completed_path_entry').set_text(options['move_completed_path']) + self.builder.get_object('move_completed_path_entry').set_text( + options['move_completed_path'] + ) self.builder.get_object('move_completed_path_entry').show() self.builder.get_object('move_completed_path').hide() - self.builder.get_object('auto_add_trackers').get_buffer().set_text('\n'.join(options['auto_add_trackers'])) + self.builder.get_object('auto_add_trackers').get_buffer().set_text( + '\n'.join(options['auto_add_trackers']) + ) self.apply_sensitivity() @@ -190,18 +213,32 @@ class OptionsDialog(object): for spin_id in self.spin_ids: options[spin_id] = self.builder.get_object(spin_id).get_value() for spin_int_id in self.spin_int_ids: - options[spin_int_id] = self.builder.get_object(spin_int_id).get_value_as_int() + options[spin_int_id] = self.builder.get_object( + spin_int_id + ).get_value_as_int() for chk_id in self.chk_ids: options[chk_id] = self.builder.get_object(chk_id).get_active() if client.is_localhost(): - options['move_completed_path'] = self.builder.get_object('move_completed_path').get_filename() + options['move_completed_path'] = self.builder.get_object( + 'move_completed_path' + ).get_filename() else: - options['move_completed_path'] = self.builder.get_object('move_completed_path_entry').get_text() - - buff = self.builder.get_object('auto_add_trackers').get_buffer() # sometimes I hate gtk... - tracker_lst = buff.get_text(buff.get_start_iter(), buff.get_end_iter()).strip().split('\n') - options['auto_add_trackers'] = [x for x in tracker_lst if x] # filter out empty lines. + options['move_completed_path'] = self.builder.get_object( + 'move_completed_path_entry' + ).get_text() + + buff = self.builder.get_object( + 'auto_add_trackers' + ).get_buffer() # sometimes I hate gtk... + tracker_lst = ( + buff.get_text(buff.get_start_iter(), buff.get_end_iter()) + .strip() + .split('\n') + ) + options['auto_add_trackers'] = [ + x for x in tracker_lst if x + ] # filter out empty lines. log.debug(options) client.label.set_options(self.label, options) diff --git a/deluge/plugins/Label/setup.py b/deluge/plugins/Label/setup.py index a0c048416..c20705a69 100644 --- a/deluge/plugins/Label/setup.py +++ b/deluge/plugins/Label/setup.py @@ -32,11 +32,9 @@ setup( url=__url__, license=__license__, long_description=__long_description__, - packages=find_packages(), namespace_packages=['deluge', 'deluge.plugins'], package_data=__pkg_data__, - entry_points=""" [deluge.plugin.core] %s = deluge.plugins.%s:CorePlugin @@ -44,5 +42,6 @@ setup( %s = deluge.plugins.%s:GtkUIPlugin [deluge.plugin.web] %s = deluge.plugins.%s:WebUIPlugin - """ % ((__plugin_name__, __plugin_name__.lower()) * 3), + """ + % ((__plugin_name__, __plugin_name__.lower()) * 3), ) diff --git a/deluge/plugins/Notifications/deluge/plugins/notifications/__init__.py b/deluge/plugins/Notifications/deluge/plugins/notifications/__init__.py index 0164464c8..810e284df 100644 --- a/deluge/plugins/Notifications/deluge/plugins/notifications/__init__.py +++ b/deluge/plugins/Notifications/deluge/plugins/notifications/__init__.py @@ -20,6 +20,7 @@ from deluge.plugins.init import PluginInitBase class CorePlugin(PluginInitBase): def __init__(self, plugin_name): from .core import Core as _pluginCls + self._plugin_cls = _pluginCls super(CorePlugin, self).__init__(plugin_name) @@ -27,6 +28,7 @@ class CorePlugin(PluginInitBase): class GtkUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .gtkui import GtkUI as _pluginCls + self._plugin_cls = _pluginCls super(GtkUIPlugin, self).__init__(plugin_name) @@ -34,5 +36,6 @@ class GtkUIPlugin(PluginInitBase): class WebUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .webui import WebUI as _pluginCls + self._plugin_cls = _pluginCls super(WebUIPlugin, self).__init__(plugin_name) diff --git a/deluge/plugins/Notifications/deluge/plugins/notifications/common.py b/deluge/plugins/Notifications/deluge/plugins/notifications/common.py index 5d6029cc1..8284d9700 100644 --- a/deluge/plugins/Notifications/deluge/plugins/notifications/common.py +++ b/deluge/plugins/Notifications/deluge/plugins/notifications/common.py @@ -27,18 +27,14 @@ log = logging.getLogger(__name__) def get_resource(filename): - return resource_filename('deluge.plugins.notifications', os.path.join('data', filename)) + return resource_filename( + 'deluge.plugins.notifications', os.path.join('data', filename) + ) class CustomNotifications(object): - def __init__(self, plugin_name=None): - self.custom_notifications = { - 'email': {}, - 'popup': {}, - 'blink': {}, - 'sound': {}, - } + self.custom_notifications = {'email': {}, 'popup': {}, 'blink': {}, 'sound': {}} def enable(self): pass @@ -50,7 +46,13 @@ class CustomNotifications(object): self._deregister_custom_provider(kind, eventtype) def _handle_custom_providers(self, kind, eventtype, *args, **kwargs): - log.debug('Calling CORE custom %s providers for %s: %s %s', kind, eventtype, args, kwargs) + log.debug( + 'Calling CORE custom %s providers for %s: %s %s', + kind, + eventtype, + args, + kwargs, + ) if eventtype in self.config['subscriptions'][kind]: wrapper, handler = self.custom_notifications[kind][eventtype] log.debug('Found handler for kind %s: %s', kind, handler) @@ -65,17 +67,18 @@ class CustomNotifications(object): if not self._handled_eventtype(eventtype, handler): return defer.succeed('Event not handled') if eventtype not in self.custom_notifications: + def wrapper(*args, **kwargs): return self._handle_custom_providers(kind, eventtype, *args, **kwargs) + self.custom_notifications[kind][eventtype] = (wrapper, handler) else: wrapper, handler = self.custom_notifications[kind][eventtype] try: - component.get('EventManager').register_event_handler( - eventtype, wrapper, - ) + component.get('EventManager').register_event_handler(eventtype, wrapper) except KeyError: from deluge.ui.client import client + client.register_event_handler(eventtype, wrapper) def _deregister_custom_provider(self, kind, eventtype): @@ -83,10 +86,11 @@ class CustomNotifications(object): wrapper, handler = self.custom_notifications[kind][eventtype] try: component.get('EventManager').deregister_event_handler( - eventtype, wrapper, + eventtype, wrapper ) except KeyError: from deluge.ui.client import client + client.deregister_event_handler(eventtype, wrapper) self.custom_notifications[kind].pop(eventtype) except KeyError: @@ -101,7 +105,7 @@ class CustomNotifications(object): return True log.error( 'You cannot register custom notification providers ' - 'for built-in event types.', + 'for built-in event types.' ) return False return True diff --git a/deluge/plugins/Notifications/deluge/plugins/notifications/core.py b/deluge/plugins/Notifications/deluge/plugins/notifications/core.py index 66c3ee078..123f9cf05 100644 --- a/deluge/plugins/Notifications/deluge/plugins/notifications/core.py +++ b/deluge/plugins/Notifications/deluge/plugins/notifications/core.py @@ -40,22 +40,18 @@ DEFAULT_PREFS = { 'smtp_tls': False, # SSL or TLS 'smtp_recipients': [], # Subscriptions - 'subscriptions': { - 'email': [], - }, + 'subscriptions': {'email': []}, } class CoreNotifications(CustomNotifications): - def __init__(self, plugin_name=None): CustomNotifications.__init__(self, plugin_name) def enable(self): CustomNotifications.enable(self) self.register_custom_email_notification( - 'TorrentFinishedEvent', - self._on_torrent_finished_event, + 'TorrentFinishedEvent', self._on_torrent_finished_event ) def disable(self): @@ -80,8 +76,7 @@ class CoreNotifications(CustomNotifications): return defer.succeed('SMTP notification not enabled.') subject, message = result log.debug( - 'Spawning new thread to send email with subject: %s: %s', - subject, message, + 'Spawning new thread to send email with subject: %s: %s', subject, message ) # Spawn thread because we don't want Deluge to lock up while we send the # email. @@ -109,20 +104,25 @@ class CoreNotifications(CustomNotifications): 'smtp_recipients': to_addrs_str, 'date': formatdate(), } - headers = """\ + headers = ( + """\ From: %(smtp_from)s To: %(smtp_recipients)s Subject: %(subject)s Date: %(date)s -""" % headers_dict +""" + % headers_dict + ) message = '\r\n'.join((headers + message).splitlines()) try: # Python 2.6 - server = smtplib.SMTP(self.config['smtp_host'], self.config['smtp_port'], timeout=60) + server = smtplib.SMTP( + self.config['smtp_host'], self.config['smtp_port'], timeout=60 + ) except Exception as ex: err_msg = _('There was an error sending the notification email: %s') % ex log.error(err_msg) @@ -154,7 +154,9 @@ Date: %(date)s try: server.sendmail(self.config['smtp_from'], to_addrs, message) except smtplib.SMTPException as ex: - err_msg = _('There was an error sending the notification email: %s') % ex + err_msg = ( + _('There was an error sending the notification email: %s') % ex + ) log.error(err_msg) return ex finally: @@ -162,6 +164,7 @@ Date: %(date)s # avoid false failure detection when the server closes # the SMTP connection with TLS enabled import socket + try: server.quit() except socket.sslerror: @@ -176,13 +179,16 @@ Date: %(date)s torrent_status = torrent.get_status({}) # Email subject = _('Finished Torrent "%(name)s"') % torrent_status - message = _( - 'This email is to inform you that Deluge has finished ' - 'downloading "%(name)s", which includes %(num_files)i files.' - '\nTo stop receiving these alerts, simply turn off email ' - "notification in Deluge's preferences.\n\n" - 'Thank you,\nDeluge.', - ) % torrent_status + message = ( + _( + 'This email is to inform you that Deluge has finished ' + 'downloading "%(name)s", which includes %(num_files)i files.' + '\nTo stop receiving these alerts, simply turn off email ' + "notification in Deluge's preferences.\n\n" + 'Thank you,\nDeluge.' + ) + % torrent_status + ) return subject, message # d = defer.maybeDeferred(self.handle_custom_email_notification, @@ -201,7 +207,7 @@ class Core(CorePluginBase, CoreNotifications): def enable(self): CoreNotifications.enable(self) self.config = deluge.configmanager.ConfigManager( - 'notifications-core.conf', DEFAULT_PREFS, + 'notifications-core.conf', DEFAULT_PREFS ) log.debug('ENABLING CORE NOTIFICATIONS') diff --git a/deluge/plugins/Notifications/deluge/plugins/notifications/gtkui.py b/deluge/plugins/Notifications/deluge/plugins/notifications/gtkui.py index 86a3daf8c..fba795de6 100644 --- a/deluge/plugins/Notifications/deluge/plugins/notifications/gtkui.py +++ b/deluge/plugins/Notifications/deluge/plugins/notifications/gtkui.py @@ -34,12 +34,14 @@ log = logging.getLogger(__name__) try: import pygame + SOUND_AVAILABLE = True except ImportError: SOUND_AVAILABLE = False try: import pynotify + POPUP_AVAILABLE = True if deluge.common.windows_check(): POPUP_AVAILABLE = False @@ -59,36 +61,35 @@ DEFAULT_PREFS = { 'sound_path': '', 'custom_sounds': {}, # Subscriptions - 'subscriptions': { - 'popup': [], - 'blink': [], - 'sound': [], - }, + 'subscriptions': {'popup': [], 'blink': [], 'sound': []}, } RECIPIENT_FIELD, RECIPIENT_EDIT = list(range(2)) ( - SUB_EVENT, SUB_EVENT_DOC, SUB_NOT_EMAIL, SUB_NOT_POPUP, SUB_NOT_BLINK, + SUB_EVENT, + SUB_EVENT_DOC, + SUB_NOT_EMAIL, + SUB_NOT_POPUP, + SUB_NOT_BLINK, SUB_NOT_SOUND, ) = list(range(6)) SND_EVENT, SND_EVENT_DOC, SND_NAME, SND_PATH = list(range(4)) class GtkUiNotifications(CustomNotifications): - def __init__(self, plugin_name=None): CustomNotifications.__init__(self, plugin_name) def enable(self): CustomNotifications.enable(self) self.register_custom_blink_notification( - 'TorrentFinishedEvent', self._on_torrent_finished_event_blink, + 'TorrentFinishedEvent', self._on_torrent_finished_event_blink ) self.register_custom_sound_notification( - 'TorrentFinishedEvent', self._on_torrent_finished_event_sound, + 'TorrentFinishedEvent', self._on_torrent_finished_event_sound ) self.register_custom_popup_notification( - 'TorrentFinishedEvent', self._on_torrent_finished_event_popup, + 'TorrentFinishedEvent', self._on_torrent_finished_event_popup ) def disable(self): @@ -150,19 +151,19 @@ class GtkUiNotifications(CustomNotifications): return defer.maybeDeferred(self.__blink) return defer.succeed( 'Will not blink. The returned value from the custom ' - 'handler was: %s' % result, + 'handler was: %s' % result ) def handle_custom_sound_notification(self, result, eventtype): if isinstance(result, ''.__class__): if not result and eventtype in self.config['custom_sounds']: return defer.maybeDeferred( - self.__play_sound, self.config['custom_sounds'][eventtype], + self.__play_sound, self.config['custom_sounds'][eventtype] ) return defer.maybeDeferred(self.__play_sound, result) return defer.succeed( 'Will not play sound. The returned value from the ' - 'custom handler was: %s' % result, + 'custom handler was: %s' % result ) def __blink(self): @@ -176,7 +177,9 @@ class GtkUiNotifications(CustomNotifications): return defer.fail(_('pynotify is not installed')) if pynotify.init('Deluge'): - icon = gtk.gdk.pixbuf_new_from_file_at_size(deluge.common.get_pixmap('deluge.svg'), 48, 48) + icon = gtk.gdk.pixbuf_new_from_file_at_size( + deluge.common.get_pixmap('deluge.svg'), 48, 48 + ) self.note = pynotify.Notification(title, message) self.note.set_icon_from_pixbuf(icon) if not self.note.show(): @@ -227,15 +230,17 @@ class GtkUiNotifications(CustomNotifications): def _on_torrent_finished_event_got_torrent_status(self, torrent_status): log.debug( - 'Handler for TorrentFinishedEvent GTKUI called. ' - 'Got Torrent Status', + 'Handler for TorrentFinishedEvent GTKUI called. ' 'Got Torrent Status' ) title = _('Finished Torrent') torrent_status['num_files'] = torrent_status['file_progress'].count(1.0) - message = _( - 'The torrent "%(name)s" including %(num_files)i file(s) ' - 'has finished downloading.', - ) % torrent_status + message = ( + _( + 'The torrent "%(name)s" including %(num_files)i file(s) ' + 'has finished downloading.' + ) + % torrent_status + ) return title, message @@ -246,7 +251,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): def enable(self): self.config = deluge.configmanager.ConfigManager( - 'notifications-gtk.conf', DEFAULT_PREFS, + 'notifications-gtk.conf', DEFAULT_PREFS ) self.builder = gtk.Builder() self.builder.add_from_file(get_resource('config.ui')) @@ -259,65 +264,52 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): self.build_notifications_model_populate_treeview() client.notifications.get_handled_events().addCallback( - self.popuplate_what_needs_handled_events, + self.popuplate_what_needs_handled_events ) - self.builder.connect_signals({ - 'on_add_button_clicked': ( - self.on_add_button_clicked, - self.recipients_treeview, - ), - 'on_delete_button_clicked': ( - self.on_delete_button_clicked, - self.recipients_treeview, - ), - 'on_enabled_toggled': self.on_enabled_toggled, - 'on_sound_enabled_toggled': self.on_sound_enabled_toggled, - 'on_sounds_edit_button_clicked': self.on_sounds_edit_button_clicked, - 'on_sounds_revert_button_clicked': self.on_sounds_revert_button_clicked, - 'on_sound_path_update_preview': self.on_sound_path_update_preview, - }) + self.builder.connect_signals( + { + 'on_add_button_clicked': ( + self.on_add_button_clicked, + self.recipients_treeview, + ), + 'on_delete_button_clicked': ( + self.on_delete_button_clicked, + self.recipients_treeview, + ), + 'on_enabled_toggled': self.on_enabled_toggled, + 'on_sound_enabled_toggled': self.on_sound_enabled_toggled, + 'on_sounds_edit_button_clicked': self.on_sounds_edit_button_clicked, + 'on_sounds_revert_button_clicked': self.on_sounds_revert_button_clicked, + 'on_sound_path_update_preview': self.on_sound_path_update_preview, + } + ) component.get('Preferences').add_page(_('Notifications'), self.prefs) component.get('PluginManager').register_hook( - 'on_apply_prefs', - self.on_apply_prefs, + 'on_apply_prefs', self.on_apply_prefs ) component.get('PluginManager').register_hook( - 'on_show_prefs', - self.on_show_prefs, + 'on_show_prefs', self.on_show_prefs ) if not POPUP_AVAILABLE: - self.builder.get_object('popup_enabled').set_property( - 'sensitive', - False, - ) + self.builder.get_object('popup_enabled').set_property('sensitive', False) if not SOUND_AVAILABLE: # for widget_name in ('sound_enabled', 'sound_path', 'sounds_page', 'sounds_page_label'): # self.builder.get_object(widget_name).set_property('sensitive', False) - self.builder.get_object('sound_enabled').set_property( - 'sensitive', - False, - ) + self.builder.get_object('sound_enabled').set_property('sensitive', False) self.builder.get_object('sound_path').set_property('sensitive', False) - self.builder.get_object('sounds_page').set_property( - 'sensitive', - False, - ) + self.builder.get_object('sounds_page').set_property('sensitive', False) self.builder.get_object('sounds_page_label').set_property( - 'sensitive', - False, + 'sensitive', False ) self.systray = component.get('SystemTray') if not hasattr(self.systray, 'tray'): # Tray is not beeing used - self.builder.get_object('blink_enabled').set_property( - 'sensitive', - False, - ) + self.builder.get_object('blink_enabled').set_property('sensitive', False) GtkUiNotifications.enable(self) @@ -325,12 +317,10 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): GtkUiNotifications.disable(self) component.get('Preferences').remove_page(_('Notifications')) component.get('PluginManager').deregister_hook( - 'on_apply_prefs', - self.on_apply_prefs, + 'on_apply_prefs', self.on_apply_prefs ) component.get('PluginManager').deregister_hook( - 'on_show_prefs', - self.on_show_prefs, + 'on_show_prefs', self.on_show_prefs ) def build_recipients_model_populate_treeview(self): @@ -338,7 +328,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): self.recipients_treeview = self.builder.get_object('smtp_recipients') treeview_selection = self.recipients_treeview.get_selection() treeview_selection.connect( - 'changed', self.on_recipients_treeview_selection_changed, + 'changed', self.on_recipients_treeview_selection_changed ) self.recipients_model = gtk.ListStore(str, bool) @@ -346,9 +336,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): renderer.connect('edited', self.on_cell_edited, self.recipients_model) renderer.set_data('recipient', RECIPIENT_FIELD) column = gtk.TreeViewColumn( - 'Recipients', renderer, - text=RECIPIENT_FIELD, - editable=RECIPIENT_EDIT, + 'Recipients', renderer, text=RECIPIENT_FIELD, editable=RECIPIENT_EDIT ) column.set_expand(True) self.recipients_treeview.append_column(column) @@ -358,9 +346,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): # Sound customisation treeview/model self.sounds_treeview = self.builder.get_object('sounds_treeview') sounds_selection = self.sounds_treeview.get_selection() - sounds_selection.connect( - 'changed', self.on_sounds_treeview_selection_changed, - ) + sounds_selection.connect('changed', self.on_sounds_treeview_selection_changed) self.sounds_treeview.set_tooltip_column(SND_EVENT_DOC) self.sounds_model = gtk.ListStore(str, str, str, str) @@ -395,7 +381,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): self.subscriptions_treeview = self.builder.get_object('subscriptions_treeview') subscriptions_selection = self.subscriptions_treeview.get_selection() subscriptions_selection.connect( - 'changed', self.on_subscriptions_treeview_selection_changed, + 'changed', self.on_subscriptions_treeview_selection_changed ) self.subscriptions_treeview.set_tooltip_column(SUB_EVENT_DOC) self.subscriptions_model = gtk.ListStore(str, str, bool, bool, bool, bool) @@ -441,7 +427,9 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): self.subscriptions_treeview.append_column(column) self.subscriptions_treeview.set_model(self.subscriptions_model) - def popuplate_what_needs_handled_events(self, handled_events, email_subscriptions=None): + def popuplate_what_needs_handled_events( + self, handled_events, email_subscriptions=None + ): if email_subscriptions is None: email_subscriptions = [] self.populate_subscriptions(handled_events, email_subscriptions) @@ -458,10 +446,14 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): if snd_path: self.sounds_model.set( self.sounds_model.append(), - SND_EVENT, event_name, - SND_EVENT_DOC, event_doc, - SND_NAME, basename(snd_path), - SND_PATH, snd_path, + SND_EVENT, + event_name, + SND_EVENT_DOC, + event_doc, + SND_NAME, + basename(snd_path), + SND_PATH, + snd_path, ) def populate_subscriptions(self, handled_events, email_subscriptions=None): @@ -469,16 +461,22 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): email_subscriptions = [] subscriptions_dict = self.config['subscriptions'] self.subscriptions_model.clear() -# self.handled_events = handled_events + # self.handled_events = handled_events for event_name, event_doc in handled_events: self.subscriptions_model.set( self.subscriptions_model.append(), - SUB_EVENT, event_name, - SUB_EVENT_DOC, event_doc, - SUB_NOT_EMAIL, event_name in email_subscriptions, - SUB_NOT_POPUP, event_name in subscriptions_dict['popup'], - SUB_NOT_BLINK, event_name in subscriptions_dict['blink'], - SUB_NOT_SOUND, event_name in subscriptions_dict['sound'], + SUB_EVENT, + event_name, + SUB_EVENT_DOC, + event_doc, + SUB_NOT_EMAIL, + event_name in email_subscriptions, + SUB_NOT_POPUP, + event_name in subscriptions_dict['popup'], + SUB_NOT_BLINK, + event_name in subscriptions_dict['blink'], + SUB_NOT_SOUND, + event_name in subscriptions_dict['sound'], ) def on_apply_prefs(self): @@ -501,8 +499,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): old_sound_file = self.config['sound_path'] new_sound_file = self.builder.get_object('sound_path').get_filename() log.debug( - 'Old Default sound file: %s New one: %s', - old_sound_file, new_sound_file, + 'Old Default sound file: %s New one: %s', old_sound_file, new_sound_file ) custom_sounds = {} for event_name, event_doc, filename, filepath in self.sounds_model: @@ -511,18 +508,20 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): continue custom_sounds[event_name] = filepath - self.config.config.update({ - 'popup_enabled': self.builder.get_object('popup_enabled').get_active(), - 'blink_enabled': self.builder.get_object('blink_enabled').get_active(), - 'sound_enabled': self.builder.get_object('sound_enabled').get_active(), - 'sound_path': new_sound_file, - 'subscriptions': { - 'popup': current_popup_subscriptions, - 'blink': current_blink_subscriptions, - 'sound': current_sound_subscriptions, - }, - 'custom_sounds': custom_sounds, - }) + self.config.config.update( + { + 'popup_enabled': self.builder.get_object('popup_enabled').get_active(), + 'blink_enabled': self.builder.get_object('blink_enabled').get_active(), + 'sound_enabled': self.builder.get_object('sound_enabled').get_active(), + 'sound_path': new_sound_file, + 'subscriptions': { + 'popup': current_popup_subscriptions, + 'blink': current_blink_subscriptions, + 'sound': current_sound_subscriptions, + }, + 'custom_sounds': custom_sounds, + } + ) self.config.save() core_config = { @@ -534,8 +533,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): 'smtp_from': self.builder.get_object('smtp_from').get_text(), 'smtp_tls': self.builder.get_object('smtp_tls').get_active(), 'smtp_recipients': [ - dest[0] for dest in self.recipients_model if - dest[0] != 'USER@HOST' + dest[0] for dest in self.recipients_model if dest[0] != 'USER@HOST' ], 'subscriptions': {'email': current_email_subscriptions}, } @@ -558,20 +556,20 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): for recipient in core_config['smtp_recipients']: self.recipients_model.set( self.recipients_model.append(), - RECIPIENT_FIELD, recipient, - RECIPIENT_EDIT, False, + RECIPIENT_FIELD, + recipient, + RECIPIENT_EDIT, + False, ) - self.builder.get_object('smtp_enabled').set_active( - core_config['smtp_enabled'], - ) + self.builder.get_object('smtp_enabled').set_active(core_config['smtp_enabled']) self.builder.get_object('sound_enabled').set_active( - self.config['sound_enabled'], + self.config['sound_enabled'] ) self.builder.get_object('popup_enabled').set_active( - self.config['popup_enabled'], + self.config['popup_enabled'] ) self.builder.get_object('blink_enabled').set_active( - self.config['blink_enabled'], + self.config['blink_enabled'] ) if self.config['sound_path']: sound_path = self.config['sound_path'] @@ -588,17 +586,11 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): ) def on_sound_path_update_preview(self, filechooser): - client.notifications.get_handled_events().addCallback( - self.populate_sounds, - ) + client.notifications.get_handled_events().addCallback(self.populate_sounds) def on_add_button_clicked(self, widget, treeview): model = treeview.get_model() - model.set( - model.append(), - RECIPIENT_FIELD, 'USER@HOST', - RECIPIENT_EDIT, True, - ) + model.set(model.append(), RECIPIENT_FIELD, 'USER@HOST', RECIPIENT_EDIT, True) def on_delete_button_clicked(self, widget, treeview): selection = treeview.get_selection() @@ -613,42 +605,40 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): def on_recipients_treeview_selection_changed(self, selection): model, selected_connection_iter = selection.get_selected() if selected_connection_iter: - self.builder.get_object('delete_button').set_property( - 'sensitive', - True, - ) + self.builder.get_object('delete_button').set_property('sensitive', True) else: - self.builder.get_object('delete_button').set_property( - 'sensitive', - False, - ) + self.builder.get_object('delete_button').set_property('sensitive', False) def on_subscriptions_treeview_selection_changed(self, selection): model, selected_connection_iter = selection.get_selected() if selected_connection_iter: - self.builder.get_object('delete_button').set_property( - 'sensitive', - True, - ) + self.builder.get_object('delete_button').set_property('sensitive', True) else: - self.builder.get_object('delete_button').set_property( - 'sensitive', - False, - ) + self.builder.get_object('delete_button').set_property('sensitive', False) def on_sounds_treeview_selection_changed(self, selection): model, selected_iter = selection.get_selected() if selected_iter: - self.builder.get_object('sounds_edit_button').set_property('sensitive', True) + self.builder.get_object('sounds_edit_button').set_property( + 'sensitive', True + ) path = model.get(selected_iter, SND_PATH)[0] log.debug('Sound selection changed: %s', path) if path != self.config['sound_path']: - self.builder.get_object('sounds_revert_button').set_property('sensitive', True) + self.builder.get_object('sounds_revert_button').set_property( + 'sensitive', True + ) else: - self.builder.get_object('sounds_revert_button').set_property('sensitive', False) + self.builder.get_object('sounds_revert_button').set_property( + 'sensitive', False + ) else: - self.builder.get_object('sounds_edit_button').set_property('sensitive', False) - self.builder.get_object('sounds_revert_button').set_property('sensitive', False) + self.builder.get_object('sounds_edit_button').set_property( + 'sensitive', False + ) + self.builder.get_object('sounds_revert_button').set_property( + 'sensitive', False + ) def on_sounds_revert_button_clicked(self, widget): log.debug('on_sounds_revert_button_clicked') @@ -658,8 +648,10 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): log.debug('on_sounds_revert_button_clicked: got iter') model.set( selected_iter, - SND_PATH, self.config['sound_path'], - SND_NAME, basename(self.config['sound_path']), + SND_PATH, + self.config['sound_path'], + SND_NAME, + basename(self.config['sound_path']), ) def on_sounds_edit_button_clicked(self, widget): @@ -686,9 +678,12 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): log.debug(new_filename) model.set( selected_iter, - SND_PATH, new_filename, - SND_NAME, basename(new_filename), + SND_PATH, + new_filename, + SND_NAME, + basename(new_filename), ) + d = defer.maybeDeferred(dialog.run) d.addCallback(update_model) @@ -696,57 +691,55 @@ class GtkUI(GtkPluginBase, GtkUiNotifications): def on_enabled_toggled(self, widget): for widget_name in ( - 'smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass', - 'smtp_pass', 'smtp_tls', 'smtp_from', + 'smtp_host', + 'smtp_port', + 'smtp_user', + 'smtp_pass', + 'smtp_pass', + 'smtp_tls', + 'smtp_from', 'smtp_recipients', ): self.builder.get_object(widget_name).set_property( - 'sensitive', - widget.get_active(), + 'sensitive', widget.get_active() ) def on_sound_enabled_toggled(self, widget): if widget.get_active(): self.builder.get_object('sound_path').set_property('sensitive', True) - self.builder.get_object('sounds_page').set_property( - 'sensitive', - True, - ) - self.builder.get_object('sounds_page_label').set_property( - 'sensitive', - True, - ) + self.builder.get_object('sounds_page').set_property('sensitive', True) + self.builder.get_object('sounds_page_label').set_property('sensitive', True) else: self.builder.get_object('sound_path').set_property('sensitive', False) - self.builder.get_object('sounds_page').set_property( - 'sensitive', - False, - ) + self.builder.get_object('sounds_page').set_property('sensitive', False) self.builder.get_object('sounds_page_label').set_property( - 'sensitive', - False, + 'sensitive', False ) -# for widget_name in ('sounds_path', 'sounds_page', 'sounds_page_label'): -# self.builder.get_object(widget_name).set_property('sensitive', -# widget.get_active()) + # for widget_name in ('sounds_path', 'sounds_page', 'sounds_page_label'): + # self.builder.get_object(widget_name).set_property('sensitive', + # widget.get_active()) def _on_email_col_toggled(self, cell, path): - self.subscriptions_model[path][SUB_NOT_EMAIL] = \ - not self.subscriptions_model[path][SUB_NOT_EMAIL] + self.subscriptions_model[path][SUB_NOT_EMAIL] = not self.subscriptions_model[ + path + ][SUB_NOT_EMAIL] return def _on_popup_col_toggled(self, cell, path): - self.subscriptions_model[path][SUB_NOT_POPUP] = \ - not self.subscriptions_model[path][SUB_NOT_POPUP] + self.subscriptions_model[path][SUB_NOT_POPUP] = not self.subscriptions_model[ + path + ][SUB_NOT_POPUP] return def _on_blink_col_toggled(self, cell, path): - self.subscriptions_model[path][SUB_NOT_BLINK] = \ - not self.subscriptions_model[path][SUB_NOT_BLINK] + self.subscriptions_model[path][SUB_NOT_BLINK] = not self.subscriptions_model[ + path + ][SUB_NOT_BLINK] return def _on_sound_col_toggled(self, cell, path): - self.subscriptions_model[path][SUB_NOT_SOUND] = \ - not self.subscriptions_model[path][SUB_NOT_SOUND] + self.subscriptions_model[path][SUB_NOT_SOUND] = not self.subscriptions_model[ + path + ][SUB_NOT_SOUND] return diff --git a/deluge/plugins/Notifications/deluge/plugins/notifications/test.py b/deluge/plugins/Notifications/deluge/plugins/notifications/test.py index 642442c5e..2e6f9755b 100644 --- a/deluge/plugins/Notifications/deluge/plugins/notifications/test.py +++ b/deluge/plugins/Notifications/deluge/plugins/notifications/test.py @@ -32,10 +32,7 @@ class TestEmailNotifications(component.Component): self.__imp = imp self.lc = task.LoopingCall(self.update) self.n = 1 - self.events = [ - FooEvent(), - CustomEvent(), - ] + self.events = [FooEvent(), CustomEvent()] self.events_classes = [] def enable(self): @@ -44,22 +41,18 @@ class TestEmailNotifications(component.Component): if self.__imp == 'core': # component.get('CorePlugin.Notifications').register_custom_email_notification( component.get('Notifications').register_custom_email_notification( - event.__class__.__name__, - self.custom_email_message_provider, + event.__class__.__name__, self.custom_email_message_provider ) elif self.__imp == 'gtk': notifications_component = component.get('Notifications') notifications_component.register_custom_popup_notification( - event.__class__.__name__, - self.custom_popup_message_provider, + event.__class__.__name__, self.custom_popup_message_provider ) notifications_component.register_custom_blink_notification( - event.__class__.__name__, - self.custom_blink_message_provider, + event.__class__.__name__, self.custom_blink_message_provider ) notifications_component.register_custom_sound_notification( - event.__class__.__name__, - self.custom_sound_message_provider, + event.__class__.__name__, self.custom_sound_message_provider ) self.lc.start(60, False) diff --git a/deluge/plugins/Notifications/setup.py b/deluge/plugins/Notifications/setup.py index 84893f53c..b24178047 100755 --- a/deluge/plugins/Notifications/setup.py +++ b/deluge/plugins/Notifications/setup.py @@ -40,11 +40,9 @@ setup( url=__url__, license=__license__, long_description=__long_description__ if __long_description__ else __description__, - packages=find_packages(exclude=['**/test.py']), namespace_packages=['deluge', 'deluge.plugins'], package_data=__pkg_data__, - entry_points=""" [deluge.plugin.core] %s = deluge.plugins.%s:CorePlugin @@ -52,5 +50,6 @@ setup( %s = deluge.plugins.%s:GtkUIPlugin [deluge.plugin.web] %s = deluge.plugins.%s:WebUIPlugin - """ % ((__plugin_name__, __plugin_name__.lower()) * 3), + """ + % ((__plugin_name__, __plugin_name__.lower()) * 3), ) diff --git a/deluge/plugins/Scheduler/deluge/plugins/scheduler/__init__.py b/deluge/plugins/Scheduler/deluge/plugins/scheduler/__init__.py index 7d3492d2c..6db72b63b 100644 --- a/deluge/plugins/Scheduler/deluge/plugins/scheduler/__init__.py +++ b/deluge/plugins/Scheduler/deluge/plugins/scheduler/__init__.py @@ -19,6 +19,7 @@ from deluge.plugins.init import PluginInitBase class CorePlugin(PluginInitBase): def __init__(self, plugin_name): from .core import Core as _pluginCls + self._plugin_cls = _pluginCls super(CorePlugin, self).__init__(plugin_name) @@ -26,6 +27,7 @@ class CorePlugin(PluginInitBase): class GtkUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .gtkui import GtkUI as _pluginCls + self._plugin_cls = _pluginCls super(GtkUIPlugin, self).__init__(plugin_name) @@ -33,5 +35,6 @@ class GtkUIPlugin(PluginInitBase): class WebUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .webui import WebUI as _pluginCls + self._plugin_cls = _pluginCls super(WebUIPlugin, self).__init__(plugin_name) diff --git a/deluge/plugins/Scheduler/deluge/plugins/scheduler/core.py b/deluge/plugins/Scheduler/deluge/plugins/scheduler/core.py index c1e9da999..388e4f0f6 100644 --- a/deluge/plugins/Scheduler/deluge/plugins/scheduler/core.py +++ b/deluge/plugins/Scheduler/deluge/plugins/scheduler/core.py @@ -35,11 +35,7 @@ DEFAULT_PREFS = { 'button_state': [[0] * 7 for dummy in range(24)], } -STATES = { - 0: 'Green', - 1: 'Yellow', - 2: 'Red', -} +STATES = {0: 'Green', 1: 'Yellow', 2: 'Red'} CONTROLLED_SETTINGS = [ 'max_download_speed', @@ -54,6 +50,7 @@ class SchedulerEvent(DelugeEvent): """ Emitted when a schedule state changes. """ + def __init__(self, colour): """ :param colour: str, the current scheduler state @@ -71,7 +68,9 @@ class Core(CorePluginBase): DEFAULT_PREFS['low_active_down'] = core_config['max_active_downloading'] DEFAULT_PREFS['low_active_up'] = core_config['max_active_seeding'] - self.config = deluge.configmanager.ConfigManager('scheduler.conf', DEFAULT_PREFS) + self.config = deluge.configmanager.ConfigManager( + 'scheduler.conf', DEFAULT_PREFS + ) self.state = self.get_state() @@ -84,12 +83,16 @@ class Core(CorePluginBase): self.timer = reactor.callLater(secs_to_next_hour, self.do_schedule) # Register for config changes so state isn't overridden - component.get('EventManager').register_event_handler('ConfigValueChangedEvent', self.on_config_value_changed) + component.get('EventManager').register_event_handler( + 'ConfigValueChangedEvent', self.on_config_value_changed + ) def disable(self): if self.timer.active(): self.timer.cancel() - component.get('EventManager').deregister_event_handler('ConfigValueChangedEvent', self.on_config_value_changed) + component.get('EventManager').deregister_event_handler( + 'ConfigValueChangedEvent', self.on_config_value_changed + ) self.__apply_set_functions() def update(self): @@ -105,7 +108,9 @@ class Core(CorePluginBase): """ core_config = deluge.configmanager.ConfigManager('core.conf') for setting in CONTROLLED_SETTINGS: - component.get('PreferencesManager').do_config_set_func(setting, core_config[setting]) + component.get('PreferencesManager').do_config_set_func( + setting, core_config[setting] + ) # Resume the session if necessary component.get('Core').resume_session() diff --git a/deluge/plugins/Scheduler/deluge/plugins/scheduler/gtkui.py b/deluge/plugins/Scheduler/deluge/plugins/scheduler/gtkui.py index 660101f7f..2525a5f9e 100644 --- a/deluge/plugins/Scheduler/deluge/plugins/scheduler/gtkui.py +++ b/deluge/plugins/Scheduler/deluge/plugins/scheduler/gtkui.py @@ -32,8 +32,10 @@ class SchedulerSelectWidget(gtk.DrawingArea): def __init__(self, hover): super(SchedulerSelectWidget, self).__init__() self.set_events( - gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | - gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.LEAVE_NOTIFY_MASK, + gtk.gdk.BUTTON_PRESS_MASK + | gtk.gdk.BUTTON_RELEASE_MASK + | gtk.gdk.POINTER_MOTION_MASK + | gtk.gdk.LEAVE_NOTIFY_MASK ) self.connect('expose_event', self.expose) @@ -65,7 +67,9 @@ class SchedulerSelectWidget(gtk.DrawingArea): # redraw the whole thing def expose(self, widget, event): context = self.window.cairo_create() - context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) + context.rectangle( + event.area.x, event.area.y, event.area.width, event.area.height + ) context.clip() width = self.window.get_size()[0] @@ -76,11 +80,14 @@ class SchedulerSelectWidget(gtk.DrawingArea): context.set_source_rgba( self.colors[self.button_state[x][y]][0], self.colors[self.button_state[x][y]][1], - self.colors[self.button_state[x][y]][2], 0.7, + self.colors[self.button_state[x][y]][2], + 0.7, ) context.rectangle( - width * (6 * x / 145 + 1 / 145), height * (6 * y / 43 + 1 / 43), - 5 * width / 145, 5 * height / 43, + width * (6 * x / 145 + 1 / 145), + height * (6 * y / 43 + 1 / 43), + 5 * width / 145, + 5 * height / 43, ) context.fill_preserve() context.set_source_rgba(0.5, 0.5, 0.5, 0.5) @@ -132,17 +139,25 @@ class SchedulerSelectWidget(gtk.DrawingArea): self.hover_point = self.get_point(event) self.hover_label.set_text( - self.hover_days[self.hover_point[1]] + - ' ' + str(self.hover_point[0]) + - ':00 - ' + str(self.hover_point[0]) + ':59', + self.hover_days[self.hover_point[1]] + + ' ' + + str(self.hover_point[0]) + + ':00 - ' + + str(self.hover_point[0]) + + ':59' ) if self.mouse_press: - points = [[self.hover_point[0], self.start_point[0]], [self.hover_point[1], self.start_point[1]]] + points = [ + [self.hover_point[0], self.start_point[0]], + [self.hover_point[1], self.start_point[1]], + ] for x in range(min(points[0]), max(points[0]) + 1): for y in range(min(points[1]), max(points[1]) + 1): - self.button_state[x][y] = self.button_state[self.start_point[0]][self.start_point[1]] + self.button_state[x][y] = self.button_state[ + self.start_point[0] + ][self.start_point[1]] self.queue_draw() @@ -156,8 +171,12 @@ class GtkUI(GtkPluginBase): def enable(self): self.create_prefs_page() - component.get('PluginManager').register_hook('on_apply_prefs', self.on_apply_prefs) - component.get('PluginManager').register_hook('on_show_prefs', self.on_show_prefs) + component.get('PluginManager').register_hook( + 'on_apply_prefs', self.on_apply_prefs + ) + component.get('PluginManager').register_hook( + 'on_show_prefs', self.on_show_prefs + ) self.statusbar = component.get('StatusBar') self.status_item = self.statusbar.add_item( image=get_resource('green.png'), @@ -169,20 +188,29 @@ class GtkUI(GtkPluginBase): def on_state_deferred(state): self.state = state self.on_scheduler_event(state) + client.scheduler.get_state().addCallback(on_state_deferred) client.register_event_handler('SchedulerEvent', self.on_scheduler_event) def disable(self): component.get('Preferences').remove_page(_('Scheduler')) # Reset statusbar dict. - self.statusbar.config_value_changed_dict['max_download_speed'] = self.statusbar._on_max_download_speed - self.statusbar.config_value_changed_dict['max_upload_speed'] = self.statusbar._on_max_upload_speed + self.statusbar.config_value_changed_dict[ + 'max_download_speed' + ] = self.statusbar._on_max_download_speed + self.statusbar.config_value_changed_dict[ + 'max_upload_speed' + ] = self.statusbar._on_max_upload_speed # Remove statusbar item. self.statusbar.remove_item(self.status_item) del self.status_item - component.get('PluginManager').deregister_hook('on_apply_prefs', self.on_apply_prefs) - component.get('PluginManager').deregister_hook('on_show_prefs', self.on_show_prefs) + component.get('PluginManager').deregister_hook( + 'on_apply_prefs', self.on_apply_prefs + ) + component.get('PluginManager').deregister_hook( + 'on_show_prefs', self.on_show_prefs + ) def on_apply_prefs(self): log.debug('applying prefs for Scheduler') @@ -221,8 +249,12 @@ class GtkUI(GtkPluginBase): # Skip error due to Plugin being enabled before statusbar items created on startup. pass else: - self.statusbar.config_value_changed_dict['max_download_speed'] = self.statusbar._on_max_download_speed - self.statusbar.config_value_changed_dict['max_upload_speed'] = self.statusbar._on_max_upload_speed + self.statusbar.config_value_changed_dict[ + 'max_download_speed' + ] = self.statusbar._on_max_download_speed + self.statusbar.config_value_changed_dict[ + 'max_upload_speed' + ] = self.statusbar._on_max_upload_speed def update_config_values(config): try: @@ -231,7 +263,10 @@ class GtkUI(GtkPluginBase): except AttributeError: # Skip error due to Plugin being enabled before statusbar items created on startup. pass - client.core.get_config_values(['max_download_speed', 'max_upload_speed']).addCallback(update_config_values) + + client.core.get_config_values( + ['max_download_speed', 'max_upload_speed'] + ).addCallback(update_config_values) def on_status_item_clicked(self, widget, event): component.get('Preferences').show('Scheduler') diff --git a/deluge/plugins/Scheduler/setup.py b/deluge/plugins/Scheduler/setup.py index ea9c6bbcf..16fadd067 100644 --- a/deluge/plugins/Scheduler/setup.py +++ b/deluge/plugins/Scheduler/setup.py @@ -32,11 +32,9 @@ setup( url=__url__, license=__license__, long_description=__long_description__ if __long_description__ else __description__, - packages=find_packages(), namespace_packages=['deluge', 'deluge.plugins'], package_data=__pkg_data__, - entry_points=""" [deluge.plugin.core] %s = deluge.plugins.%s:CorePlugin @@ -44,5 +42,6 @@ setup( %s = deluge.plugins.%s:GtkUIPlugin [deluge.plugin.web] %s = deluge.plugins.%s:WebUIPlugin - """ % ((__plugin_name__, __plugin_name__.lower()) * 3), + """ + % ((__plugin_name__, __plugin_name__.lower()) * 3), ) diff --git a/deluge/plugins/Stats/deluge/plugins/stats/__init__.py b/deluge/plugins/Stats/deluge/plugins/stats/__init__.py index 77c85128e..a40379b9a 100644 --- a/deluge/plugins/Stats/deluge/plugins/stats/__init__.py +++ b/deluge/plugins/Stats/deluge/plugins/stats/__init__.py @@ -19,6 +19,7 @@ from deluge.plugins.init import PluginInitBase class CorePlugin(PluginInitBase): def __init__(self, plugin_name): from .core import Core as _pluginCls + self._plugin_cls = _pluginCls super(CorePlugin, self).__init__(plugin_name) @@ -26,6 +27,7 @@ class CorePlugin(PluginInitBase): class GtkUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .gtkui import GtkUI as _pluginCls + self._plugin_cls = _pluginCls super(GtkUIPlugin, self).__init__(plugin_name) @@ -33,5 +35,6 @@ class GtkUIPlugin(PluginInitBase): class WebUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .webui import WebUI as _pluginCls + self._plugin_cls = _pluginCls super(WebUIPlugin, self).__init__(plugin_name) diff --git a/deluge/plugins/Stats/deluge/plugins/stats/core.py b/deluge/plugins/Stats/deluge/plugins/stats/core.py index a269516a3..635c54db6 100644 --- a/deluge/plugins/Stats/deluge/plugins/stats/core.py +++ b/deluge/plugins/Stats/deluge/plugins/stats/core.py @@ -119,13 +119,15 @@ class Core(CorePluginBase): stats.update(self.core.get_session_status([key])) except AttributeError: pass - stats['num_connections'] = stats['num_peers'] + stats['peer.num_peers_half_open'] + stats['num_connections'] = ( + stats['num_peers'] + stats['peer.num_peers_half_open'] + ) stats['dht_cache_nodes'] = stats['dht.dht_node_cache'] - stats.update(self.core.get_config_values([ - 'max_download', - 'max_upload', - 'max_num_connections', - ])) + stats.update( + self.core.get_config_values( + ['max_download', 'max_upload', 'max_num_connections'] + ) + ) # status = self.core.session.status() # for stat in dir(status): # if not stat.startswith('_') and stat not in stats: @@ -195,7 +197,12 @@ class Core(CorePluginBase): @export def get_session_totals(self): return self.core.get_session_status( - ['total_upload', 'total_download', 'total_payload_upload', 'total_payload_download'], + [ + 'total_upload', + 'total_download', + 'total_payload_upload', + 'total_payload_download', + ] ) @export diff --git a/deluge/plugins/Stats/deluge/plugins/stats/graph.py b/deluge/plugins/Stats/deluge/plugins/stats/graph.py index e87148e70..d0d1e83c6 100644 --- a/deluge/plugins/Stats/deluge/plugins/stats/graph.py +++ b/deluge/plugins/Stats/deluge/plugins/stats/graph.py @@ -136,7 +136,9 @@ class Graph(object): seconds_to_step = math.ceil(start / x_step) * x_step - start for i in range(0, duration // x_step + 1): - text = time.strftime('%H:%M', time.localtime(start + seconds_to_step + i * x_step)) + text = time.strftime( + '%H:%M', time.localtime(start + seconds_to_step + i * x_step) + ) # + 0.5 to allign x to nearest pixel x = int(ratio * (seconds_to_step + i * x_step) + left) + 0.5 self.draw_x_text(text, x, bottom) @@ -171,6 +173,7 @@ class Graph(object): def space_required(text): te = self.ctx.text_extents(text) return math.ceil(te[4] - te[0]) + y_tick_width = max((space_required(text) for text in y_tick_text)) top = font_extents[2] / 2 @@ -220,7 +223,9 @@ class Graph(object): else: interval = interval * 2 - intervals = [i * interval * scale for i in range(1 + int(math.ceil(x / interval)))] + intervals = [ + i * interval * scale for i in range(1 + int(math.ceil(x / interval))) + ] return intervals def draw_left_axis(self, bounds, y_ticks, y_tick_text): @@ -247,7 +252,9 @@ class Graph(object): for stat, info in stats.items(): if len(info['values']) > 0: self.draw_value_poly(info['values'], info['color'], max_value, bounds) - self.draw_value_poly(info['values'], info['fill_color'], max_value, bounds, info['fill']) + self.draw_value_poly( + info['values'], info['fill_color'], max_value, bounds, info['fill'] + ) def draw_legend(self): pass @@ -271,10 +278,7 @@ class Graph(object): self.ctx.line_to(x, int(bottom - value * ratio)) x -= step - self.ctx.line_to( - int(right - (len(values) - 1) * step), - bottom, - ) + self.ctx.line_to(int(right - (len(values) - 1) * step), bottom) self.ctx.close_path() def draw_value_poly(self, values, color, max_value, bounds, fill=False): @@ -305,7 +309,9 @@ class Graph(object): ascent = fe[0] x_bearing = te[0] width = te[4] - self.ctx.move_to(int(x - width - x_bearing - 2), int(y + (ascent - descent) / 2)) + self.ctx.move_to( + int(x - width - x_bearing - 2), int(y + (ascent - descent) / 2) + ) self.ctx.set_source_rgba(*self.black) self.ctx.show_text(text) diff --git a/deluge/plugins/Stats/deluge/plugins/stats/gtkui.py b/deluge/plugins/Stats/deluge/plugins/stats/gtkui.py index f1d664f31..f7083773e 100644 --- a/deluge/plugins/Stats/deluge/plugins/stats/gtkui.py +++ b/deluge/plugins/Stats/deluge/plugins/stats/gtkui.py @@ -43,9 +43,7 @@ DEFAULT_CONF = { 'dht_torrents': str(gtk.gdk.Color('green')), 'num_connections': str(gtk.gdk.Color('darkred')), }, - 'seeds_graph': { - 'num_peers': str(gtk.gdk.Color('blue')), - }, + 'seeds_graph': {'num_peers': str(gtk.gdk.Color('blue'))}, }, } @@ -126,7 +124,9 @@ class GraphsTab(Tab): def graph_expose(self, widget, event): context = self.graph_widget.window.cairo_create() # set a clip region - context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) + context.rectangle( + event.area.x, event.area.y, event.area.width, event.area.height + ) context.clip() self.graph.draw_to_context( context, @@ -143,6 +143,7 @@ class GraphsTab(Tab): def _update_complete(result): self.graph_widget.queue_draw() return result + d1.addCallback(_update_complete) return d1 @@ -158,16 +159,17 @@ class GraphsTab(Tab): self.graph = Graph() colors = self.colors['bandwidth_graph'] self.graph.add_stat( - 'download_rate', label='Download Rate', + 'download_rate', + label='Download Rate', color=gtk_to_graph_color(colors['download_rate']), ) self.graph.add_stat( - 'upload_rate', label='Upload Rate', + 'upload_rate', + label='Upload Rate', color=gtk_to_graph_color(colors['upload_rate']), ) self.graph.set_left_axis( - formatter=fspeed_shortform, min=10240, - formatter_scale=size_formatter_scale, + formatter=fspeed_shortform, min=10240, formatter_scale=size_formatter_scale ) def select_connections_graph(self): @@ -177,9 +179,13 @@ class GraphsTab(Tab): self.graph = g colors = self.colors['connections_graph'] g.add_stat('dht_nodes', color=gtk_to_graph_color(colors['dht_nodes'])) - g.add_stat('dht_cache_nodes', color=gtk_to_graph_color(colors['dht_cache_nodes'])) + g.add_stat( + 'dht_cache_nodes', color=gtk_to_graph_color(colors['dht_cache_nodes']) + ) g.add_stat('dht_torrents', color=gtk_to_graph_color(colors['dht_torrents'])) - g.add_stat('num_connections', color=gtk_to_graph_color(colors['num_connections'])) + g.add_stat( + 'num_connections', color=gtk_to_graph_color(colors['num_connections']) + ) g.set_left_axis(formatter=int_str, min=10) def select_seeds_graph(self): @@ -194,9 +200,7 @@ class GraphsTab(Tab): self.colors = colors # Fake switch page to update the graph colors (HACKY) self._on_notebook_switch_page( - self.notebook, - None, # This is unused - self.notebook.get_current_page(), + self.notebook, None, self.notebook.get_current_page() # This is unused ) def _on_intervals_changed(self, intervals): @@ -233,17 +237,24 @@ class GraphsTab(Tab): class GtkUI(GtkPluginBase): - def enable(self): log.debug('Stats plugin enable called') - self.config = deluge.configmanager.ConfigManager('stats.gtkui.conf', DEFAULT_CONF) + self.config = deluge.configmanager.ConfigManager( + 'stats.gtkui.conf', DEFAULT_CONF + ) self.builder = gtk.Builder() self.builder.add_from_file(get_resource('config.ui')) - component.get('Preferences').add_page('Stats', self.builder.get_object('prefs_box')) - component.get('PluginManager').register_hook('on_apply_prefs', self.on_apply_prefs) - component.get('PluginManager').register_hook('on_show_prefs', self.on_show_prefs) + component.get('Preferences').add_page( + 'Stats', self.builder.get_object('prefs_box') + ) + component.get('PluginManager').register_hook( + 'on_apply_prefs', self.on_apply_prefs + ) + component.get('PluginManager').register_hook( + 'on_show_prefs', self.on_show_prefs + ) self.on_show_prefs() self.graphs_tab = GraphsTab(self.config['colors']) @@ -252,8 +263,12 @@ class GtkUI(GtkPluginBase): def disable(self): component.get('Preferences').remove_page('Stats') - component.get('PluginManager').deregister_hook('on_apply_prefs', self.on_apply_prefs) - component.get('PluginManager').deregister_hook('on_show_prefs', self.on_show_prefs) + component.get('PluginManager').deregister_hook( + 'on_apply_prefs', self.on_apply_prefs + ) + component.get('PluginManager').deregister_hook( + 'on_show_prefs', self.on_show_prefs + ) self.torrent_details.remove_tab(self.graphs_tab.get_name()) def on_apply_prefs(self): diff --git a/deluge/plugins/Stats/deluge/plugins/stats/tests/test_stats.py b/deluge/plugins/Stats/deluge/plugins/stats/tests/test_stats.py index ca5f2bdf7..60dd7768f 100644 --- a/deluge/plugins/Stats/deluge/plugins/stats/tests/test_stats.py +++ b/deluge/plugins/Stats/deluge/plugins/stats/tests/test_stats.py @@ -27,7 +27,6 @@ def print_totals(totals): class StatsTestCase(BaseTestCase): - def set_up(self): defer.setDebugging(True) tests_common.set_tmp_config_dir() diff --git a/deluge/plugins/Stats/setup.py b/deluge/plugins/Stats/setup.py index 593f68271..a3eeb9769 100644 --- a/deluge/plugins/Stats/setup.py +++ b/deluge/plugins/Stats/setup.py @@ -36,11 +36,9 @@ setup( url=__url__, license=__license__, long_description=__long_description__, - packages=find_packages(), namespace_packages=['deluge', 'deluge.plugins'], package_data=__pkg_data__, - entry_points=""" [deluge.plugin.core] %s = deluge.plugins.%s:CorePlugin @@ -48,5 +46,6 @@ setup( %s = deluge.plugins.%s:GtkUIPlugin [deluge.plugin.web] %s = deluge.plugins.%s:WebUIPlugin - """ % ((__plugin_name__, __plugin_name__.lower()) * 3), + """ + % ((__plugin_name__, __plugin_name__.lower()) * 3), ) diff --git a/deluge/plugins/Toggle/deluge/plugins/toggle/__init__.py b/deluge/plugins/Toggle/deluge/plugins/toggle/__init__.py index b83206a77..e63e4aa4c 100644 --- a/deluge/plugins/Toggle/deluge/plugins/toggle/__init__.py +++ b/deluge/plugins/Toggle/deluge/plugins/toggle/__init__.py @@ -20,6 +20,7 @@ from deluge.plugins.init import PluginInitBase class CorePlugin(PluginInitBase): def __init__(self, plugin_name): from .core import Core as _pluginCls + self._plugin_cls = _pluginCls super(CorePlugin, self).__init__(plugin_name) @@ -27,6 +28,7 @@ class CorePlugin(PluginInitBase): class GtkUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .gtkui import GtkUI as _pluginCls + self._plugin_cls = _pluginCls super(GtkUIPlugin, self).__init__(plugin_name) @@ -34,5 +36,6 @@ class GtkUIPlugin(PluginInitBase): class WebUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .webui import WebUI as _pluginCls + self._plugin_cls = _pluginCls super(WebUIPlugin, self).__init__(plugin_name) diff --git a/deluge/plugins/Toggle/deluge/plugins/toggle/core.py b/deluge/plugins/Toggle/deluge/plugins/toggle/core.py index eb734275f..dad52ce61 100644 --- a/deluge/plugins/Toggle/deluge/plugins/toggle/core.py +++ b/deluge/plugins/Toggle/deluge/plugins/toggle/core.py @@ -22,8 +22,7 @@ from deluge.plugins.pluginbase import CorePluginBase log = logging.getLogger(__name__) -DEFAULT_PREFS = { -} +DEFAULT_PREFS = {} class Core(CorePluginBase): diff --git a/deluge/plugins/Toggle/deluge/plugins/toggle/gtkui.py b/deluge/plugins/Toggle/deluge/plugins/toggle/gtkui.py index 7fcce70f0..1be3faef7 100644 --- a/deluge/plugins/Toggle/deluge/plugins/toggle/gtkui.py +++ b/deluge/plugins/Toggle/deluge/plugins/toggle/gtkui.py @@ -29,8 +29,10 @@ class GtkUI(GtkPluginBase): self.plugin = component.get('PluginManager') self.separator = self.plugin.add_toolbar_separator() self.button = self.plugin.add_toolbar_button( - self._on_button_clicked, label='Pause Session', - stock='gtk-media-pause', tooltip='Pause the session', + self._on_button_clicked, + label='Pause Session', + stock='gtk-media-pause', + tooltip='Pause the session', ) def disable(self): @@ -47,6 +49,7 @@ class GtkUI(GtkPluginBase): self.button.set_label('Pause Session') self.button.set_tooltip_text('Pause the session') self.button.set_stock_id('gtk-media-pause') + self.core.get_status().addCallback(_on_get_status) def _on_button_clicked(self, widget): diff --git a/deluge/plugins/Toggle/setup.py b/deluge/plugins/Toggle/setup.py index d3a754bdc..3dcf83f4d 100644 --- a/deluge/plugins/Toggle/setup.py +++ b/deluge/plugins/Toggle/setup.py @@ -33,11 +33,9 @@ setup( url=__url__, license=__license__, long_description=__long_description__ if __long_description__ else __description__, - packages=find_packages(), namespace_packages=['deluge', 'deluge.plugins'], package_data=__pkg_data__, - entry_points=""" [deluge.plugin.core] %s = deluge.plugins.%s:CorePlugin @@ -45,5 +43,6 @@ setup( %s = deluge.plugins.%s:GtkUIPlugin [deluge.plugin.web] %s = deluge.plugins.%s:WebUIPlugin - """ % ((__plugin_name__, __plugin_name__.lower()) * 3), + """ + % ((__plugin_name__, __plugin_name__.lower()) * 3), ) diff --git a/deluge/plugins/WebUi/deluge/plugins/webui/__init__.py b/deluge/plugins/WebUi/deluge/plugins/webui/__init__.py index 2e53a6b59..a3d29805a 100644 --- a/deluge/plugins/WebUi/deluge/plugins/webui/__init__.py +++ b/deluge/plugins/WebUi/deluge/plugins/webui/__init__.py @@ -19,6 +19,7 @@ from deluge.plugins.init import PluginInitBase class CorePlugin(PluginInitBase): def __init__(self, plugin_name): from .core import Core as _pluginCls + self._plugin_cls = _pluginCls super(CorePlugin, self).__init__(plugin_name) @@ -26,6 +27,7 @@ class CorePlugin(PluginInitBase): class GtkUIPlugin(PluginInitBase): def __init__(self, plugin_name): from .gtkui import GtkUI as _pluginCls + self._plugin_cls = _pluginCls super(GtkUIPlugin, self).__init__(plugin_name) @@ -33,5 +35,6 @@ class GtkUIPlugin(PluginInitBase): class WebUIPlugin(PluginInitBase): def __init__(self, plugin_name): from webui import WebUI as _pluginCls + self._plugin_cls = _pluginCls super(WebUIPlugin, self).__init__(plugin_name) diff --git a/deluge/plugins/WebUi/deluge/plugins/webui/core.py b/deluge/plugins/WebUi/deluge/plugins/webui/core.py index 72923a6f0..cc3330fc0 100644 --- a/deluge/plugins/WebUi/deluge/plugins/webui/core.py +++ b/deluge/plugins/WebUi/deluge/plugins/webui/core.py @@ -30,11 +30,7 @@ except ImportError: log = logging.getLogger(__name__) -DEFAULT_PREFS = { - 'enabled': False, - 'ssl': False, - 'port': 8112, -} +DEFAULT_PREFS = {'enabled': False, 'ssl': False, 'port': 8112} class Core(CorePluginBase): diff --git a/deluge/plugins/WebUi/deluge/plugins/webui/gtkui.py b/deluge/plugins/WebUi/deluge/plugins/webui/gtkui.py index 992c9b038..f72769868 100644 --- a/deluge/plugins/WebUi/deluge/plugins/webui/gtkui.py +++ b/deluge/plugins/WebUi/deluge/plugins/webui/gtkui.py @@ -31,16 +31,26 @@ class GtkUI(GtkPluginBase): self.builder = gtk.Builder() self.builder.add_from_file(get_resource('config.ui')) - component.get('Preferences').add_page(_('WebUi'), self.builder.get_object('prefs_box')) - component.get('PluginManager').register_hook('on_apply_prefs', self.on_apply_prefs) - component.get('PluginManager').register_hook('on_show_prefs', self.on_show_prefs) + component.get('Preferences').add_page( + _('WebUi'), self.builder.get_object('prefs_box') + ) + component.get('PluginManager').register_hook( + 'on_apply_prefs', self.on_apply_prefs + ) + component.get('PluginManager').register_hook( + 'on_show_prefs', self.on_show_prefs + ) client.webui.get_config().addCallback(self.cb_get_config) client.webui.got_deluge_web().addCallback(self.cb_chk_deluge_web) def disable(self): component.get('Preferences').remove_page(_('WebUi')) - component.get('PluginManager').deregister_hook('on_apply_prefs', self.on_apply_prefs) - component.get('PluginManager').deregister_hook('on_show_prefs', self.on_show_prefs) + component.get('PluginManager').deregister_hook( + 'on_apply_prefs', self.on_apply_prefs + ) + component.get('PluginManager').deregister_hook( + 'on_show_prefs', self.on_show_prefs + ) def on_apply_prefs(self): if not self.have_web: @@ -71,14 +81,18 @@ class GtkUI(GtkPluginBase): vbox = self.builder.get_object('prefs_box') hbox = gtk.HBox() - icon = gtk.image_new_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_SMALL_TOOLBAR) + icon = gtk.image_new_from_stock( + gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_SMALL_TOOLBAR + ) icon.set_padding(5, 5) hbox.pack_start(icon, False, False) - label = gtk.Label(_( - 'The Deluge web interface is not installed, ' - 'please install the\ninterface and try again', - )) + label = gtk.Label( + _( + 'The Deluge web interface is not installed, ' + 'please install the\ninterface and try again' + ) + ) label.set_alignment(0, 0.5) label.set_padding(5, 5) hbox.pack_start(label) diff --git a/deluge/plugins/WebUi/deluge/plugins/webui/tests/test_plugin_webui.py b/deluge/plugins/WebUi/deluge/plugins/webui/tests/test_plugin_webui.py index 8f70a19b3..56e1cc023 100644 --- a/deluge/plugins/WebUi/deluge/plugins/webui/tests/test_plugin_webui.py +++ b/deluge/plugins/WebUi/deluge/plugins/webui/tests/test_plugin_webui.py @@ -21,7 +21,6 @@ common.disable_new_release_check() class WebUIPluginTestCase(BaseTestCase): - def set_up(self): common.set_tmp_config_dir() self.rpcserver = RPCServer(listen=False) @@ -29,10 +28,10 @@ class WebUIPluginTestCase(BaseTestCase): return component.start() def tear_down(self): - def on_shutdown(result): del self.rpcserver del self.core + return component.shutdown().addCallback(on_shutdown) def test_enable_webui(self): diff --git a/deluge/plugins/WebUi/setup.py b/deluge/plugins/WebUi/setup.py index 7bc61259e..646467882 100644 --- a/deluge/plugins/WebUi/setup.py +++ b/deluge/plugins/WebUi/setup.py @@ -32,15 +32,14 @@ setup( url=__url__, license=__license__, long_description=__long_description__ if __long_description__ else __description__, - packages=find_packages(), namespace_packages=['deluge', 'deluge.plugins'], package_data=__pkg_data__, - entry_points=""" [deluge.plugin.core] %s = deluge.plugins.%s:CorePlugin [deluge.plugin.gtkui] %s = deluge.plugins.%s:GtkUIPlugin - """ % ((__plugin_name__, __plugin_name__.lower()) * 2), + """ + % ((__plugin_name__, __plugin_name__.lower()) * 2), ) diff --git a/deluge/plugins/pluginbase.py b/deluge/plugins/pluginbase.py index 5bc3e1c59..4ae268d68 100644 --- a/deluge/plugins/pluginbase.py +++ b/deluge/plugins/pluginbase.py @@ -31,7 +31,6 @@ class PluginBase(component.Component): class CorePluginBase(PluginBase): - def __init__(self, plugin_name): super(CorePluginBase, self).__init__('CorePlugin.' + plugin_name) # Register RPC methods @@ -49,7 +48,6 @@ class CorePluginBase(PluginBase): class GtkPluginBase(PluginBase): - def __init__(self, plugin_name): super(GtkPluginBase, self).__init__('GtkPlugin.' + plugin_name) log.debug('GtkPlugin initialized..') diff --git a/deluge/rencode.py b/deluge/rencode.py index 7f5378dee..2237dde3a 100644 --- a/deluge/rencode.py +++ b/deluge/rencode.py @@ -78,10 +78,14 @@ if py3: def int2byte(c): return bytes([c]) + + else: + def int2byte(c): return chr(c) + # Default number of bits for serialized floats, either 32 or 64 (also a parameter for dumps()). DEFAULT_FLOAT_BITS = 32 @@ -137,44 +141,44 @@ def decode_int(x, f): n = int(x[f:newf]) except (OverflowError, ValueError): n = long(x[f:newf]) - if x[f:f + 1] == '-': - if x[f + 1:f + 2] == '0': + if x[f : f + 1] == '-': + if x[f + 1 : f + 2] == '0': raise ValueError - elif x[f:f + 1] == '0' and newf != f + 1: + elif x[f : f + 1] == '0' and newf != f + 1: raise ValueError return (n, newf + 1) def decode_intb(x, f): f += 1 - return (struct.unpack('!b', x[f:f + 1])[0], f + 1) + return (struct.unpack('!b', x[f : f + 1])[0], f + 1) def decode_inth(x, f): f += 1 - return (struct.unpack('!h', x[f:f + 2])[0], f + 2) + return (struct.unpack('!h', x[f : f + 2])[0], f + 2) def decode_intl(x, f): f += 1 - return (struct.unpack('!l', x[f:f + 4])[0], f + 4) + return (struct.unpack('!l', x[f : f + 4])[0], f + 4) def decode_intq(x, f): f += 1 - return (struct.unpack('!q', x[f:f + 8])[0], f + 8) + return (struct.unpack('!q', x[f : f + 8])[0], f + 8) def decode_float32(x, f): f += 1 - n = struct.unpack('!f', x[f:f + 4])[0] + n = struct.unpack('!f', x[f : f + 4])[0] return (n, f + 4) def decode_float64(x, f): f += 1 - n = struct.unpack('!d', x[f:f + 8])[0] + n = struct.unpack('!d', x[f : f + 8])[0] return (n, f + 8) @@ -187,7 +191,7 @@ def decode_string(x, f): if x[f] == '0' and colon != f + 1: raise ValueError colon += 1 - s = x[colon:colon + n] + s = x[colon : colon + n] if _decode_utf8: s = s.decode('utf8') return (s, colon + n) @@ -195,17 +199,17 @@ def decode_string(x, f): def decode_list(x, f): r, f = [], f + 1 - while x[f:f + 1] != CHR_TERM: - v, f = decode_func[x[f:f + 1]](x, f) + while x[f : f + 1] != CHR_TERM: + v, f = decode_func[x[f : f + 1]](x, f) r.append(v) return (tuple(r), f + 1) def decode_dict(x, f): r, f = {}, f + 1 - while x[f:f + 1] != CHR_TERM: - k, f = decode_func[x[f:f + 1]](x, f) - r[k], f = decode_func[x[f:f + 1]](x, f) + while x[f : f + 1] != CHR_TERM: + k, f = decode_func[x[f : f + 1]](x, f) + r[k], f = decode_func[x[f : f + 1]](x, f) return (r, f + 1) @@ -249,11 +253,13 @@ decode_func[CHR_NONE] = decode_none def make_fixed_length_string_decoders(): def make_decoder(slen): def f(x, f): - s = x[f + 1:f + 1 + slen] + s = x[f + 1 : f + 1 + slen] if _decode_utf8: s = s.decode('utf8') return (s, f + 1 + slen) + return f + for i in range(STR_FIXED_COUNT): decode_func[int2byte(STR_FIXED_START + i)] = make_decoder(i) @@ -266,10 +272,12 @@ def make_fixed_length_list_decoders(): def f(x, f): r, f = [], f + 1 for _ in range(slen): - v, f = decode_func[x[f:f + 1]](x, f) + v, f = decode_func[x[f : f + 1]](x, f) r.append(v) return (tuple(r), f) + return f + for i in range(LIST_FIXED_COUNT): decode_func[int2byte(LIST_FIXED_START + i)] = make_decoder(i) @@ -281,7 +289,9 @@ def make_fixed_length_int_decoders(): def make_decoder(j): def f(x, f): return (j, f + 1) + return f + for i in range(INT_POS_FIXED_COUNT): decode_func[int2byte(INT_POS_FIXED_START + i)] = make_decoder(i) for i in range(INT_NEG_FIXED_COUNT): @@ -296,10 +306,12 @@ def make_fixed_length_dict_decoders(): def f(x, f): r, f = {}, f + 1 for _ in range(slen): - k, f = decode_func[x[f:f + 1]](x, f) - r[k], f = decode_func[x[f:f + 1]](x, f) + k, f = decode_func[x[f : f + 1]](x, f) + r[k], f = decode_func[x[f : f + 1]](x, f) return (r, f) + return f + for i in range(DICT_FIXED_COUNT): decode_func[int2byte(DICT_FIXED_START + i)] = make_decoder(i) @@ -436,17 +448,45 @@ def test(): f3 = struct.unpack('!f', struct.pack('!f', -0.6))[0] ld = ( ( - {b'a': 15, b'bb': f1, b'ccc': f2, b'': (f3, (), False, True, b'')}, (b'a', 10**20), - tuple(range(-100000, 100000)), b'b' * 31, b'b' * 62, b'b' * 64, 2**30, 2**33, 2**62, - 2**64, 2**30, 2**33, 2**62, 2**64, False, False, True, -1, 2, 0, + {b'a': 15, b'bb': f1, b'ccc': f2, b'': (f3, (), False, True, b'')}, + (b'a', 10 ** 20), + tuple(range(-100000, 100000)), + b'b' * 31, + b'b' * 62, + b'b' * 64, + 2 ** 30, + 2 ** 33, + 2 ** 62, + 2 ** 64, + 2 ** 30, + 2 ** 33, + 2 ** 62, + 2 ** 64, + False, + False, + True, + -1, + 2, + 0, ), ) assert loads(dumps(ld)) == ld d = dict(zip(range(-100000, 100000), range(-100000, 100000))) - d.update({b'a': 20, 20: 40, 40: 41, f1: f2, f2: f3, f3: False, False: True, True: False}) + d.update( + {b'a': 20, 20: 40, 40: 41, f1: f2, f2: f3, f3: False, False: True, True: False} + ) ld = (d, {}, {5: 6}, {7: 7, True: 8}, {9: 10, 22: 39, 49: 50, 44: b''}) assert loads(dumps(ld)) == ld - ld = (b'', b'a' * 10, b'a' * 100, b'a' * 1000, b'a' * 10000, b'a' * 100000, b'a' * 1000000, b'a' * 10000000) + ld = ( + b'', + b'a' * 10, + b'a' * 100, + b'a' * 1000, + b'a' * 10000, + b'a' * 100000, + b'a' * 1000000, + b'a' * 10000000, + ) assert loads(dumps(ld)) == ld ld = tuple(dict(zip(range(n), range(n))) for n in range(100)) + (b'b',) assert loads(dumps(ld)) == ld @@ -468,6 +508,7 @@ def test(): try: import psyco + psyco.bind(dumps) psyco.bind(loads) except ImportError: diff --git a/deluge/scripts/create_plugin.py b/deluge/scripts/create_plugin.py index 93b6f8d95..aa58e3425 100644 --- a/deluge/scripts/create_plugin.py +++ b/deluge/scripts/create_plugin.py @@ -17,19 +17,35 @@ from datetime import datetime import deluge.common parser = ArgumentParser() -parser.add_argument('-n', '--name', metavar='<plugin name>', required=True, help='Plugin name') +parser.add_argument( + '-n', '--name', metavar='<plugin name>', required=True, help='Plugin name' +) parser.add_argument('-m', '--module-name', metavar='<module name>', help='Module name') -parser.add_argument('-p', '--basepath', metavar='<path>', required=True, help='Base path') parser.add_argument( - '-a', '--author-name', metavar='<author name>', required=True, + '-p', '--basepath', metavar='<path>', required=True, help='Base path' +) +parser.add_argument( + '-a', + '--author-name', + metavar='<author name>', + required=True, help='Author name,for the GPL header', ) parser.add_argument( - '-e', '--author-email', metavar='<author email>', required=True, + '-e', + '--author-email', + metavar='<author email>', + required=True, help='Author email,for the GPL header', ) parser.add_argument('-u', '--url', metavar='<URL>', help='Homepage URL') -parser.add_argument('-c', '--config', metavar='<Config dir>', dest='configdir', help='Location of deluge configuration') +parser.add_argument( + '-c', + '--config', + metavar='<Config dir>', + dest='configdir', + help='Location of deluge configuration', +) options = parser.parse_args() diff --git a/deluge/scripts/deluge_remote.py b/deluge/scripts/deluge_remote.py index c1b5de7b4..bacc4f88d 100644 --- a/deluge/scripts/deluge_remote.py +++ b/deluge/scripts/deluge_remote.py @@ -32,33 +32,48 @@ def is_float_digit(string): # set up command-line options parser = OptionParser() -parser.add_option('--port', help='port for deluge backend host (default: 58846)', default='58846', dest='port') parser.add_option( - '--host', help='hostname of deluge backend to connect to (default: localhost)', - default='localhost', dest='host', + '--port', + help='port for deluge backend host (default: 58846)', + default='58846', + dest='port', ) parser.add_option( - '--max_active_limit', dest='max_active_limit', + '--host', + help='hostname of deluge backend to connect to (default: localhost)', + default='localhost', + dest='host', +) +parser.add_option( + '--max_active_limit', + dest='max_active_limit', help='sets the absolute maximum number of active torrents on the deluge backend', ) parser.add_option( - '--max_active_downloading', dest='max_active_downloading', + '--max_active_downloading', + dest='max_active_downloading', help='sets the maximum number of active downloading torrents on the deluge backend', ) parser.add_option( - '--max_active_seeding', dest='max_active_seeding', + '--max_active_seeding', + dest='max_active_seeding', help='sets the maximum number of active seeding torrents on the deluge backend', ) parser.add_option( - '--max_download_speed', help='sets the maximum global download speed on the deluge backend', + '--max_download_speed', + help='sets the maximum global download speed on the deluge backend', dest='max_download_speed', ) parser.add_option( - '--max_upload_speed', help='sets the maximum global upload speed on the deluge backend', + '--max_upload_speed', + help='sets the maximum global upload speed on the deluge backend', dest='max_upload_speed', ) parser.add_option( - '--debug', help='outputs debug information to the console', default=False, action='store_true', + '--debug', + help='outputs debug information to the console', + default=False, + action='store_true', dest='debug', ) @@ -79,7 +94,10 @@ if options.max_active_limit: sys.exit(-1) if options.max_active_downloading: - if options.max_active_downloading.isdigit() and int(options.max_active_downloading) >= 0: + if ( + options.max_active_downloading.isdigit() + and int(options.max_active_downloading) >= 0 + ): settings['max_active_downloading'] = int(options.max_active_downloading) else: sys.stderr.write('ERROR: Invalid max_active_downloading parameter!\n') @@ -94,7 +112,8 @@ if options.max_active_seeding: if options.max_download_speed: if is_float_digit(options.max_download_speed) and ( - float(options.max_download_speed) >= 0.0 or float(options.max_download_speed) == -1.0 + float(options.max_download_speed) >= 0.0 + or float(options.max_download_speed) == -1.0 ): settings['max_download_speed'] = float(options.max_download_speed) else: @@ -103,7 +122,8 @@ if options.max_download_speed: if options.max_upload_speed: if is_float_digit(options.max_upload_speed) and ( - float(options.max_upload_speed) >= 0.0 or float(options.max_upload_speed) == -1.0 + float(options.max_upload_speed) >= 0.0 + or float(options.max_upload_speed) == -1.0 ): settings['max_upload_speed'] = float(options.max_upload_speed) else: @@ -114,6 +134,7 @@ if options.max_upload_speed: if settings: # create connection to daemon from deluge.ui.client import sclient as client + client.set_core_uri('http://' + options.host + ':' + options.port) # commit configurations changes diff --git a/deluge/tests/basetest.py b/deluge/tests/basetest.py index 19796dca2..11ca18e53 100644 --- a/deluge/tests/basetest.py +++ b/deluge/tests/basetest.py @@ -22,13 +22,14 @@ class BaseTestCase(unittest.TestCase): have finished. """ + def setUp(self): # NOQA: N803 if len(component._ComponentRegistry.components) != 0: warnings.warn( 'The component._ComponentRegistry.components is not empty on test setup.\n' - 'This is probably caused by another test that did not clean up after finishing!: %s' % - component._ComponentRegistry.components, + 'This is probably caused by another test that did not clean up after finishing!: %s' + % component._ComponentRegistry.components ) d = maybeDeferred(self.set_up) diff --git a/deluge/tests/common.py b/deluge/tests/common.py index 877b67654..4ca34d400 100644 --- a/deluge/tests/common.py +++ b/deluge/tests/common.py @@ -60,7 +60,6 @@ def todo_test(caller): def add_watchdog(deferred, timeout=0.05, message=None): - def callback(value): if not watchdog.called and not watchdog.cancelled: watchdog.cancel() @@ -102,8 +101,9 @@ class ReactorOverride(object): class ProcessOutputHandler(protocol.ProcessProtocol): - - def __init__(self, script, callbacks, logfile=None, print_stdout=True, print_stderr=True): + def __init__( + self, script, callbacks, logfile=None, print_stdout=True, print_stderr=True + ): """Executes a script and handle the process' output to stdout and stderr. Args: @@ -210,8 +210,14 @@ class ProcessOutputHandler(protocol.ProcessProtocol): def start_core( - listen_port=58846, logfile=None, timeout=10, timeout_msg=None, - custom_script='', print_stdout=True, print_stderr=True, extra_callbacks=None, + listen_port=58846, + logfile=None, + timeout=10, + timeout_msg=None, + custom_script='', + print_stdout=True, + print_stderr=True, + extra_callbacks=None, ): """Start the deluge core as a daemon. @@ -252,7 +258,11 @@ try: except Exception: import traceback sys.stderr.write('Exception raised:\\n %%s' %% traceback.format_exc()) -""" % {'dir': config_directory, 'port': listen_port, 'script': custom_script} +""" % { + 'dir': config_directory, + 'port': listen_port, + 'script': custom_script, + } callbacks = [] default_core_cb = {'deferred': Deferred(), 'types': 'stdout'} @@ -263,15 +273,20 @@ except Exception: default_core_cb['triggers'] = [ {'expr': 'Finished loading ', 'value': lambda reader, data, data_all: reader}, { - 'expr': 'Could not listen on localhost:%d' % (listen_port), 'type': 'errback', # Error from libtorrent + 'expr': 'Could not listen on localhost:%d' % (listen_port), + 'type': 'errback', # Error from libtorrent 'value': lambda reader, data, data_all: CannotListenError( - 'localhost', listen_port, + 'localhost', + listen_port, 'Could not start deluge test client!\n%s' % data, ), }, { - 'expr': 'Traceback', 'type': 'errback', - 'value': lambda reader, data, data_all: DelugeError('Traceback found when starting daemon:\n%s' % data), + 'expr': 'Traceback', + 'type': 'errback', + 'value': lambda reader, data, data_all: DelugeError( + 'Traceback found when starting daemon:\n%s' % data + ), }, ] @@ -279,11 +294,15 @@ except Exception: if extra_callbacks: callbacks.extend(extra_callbacks) - process_protocol = start_process(daemon_script, callbacks, logfile, print_stdout, print_stderr) + process_protocol = start_process( + daemon_script, callbacks, logfile, print_stdout, print_stderr + ) return default_core_cb['deferred'], process_protocol -def start_process(script, callbacks, logfile=None, print_stdout=True, print_stderr=True): +def start_process( + script, callbacks, logfile=None, print_stdout=True, print_stderr=True +): """ Starts an external python process which executes the given script. @@ -309,13 +328,19 @@ def start_process(script, callbacks, logfile=None, print_stdout=True, print_stde """ cwd = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) - process_protocol = ProcessOutputHandler(script.encode('utf8'), callbacks, logfile, print_stdout, print_stderr) + process_protocol = ProcessOutputHandler( + script.encode('utf8'), callbacks, logfile, print_stdout, print_stderr + ) # Add timeouts to deferreds for c in callbacks: if 'timeout' in c: - w = add_watchdog(c['deferred'], timeout=c['timeout'], message=c.get('timeout_msg', None)) + w = add_watchdog( + c['deferred'], timeout=c['timeout'], message=c.get('timeout_msg', None) + ) process_protocol.watchdogs.append(w) - reactor.spawnProcess(process_protocol, sys.executable, args=[sys.executable], path=cwd) + reactor.spawnProcess( + process_protocol, sys.executable, args=[sys.executable], path=cwd + ) return process_protocol diff --git a/deluge/tests/common_web.py b/deluge/tests/common_web.py index b9a7ee959..7669fd4da 100644 --- a/deluge/tests/common_web.py +++ b/deluge/tests/common_web.py @@ -26,6 +26,7 @@ class WebServerTestBase(BaseTestCase, DaemonBase): Base class for tests that need a running webapi """ + def set_up(self): self.host_id = None deluge.ui.web.server.reactor = ReactorOverride() @@ -60,16 +61,16 @@ class WebServerMockBase(object): Class with utility functions for mocking with tests using the webserver """ - def mock_authentication_ignore(self, auth): + def mock_authentication_ignore(self, auth): def check_request(request, method=None, level=None): pass self.patch(auth, 'check_request', check_request) def mock_compress_body(self): - def compress(contents, request): return contents + # Patch compress to avoid having to decompress output with zlib self.patch(deluge.ui.web.json_api, 'compress', compress) diff --git a/deluge/tests/daemon_base.py b/deluge/tests/daemon_base.py index 9cd286ba3..eda2193e9 100644 --- a/deluge/tests/daemon_base.py +++ b/deluge/tests/daemon_base.py @@ -41,8 +41,15 @@ class DaemonBase(object): @defer.inlineCallbacks def start_core( - self, arg, custom_script='', logfile='', print_stdout=True, print_stderr=True, timeout=5, - port_range=10, extra_callbacks=None, + self, + arg, + custom_script='', + logfile='', + print_stdout=True, + print_stderr=True, + timeout=5, + port_range=10, + extra_callbacks=None, ): if logfile == '': logfile = 'daemon_%s.log' % self.id() @@ -59,8 +66,10 @@ class DaemonBase(object): for dummy in range(port_range): try: d, self.core = common.start_core( - listen_port=self.listen_port, logfile=logfile, - timeout=timeout, timeout_msg='Timeout!', + listen_port=self.listen_port, + logfile=logfile, + timeout=timeout, + timeout_msg='Timeout!', custom_script=custom_script, print_stdout=print_stdout, print_stderr=print_stderr, diff --git a/deluge/tests/test_alertmanager.py b/deluge/tests/test_alertmanager.py index 0f740394a..f197882cd 100644 --- a/deluge/tests/test_alertmanager.py +++ b/deluge/tests/test_alertmanager.py @@ -14,7 +14,6 @@ from .basetest import BaseTestCase class AlertManagerTestCase(BaseTestCase): - def set_up(self): self.core = Core() self.core.config.config['lsd'] = False diff --git a/deluge/tests/test_authmanager.py b/deluge/tests/test_authmanager.py index d714a5a82..91e122f73 100644 --- a/deluge/tests/test_authmanager.py +++ b/deluge/tests/test_authmanager.py @@ -24,7 +24,4 @@ class AuthManagerTestCase(BaseTestCase): return component.shutdown() def test_authorize(self): - self.assertEqual( - self.auth.authorize(*get_localhost_auth()), - AUTH_LEVEL_ADMIN, - ) + self.assertEqual(self.auth.authorize(*get_localhost_auth()), AUTH_LEVEL_ADMIN) diff --git a/deluge/tests/test_bencode.py b/deluge/tests/test_bencode.py index 760a691af..bdad16fa4 100644 --- a/deluge/tests/test_bencode.py +++ b/deluge/tests/test_bencode.py @@ -14,7 +14,6 @@ from . import common class BencodeTestCase(unittest.TestCase): - def test_bencode_unicode_metainfo(self): filename = common.get_test_data_file('test.torrent') with open(filename, 'rb') as _file: diff --git a/deluge/tests/test_client.py b/deluge/tests/test_client.py index 9a237f261..c89ad5309 100644 --- a/deluge/tests/test_client.py +++ b/deluge/tests/test_client.py @@ -35,9 +35,12 @@ class NoVersionSendingDaemonSSLProxy(DaemonSSLProxy): class NoVersionSendingClient(Client): - def connect( - self, host='127.0.0.1', port=58846, username='', password='', + self, + host='127.0.0.1', + port=58846, + username='', + password='', skip_authentication=False, ): self._daemon_proxy = NoVersionSendingDaemonSSLProxy() @@ -104,7 +107,9 @@ class ClientTestCase(BaseTestCase, DaemonBase): def test_connect_localclient(self): username, password = get_localhost_auth() - d = client.connect('localhost', self.listen_port, username=username, password=password) + d = client.connect( + 'localhost', self.listen_port, username=username, password=password + ) def on_connect(result): self.assertEqual(client.get_auth_level(), AUTH_LEVEL_ADMIN) @@ -116,13 +121,12 @@ class ClientTestCase(BaseTestCase, DaemonBase): def test_connect_bad_password(self): username, password = get_localhost_auth() - d = client.connect('localhost', self.listen_port, username=username, password=password + '1') + d = client.connect( + 'localhost', self.listen_port, username=username, password=password + '1' + ) def on_failure(failure): - self.assertEqual( - failure.trap(error.BadLoginError), - error.BadLoginError, - ) + self.assertEqual(failure.trap(error.BadLoginError), error.BadLoginError) self.assertEqual(failure.value.message, 'Password does not match') self.addCleanup(client.disconnect) @@ -134,10 +138,7 @@ class ClientTestCase(BaseTestCase, DaemonBase): d = client.connect('localhost', self.listen_port, username='invalid-user') def on_failure(failure): - self.assertEqual( - failure.trap(error.BadLoginError), - error.BadLoginError, - ) + self.assertEqual(failure.trap(error.BadLoginError), error.BadLoginError) self.assertEqual(failure.value.message, 'Username does not exist') self.addCleanup(client.disconnect) @@ -150,8 +151,7 @@ class ClientTestCase(BaseTestCase, DaemonBase): def on_failure(failure): self.assertEqual( - failure.trap(error.AuthenticationRequired), - error.AuthenticationRequired, + failure.trap(error.AuthenticationRequired), error.AuthenticationRequired ) self.assertEqual(failure.value.username, username) self.addCleanup(client.disconnect) @@ -162,10 +162,14 @@ class ClientTestCase(BaseTestCase, DaemonBase): @defer.inlineCallbacks def test_connect_with_password(self): username, password = get_localhost_auth() - yield client.connect('localhost', self.listen_port, username=username, password=password) + yield client.connect( + 'localhost', self.listen_port, username=username, password=password + ) yield client.core.create_account('testuser', 'testpw', 'DEFAULT') yield client.disconnect() - ret = yield client.connect('localhost', self.listen_port, username='testuser', password='testpw') + ret = yield client.connect( + 'localhost', self.listen_port, username='testuser', password='testpw' + ) self.assertEqual(ret, AUTH_LEVEL_NORMAL) yield @@ -175,8 +179,11 @@ class ClientTestCase(BaseTestCase, DaemonBase): d = client.core.invalid_method() def on_failure(failure): - self.assertEqual(failure.trap(error.WrappedException), error.WrappedException) + self.assertEqual( + failure.trap(error.WrappedException), error.WrappedException + ) self.addCleanup(client.disconnect) + d.addCallbacks(self.fail, on_failure) yield d @@ -184,13 +191,12 @@ class ClientTestCase(BaseTestCase, DaemonBase): username, password = get_localhost_auth() no_version_sending_client = NoVersionSendingClient() d = no_version_sending_client.connect( - 'localhost', self.listen_port, username=username, password=password, + 'localhost', self.listen_port, username=username, password=password ) def on_failure(failure): self.assertEqual( - failure.trap(error.IncompatibleClient), - error.IncompatibleClient, + failure.trap(error.IncompatibleClient), error.IncompatibleClient ) self.addCleanup(no_version_sending_client.disconnect) diff --git a/deluge/tests/test_common.py b/deluge/tests/test_common.py index 0a35ce6af..9b48ae220 100644 --- a/deluge/tests/test_common.py +++ b/deluge/tests/test_common.py @@ -12,8 +12,24 @@ import tarfile from twisted.trial import unittest -from deluge.common import (VersionSplit, archive_files, fdate, fpcnt, fpeer, fsize, fspeed, ftime, get_path_size, - is_infohash, is_ip, is_ipv4, is_ipv6, is_magnet, is_url, windows_check) +from deluge.common import ( + VersionSplit, + archive_files, + fdate, + fpcnt, + fpeer, + fsize, + fspeed, + ftime, + get_path_size, + is_infohash, + is_ip, + is_ipv4, + is_ipv6, + is_magnet, + is_url, + windows_check, +) from deluge.ui.translations_util import setup_translations from .common import get_test_data_file, set_tmp_config_dir @@ -73,7 +89,9 @@ class CommonTestCase(unittest.TestCase): self.assertFalse(is_url('file://test.torrent')) def test_is_magnet(self): - self.assertTrue(is_magnet('magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN')) + self.assertTrue( + is_magnet('magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN') + ) self.assertFalse(is_magnet(None)) def test_is_infohash(self): @@ -121,23 +139,26 @@ class CommonTestCase(unittest.TestCase): def test_parse_human_size(self): from deluge.common import parse_human_size + sizes = [ ('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)), - ('1M', 10**6), - ('1MB', 10**6), - ('1 GB', 10**9), - ('1 TB', 10**12), + ('1MiB', 2 ** (10 * 2)), + ('1 MiB', 2 ** (10 * 2)), + ('1 GiB', 2 ** (10 * 3)), + ('1 GiB', 2 ** (10 * 3)), + ('1M', 10 ** 6), + ('1MB', 10 ** 6), + ('1 GB', 10 ** 9), + ('1 TB', 10 ** 12), ] for human_size, byte_size in sizes: parsed = parse_human_size(human_size) - self.assertEqual(parsed, byte_size, 'Mismatch when converting: %s' % human_size) + self.assertEqual( + parsed, byte_size, 'Mismatch when converting: %s' % human_size + ) def test_archive_files(self): arc_filelist = [ @@ -149,4 +170,6 @@ class CommonTestCase(unittest.TestCase): with tarfile.open(arc_filepath, 'r') as tar: for tar_info in tar: self.assertTrue(tar_info.isfile()) - self.assertTrue(tar_info.name in [os.path.basename(arcf) for arcf in arc_filelist]) + self.assertTrue( + tar_info.name in [os.path.basename(arcf) for arcf in arc_filelist] + ) diff --git a/deluge/tests/test_component.py b/deluge/tests/test_component.py index 06639b0aa..26f24ad00 100644 --- a/deluge/tests/test_component.py +++ b/deluge/tests/test_component.py @@ -16,7 +16,6 @@ from .basetest import BaseTestCase class ComponentTester(component.Component): - def __init__(self, name, depend=None): component.Component.__init__(self, name, depend=depend) self.start_count = 0 @@ -30,20 +29,21 @@ class ComponentTester(component.Component): class ComponentTesterDelayStart(ComponentTester): - def start(self): def do_sleep(): import time + time.sleep(1) + d = threads.deferToThread(do_sleep) def on_done(result): self.start_count += 1 + return d.addCallback(on_done) class ComponentTesterUpdate(component.Component): - def __init__(self, name): component.Component.__init__(self, name) self.counter = 0 @@ -58,7 +58,6 @@ class ComponentTesterUpdate(component.Component): class ComponentTesterShutdown(component.Component): - def __init__(self, name): component.Component.__init__(self, name) self.shutdowned = False @@ -72,7 +71,6 @@ class ComponentTesterShutdown(component.Component): class ComponentTestClass(BaseTestCase): - def tear_down(self): return component.shutdown() @@ -98,7 +96,9 @@ class ComponentTestClass(BaseTestCase): self.assertEqual(c2._component_state, 'Started') self.assertEqual(c1.start_count, 1) self.assertEqual(c2.start_count, 1) - return component.stop(['test_start_depends_c1']).addCallback(on_stop, c1, c2) + return component.stop(['test_start_depends_c1']).addCallback( + on_stop, c1, c2 + ) c1 = ComponentTester('test_start_depends_c1') c2 = ComponentTester('test_start_depends_c2', depend=['test_start_depends_c1']) @@ -110,7 +110,9 @@ class ComponentTestClass(BaseTestCase): def start_with_depends(self): c1 = ComponentTesterDelayStart('test_start_all_c1') c2 = ComponentTester('test_start_all_c2', depend=['test_start_all_c4']) - c3 = ComponentTesterDelayStart('test_start_all_c3', depend=['test_start_all_c5', 'test_start_all_c1']) + c3 = ComponentTesterDelayStart( + 'test_start_all_c3', depend=['test_start_all_c5', 'test_start_all_c1'] + ) c4 = ComponentTester('test_start_all_c4', depend=['test_start_all_c3']) c5 = ComponentTester('test_start_all_c5') @@ -214,8 +216,12 @@ class ComponentTestClass(BaseTestCase): try: result = self.failureResultOf(test_comp._component_start()) except AttributeError: - raise SkipTest('This test requires trial failureResultOf() in Twisted version >= 13') - self.assertEqual(result.check(component.ComponentException), component.ComponentException) + raise SkipTest( + 'This test requires trial failureResultOf() in Twisted version >= 13' + ) + self.assertEqual( + result.check(component.ComponentException), component.ComponentException + ) @defer.inlineCallbacks def test_start_paused_error(self): @@ -231,14 +237,17 @@ class ComponentTestClass(BaseTestCase): result = yield component.start() self.assertEqual( [(result[0][0], result[0][1].value)], - [( - defer.FAILURE, - component.ComponentException( - 'Trying to start component "%s" but it is ' - 'not in a stopped state. Current state: %s' % - ('test_pause_c1', 'Paused'), '', - ), - )], + [ + ( + defer.FAILURE, + component.ComponentException( + 'Trying to start component "%s" but it is ' + 'not in a stopped state. Current state: %s' + % ('test_pause_c1', 'Paused'), + '', + ), + ) + ], ) def test_shutdown(self): diff --git a/deluge/tests/test_config.py b/deluge/tests/test_config.py index 091290abc..270cc5a5b 100644 --- a/deluge/tests/test_config.py +++ b/deluge/tests/test_config.py @@ -19,7 +19,13 @@ from deluge.config import Config from .common import set_tmp_config_dir -DEFAULTS = {'string': 'foobar', 'int': 1, 'float': 0.435, 'bool': True, 'unicode': 'foobar'} +DEFAULTS = { + 'string': 'foobar', + 'int': 1, + 'float': 0.435, + 'bool': True, + 'unicode': 'foobar', +} class ConfigTestCase(unittest.TestCase): @@ -92,6 +98,7 @@ class ConfigTestCase(unittest.TestCase): # Test opening a previous 1.2 config file of just a json object import json + with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file: json.dump(DEFAULTS, getwriter('utf8')(_file), **JSON_FORMAT) diff --git a/deluge/tests/test_core.py b/deluge/tests/test_core.py index be2386d48..62be33e9e 100644 --- a/deluge/tests/test_core.py +++ b/deluge/tests/test_core.py @@ -35,22 +35,24 @@ common.disable_new_release_check() class CookieResource(Resource): - def render(self, request): if request.getCookie(b'password') != b'deluge': request.setResponseCode(FORBIDDEN) return request.setHeader(b'Content-Type', b'application/x-bittorrent') - with open(common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent'), 'rb') as _file: + with open( + common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent'), 'rb' + ) as _file: data = _file.read() return data class PartialDownload(Resource): - def render(self, request): - with open(common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent'), 'rb') as _file: + with open( + common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent'), 'rb' + ) as _file: data = _file.read() request.setHeader(b'Content-Type', len(data)) request.setHeader(b'Content-Type', b'application/x-bittorrent') @@ -60,7 +62,6 @@ class PartialDownload(Resource): class RedirectResource(Resource): - def render(self, request): request.redirect(b'/ubuntu-9.04-desktop-i386.iso.torrent') return b'' @@ -82,7 +83,6 @@ class TopLevelResource(Resource): class CoreTestCase(BaseTestCase): - def set_up(self): common.set_tmp_config_dir() self.rpcserver = RPCServer(listen=False) @@ -109,7 +109,6 @@ class CoreTestCase(BaseTestCase): return result def tear_down(self): - def on_shutdown(result): del self.rpcserver del self.core @@ -120,11 +119,7 @@ class CoreTestCase(BaseTestCase): def add_torrent(self, filename, paused=False): if not paused: # Patch libtorrent flags starting torrents paused - self.patch( - deluge.core.torrentmanager, - 'LT_DEFAULT_ADD_TORRENT_FLAGS', - 592, - ) + self.patch(deluge.core.torrentmanager, 'LT_DEFAULT_ADD_TORRENT_FLAGS', 592) options = {'add_paused': paused, 'auto_managed': False} filepath = common.get_test_data_file(filename) with open(filepath, 'rb') as _file: @@ -169,6 +164,7 @@ class CoreTestCase(BaseTestCase): # Get the info hash from the test.torrent from deluge.bencode import bdecode, bencode + with open(filename, 'rb') as _file: info_hash = sha(bencode(bdecode(_file.read())[b'info'])).hexdigest() self.assertEqual(torrent_id, info_hash) @@ -176,11 +172,16 @@ class CoreTestCase(BaseTestCase): def test_add_torrent_file_invalid_filedump(self): options = {} filename = common.get_test_data_file('test.torrent') - self.assertRaises(AddTorrentError, self.core.add_torrent_file, filename, False, options) + self.assertRaises( + AddTorrentError, self.core.add_torrent_file, filename, False, options + ) @defer.inlineCallbacks def test_add_torrent_url(self): - url = 'http://localhost:%d/ubuntu-9.04-desktop-i386.iso.torrent' % self.listen_port + url = ( + 'http://localhost:%d/ubuntu-9.04-desktop-i386.iso.torrent' + % self.listen_port + ) options = {} info_hash = '60d5d82328b4547511fdeac9bf4d0112daa0ce00' @@ -340,7 +341,12 @@ class CoreTestCase(BaseTestCase): self.assertEqual(len(self.core.get_session_state()), 0) def test_remove_torrent_invalid(self): - self.assertRaises(InvalidTorrentError, self.core.remove_torrent, 'torrentidthatdoesntexist', True) + self.assertRaises( + InvalidTorrentError, + self.core.remove_torrent, + 'torrentidthatdoesntexist', + True, + ) @defer.inlineCallbacks def test_remove_torrents(self): @@ -353,15 +359,19 @@ class CoreTestCase(BaseTestCase): filename2 = common.get_test_data_file('unicode_filenames.torrent') with open(filename2, 'rb') as _file: filedump = b64encode(_file.read()) - torrent_id2 = yield self.core.add_torrent_file_async(filename2, filedump, options) + torrent_id2 = yield self.core.add_torrent_file_async( + filename2, filedump, options + ) d = self.core.remove_torrents([torrent_id, torrent_id2], True) def test_ret(val): self.assertTrue(val == []) + d.addCallback(test_ret) def test_session_state(val): self.assertEqual(len(self.core.get_session_state()), 0) + d.addCallback(test_session_state) yield d @@ -371,15 +381,24 @@ class CoreTestCase(BaseTestCase): filename = common.get_test_data_file('test.torrent') with open(filename, 'rb') as _file: filedump = b64encode(_file.read()) - torrent_id = yield self.core.add_torrent_file_async(filename, filedump, options) - val = yield self.core.remove_torrents(['invalidid1', 'invalidid2', torrent_id], False) + torrent_id = yield self.core.add_torrent_file_async( + filename, filedump, options + ) + val = yield self.core.remove_torrents( + ['invalidid1', 'invalidid2', torrent_id], False + ) self.assertEqual(len(val), 2) - self.assertEqual(val[0], ('invalidid1', 'torrent_id invalidid1 not in session.')) - self.assertEqual(val[1], ('invalidid2', 'torrent_id invalidid2 not in session.')) + self.assertEqual( + val[0], ('invalidid1', 'torrent_id invalidid1 not in session.') + ) + self.assertEqual( + val[1], ('invalidid2', 'torrent_id invalidid2 not in session.') + ) def test_get_session_status(self): status = self.core.get_session_status( - ['net.recv_tracker_bytes', 'net.sent_tracker_bytes']) + ['net.recv_tracker_bytes', 'net.sent_tracker_bytes'] + ) self.assertIsInstance(status, dict) self.assertEqual(status['net.recv_tracker_bytes'], 0) self.assertEqual(status['net.sent_tracker_bytes'], 0) @@ -402,8 +421,7 @@ class CoreTestCase(BaseTestCase): self.assertEqual(status['upload_rate'], 0) def test_get_session_status_ratio(self): - status = self.core.get_session_status([ - 'write_hit_ratio', 'read_hit_ratio']) + status = self.core.get_session_status(['write_hit_ratio', 'read_hit_ratio']) self.assertIsInstance(status, dict) self.assertEqual(status['write_hit_ratio'], 0.0) self.assertEqual(status['read_hit_ratio'], 0.0) @@ -438,14 +456,23 @@ class CoreTestCase(BaseTestCase): } for key in pathlist: - self.assertEqual(deluge.core.torrent.sanitize_filepath(key, folder=False), pathlist[key]) - self.assertEqual(deluge.core.torrent.sanitize_filepath(key, folder=True), pathlist[key] + '/') + self.assertEqual( + deluge.core.torrent.sanitize_filepath(key, folder=False), pathlist[key] + ) + self.assertEqual( + deluge.core.torrent.sanitize_filepath(key, folder=True), + pathlist[key] + '/', + ) def test_get_set_config_values(self): - self.assertEqual(self.core.get_config_values(['abc', 'foo']), {'foo': None, 'abc': None}) + self.assertEqual( + self.core.get_config_values(['abc', 'foo']), {'foo': None, 'abc': None} + ) self.assertEqual(self.core.get_config_value('foobar'), None) self.core.set_config({'abc': 'def', 'foo': 10, 'foobar': 'barfoo'}) - self.assertEqual(self.core.get_config_values(['foo', 'abc']), {'foo': 10, 'abc': 'def'}) + self.assertEqual( + self.core.get_config_values(['foo', 'abc']), {'foo': 10, 'abc': 'def'} + ) self.assertEqual(self.core.get_config_value('foobar'), 'barfoo') def test_read_only_config_keys(self): diff --git a/deluge/tests/test_error.py b/deluge/tests/test_error.py index 24512c3ce..c552e9422 100644 --- a/deluge/tests/test_error.py +++ b/deluge/tests/test_error.py @@ -24,6 +24,7 @@ class ErrorTestCase(unittest.TestCase): e = deluge.error.DelugeError(msg) self.assertEqual(str(e), msg) from twisted.internet.defer import DebugInfo + del DebugInfo.__del__ # Hides all errors self.assertEqual(e._args, (msg,)) self.assertEqual(e._kwargs, {}) @@ -32,15 +33,19 @@ class ErrorTestCase(unittest.TestCase): version = '1.3.6' e = deluge.error.IncompatibleClient(version) self.assertEqual( - str(e), 'Your deluge client is not compatible with the daemon. \ -Please upgrade your client to %s' % version, + str(e), + 'Your deluge client is not compatible with the daemon. \ +Please upgrade your client to %s' + % version, ) def test_not_authorized_error(self): current_level = 5 required_level = 10 e = deluge.error.NotAuthorizedError(current_level, required_level) - self.assertEqual(str(e), 'Auth level too low: %d < %d' % (current_level, required_level)) + self.assertEqual( + str(e), 'Auth level too low: %d < %d' % (current_level, required_level) + ) def test_bad_login_error(self): message = 'Login failed' diff --git a/deluge/tests/test_files_tab.py b/deluge/tests/test_files_tab.py index 4d7cb2a3c..c99f7902c 100644 --- a/deluge/tests/test_files_tab.py +++ b/deluge/tests/test_files_tab.py @@ -28,6 +28,7 @@ try: except ImportError as err: libs_available = False import traceback + traceback.print_exc() setup_translations() @@ -35,7 +36,6 @@ setup_translations() @pytest.mark.gtkui class FilesTabTestCase(BaseTestCase): - def set_up(self): if libs_available is False: raise unittest.SkipTest('GTKUI dependencies not available') @@ -70,7 +70,6 @@ class FilesTabTestCase(BaseTestCase): print('') def verify_treestore(self, treestore, tree): - def _verify_treestore(itr, tree_values): i = 0 while itr: @@ -83,6 +82,7 @@ class FilesTabTestCase(BaseTestCase): itr = treestore.iter_next(itr) i += 1 return True + return _verify_treestore(treestore.get_iter_root(), tree) def test_files_tab(self): @@ -91,9 +91,14 @@ class FilesTabTestCase(BaseTestCase): {'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14}, ) self.filestab.update_files() - self.filestab._on_torrentfilerenamed_event(self.t_id, self.index, '2/test_100.txt') + self.filestab._on_torrentfilerenamed_event( + self.t_id, self.index, '2/test_100.txt' + ) - ret = self.verify_treestore(self.filestab.treestore, [['1/', [['test_10.txt']]], ['2/', [['test_100.txt']]]]) + ret = self.verify_treestore( + self.filestab.treestore, + [['1/', [['test_10.txt']]], ['2/', [['test_100.txt']]]], + ) if not ret: self.print_treestore('Treestore not expected:', self.filestab.treestore) self.assertTrue(ret) @@ -106,9 +111,14 @@ class FilesTabTestCase(BaseTestCase): {'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14}, ) self.filestab.update_files() - self.filestab._on_torrentfilerenamed_event(self.t_id, self.index, '1/1/test_100.txt') + self.filestab._on_torrentfilerenamed_event( + self.t_id, self.index, '1/1/test_100.txt' + ) - ret = self.verify_treestore(self.filestab.treestore, [['1/', [['1/', [['test_100.txt'], ['test_10.txt']]]]]]) + ret = self.verify_treestore( + self.filestab.treestore, + [['1/', [['1/', [['test_100.txt'], ['test_10.txt']]]]]], + ) if not ret: self.print_treestore('Treestore not expected:', self.filestab.treestore) self.assertTrue(ret) @@ -121,9 +131,13 @@ class FilesTabTestCase(BaseTestCase): {'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14}, ) self.filestab.update_files() - self.filestab._on_torrentfilerenamed_event(self.t_id, self.index, '1/test_100.txt') + self.filestab._on_torrentfilerenamed_event( + self.t_id, self.index, '1/test_100.txt' + ) - ret = self.verify_treestore(self.filestab.treestore, [['1/', [['test_100.txt'], ['test_10.txt']]]]) + ret = self.verify_treestore( + self.filestab.treestore, [['1/', [['test_100.txt'], ['test_10.txt']]]] + ) if not ret: self.print_treestore('Treestore not expected:', self.filestab.treestore) self.assertTrue(ret) @@ -134,15 +148,13 @@ class FilesTabTestCase(BaseTestCase): {'index': 1, 'path': '1/test_100.txt', 'offset': 13, 'size': 14}, ) self.filestab.update_files() - self.filestab._on_torrentfilerenamed_event(self.t_id, self.index, '1/2/test_100.txt') + self.filestab._on_torrentfilerenamed_event( + self.t_id, self.index, '1/2/test_100.txt' + ) ret = self.verify_treestore( - self.filestab.treestore, [[ - '1/', [ - ['2/', [['test_100.txt']]], - ['test_10.txt'], - ], - ]], + self.filestab.treestore, + [['1/', [['2/', [['test_100.txt']]], ['test_10.txt']]]], ) if not ret: self.print_treestore('Treestore not expected:', self.filestab.treestore) @@ -156,9 +168,13 @@ class FilesTabTestCase(BaseTestCase): {'index': 1, 'path': '2/test_100.txt', 'offset': 13, 'size': 14}, ) self.filestab.update_files() - self.filestab._on_torrentfilerenamed_event(self.t_id, self.index, '1/test_100.txt') + self.filestab._on_torrentfilerenamed_event( + self.t_id, self.index, '1/test_100.txt' + ) - ret = self.verify_treestore(self.filestab.treestore, [['1/', [['test_100.txt'], ['test_10.txt']]]]) + ret = self.verify_treestore( + self.filestab.treestore, [['1/', [['test_100.txt'], ['test_10.txt']]]] + ) if not ret: self.print_treestore('Treestore not expected:', self.filestab.treestore) self.assertTrue(ret) diff --git a/deluge/tests/test_httpdownloader.py b/deluge/tests/test_httpdownloader.py index d917d45dd..e3746a1ad 100644 --- a/deluge/tests/test_httpdownloader.py +++ b/deluge/tests/test_httpdownloader.py @@ -33,26 +33,20 @@ def fname(name): class RedirectResource(Resource): - def render(self, request): url = self.get_url().encode('utf8') return redirectTo(url, request) class RenameResource(Resource): - def render(self, request): filename = request.args.get(b'filename', [b'renamed_file'])[0] request.setHeader(b'Content-Type', b'text/plain') - request.setHeader( - b'Content-Disposition', b'attachment; filename=' + - filename, - ) + request.setHeader(b'Content-Disposition', b'attachment; filename=' + filename) return b'This file should be called ' + filename class AttachmentResource(Resource): - def render(self, request): request.setHeader(b'Content-Type', b'text/plain') request.setHeader(b'Content-Disposition', b'attachment') @@ -60,7 +54,6 @@ class AttachmentResource(Resource): class CookieResource(Resource): - def render(self, request): request.setHeader(b'Content-Type', b'text/plain') if request.getCookie(b'password') is None: @@ -73,7 +66,6 @@ class CookieResource(Resource): class GzipResource(Resource): - def render(self, request): message = request.args.get(b'msg', [b'EFFICIENCY!'])[0] request.setHeader(b'Content-Type', b'text/plain') @@ -81,7 +73,6 @@ class GzipResource(Resource): class PartialDownloadResource(Resource): - def __init__(self, *args, **kwargs): Resource.__init__(self) self.render_count = 0 @@ -126,7 +117,6 @@ class TopLevelResource(Resource): class DownloadFileTestCase(unittest.TestCase): - def get_url(self, path=''): return 'http://localhost:%d/%s' % (self.listen_port, path) @@ -245,6 +235,7 @@ class DownloadFileTestCase(unittest.TestCase): def cb(result): print(result) + d.addCallback(self.assertNotContains, b'fail', file_mode='rb') return d @@ -255,6 +246,7 @@ class DownloadFileTestCase(unittest.TestCase): def on_redirect(failure): self.assertTrue(type(failure), PageRedirect) + d.addErrback(on_redirect) return d diff --git a/deluge/tests/test_json_api.py b/deluge/tests/test_json_api.py index 68d7b6001..a8eaebda1 100644 --- a/deluge/tests/test_json_api.py +++ b/deluge/tests/test_json_api.py @@ -34,10 +34,11 @@ common.disable_new_release_check() class JSONBase(BaseTestCase, DaemonBase): - def connect_client(self, *args, **kwargs): return client.connect( - 'localhost', self.listen_port, username=kwargs.get('user', ''), + 'localhost', + self.listen_port, + username=kwargs.get('user', ''), password=kwargs.get('password', ''), ) @@ -52,7 +53,6 @@ class JSONBase(BaseTestCase, DaemonBase): class JSONTestCase(JSONBase): - def set_up(self): d = self.common_set_up() d.addCallback(self.start_core) @@ -81,6 +81,7 @@ class JSONTestCase(JSONBase): def compress(contents, request): return contents + self.patch(deluge.ui.web.json_api, 'compress', compress) def write(response_str): @@ -88,7 +89,9 @@ class JSONTestCase(JSONBase): response = json_lib.loads(response_str) self.assertEqual(response['result'], None) self.assertEqual(response['id'], None) - self.assertEqual(response['error']['message'], 'JSONException: JSON not decodable') + self.assertEqual( + response['error']['message'], 'JSONException: JSON not decodable' + ) self.assertEqual(response['error']['code'], 5) request.write = write @@ -127,7 +130,6 @@ class JSONTestCase(JSONBase): class JSONCustomUserTestCase(JSONBase): - def set_up(self): d = self.common_set_up() d.addCallback(self.start_core) @@ -152,7 +154,6 @@ class JSONCustomUserTestCase(JSONBase): class RPCRaiseDelugeErrorJSONTestCase(JSONBase): - def set_up(self): d = self.common_set_up() custom_script = """ @@ -176,6 +177,7 @@ class RPCRaiseDelugeErrorJSONTestCase(JSONBase): def get_session_id(s_id): return s_id + self.patch(deluge.ui.web.auth, 'get_session_id', get_session_id) auth_conf = {'session_timeout': 10, 'sessions': {}} auth = Auth(auth_conf) @@ -195,12 +197,12 @@ class RPCRaiseDelugeErrorJSONTestCase(JSONBase): def on_error(error): self.assertEqual(error.type, DelugeError) + result.addErrback(on_error) yield result class JSONRequestFailedTestCase(JSONBase, WebServerMockBase): - def set_up(self): d = self.common_set_up() custom_script = """ @@ -219,13 +221,17 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase): daemon.rpcserver.register_object(test) """ from twisted.internet.defer import Deferred + extra_callback = { - 'deferred': Deferred(), 'types': ['stderr'], + 'deferred': Deferred(), + 'types': ['stderr'], 'timeout': 10, - 'triggers': [{ - 'expr': 'in test_raise_error', - 'value': lambda reader, data, data_all: 'Test', - }], + 'triggers': [ + { + 'expr': 'in test_raise_error', + 'value': lambda reader, data, data_all: 'Test', + } + ], } def on_test_raise(*args): @@ -234,8 +240,12 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase): extra_callback['deferred'].addCallback(on_test_raise) d.addCallback( - self.start_core, custom_script=custom_script, print_stdout=False, print_stderr=False, - timeout=5, extra_callbacks=[extra_callback], + self.start_core, + custom_script=custom_script, + print_stdout=False, + print_stderr=False, + timeout=5, + extra_callbacks=[extra_callback], ) d.addCallbacks(self.connect_client, self.terminate_core) return d @@ -278,5 +288,6 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase): def on_success(arg): self.assertEqual(arg, server.NOT_DONE_YET) return True + d.addCallbacks(on_success, self.fail) yield d diff --git a/deluge/tests/test_log.py b/deluge/tests/test_log.py index fd65bbf6c..572693b7c 100644 --- a/deluge/tests/test_log.py +++ b/deluge/tests/test_log.py @@ -27,6 +27,7 @@ class LogTestCase(BaseTestCase): def test_old_log_deprecation_warning(self): from deluge.log import LOG + with warnings.catch_warnings(record=True) as w: # Cause all warnings to always be triggered. warnings.simplefilter('always') diff --git a/deluge/tests/test_maketorrent.py b/deluge/tests/test_maketorrent.py index da008e876..4e0099653 100644 --- a/deluge/tests/test_maketorrent.py +++ b/deluge/tests/test_maketorrent.py @@ -19,10 +19,12 @@ from deluge.common import windows_check def check_torrent(filename): # Test loading with libtorrent to make sure it's valid from deluge._libtorrent import lt + lt.torrent_info(filename) # Test loading with our internal TorrentInfo class from deluge.ui.common import TorrentInfo + TorrentInfo(filename) diff --git a/deluge/tests/test_metafile.py b/deluge/tests/test_metafile.py index 4a41a50ce..20b6b1056 100644 --- a/deluge/tests/test_metafile.py +++ b/deluge/tests/test_metafile.py @@ -19,10 +19,12 @@ from deluge.common import windows_check def check_torrent(filename): # Test loading with libtorrent to make sure it's valid from deluge._libtorrent import lt + lt.torrent_info(filename) # Test loading with our internal TorrentInfo class from deluge.ui.common import TorrentInfo + TorrentInfo(filename) diff --git a/deluge/tests/test_plugin_metadata.py b/deluge/tests/test_plugin_metadata.py index 63bbb9ab5..436fc2c50 100644 --- a/deluge/tests/test_plugin_metadata.py +++ b/deluge/tests/test_plugin_metadata.py @@ -16,7 +16,6 @@ from .basetest import BaseTestCase class PluginManagerBaseTestCase(BaseTestCase): - def set_up(self): common.set_tmp_config_dir() diff --git a/deluge/tests/test_rpcserver.py b/deluge/tests/test_rpcserver.py index 0afcfd63b..02f9af023 100644 --- a/deluge/tests/test_rpcserver.py +++ b/deluge/tests/test_rpcserver.py @@ -31,7 +31,6 @@ class DelugeRPCProtocolTester(DelugeRPCProtocol): class RPCServerTestCase(BaseTestCase): - def set_up(self): self.rpcserver = RPCServer(listen=False) self.rpcserver.factory.protocol = DelugeRPCProtocolTester @@ -50,11 +49,13 @@ class RPCServerTestCase(BaseTestCase): def tear_down(self): def on_shutdown(result): del self.rpcserver + return component.shutdown().addCallback(on_shutdown) def test_emit_event_for_session_id(self): torrent_id = '12' from deluge.event import TorrentFolderRenamedEvent + data = [torrent_id, 'new name', 'old name'] e = TorrentFolderRenamedEvent(*data) self.rpcserver.emit_event_for_session_id(self.session_id, e) @@ -72,7 +73,9 @@ class RPCServerTestCase(BaseTestCase): def test_valid_client_login(self): self.authmanager = AuthManager() auth = get_localhost_auth() - self.protocol.dispatch(self.request_id, 'daemon.login', auth, {'client_version': 'Test'}) + self.protocol.dispatch( + self.request_id, 'daemon.login', auth, {'client_version': 'Test'} + ) msg = self.protocol.messages.pop() self.assertEqual(msg[0], rpcserver.RPC_RESPONSE, str(msg)) self.assertEqual(msg[1], self.request_id, str(msg)) @@ -80,10 +83,12 @@ class RPCServerTestCase(BaseTestCase): def test_client_login_error(self): # This test causes error log prints while running the test... - self.protocol.transport = None # This should cause AttributeError + self.protocol.transport = None # This should cause AttributeError self.authmanager = AuthManager() auth = get_localhost_auth() - self.protocol.dispatch(self.request_id, 'daemon.login', auth, {'client_version': 'Test'}) + self.protocol.dispatch( + self.request_id, 'daemon.login', auth, {'client_version': 'Test'} + ) msg = self.protocol.messages.pop() self.assertEqual(msg[0], rpcserver.RPC_ERROR) self.assertEqual(msg[1], self.request_id) diff --git a/deluge/tests/test_security.py b/deluge/tests/test_security.py index ff6a3e83b..379404906 100644 --- a/deluge/tests/test_security.py +++ b/deluge/tests/test_security.py @@ -49,7 +49,8 @@ class SecurityBaseTestCase(object): '0', test, '127.0.0.1:%d' % self.port, - ]) + ], + ) def on_result(results): @@ -162,7 +163,6 @@ class DaemonSecurityTestCase(BaseTestCase, DaemonBase, SecurityBaseTestCase): @pytest.mark.security class WebUISecurityTestBase(WebServerTestBase, SecurityBaseTestCase): - def __init__(self, testname): super(WebUISecurityTestBase, self).__init__(testname) SecurityBaseTestCase.__init__(self) diff --git a/deluge/tests/test_sessionproxy.py b/deluge/tests/test_sessionproxy.py index 5f2ea4f84..03f3cc27e 100644 --- a/deluge/tests/test_sessionproxy.py +++ b/deluge/tests/test_sessionproxy.py @@ -19,7 +19,6 @@ from .basetest import BaseTestCase class Core(object): - def __init__(self): self.reset() @@ -48,7 +47,10 @@ class Core(object): ret = {} if torrent_id in self.prev_status: for key in keys: - if self.prev_status[torrent_id][key] != self.torrents[torrent_id][key]: + if ( + self.prev_status[torrent_id][key] + != self.torrents[torrent_id][key] + ): ret[key] = self.torrents[torrent_id][key] else: ret = self.torrents[torrent_id] @@ -77,7 +79,10 @@ class Core(object): ret[torrent] = {} if torrent in self.prev_status: for key in self.prev_status[torrent]: - if self.prev_status[torrent][key] != self.torrents[torrent][key]: + if ( + self.prev_status[torrent][key] + != self.torrents[torrent][key] + ): ret[torrent][key] = self.torrents[torrent][key] else: ret[torrent] = dict(self.torrents[torrent]) @@ -101,7 +106,6 @@ client = Client() class SessionProxyTestCase(BaseTestCase): - def set_up(self): self.clock = Clock() self.patch(deluge.ui.sessionproxy, 'time', self.clock.seconds) @@ -115,6 +119,7 @@ class SessionProxyTestCase(BaseTestCase): # Advance clock to expire the cache times self.clock.advance(2) return self.sp.get_torrents_status({'id': torrent_ids}, inital_keys) + d.addCallback(do_get_torrents_status) return d diff --git a/deluge/tests/test_torrent.py b/deluge/tests/test_torrent.py index 39cd47554..4d028fda9 100644 --- a/deluge/tests/test_torrent.py +++ b/deluge/tests/test_torrent.py @@ -30,11 +30,11 @@ from .basetest import BaseTestCase class TorrentTestCase(BaseTestCase): - def setup_config(self): config_dir = common.set_tmp_config_dir() core_config = deluge.config.Config( - 'core.conf', defaults=deluge.core.preferencesmanager.DEFAULT_PREFS, + 'core.conf', + defaults=deluge.core.preferencesmanager.DEFAULT_PREFS, config_dir=config_dir, ) core_config.save() @@ -82,12 +82,38 @@ class TorrentTestCase(BaseTestCase): def test_set_prioritize_first_last_pieces(self): piece_indexes = [ - 0, 1, 50, 51, 52, 110, 111, 112, 113, 200, 201, 202, 212, - 213, 214, 215, 216, 217, 457, 458, 459, 460, 461, 462, + 0, + 1, + 50, + 51, + 52, + 110, + 111, + 112, + 113, + 200, + 201, + 202, + 212, + 213, + 214, + 215, + 216, + 217, + 457, + 458, + 459, + 460, + 461, + 462, ] - self.run_test_set_prioritize_first_last_pieces('dir_with_6_files.torrent', piece_indexes) + self.run_test_set_prioritize_first_last_pieces( + 'dir_with_6_files.torrent', piece_indexes + ) - def run_test_set_prioritize_first_last_pieces(self, torrent_file, prioritized_piece_indexes): + def run_test_set_prioritize_first_last_pieces( + self, torrent_file, prioritized_piece_indexes + ): atp = self.get_torrent_atp(torrent_file) handle = self.session.add_torrent(atp) @@ -167,21 +193,47 @@ class TorrentTestCase(BaseTestCase): if windows_check(): raise unittest.SkipTest('unexpected end of file in bencoded string') resume_data = { - 'active_time': 13399, 'num_incomplete': 16777215, 'announce_to_lsd': 1, 'seed_mode': 0, - 'pieces': '\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01', 'paused': 0, - 'seeding_time': 13399, 'last_scrape': 13399, - 'info-hash': '-\xc5\xd0\xe7\x1af\xfeid\x9ad\r9\xcb\x00\xa2YpIs', 'max_uploads': 16777215, - 'max_connections': 16777215, 'num_downloaders': 16777215, 'total_downloaded': 0, - 'file-format': 'libtorrent resume file', 'peers6': '', 'added_time': 1411826665, - 'banned_peers6': '', 'file_priority': [1], 'last_seen_complete': 0, 'total_uploaded': 0, + 'active_time': 13399, + 'num_incomplete': 16777215, + 'announce_to_lsd': 1, + 'seed_mode': 0, + 'pieces': '\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01', + 'paused': 0, + 'seeding_time': 13399, + 'last_scrape': 13399, + 'info-hash': '-\xc5\xd0\xe7\x1af\xfeid\x9ad\r9\xcb\x00\xa2YpIs', + 'max_uploads': 16777215, + 'max_connections': 16777215, + 'num_downloaders': 16777215, + 'total_downloaded': 0, + 'file-format': 'libtorrent resume file', + 'peers6': '', + 'added_time': 1411826665, + 'banned_peers6': '', + 'file_priority': [1], + 'last_seen_complete': 0, + 'total_uploaded': 0, 'piece_priority': '\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01', - 'file-version': 1, 'announce_to_dht': 1, 'auto_managed': 1, 'upload_rate_limit': 0, - 'completed_time': 1411826665, 'allocation': 'sparse', 'blocks per piece': 2, - 'download_rate_limit': 0, 'libtorrent-version': '0.16.17.0', 'banned_peers': '', - 'num_seeds': 16777215, 'sequential_download': 0, 'announce_to_trackers': 1, - 'peers': '\n\x00\x02\x0f=\xc6SC\x17]\xd8}\x7f\x00\x00\x01=\xc6', 'finished_time': 13399, - 'last_upload': 13399, 'trackers': [[]], 'super_seeding': 0, - 'file sizes': [[512000, 1411826586]], 'last_download': 13399, + 'file-version': 1, + 'announce_to_dht': 1, + 'auto_managed': 1, + 'upload_rate_limit': 0, + 'completed_time': 1411826665, + 'allocation': 'sparse', + 'blocks per piece': 2, + 'download_rate_limit': 0, + 'libtorrent-version': '0.16.17.0', + 'banned_peers': '', + 'num_seeds': 16777215, + 'sequential_download': 0, + 'announce_to_trackers': 1, + 'peers': '\n\x00\x02\x0f=\xc6SC\x17]\xd8}\x7f\x00\x00\x01=\xc6', + 'finished_time': 13399, + 'last_upload': 13399, + 'trackers': [[]], + 'super_seeding': 0, + 'file sizes': [[512000, 1411826586]], + 'last_download': 13399, } torrent_state = TorrentState( torrent_id='2dc5d0e71a66fe69649a640d39cb00a259704973', @@ -197,13 +249,15 @@ class TorrentTestCase(BaseTestCase): filedump = _file.read() resume_data = utf8_encode_structure(resume_data) torrent_id = self.core.torrentmanager.add( - state=torrent_state, filedump=filedump, resume_data=lt.bencode(resume_data), + state=torrent_state, filedump=filedump, resume_data=lt.bencode(resume_data) ) torrent = self.core.torrentmanager.torrents[torrent_id] def assert_resume_data(): self.assert_state(torrent, 'Error') - tm_resume_data = lt.bdecode(self.core.torrentmanager.resume_data[torrent.torrent_id]) + tm_resume_data = lt.bdecode( + self.core.torrentmanager.resume_data[torrent.torrent_id] + ) self.assertEqual(tm_resume_data, resume_data) return deferLater(reactor, 0.5, assert_resume_data) @@ -224,10 +278,7 @@ class TorrentTestCase(BaseTestCase): # Test finished and uploading but no stop_at_ratio set. self.assertEqual(self.torrent.get_eta(), 0) - self.torrent.options = { - 'stop_at_ratio': True, - 'stop_ratio': 1.5, - } + self.torrent.options = {'stop_at_ratio': True, 'stop_ratio': 1.5} result = self.torrent.get_eta() self.assertEqual(result, 2) self.assertIsInstance(result, int) diff --git a/deluge/tests/test_torrentmanager.py b/deluge/tests/test_torrentmanager.py index 711f778c2..0aacea41c 100644 --- a/deluge/tests/test_torrentmanager.py +++ b/deluge/tests/test_torrentmanager.py @@ -28,7 +28,6 @@ warnings.resetwarnings() class TorrentmanagerTestCase(BaseTestCase): - def set_up(self): common.set_tmp_config_dir() self.rpcserver = RPCServer(listen=False) @@ -40,7 +39,6 @@ class TorrentmanagerTestCase(BaseTestCase): return component.start() def tear_down(self): - def on_shutdown(result): del self.rpcserver del self.core @@ -53,18 +51,20 @@ class TorrentmanagerTestCase(BaseTestCase): with open(filename, 'rb') as _file: filedump = _file.read() torrent_id = yield self.core.add_torrent_file_async( - filename, b64encode(filedump), {}) + filename, b64encode(filedump), {} + ) self.assertTrue(self.tm.remove(torrent_id, False)) def test_prefetch_metadata(self): from deluge._libtorrent import lt + with open(common.get_test_data_file('test.torrent'), 'rb') as _file: t_info = lt.torrent_info(lt.bdecode(_file.read())) mock_alert = mock.MagicMock() mock_alert.handle.info_hash = mock.MagicMock( - return_value='ab570cdd5a17ea1b61e970bb72047de141bce173') - mock_alert.handle.get_torrent_info = mock.MagicMock( - return_value=t_info) + return_value='ab570cdd5a17ea1b61e970bb72047de141bce173' + ) + mock_alert.handle.get_torrent_info = mock.MagicMock(return_value=t_info) magnet = 'magnet:?xt=urn:btih:ab570cdd5a17ea1b61e970bb72047de141bce173' d = self.tm.prefetch_metadata(magnet, 30) @@ -72,34 +72,36 @@ class TorrentmanagerTestCase(BaseTestCase): expected = ( 'ab570cdd5a17ea1b61e970bb72047de141bce173', - bencode({ - 'piece length': 32768, - 'sha1': ( - b'2\xce\xb6\xa8"\xd7\xf0\xd4\xbf\xdc^K\xba\x1bh' - b'\x9d\xc5\xb7\xac\xdd' - ), - 'name': 'azcvsupdater_2.6.2.jar', - 'private': 0, - 'pieces': ( - b'\xdb\x04B\x05\xc3\'\xdab\xb8su97\xa9u' - b'\xca<w\\\x1ef\xd4\x9b\x16\xa9}\xc0\x9f:\xfd' - b'\x97qv\x83\xa2"\xef\x9d7\x0by!\rl\xe5v\xb7' - b'\x18{\xf7/"P\xe9\x8d\x01D\x9e8\xbd\x16\xe3' - b'\xfb-\x9d\xaa\xbcM\x11\xba\x92\xfc\x13F\xf0' - b'\x1c\x86x+\xc8\xd0S\xa9\x90`\xa1\xe4\x82\xe8' - b'\xfc\x08\xf7\xe3\xe5\xf6\x85\x1c%\xe7%\n\xed' - b'\xc0\x1f\xa1;\x9a\xea\xcf\x90\x0c/F>\xdf\xdagA' - b'\xc42|\xda\x82\xf5\xa6b\xa1\xb8#\x80wI\xd8f' - b'\xf8\xbd\xacW\xab\xc3s\xe0\xbbw\xf2K\xbe\xee' - b'\xa8rG\xe1W\xe8\xb7\xc2i\xf3\xd8\xaf\x9d\xdc' - b'\xd0#\xf4\xc1\x12u\xcd\x0bE?:\xe8\x9c\x1cu' - b'\xabb(oj\r^\xd5\xd5A\x83\x88\x9a\xa1J\x1c?' - b'\xa1\xd6\x8c\x83\x9e&' - ), - 'length': 307949, - 'name.utf-8': b'azcvsupdater_2.6.2.jar', - 'ed2k': b'>p\xefl\xfa]\x95K\x1b^\xc2\\;;e\xb7', - }), + bencode( + { + 'piece length': 32768, + 'sha1': ( + b'2\xce\xb6\xa8"\xd7\xf0\xd4\xbf\xdc^K\xba\x1bh' + b'\x9d\xc5\xb7\xac\xdd' + ), + 'name': 'azcvsupdater_2.6.2.jar', + 'private': 0, + 'pieces': ( + b'\xdb\x04B\x05\xc3\'\xdab\xb8su97\xa9u' + b'\xca<w\\\x1ef\xd4\x9b\x16\xa9}\xc0\x9f:\xfd' + b'\x97qv\x83\xa2"\xef\x9d7\x0by!\rl\xe5v\xb7' + b'\x18{\xf7/"P\xe9\x8d\x01D\x9e8\xbd\x16\xe3' + b'\xfb-\x9d\xaa\xbcM\x11\xba\x92\xfc\x13F\xf0' + b'\x1c\x86x+\xc8\xd0S\xa9\x90`\xa1\xe4\x82\xe8' + b'\xfc\x08\xf7\xe3\xe5\xf6\x85\x1c%\xe7%\n\xed' + b'\xc0\x1f\xa1;\x9a\xea\xcf\x90\x0c/F>\xdf\xdagA' + b'\xc42|\xda\x82\xf5\xa6b\xa1\xb8#\x80wI\xd8f' + b'\xf8\xbd\xacW\xab\xc3s\xe0\xbbw\xf2K\xbe\xee' + b'\xa8rG\xe1W\xe8\xb7\xc2i\xf3\xd8\xaf\x9d\xdc' + b'\xd0#\xf4\xc1\x12u\xcd\x0bE?:\xe8\x9c\x1cu' + b'\xabb(oj\r^\xd5\xd5A\x83\x88\x9a\xa1J\x1c?' + b'\xa1\xd6\x8c\x83\x9e&' + ), + 'length': 307949, + 'name.utf-8': b'azcvsupdater_2.6.2.jar', + 'ed2k': b'>p\xefl\xfa]\x95K\x1b^\xc2\\;;e\xb7', + } + ), ) self.assertEqual(expected, self.successResultOf(d)) @@ -117,4 +119,5 @@ class TorrentmanagerTestCase(BaseTestCase): def test_remove_invalid_torrent(self): self.assertRaises( - InvalidTorrentError, self.tm.remove, 'torrentidthatdoesntexist') + InvalidTorrentError, self.tm.remove, 'torrentidthatdoesntexist' + ) diff --git a/deluge/tests/test_torrentview.py b/deluge/tests/test_torrentview.py index 9cf9ce282..3badb20da 100644 --- a/deluge/tests/test_torrentview.py +++ b/deluge/tests/test_torrentview.py @@ -27,10 +27,13 @@ except ImportError as err: libs_available = False TYPE_UINT64 = 'Whatever' import traceback + traceback.print_exc() else: libs_available = True - from deluge.ui.gtkui.mainwindow import MainWindow # pylint: disable=ungrouped-imports + from deluge.ui.gtkui.mainwindow import ( + MainWindow, + ) # pylint: disable=ungrouped-imports from deluge.ui.gtkui.menubar import MenuBar from deluge.ui.gtkui.torrentdetails import TorrentDetails from deluge.ui.gtkui.torrentview import TorrentView @@ -43,27 +46,66 @@ setup_translations() class TorrentviewTestCase(BaseTestCase): default_column_index = [ - 'filter', 'torrent_id', 'dirty', '#', + 'filter', + 'torrent_id', + 'dirty', + '#', 'Name', - 'Size', 'Downloaded', 'Uploaded', 'Remaining', + 'Size', + 'Downloaded', + 'Uploaded', + 'Remaining', 'Progress', - 'Seeds', 'Peers', 'Seeds:Peers', - 'Down Speed', 'Up Speed', 'Down Limit', 'Up Limit', - 'ETA', 'Ratio', 'Avail', - 'Added', 'Completed', 'Complete Seen', - 'Tracker', 'Download Folder', 'Owner', 'Shared', + 'Seeds', + 'Peers', + 'Seeds:Peers', + 'Down Speed', + 'Up Speed', + 'Down Limit', + 'Up Limit', + 'ETA', + 'Ratio', + 'Avail', + 'Added', + 'Completed', + 'Complete Seen', + 'Tracker', + 'Download Folder', + 'Owner', + 'Shared', ] default_liststore_columns = [ - bool, str, bool, int, - str, str, # Name - TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, - float, str, # Progress - int, int, int, int, float, # Seeds, Peers - int, int, float, float, - int, float, float, # ETA, Ratio, Avail - int, int, int, - str, str, # Tracker - str, str, + bool, + str, + bool, + int, + str, + str, # Name + TYPE_UINT64, + TYPE_UINT64, + TYPE_UINT64, + TYPE_UINT64, + float, + str, # Progress + int, + int, + int, + int, + float, # Seeds, Peers + int, + int, + float, + float, + int, + float, + float, # ETA, Ratio, Avail + int, + int, + int, + str, + str, # Tracker + str, + str, bool, ] # shared @@ -84,9 +126,16 @@ class TorrentviewTestCase(BaseTestCase): def test_torrentview_columns(self): - self.assertEqual(self.torrentview.column_index, TorrentviewTestCase.default_column_index) - self.assertEqual(self.torrentview.liststore_columns, TorrentviewTestCase.default_liststore_columns) - self.assertEqual(self.torrentview.columns['Download Folder'].column_indices, [29]) + self.assertEqual( + self.torrentview.column_index, TorrentviewTestCase.default_column_index + ) + self.assertEqual( + self.torrentview.liststore_columns, + TorrentviewTestCase.default_liststore_columns, + ) + self.assertEqual( + self.torrentview.columns['Download Folder'].column_indices, [29] + ) def test_add_column(self): @@ -137,10 +186,24 @@ class TorrentviewTestCase(BaseTestCase): self.torrentview.add_text_column(test_col, status_field=['label']) self.torrentview.remove_column(test_col) - self.assertEqual(len(self.torrentview.liststore_columns), len(TorrentviewTestCase.default_liststore_columns)) - self.assertEqual(len(self.torrentview.column_index), len(TorrentviewTestCase.default_column_index)) - self.assertEqual(self.torrentview.column_index[-1], TorrentviewTestCase.default_column_index[-1]) - self.assertEqual(self.torrentview.columns[TorrentviewTestCase.default_column_index[-1]].column_indices, [31]) + self.assertEqual( + len(self.torrentview.liststore_columns), + len(TorrentviewTestCase.default_liststore_columns), + ) + self.assertEqual( + len(self.torrentview.column_index), + len(TorrentviewTestCase.default_column_index), + ) + self.assertEqual( + self.torrentview.column_index[-1], + TorrentviewTestCase.default_column_index[-1], + ) + self.assertEqual( + self.torrentview.columns[ + TorrentviewTestCase.default_column_index[-1] + ].column_indices, + [31], + ) def test_remove_columns(self): @@ -165,16 +228,32 @@ class TorrentviewTestCase(BaseTestCase): # Remove test_col2 self.torrentview.remove_column(test_col2) - self.assertEqual(len(self.torrentview.liststore_columns), len(TorrentviewTestCase.default_liststore_columns)) - self.assertEqual(len(self.torrentview.column_index), len(TorrentviewTestCase.default_column_index)) - self.assertEqual(self.torrentview.column_index[-1], TorrentviewTestCase.default_column_index[-1]) - self.assertEqual(self.torrentview.columns[TorrentviewTestCase.default_column_index[-1]].column_indices, [31]) + self.assertEqual( + len(self.torrentview.liststore_columns), + len(TorrentviewTestCase.default_liststore_columns), + ) + self.assertEqual( + len(self.torrentview.column_index), + len(TorrentviewTestCase.default_column_index), + ) + self.assertEqual( + self.torrentview.column_index[-1], + TorrentviewTestCase.default_column_index[-1], + ) + self.assertEqual( + self.torrentview.columns[ + TorrentviewTestCase.default_column_index[-1] + ].column_indices, + [31], + ) def test_add_remove_column_multiple_types(self): # Add a column with multiple column types test_col3 = 'Test column3' - self.torrentview.add_progress_column(test_col3, status_field=['progress', 'label3'], col_types=[float, str]) + self.torrentview.add_progress_column( + test_col3, status_field=['progress', 'label3'], col_types=[float, str] + ) self.assertEqual( len(self.torrentview.liststore_columns), len(TorrentviewTestCase.default_liststore_columns) + 2, @@ -189,7 +268,21 @@ class TorrentviewTestCase(BaseTestCase): # Remove multiple column-types column self.torrentview.remove_column(test_col3) - self.assertEqual(len(self.torrentview.liststore_columns), len(TorrentviewTestCase.default_liststore_columns)) - self.assertEqual(len(self.torrentview.column_index), len(TorrentviewTestCase.default_column_index)) - self.assertEqual(self.torrentview.column_index[-1], TorrentviewTestCase.default_column_index[-1]) - self.assertEqual(self.torrentview.columns[TorrentviewTestCase.default_column_index[-1]].column_indices, [31]) + self.assertEqual( + len(self.torrentview.liststore_columns), + len(TorrentviewTestCase.default_liststore_columns), + ) + self.assertEqual( + len(self.torrentview.column_index), + len(TorrentviewTestCase.default_column_index), + ) + self.assertEqual( + self.torrentview.column_index[-1], + TorrentviewTestCase.default_column_index[-1], + ) + self.assertEqual( + self.torrentview.columns[ + TorrentviewTestCase.default_column_index[-1] + ].column_indices, + [31], + ) diff --git a/deluge/tests/test_transfer.py b/deluge/tests/test_transfer.py index cc13cef93..b7e0e8388 100644 --- a/deluge/tests/test_transfer.py +++ b/deluge/tests/test_transfer.py @@ -21,7 +21,6 @@ deluge.log.setup_logger('none') class TransferTestClass(DelugeTransferProtocol): - def __init__(self): DelugeTransferProtocol.__init__(self) self.transport = self @@ -57,6 +56,7 @@ class TransferTestClass(DelugeTransferProtocol): """ import zlib + print('\n=== New Data Received ===\nBytes received:', len(data)) if self._buffer: @@ -87,15 +87,23 @@ class TransferTestClass(DelugeTransferProtocol): try: request = rencode.loads(dobj.decompress(data)) print('Successfully loaded message', end=' ') - print(' - Buffer length: %d, data length: %d, unused length: %d' % - (len(data), len(data) - len(dobj.unused_data), len(dobj.unused_data))) + print( + ' - Buffer length: %d, data length: %d, unused length: %d' + % ( + len(data), + len(data) - len(dobj.unused_data), + len(dobj.unused_data), + ) + ) print('Packet count:', self.packet_count) except Exception as ex: # log.debug('Received possible invalid message (%r): %s', data, e) # This could be cut-off data, so we'll save this in the buffer # and try to prepend it on the next dataReceived() self._buffer = data - print('Failed to load buffer (size %d): %s' % (len(self._buffer), str(ex))) + print( + 'Failed to load buffer (size %d): %s' % (len(self._buffer), str(ex)) + ) return else: data = dobj.unused_data @@ -105,7 +113,6 @@ class TransferTestClass(DelugeTransferProtocol): class DelugeTransferProtocolTestCase(unittest.TestCase): - def setUp(self): # NOQA: N803 """ The expected messages corresponds to the test messages (msg1, msg2) after they've been processed @@ -117,9 +124,17 @@ class DelugeTransferProtocolTestCase(unittest.TestCase): """ self.transfer = TransferTestClass() - self.msg1 = (0, 1, {'key_int': 1242429423}, {'key_str': b'some string'}, {'key_bool': True}) + self.msg1 = ( + 0, + 1, + {'key_int': 1242429423}, + {'key_str': b'some string'}, + {'key_bool': True}, + ) self.msg2 = ( - 2, 3, {'key_float': 12424.29423}, + 2, + 3, + {'key_float': 12424.29423}, {'key_unicode': 'some string'}, {'key_dict_with_tuple': {'key_tuple': (1, 2, 3)}}, {'keylist': [4, '5', 6.7]}, @@ -153,7 +168,9 @@ class DelugeTransferProtocolTestCase(unittest.TestCase): method 'message_received'. """ - self.transfer.dataReceived(base64.b64decode(self.msg1_expected_compressed_base64)) + self.transfer.dataReceived( + base64.b64decode(self.msg1_expected_compressed_base64) + ) # Get the data as sent by DelugeTransferProtocol messages = self.transfer.get_messages_in().pop(0) self.assertEqual(rencode.dumps(self.msg1), rencode.dumps(messages)) @@ -174,8 +191,9 @@ class DelugeTransferProtocolTestCase(unittest.TestCase): and lets DelugeTransferProtocol receive the data as one string. """ - two_concatenated = base64.b64decode(self.msg1_expected_compressed_base64) + \ - base64.b64decode(self.msg2_expected_compressed_base64) + two_concatenated = base64.b64decode( + self.msg1_expected_compressed_base64 + ) + base64.b64decode(self.msg2_expected_compressed_base64) self.transfer.dataReceived(two_concatenated) # Get the data as sent by DelugeTransferProtocol @@ -190,16 +208,22 @@ class DelugeTransferProtocolTestCase(unittest.TestCase): and lets DelugeTransferProtocol receive the data in multiple parts. """ - msg_bytes = base64.b64decode(self.msg1_expected_compressed_base64) + \ - base64.b64decode(self.msg2_expected_compressed_base64) + \ + msg_bytes = ( base64.b64decode(self.msg1_expected_compressed_base64) + + base64.b64decode(self.msg2_expected_compressed_base64) + + base64.b64decode(self.msg1_expected_compressed_base64) + ) packet_size = 40 - one_message_byte_count = len(base64.b64decode(self.msg1_expected_compressed_base64)) - two_messages_byte_count = one_message_byte_count + \ - len(base64.b64decode(self.msg2_expected_compressed_base64)) - three_messages_byte_count = two_messages_byte_count + \ - len(base64.b64decode(self.msg1_expected_compressed_base64)) + one_message_byte_count = len( + base64.b64decode(self.msg1_expected_compressed_base64) + ) + two_messages_byte_count = one_message_byte_count + len( + base64.b64decode(self.msg2_expected_compressed_base64) + ) + three_messages_byte_count = two_messages_byte_count + len( + base64.b64decode(self.msg1_expected_compressed_base64) + ) for d in self.receive_parts_helper(msg_bytes, packet_size): bytes_received = self.transfer.get_bytes_recv() @@ -213,7 +237,9 @@ class DelugeTransferProtocolTestCase(unittest.TestCase): else: expected_msgs_received_count = 0 # Verify that the expected number of complete messages has arrived - self.assertEqual(expected_msgs_received_count, len(self.transfer.get_messages_in())) + self.assertEqual( + expected_msgs_received_count, len(self.transfer.get_messages_in()) + ) # Get the data as received by DelugeTransferProtocol message1 = self.transfer.get_messages_in().pop(0) @@ -230,28 +256,45 @@ class DelugeTransferProtocolTestCase(unittest.TestCase): This test tries to test the protocol that relies on errors from rencode. """ - msg_bytes = base64.b64decode(self.msg1_expected_compressed_base64) + \ - base64.b64decode(self.msg2_expected_compressed_base64) + \ + msg_bytes = ( base64.b64decode(self.msg1_expected_compressed_base64) + + base64.b64decode(self.msg2_expected_compressed_base64) + + base64.b64decode(self.msg1_expected_compressed_base64) + ) packet_size = 149 - one_message_byte_count = len(base64.b64decode(self.msg1_expected_compressed_base64)) - two_messages_byte_count = one_message_byte_count + \ - len(base64.b64decode(self.msg2_expected_compressed_base64)) - three_messages_byte_count = two_messages_byte_count + \ - len(base64.b64decode(self.msg1_expected_compressed_base64)) + one_message_byte_count = len( + base64.b64decode(self.msg1_expected_compressed_base64) + ) + two_messages_byte_count = one_message_byte_count + len( + base64.b64decode(self.msg2_expected_compressed_base64) + ) + three_messages_byte_count = two_messages_byte_count + len( + base64.b64decode(self.msg1_expected_compressed_base64) + ) print() - print('Msg1 size:', len(base64.b64decode(self.msg1_expected_compressed_base64)) - 4) - print('Msg2 size:', len(base64.b64decode(self.msg2_expected_compressed_base64)) - 4) - print('Msg3 size:', len(base64.b64decode(self.msg1_expected_compressed_base64)) - 4) + print( + 'Msg1 size:', + len(base64.b64decode(self.msg1_expected_compressed_base64)) - 4, + ) + print( + 'Msg2 size:', + len(base64.b64decode(self.msg2_expected_compressed_base64)) - 4, + ) + print( + 'Msg3 size:', + len(base64.b64decode(self.msg1_expected_compressed_base64)) - 4, + ) print('one_message_byte_count:', one_message_byte_count) print('two_messages_byte_count:', two_messages_byte_count) print('three_messages_byte_count:', three_messages_byte_count) - for d in self.receive_parts_helper(msg_bytes, packet_size, self.transfer.data_received_old_protocol): + for d in self.receive_parts_helper( + msg_bytes, packet_size, self.transfer.data_received_old_protocol + ): bytes_received = self.transfer.get_bytes_recv() if bytes_received >= three_messages_byte_count: @@ -264,8 +307,13 @@ class DelugeTransferProtocolTestCase(unittest.TestCase): expected_msgs_received_count = 0 # Verify that the expected number of complete messages has arrived if expected_msgs_received_count != len(self.transfer.get_messages_in()): - print('Expected number of messages received is %d, but %d have been received.' % - (expected_msgs_received_count, len(self.transfer.get_messages_in()))) + print( + 'Expected number of messages received is %d, but %d have been received.' + % ( + expected_msgs_received_count, + len(self.transfer.get_messages_in()), + ) + ) # Get the data as received by DelugeTransferProtocol message1 = self.transfer.get_messages_in().pop(0) @@ -287,18 +335,19 @@ class DelugeTransferProtocolTestCase(unittest.TestCase): to read and parse the size of the payload. """ - two_concatenated = base64.b64decode(self.msg1_expected_compressed_base64) + \ - base64.b64decode(self.msg2_expected_compressed_base64) + two_concatenated = base64.b64decode( + self.msg1_expected_compressed_base64 + ) + base64.b64decode(self.msg2_expected_compressed_base64) first_len = len(base64.b64decode(self.msg1_expected_compressed_base64)) # Now found the entire first message, and half the header of the next message (2 bytes into the header) - self.transfer.dataReceived(two_concatenated[:first_len + 2]) + self.transfer.dataReceived(two_concatenated[: first_len + 2]) # Should be 1 message in the list self.assertEqual(1, len(self.transfer.get_messages_in())) # Send the rest - self.transfer.dataReceived(two_concatenated[first_len + 2:]) + self.transfer.dataReceived(two_concatenated[first_len + 2 :]) # Should be 2 messages in the list self.assertEqual(2, len(self.transfer.get_messages_in())) diff --git a/deluge/tests/test_ui_common.py b/deluge/tests/test_ui_common.py index 20e0818c3..2ccdc1cc9 100644 --- a/deluge/tests/test_ui_common.py +++ b/deluge/tests/test_ui_common.py @@ -17,7 +17,6 @@ from . import common class UICommonTestCase(unittest.TestCase): - def setUp(self): # NOQA: N803 pass @@ -40,13 +39,15 @@ class UICommonTestCase(unittest.TestCase): ( b'\xe3\x83\x86\xe3\x82\xaf\xe3\x82\xb9\xe3\x83\xbb\xe3\x83' b'\x86\xe3\x82\xaf\xe3\x82\xb5\xe3\x83\xb3.mkv' - ).decode('utf8') in files, + ).decode('utf8') + in files ) self.assertTrue( ( b'\xd0\x9c\xd0\xb8\xd1\x85\xd0\xb0\xd0\xb8\xd0\xbb \xd0\x93' b'\xd0\xbe\xd1\x80\xd0\xb1\xd0\xb0\xd1\x87\xd1\x91\xd0\xb2.mkv' - ).decode('utf8') in files, + ).decode('utf8') + in files ) self.assertTrue(b"Alisher ibn G'iyosiddin Navoiy.mkv".decode('utf8') in files) self.assertTrue(b'Ascii title.mkv'.decode('utf8') in files) @@ -54,5 +55,6 @@ class UICommonTestCase(unittest.TestCase): ( b'\xe0\xa6\xb8\xe0\xa7\x81\xe0\xa6\x95\xe0\xa7\x81\xe0\xa6\xae\xe0\xa6\xbe' b'\xe0\xa6\xb0 \xe0\xa6\xb0\xe0\xa6\xbe\xe0\xa7\x9f.mkv' - ).decode('utf8') in files, + ).decode('utf8') + in files ) diff --git a/deluge/tests/test_ui_console_fields.py b/deluge/tests/test_ui_console_fields.py index 05d7132ec..2d4f4d300 100644 --- a/deluge/tests/test_ui_console_fields.py +++ b/deluge/tests/test_ui_console_fields.py @@ -13,14 +13,12 @@ from deluge.ui.console.widgets.fields import TextInput class Parent(object): - def __init__(self): self.border_off_x = 1 self.pane_width = 20 class UICommonTestCase(unittest.TestCase): - def setUp(self): # NOQA: N803 self.parent = Parent() @@ -32,5 +30,13 @@ class UICommonTestCase(unittest.TestCase): self._cursor_row = r self._cursor_col = c - t = TextInput(self.parent, 'name', 'message', move_func, 20, '/text/field/file/path', complete=False) + t = TextInput( + self.parent, + 'name', + 'message', + move_func, + 20, + '/text/field/file/path', + complete=False, + ) self.assertTrue(t) # Shut flake8 up (unused variable) diff --git a/deluge/tests/test_ui_entry.py b/deluge/tests/test_ui_entry.py index 39a0b7b50..8a032a26c 100644 --- a/deluge/tests/test_ui_entry.py +++ b/deluge/tests/test_ui_entry.py @@ -45,6 +45,7 @@ sys_stdout = sys.stdout class StringFileDescriptor(object): """File descriptor that writes to string buffer""" + def __init__(self, fd): self.out = StringIO() self.fd = fd @@ -63,7 +64,6 @@ class StringFileDescriptor(object): class UIBaseTestCase(object): - def __init__(self): self.var = {} @@ -139,7 +139,13 @@ class DelugeEntryTestCase(BaseTestCase): def test_start_with_log_level(self): _level = [] - def setup_logger(level='error', filename=None, filemode='w', logrotate=None, output_stream=sys.stdout): + def setup_logger( + level='error', + filename=None, + filemode='w', + logrotate=None, + output_stream=sys.stdout, + ): _level.append(level) self.patch(deluge.log, 'setup_logger', setup_logger) @@ -163,13 +169,13 @@ class GtkUIBaseTestCase(UIBaseTestCase): self.patch(sys, 'argv', utf8_encode_structure(self.var['sys_arg_cmd'])) from deluge.ui.gtkui import gtkui + with mock.patch.object(gtkui.GtkUI, 'start', autospec=True): self.exec_command() @pytest.mark.gtkui class GtkUIDelugeScriptEntryTestCase(BaseTestCase, GtkUIBaseTestCase): - def __init__(self, testname): super(GtkUIDelugeScriptEntryTestCase, self).__init__(testname) GtkUIBaseTestCase.__init__(self) @@ -187,11 +193,11 @@ class GtkUIDelugeScriptEntryTestCase(BaseTestCase, GtkUIBaseTestCase): @pytest.mark.gtkui class GtkUIScriptEntryTestCase(BaseTestCase, GtkUIBaseTestCase): - def __init__(self, testname): super(GtkUIScriptEntryTestCase, self).__init__(testname) GtkUIBaseTestCase.__init__(self) from deluge.ui import gtkui + self.var['cmd_name'] = 'deluge-gtk' self.var['start_cmd'] = gtkui.start self.var['sys_arg_cmd'] = ['./deluge-gtk'] @@ -220,7 +226,13 @@ class WebUIBaseTestCase(UIBaseTestCase): def test_start_web_with_log_level(self): _level = [] - def setup_logger(level='error', filename=None, filemode='w', logrotate=None, output_stream=sys.stdout): + def setup_logger( + level='error', + filename=None, + filemode='w', + logrotate=None, + output_stream=sys.stdout, + ): _level.append(level) self.patch(deluge.log, 'setup_logger', setup_logger) @@ -284,7 +296,13 @@ class ConsoleUIBaseTestCase(UIBaseTestCase): def test_start_console_with_log_level(self): _level = [] - def setup_logger(level='error', filename=None, filemode='w', logrotate=None, output_stream=sys.stdout): + def setup_logger( + level='error', + filename=None, + filemode='w', + logrotate=None, + output_stream=sys.stdout, + ): _level.append(level) self.patch(deluge.log, 'setup_logger', setup_logger) @@ -308,11 +326,18 @@ class ConsoleUIBaseTestCase(UIBaseTestCase): with mock.patch('deluge.ui.console.main.ConsoleUI'): self.assertRaises(SystemExit, self.exec_command) std_output = fd.out.getvalue() - self.assertTrue(('usage: %s' % self.var['cmd_name']) in std_output) # Check command name + self.assertTrue( + ('usage: %s' % self.var['cmd_name']) in std_output + ) # Check command name self.assertTrue('Common Options:' in std_output) self.assertTrue('Console Options:' in std_output) - self.assertTrue('Console Commands:\n The following console commands are available:' in std_output) - self.assertTrue('The following console commands are available:' in std_output) + self.assertTrue( + 'Console Commands:\n The following console commands are available:' + in std_output + ) + self.assertTrue( + 'The following console commands are available:' in std_output + ) def test_console_command_info(self): self.patch(sys, 'argv', self.var['sys_arg_cmd'] + ['info']) @@ -334,7 +359,9 @@ class ConsoleUIBaseTestCase(UIBaseTestCase): self.assertTrue('Show information about the torrents' in std_output) def test_console_unrecognized_arguments(self): - self.patch(sys, 'argv', ['./deluge', '--ui', 'console']) # --ui is not longer supported + self.patch( + sys, 'argv', ['./deluge', '--ui', 'console'] + ) # --ui is not longer supported fd = StringFileDescriptor(sys.stdout) self.patch(argparse._sys, 'stderr', fd) with mock.patch('deluge.ui.console.main.ConsoleUI'): @@ -354,8 +381,16 @@ class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase): def test_console_command_status(self): username, password = get_localhost_auth() self.patch( - sys, 'argv', self.var['sys_arg_cmd'] + ['--port'] + ['58900'] + ['--username'] + - [username] + ['--password'] + [password] + ['status'], + sys, + 'argv', + self.var['sys_arg_cmd'] + + ['--port'] + + ['58900'] + + ['--username'] + + [username] + + ['--password'] + + [password] + + ['status'], ) fd = StringFileDescriptor(sys.stdout) self.patch(sys, 'stdout', fd) @@ -363,10 +398,15 @@ class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase): yield self.exec_command() std_output = fd.out.getvalue() - self.assertTrue(std_output.startswith('Total upload: ') and std_output.endswith(' Moving: 0\n')) + self.assertTrue( + std_output.startswith('Total upload: ') + and std_output.endswith(' Moving: 0\n') + ) -class ConsoleScriptEntryWithDaemonTestCase(BaseTestCase, ConsoleUIWithDaemonBaseTestCase): +class ConsoleScriptEntryWithDaemonTestCase( + BaseTestCase, ConsoleUIWithDaemonBaseTestCase +): if windows_check(): skip = 'cannot test console ui on windows' diff --git a/deluge/tests/test_web_api.py b/deluge/tests/test_web_api.py index 22ec233e9..982a93b1f 100644 --- a/deluge/tests/test_web_api.py +++ b/deluge/tests/test_web_api.py @@ -27,7 +27,6 @@ common.disable_new_release_check() class WebAPITestCase(WebServerTestBase): - def test_connect_invalid_host(self): d = self.deluge_web.web_api.connect('id') d.addCallback(self.fail) @@ -73,7 +72,7 @@ class WebAPITestCase(WebServerTestBase): 'login': 'skrot', 'expires': 1460030877.0, 'level': 10, - }, + } } self.deluge_web.web_api.set_config(config) web_config = component.get('DelugeWeb').config.config @@ -98,7 +97,9 @@ class WebAPITestCase(WebServerTestBase): conn = ['abcdef', '10.0.0.1', 0, 'user123', 'pass123'] self.assertFalse(self.deluge_web.web_api._get_host(conn[0])) # Add valid host - result, host_id = self.deluge_web.web_api.add_host(conn[1], conn[2], conn[3], conn[4]) + result, host_id = self.deluge_web.web_api.add_host( + conn[1], conn[2], conn[3], conn[4] + ) self.assertEqual(result, True) conn[0] = host_id self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn[0:4]) @@ -130,7 +131,9 @@ class WebAPITestCase(WebServerTestBase): self.assertTrue('files_tree' in ret) def test_get_magnet_info(self): - ret = self.deluge_web.web_api.get_magnet_info('magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN') + ret = self.deluge_web.web_api.get_magnet_info( + 'magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN' + ) self.assertEqual(ret['name'], '953bad769164e8482c7785a21d12166f94b9e14d') self.assertEqual(ret['info_hash'], '953bad769164e8482c7785a21d12166f94b9e14d') self.assertTrue('files_tree' in ret) @@ -139,16 +142,26 @@ class WebAPITestCase(WebServerTestBase): def test_get_torrent_files(self): yield self.deluge_web.web_api.connect(self.host_id) filename = common.get_test_data_file('test.torrent') - torrents = [{'path': filename, 'options': {'download_location': '/home/deluge/'}}] + torrents = [ + {'path': filename, 'options': {'download_location': '/home/deluge/'}} + ] yield self.deluge_web.web_api.add_torrents(torrents) - ret = yield self.deluge_web.web_api.get_torrent_files('ab570cdd5a17ea1b61e970bb72047de141bce173') + ret = yield self.deluge_web.web_api.get_torrent_files( + 'ab570cdd5a17ea1b61e970bb72047de141bce173' + ) self.assertEqual(ret['type'], 'dir') self.assertEqual( - ret['contents'], { + ret['contents'], + { 'azcvsupdater_2.6.2.jar': { - 'priority': 4, 'index': 0, 'offset': 0, 'progress': 0.0, 'path': - 'azcvsupdater_2.6.2.jar', 'type': 'file', 'size': 307949, - }, + 'priority': 4, + 'index': 0, + 'offset': 0, + 'progress': 0.0, + 'path': 'azcvsupdater_2.6.2.jar', + 'type': 'file', + 'size': 307949, + } }, ) @@ -156,7 +169,8 @@ class WebAPITestCase(WebServerTestBase): def test_download_torrent_from_url(self): filename = 'ubuntu-9.04-desktop-i386.iso.torrent' self.deluge_web.top_level.putChild( - filename.encode(), File(common.get_test_data_file(filename))) + filename.encode(), File(common.get_test_data_file(filename)) + ) url = 'http://localhost:%d/%s' % (self.webserver_listen_port, filename) res = yield self.deluge_web.web_api.download_torrent_from_url(url) self.assertTrue(res.endswith(filename)) @@ -174,10 +188,12 @@ class WebAPITestCase(WebServerTestBase): d = yield agent.request( b'POST', b'http://127.0.0.1:%i/json' % self.webserver_listen_port, - Headers({ - b'User-Agent': [b'Twisted Web Client Example'], - b'Content-Type': [b'application/json'], - }), + Headers( + { + b'User-Agent': [b'Twisted Web Client Example'], + b'Content-Type': [b'application/json'], + } + ), FileBodyProducer(BytesIO(bad_body)), ) yield d diff --git a/deluge/tests/test_webserver.py b/deluge/tests/test_webserver.py index b2454d2d6..abfac366e 100644 --- a/deluge/tests/test_webserver.py +++ b/deluge/tests/test_webserver.py @@ -25,7 +25,6 @@ common.disable_new_release_check() class WebServerTestCase(WebServerTestBase, WebServerMockBase): - @defer.inlineCallbacks def test_get_torrent_info(self): @@ -38,7 +37,9 @@ class WebServerTestCase(WebServerTestBase, WebServerMockBase): # encoded to allow dumping the torrent info to json. Otherwise it will fail with: # UnicodeDecodeError: 'utf8' codec can't decode byte 0xe5 in position 0: invalid continuation byte filename = get_test_data_file('filehash_field.torrent') - input_file = '{"params": ["%s"], "method": "web.get_torrent_info", "id": 22}' % filename + input_file = ( + '{"params": ["%s"], "method": "web.get_torrent_info", "id": 22}' % filename + ) headers = { b'User-Agent': ['Twisted Web Client Example'], b'Content-Type': ['application/json'], diff --git a/deluge/tests/twisted/plugins/delugereporter.py b/deluge/tests/twisted/plugins/delugereporter.py index e0f4fc7d1..340b61cb5 100644 --- a/deluge/tests/twisted/plugins/delugereporter.py +++ b/deluge/tests/twisted/plugins/delugereporter.py @@ -19,7 +19,9 @@ from zope.interface import implements class _Reporter(object): implements(IPlugin, IReporter) - def __init__(self, name, module, description, longOpt, shortOpt, klass): # NOQA: N803 + def __init__( # NOQA: N803 + self, name, module, description, longOpt, shortOpt, klass + ): self.name = name self.module = module self.description = description @@ -39,7 +41,6 @@ deluge = _Reporter( class DelugeReporter(TreeReporter): - def __init__(self, *args, **kwargs): os.environ['DELUGE_REPORTER'] = 'true' TreeReporter.__init__(self, *args, **kwargs) diff --git a/deluge/transfer.py b/deluge/transfer.py index c1859eb67..85274d46c 100644 --- a/deluge/transfer.py +++ b/deluge/transfer.py @@ -32,6 +32,7 @@ class DelugeTransferProtocol(Protocol, object): the length of the data to be transfered (payload). """ + def __init__(self): self._buffer = b'' # TODO: Look into using bytearray instead of byte string. self._message_length = 0 @@ -78,9 +79,9 @@ class DelugeTransferProtocol(Protocol, object): self._handle_new_message() # We have a complete packet if len(self._buffer) >= self._message_length: - self._handle_complete_message(self._buffer[:self._message_length]) + self._handle_complete_message(self._buffer[: self._message_length]) # Remove message data from buffer - self._buffer = self._buffer[self._message_length:] + self._buffer = self._buffer[self._message_length :] self._message_length = 0 else: break @@ -96,7 +97,9 @@ class DelugeTransferProtocol(Protocol, object): header = self._buffer[:MESSAGE_HEADER_SIZE] payload_len = header[1:MESSAGE_HEADER_SIZE] if header[0:1] != b'D': - raise Exception('Invalid header format. First byte is %d' % ord(header[0:1])) + raise Exception( + 'Invalid header format. First byte is %d' % ord(header[0:1]) + ) # Extract the length stored as a signed integer (using 4 bytes) self._message_length = struct.unpack('!i', payload_len)[0] if self._message_length < 0: @@ -105,7 +108,9 @@ class DelugeTransferProtocol(Protocol, object): self._buffer = self._buffer[MESSAGE_HEADER_SIZE:] except Exception as ex: log.warning('Error occurred when parsing message header: %s.', ex) - log.warning('This version of Deluge cannot communicate with the sender of this data.') + log.warning( + 'This version of Deluge cannot communicate with the sender of this data.' + ) self._message_length = 0 self._buffer = b'' @@ -117,9 +122,15 @@ class DelugeTransferProtocol(Protocol, object): """ try: - self.message_received(rencode.loads(zlib.decompress(data), decode_utf8=True)) + self.message_received( + rencode.loads(zlib.decompress(data), decode_utf8=True) + ) except Exception as ex: - log.warning('Failed to decompress (%d bytes) and load serialized data with rencode: %s', len(data), ex) + log.warning( + 'Failed to decompress (%d bytes) and load serialized data with rencode: %s', + len(data), + ex, + ) def get_bytes_recv(self): """ diff --git a/deluge/ui/Win32IconImagePlugin.py b/deluge/ui/Win32IconImagePlugin.py index caa653590..3d720e87b 100644 --- a/deluge/ui/Win32IconImagePlugin.py +++ b/deluge/ui/Win32IconImagePlugin.py @@ -82,20 +82,34 @@ class Win32IcoFile(object): self.nb_items = header[2] - dir_fields = ('width', 'height', 'nb_color', 'reserved', 'planes', 'bpp', 'size', 'offset') + dir_fields = ( + 'width', + 'height', + 'nb_color', + 'reserved', + 'planes', + 'bpp', + 'size', + 'offset', + ) for i in range(self.nb_items): directory = list(struct.unpack('<4B2H2I', buf.read(16))) for j in range(3): if not directory[j]: directory[j] = 256 icon_header = dict(zip(dir_fields, directory)) - icon_header['color_depth'] = (icon_header['bpp'] or (icon_header['nb_color'] == 16 and 4)) + icon_header['color_depth'] = icon_header['bpp'] or ( + icon_header['nb_color'] == 16 and 4 + ) icon_header['dim'] = (icon_header['width'], icon_header['height']) self.entry.append(icon_header) # end for (read headers) # order by size and color depth - self.entry.sort(lambda x, y: cmp(x['width'], y['width']) or cmp(x['color_depth'], y['color_depth'])) + self.entry.sort( + lambda x, y: cmp(x['width'], y['width']) + or cmp(x['color_depth'], y['color_depth']) + ) self.entry.reverse() def sizes(self): @@ -167,11 +181,11 @@ class Win32IcoFile(object): # convert to an 8bpp grayscale image mask = PIL.Image.frombuffer( - 'L', # 8bpp - im.size, # (w, h) - alpha_bytes, # source chars - 'raw', # raw decoder - ('L', 0, -1), # 8bpp inverted, unpadded, reversed + 'L', # 8bpp + im.size, # (w, h) + alpha_bytes, # source chars + 'raw', # raw decoder + ('L', 0, -1), # 8bpp inverted, unpadded, reversed ) # apply mask image as alpha channel @@ -187,17 +201,23 @@ class Win32IcoFile(object): w += 32 - (im.size[0] % 32) # the total mask data is padded row size * height / bits per char total_bytes = (w * im.size[1]) // 8 - log.debug('tot=%d, off=%d, w=%d, size=%d', len(data), and_mask_offset, w, total_bytes) + log.debug( + 'tot=%d, off=%d, w=%d, size=%d', + len(data), + and_mask_offset, + w, + total_bytes, + ) self.buf.seek(and_mask_offset) mask_data = self.buf.read(total_bytes) # convert raw data to image mask = PIL.Image.frombuffer( - '1', # 1 bpp - im.size, # (w, h) - mask_data, # source chars - 'raw', # raw decoder + '1', # 1 bpp + im.size, # (w, h) + mask_data, # source chars + 'raw', # raw decoder ('1;I', w // 8, -1), # 1bpp inverted, padded, reversed ) @@ -210,14 +230,19 @@ class Win32IcoFile(object): # end if (png)/else(bmp) return im + # end frame def __repr__(self): s = 'Microsoft Icon: %d images (max %dx%d %dbpp)' % ( - len(self.entry), self.entry[0]['width'], self.entry[0]['height'], + len(self.entry), + self.entry[0]['width'], + self.entry[0]['height'], self.entry[0]['bpp'], ) return s + + # end Win32IcoFile @@ -250,6 +275,8 @@ class Win32IconImageFile(PIL.ImageFile.ImageFile): # pylint: disable=abstract-m self.im = im.im self.mode = im.mode self.size = im.size + + # end class Win32IconImageFile @@ -258,6 +285,8 @@ def _accept(prefix): Quick file test helper for Image.open() """ return prefix[:4] == _MAGIC + + # end _accept diff --git a/deluge/ui/baseargparser.py b/deluge/ui/baseargparser.py index 6bedc8093..6024e99d5 100644 --- a/deluge/ui/baseargparser.py +++ b/deluge/ui/baseargparser.py @@ -90,6 +90,7 @@ def get_version(): version_str = '%s\n' % (common.get_version()) try: from deluge._libtorrent import LT_VERSION + version_str += 'libtorrent: %s\n' % LT_VERSION except ImportError: pass @@ -141,7 +142,6 @@ class DelugeTextHelpFormatter(argparse.RawDescriptionHelpFormatter): class HelpAction(argparse._HelpAction): - def __call__(self, parser, namespace, values, option_string=None): if hasattr(parser, 'subparser'): subparser = getattr(parser, 'subparser') @@ -152,10 +152,11 @@ class HelpAction(argparse._HelpAction): class BaseArgParser(argparse.ArgumentParser): - def __init__(self, *args, **kwargs): if 'formatter_class' not in kwargs: - kwargs['formatter_class'] = lambda prog: DelugeTextHelpFormatter(prog, max_help_position=33, width=90) + kwargs['formatter_class'] = lambda prog: DelugeTextHelpFormatter( + prog, max_help_position=33, width=90 + ) kwargs['add_help'] = kwargs.get('add_help', False) common_help = kwargs.pop('common_help', True) @@ -171,45 +172,64 @@ class BaseArgParser(argparse.ArgumentParser): self.group = self.add_argument_group(_('Common Options')) if common_help: self.group.add_argument( - '-h', '--help', action=HelpAction, - help=_('Print this help message'), + '-h', '--help', action=HelpAction, help=_('Print this help message') ) self.group.add_argument( - '-V', '--version', action='version', version='%(prog)s ' + get_version(), + '-V', + '--version', + action='version', + version='%(prog)s ' + get_version(), help=_('Print version information'), ) self.group.add_argument( - '-v', action='version', version='%(prog)s ' + get_version(), + '-v', + action='version', + version='%(prog)s ' + get_version(), help=argparse.SUPPRESS, ) # Deprecated arg self.group.add_argument( - '-c', '--config', metavar='<config>', + '-c', + '--config', + metavar='<config>', help=_('Set the config directory path'), ) self.group.add_argument( - '-l', '--logfile', metavar='<logfile>', + '-l', + '--logfile', + metavar='<logfile>', help=_('Output to specified logfile instead of stdout'), ) self.group.add_argument( - '-L', '--loglevel', choices=[l for k in deluge.log.levels for l in (k, k.upper())], - help=_('Set the log level (none, error, warning, info, debug)'), metavar='<level>', + '-L', + '--loglevel', + choices=[l for k in deluge.log.levels for l in (k, k.upper())], + help=_('Set the log level (none, error, warning, info, debug)'), + metavar='<level>', ) self.group.add_argument( - '--logrotate', nargs='?', const='2M', metavar='<max-size>', + '--logrotate', + nargs='?', + const='2M', + metavar='<max-size>', help=_( 'Enable logfile rotation, with optional maximum logfile size, ' - 'default: %(const)s (Logfile rotation count is 5)', + 'default: %(const)s (Logfile rotation count is 5)' ), ) self.group.add_argument( - '-q', '--quiet', action='store_true', + '-q', + '--quiet', + action='store_true', help=_('Quieten logging output (Same as `--loglevel none`)'), ) self.group.add_argument( - '--profile', metavar='<profile-file>', nargs='?', default=False, + '--profile', + metavar='<profile-file>', + nargs='?', + default=False, help=_( 'Profile %(prog)s with cProfile. Outputs to stdout ' - 'unless a filename is specified', + 'unless a filename is specified' ), ) @@ -274,8 +294,11 @@ class BaseArgParser(argparse.ArgumentParser): # Setup the logger deluge.log.setup_logger( - level=options.loglevel, filename=options.logfile, filemode=logfile_mode, - logrotate=logrotate, output_stream=self.log_stream, + level=options.loglevel, + filename=options.logfile, + filemode=logfile_mode, + logrotate=logrotate, + output_stream=self.log_stream, ) if options.config: @@ -309,11 +332,13 @@ class BaseArgParser(argparse.ArgumentParser): if options.user: if not options.user.isdigit(): import pwd + options.user = pwd.getpwnam(options.user)[2] os.setuid(options.user) if options.group: if not options.group.isdigit(): import grp + options.group = grp.getgrnam(options.group)[2] os.setuid(options.group) @@ -325,23 +350,38 @@ class BaseArgParser(argparse.ArgumentParser): self.process_arg_group = True self.group = self.add_argument_group(_('Process Control Options')) self.group.add_argument( - '-P', '--pidfile', metavar='<pidfile>', action='store', + '-P', + '--pidfile', + metavar='<pidfile>', + action='store', help=_('Pidfile to store the process id'), ) if not common.windows_check(): self.group.add_argument( - '-d', '--do-not-daemonize', dest='donotdaemonize', action='store_true', + '-d', + '--do-not-daemonize', + dest='donotdaemonize', + action='store_true', help=_('Do not daemonize (fork) this process'), ) self.group.add_argument( - '-f', '--fork', dest='donotdaemonize', action='store_false', + '-f', + '--fork', + dest='donotdaemonize', + action='store_false', help=argparse.SUPPRESS, ) # Deprecated arg self.group.add_argument( - '-U', '--user', metavar='<user>', action='store', + '-U', + '--user', + metavar='<user>', + action='store', help=_('Change to this user on startup (Requires root)'), ) self.group.add_argument( - '-g', '--group', metavar='<group>', action='store', + '-g', + '--group', + metavar='<group>', + action='store', help=_('Change to this group on startup (Requires root)'), ) diff --git a/deluge/ui/client.py b/deluge/ui/client.py index 64db4ce06..7f37c7b1c 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -67,14 +67,20 @@ class DelugeRPCRequest(object): :returns: a properly formated RPCRequest """ - if self.request_id is None or self.method is None or self.args is None or self.kwargs is None: - raise TypeError('You must set the properties of this object before calling format_message!') + if ( + self.request_id is None + or self.method is None + or self.args is None + or self.kwargs is None + ): + raise TypeError( + 'You must set the properties of this object before calling format_message!' + ) return (self.request_id, self.method, self.args, self.kwargs) class DelugeRPCProtocol(DelugeTransferProtocol): - def connectionMade(self): # NOQA: N802 self.__rpc_requests = {} # Set the protocol in the daemon so it can send data @@ -99,8 +105,8 @@ class DelugeRPCProtocol(DelugeTransferProtocol): return if len(request) < 3: log.debug( - 'Received invalid message: number of items in ' - 'response is %s', len(request), + 'Received invalid message: number of items in ' 'response is %s', + len(request), ) return @@ -133,7 +139,9 @@ class DelugeRPCProtocol(DelugeTransferProtocol): exception_cls = getattr(error, request[2]) exception = exception_cls(*request[3], **request[4]) except TypeError: - log.warning('Received invalid RPC_ERROR (Old daemon?): %s', request[2]) + log.warning( + 'Received invalid RPC_ERROR (Old daemon?): %s', request[2] + ) return # Ideally we would chain the deferreds instead of instance @@ -165,9 +173,11 @@ class DelugeRPCProtocol(DelugeTransferProtocol): log.debug(msg) except Exception: import traceback + log.error( 'Failed to handle RPC_ERROR (Old daemon?): %s\nLocal error: %s', - request[2], traceback.format_exc(), + request[2], + traceback.format_exc(), ) d.errback(exception) del self.__rpc_requests[request_id] @@ -199,29 +209,33 @@ class DelugeRPCClientFactory(ClientFactory): self.event_handlers = event_handlers def startedConnecting(self, connector): # NOQA: N802 - log.debug( - 'Connecting to daemon at "%s:%s"...', - connector.host, connector.port, - ) + log.debug('Connecting to daemon at "%s:%s"...', connector.host, connector.port) def clientConnectionFailed(self, connector, reason): # NOQA: N802 log.debug( 'Connection to daemon at "%s:%s" failed: %s', - connector.host, connector.port, reason.value, + connector.host, + connector.port, + reason.value, ) self.daemon.connect_deferred.errback(reason) def clientConnectionLost(self, connector, reason): # NOQA: N802 log.debug( 'Connection lost to daemon at "%s:%s" reason: %s', - connector.host, connector.port, reason.value, + connector.host, + connector.port, + reason.value, ) self.daemon.host = None self.daemon.port = None self.daemon.username = None self.daemon.connected = False - if self.daemon.disconnect_deferred and not self.daemon.disconnect_deferred.called: + if ( + self.daemon.disconnect_deferred + and not self.daemon.disconnect_deferred.called + ): self.daemon.disconnect_deferred.callback(reason.value) self.daemon.disconnect_deferred = None @@ -273,9 +287,7 @@ class DaemonSSLProxy(DaemonProxy): self.host = host self.port = port self.__connector = reactor.connectSSL( - self.host, self.port, - self.__factory, - ssl.ClientContextFactory(), + self.host, self.port, self.__factory, ssl.ClientContextFactory() ) self.connect_deferred = defer.Deferred() self.daemon_info_deferred = defer.Deferred() @@ -370,7 +382,10 @@ class DaemonSSLProxy(DaemonProxy): :type handler: function """ - if event in self.__factory.event_handlers and handler in self.__factory.event_handlers[event]: + if ( + event in self.__factory.event_handlers + and handler in self.__factory.event_handlers[event] + ): self.__factory.event_handlers[event].remove(handler) def __on_connect(self, result): @@ -397,7 +412,9 @@ class DaemonSSLProxy(DaemonProxy): login_deferred = defer.Deferred() d = self.call('daemon.login', username, password, client_version=get_version()) d.addCallbacks( - self.__on_login, self.__on_login_fail, callbackArgs=[username, login_deferred], + self.__on_login, + self.__on_login_fail, + callbackArgs=[username, login_deferred], errbackArgs=[login_deferred], ) return login_deferred @@ -410,7 +427,7 @@ class DaemonSSLProxy(DaemonProxy): if self.__factory.event_handlers: self.call('daemon.set_event_interest', list(self.__factory.event_handlers)) self.call('core.get_auth_levels_mappings').addCallback( - self.__on_auth_levels_mappings, + self.__on_auth_levels_mappings ) login_deferred.callback(result) @@ -442,6 +459,7 @@ class DaemonStandaloneProxy(DaemonProxy): if event_handlers is None: event_handlers = {} from deluge.core import daemon + self.__daemon = daemon.Daemon(standalone=True) self.__daemon.start() log.debug('daemon created!') @@ -454,6 +472,7 @@ class DaemonStandaloneProxy(DaemonProxy): AUTH_LEVELS_MAPPING, AUTH_LEVELS_MAPPING_REVERSE, ) + self.username = 'localclient' self.authentication_level = AUTH_LEVEL_ADMIN self.auth_levels_mapping = AUTH_LEVELS_MAPPING @@ -478,9 +497,7 @@ class DaemonStandaloneProxy(DaemonProxy): log.exception(ex) return defer.fail(ex) else: - return defer.maybeDeferred( - m, *copy.deepcopy(args), **copy.deepcopy(kwargs) - ) + return defer.maybeDeferred(m, *copy.deepcopy(args), **copy.deepcopy(kwargs)) def register_event_handler(self, event, handler): """ @@ -513,6 +530,7 @@ class DottedObject(object): """ This is used for dotted name calls to client """ + def __init__(self, daemon, method): self.daemon = daemon self.base = method @@ -528,6 +546,7 @@ class RemoteMethod(DottedObject): """ This is used when something like 'client.core.get_something()' is attempted. """ + def __call__(self, *args, **kwargs): return self.daemon.call(self.base, *args, **kwargs) @@ -537,8 +556,7 @@ class Client(object): This class is used to connect to a daemon process and issue RPC requests. """ - __event_handlers = { - } + __event_handlers = {} def __init__(self): self._daemon_proxy = None @@ -546,7 +564,11 @@ class Client(object): self.__started_standalone = False def connect( - self, host='127.0.0.1', port=58846, username='', password='', + self, + host='127.0.0.1', + port=58846, + username='', + password='', skip_authentication=False, ): """ @@ -652,12 +674,13 @@ class Client(object): subprocess.Popen(['deluged', '--port=%s' % port, '--config=%s' % config]) except OSError as ex: from errno import ENOENT + if ex.errno == ENOENT: log.error( _( 'Deluge cannot find the `deluged` executable, check that ' - 'the deluged package is installed, or added to your PATH.', - ), + 'the deluged package is installed, or added to your PATH.' + ) ) else: log.exception(ex) @@ -675,8 +698,11 @@ class Client(object): :returns: bool, True if connected to a localhost """ - if (self._daemon_proxy and self._daemon_proxy.host in ('127.0.0.1', 'localhost') or - isinstance(self._daemon_proxy, DaemonStandaloneProxy)): + if ( + self._daemon_proxy + and self._daemon_proxy.host in ('127.0.0.1', 'localhost') + or isinstance(self._daemon_proxy, DaemonStandaloneProxy) + ): return True return False @@ -710,7 +736,11 @@ class Client(object): :returns: a tuple of (host, port, username) or None if not connected """ if self.connected(): - return (self._daemon_proxy.host, self._daemon_proxy.port, self._daemon_proxy.username) + return ( + self._daemon_proxy.host, + self._daemon_proxy.port, + self._daemon_proxy.username, + ) return None diff --git a/deluge/ui/common.py b/deluge/ui/common.py index b9866d21c..91b56ea44 100644 --- a/deluge/ui/common.py +++ b/deluge/ui/common.py @@ -45,96 +45,75 @@ STATE_TRANSLATION = { } TORRENT_DATA_FIELD = { - 'queue': - {'name': '#', 'status': ['queue']}, - 'name': - {'name': _('Name'), 'status': ['state', 'name']}, - 'progress_state': - {'name': _('Progress'), 'status': ['progress', 'state']}, - 'state': - {'name': _('State'), 'status': ['state']}, - 'progress': - {'name': _('Progress'), 'status': ['progress']}, - 'size': - {'name': _('Size'), 'status': ['total_wanted']}, - 'downloaded': - {'name': _('Downloaded'), 'status': ['all_time_download']}, - 'uploaded': - {'name': _('Uploaded'), 'status': ['total_uploaded']}, - 'remaining': - {'name': _('Remaining'), 'status': ['total_remaining']}, - 'ratio': - {'name': _('Ratio'), 'status': ['ratio']}, - 'download_speed': - {'name': _('Down Speed'), 'status': ['download_payload_rate']}, - 'upload_speed': - {'name': _('Up Speed'), 'status': ['upload_payload_rate']}, - 'max_download_speed': - {'name': _('Down Limit'), 'status': ['max_download_speed']}, - 'max_upload_speed': - {'name': _('Up Limit'), 'status': ['max_upload_speed']}, - 'max_connections': - {'name': _('Max Connections'), 'status': ['max_connections']}, - 'max_upload_slots': - {'name': _('Max Upload Slots'), 'status': ['max_upload_slots']}, - 'peers': - {'name': _('Peers'), 'status': ['num_peers', 'total_peers']}, - 'seeds': - {'name': _('Seeds'), 'status': ['num_seeds', 'total_seeds']}, - 'avail': - {'name': _('Avail'), 'status': ['distributed_copies']}, - 'seeds_peers_ratio': - {'name': _('Seeds:Peers'), 'status': ['seeds_peers_ratio']}, - 'time_added': - {'name': _('Added'), 'status': ['time_added']}, - 'tracker': - {'name': _('Tracker'), 'status': ['tracker_host']}, - 'download_location': - {'name': _('Download Folder'), 'status': ['download_location']}, - 'seeding_time': - {'name': _('Seeding Time'), 'status': ['seeding_time']}, - 'active_time': - {'name': _('Active Time'), 'status': ['active_time']}, - 'time_since_transfer': - {'name': _('Last Activity'), 'status': ['time_since_transfer']}, - 'finished_time': - {'name': _('Finished Time'), 'status': ['finished_time']}, - 'last_seen_complete': - {'name': _('Complete Seen'), 'status': ['last_seen_complete']}, - 'completed_time': - {'name': _('Completed'), 'status': ['completed_time']}, - 'eta': - {'name': _('ETA'), 'status': ['eta']}, - 'shared': - {'name': _('Shared'), 'status': ['shared']}, - 'prioritize_first_last': - {'name': _('Prioritize First/Last'), 'status': ['prioritize_first_last']}, - 'sequential_download': - {'name': _('Sequential Download'), 'status': ['sequential_download']}, - 'is_auto_managed': - {'name': _('Auto Managed'), 'status': ['is_auto_managed']}, - 'auto_managed': - {'name': _('Auto Managed'), 'status': ['auto_managed']}, - 'stop_at_ratio': - {'name': _('Stop At Ratio'), 'status': ['stop_at_ratio']}, - 'stop_ratio': - {'name': _('Stop Ratio'), 'status': ['stop_ratio']}, - 'remove_at_ratio': - {'name': _('Remove At Ratio'), 'status': ['remove_at_ratio']}, - 'move_completed': - {'name': _('Move On Completed'), 'status': ['move_completed']}, - 'move_completed_path': - {'name': _('Move Completed Path'), 'status': ['move_completed_path']}, - 'move_on_completed': - {'name': _('Move On Completed'), 'status': ['move_on_completed']}, - 'move_on_completed_path': - {'name': _('Move On Completed Path'), 'status': ['move_on_completed_path']}, - 'owner': - {'name': _('Owner'), 'status': ['owner']}, - 'pieces': - {'name': _('Pieces'), 'status': ['num_pieces', 'piece_length']}, - 'seed_rank': - {'name': _('Seed Rank'), 'status': ['seed_rank']}, + 'queue': {'name': '#', 'status': ['queue']}, + 'name': {'name': _('Name'), 'status': ['state', 'name']}, + 'progress_state': {'name': _('Progress'), 'status': ['progress', 'state']}, + 'state': {'name': _('State'), 'status': ['state']}, + 'progress': {'name': _('Progress'), 'status': ['progress']}, + 'size': {'name': _('Size'), 'status': ['total_wanted']}, + 'downloaded': {'name': _('Downloaded'), 'status': ['all_time_download']}, + 'uploaded': {'name': _('Uploaded'), 'status': ['total_uploaded']}, + 'remaining': {'name': _('Remaining'), 'status': ['total_remaining']}, + 'ratio': {'name': _('Ratio'), 'status': ['ratio']}, + 'download_speed': {'name': _('Down Speed'), 'status': ['download_payload_rate']}, + 'upload_speed': {'name': _('Up Speed'), 'status': ['upload_payload_rate']}, + 'max_download_speed': {'name': _('Down Limit'), 'status': ['max_download_speed']}, + 'max_upload_speed': {'name': _('Up Limit'), 'status': ['max_upload_speed']}, + 'max_connections': {'name': _('Max Connections'), 'status': ['max_connections']}, + 'max_upload_slots': {'name': _('Max Upload Slots'), 'status': ['max_upload_slots']}, + 'peers': {'name': _('Peers'), 'status': ['num_peers', 'total_peers']}, + 'seeds': {'name': _('Seeds'), 'status': ['num_seeds', 'total_seeds']}, + 'avail': {'name': _('Avail'), 'status': ['distributed_copies']}, + 'seeds_peers_ratio': {'name': _('Seeds:Peers'), 'status': ['seeds_peers_ratio']}, + 'time_added': {'name': _('Added'), 'status': ['time_added']}, + 'tracker': {'name': _('Tracker'), 'status': ['tracker_host']}, + 'download_location': { + 'name': _('Download Folder'), + 'status': ['download_location'], + }, + 'seeding_time': {'name': _('Seeding Time'), 'status': ['seeding_time']}, + 'active_time': {'name': _('Active Time'), 'status': ['active_time']}, + 'time_since_transfer': { + 'name': _('Last Activity'), + 'status': ['time_since_transfer'], + }, + 'finished_time': {'name': _('Finished Time'), 'status': ['finished_time']}, + 'last_seen_complete': { + 'name': _('Complete Seen'), + 'status': ['last_seen_complete'], + }, + 'completed_time': {'name': _('Completed'), 'status': ['completed_time']}, + 'eta': {'name': _('ETA'), 'status': ['eta']}, + 'shared': {'name': _('Shared'), 'status': ['shared']}, + 'prioritize_first_last': { + 'name': _('Prioritize First/Last'), + 'status': ['prioritize_first_last'], + }, + 'sequential_download': { + 'name': _('Sequential Download'), + 'status': ['sequential_download'], + }, + 'is_auto_managed': {'name': _('Auto Managed'), 'status': ['is_auto_managed']}, + 'auto_managed': {'name': _('Auto Managed'), 'status': ['auto_managed']}, + 'stop_at_ratio': {'name': _('Stop At Ratio'), 'status': ['stop_at_ratio']}, + 'stop_ratio': {'name': _('Stop Ratio'), 'status': ['stop_ratio']}, + 'remove_at_ratio': {'name': _('Remove At Ratio'), 'status': ['remove_at_ratio']}, + 'move_completed': {'name': _('Move On Completed'), 'status': ['move_completed']}, + 'move_completed_path': { + 'name': _('Move Completed Path'), + 'status': ['move_completed_path'], + }, + 'move_on_completed': { + 'name': _('Move On Completed'), + 'status': ['move_on_completed'], + }, + 'move_on_completed_path': { + 'name': _('Move On Completed Path'), + 'status': ['move_on_completed_path'], + }, + 'owner': {'name': _('Owner'), 'status': ['owner']}, + 'pieces': {'name': _('Pieces'), 'status': ['num_pieces', 'piece_length']}, + 'seed_rank': {'name': _('Seed Rank'), 'status': ['seed_rank']}, } TRACKER_STATUS_TRANSLATION = [ @@ -176,8 +155,14 @@ del _ # The keys from session statistics for cache status. DISK_CACHE_KEYS = [ - 'disk.num_blocks_read', 'disk.num_blocks_written', 'disk.num_read_ops', 'disk.num_write_ops', - 'disk.num_blocks_cache_hits', 'read_hit_ratio', 'write_hit_ratio', 'disk.disk_blocks_in_use', + 'disk.num_blocks_read', + 'disk.num_blocks_written', + 'disk.num_read_ops', + 'disk.num_write_ops', + 'disk.num_blocks_cache_hits', + 'read_hit_ratio', + 'write_hit_ratio', + 'disk.disk_blocks_in_use', 'disk.read_cache_blocks', ] @@ -192,6 +177,7 @@ class TorrentInfo(object): metadata (bytes, optional): A bencoded metadata info_dict. """ + def __init__(self, filename='', filetree=1, metainfo=None, metadata=None): # Get the torrent metainfo from the torrent file if metadata: @@ -264,6 +250,7 @@ class TorrentInfo(object): dirname = os.path.dirname(dirname) if filetree == 2: + def walk(path, item): if item['type'] == 'dir': item.update(dirs[path]) @@ -274,6 +261,7 @@ class TorrentInfo(object): file_tree = FileTree2(list(paths)) file_tree.walk(walk) else: + def walk(path, item): if isinstance(item, dict): return item @@ -291,13 +279,11 @@ class TorrentInfo(object): 'index': 0, 'length': info_dict[b'length'], 'download': True, - }, - }, + } + } } else: - self._files_tree = { - self._name: (0, info_dict[b'length'], True), - } + self._files_tree = {self._name: (0, info_dict[b'length'], True)} self._files = [] if b'files' in info_dict: @@ -306,17 +292,13 @@ class TorrentInfo(object): prefix = self._name for f in info_dict[b'files']: - self._files.append({ - 'path': f[b'path'], - 'size': f[b'length'], - 'download': True, - }) + self._files.append( + {'path': f[b'path'], 'size': f[b'length'], 'download': True} + ) else: - self._files.append({ - 'path': self._name, - 'size': info_dict[b'length'], - 'download': True, - }) + self._files.append( + {'path': self._name, 'size': info_dict[b'length'], 'download': True} + ) def as_dict(self, *keys): """The torrent info as a dictionary, filtered by keys. @@ -414,10 +396,7 @@ class FileTree2(object): directory, path = path.split('/', 1) child = parent['contents'].get(directory) if child is None: - parent['contents'][directory] = { - 'type': 'dir', - 'contents': {}, - } + parent['contents'][directory] = {'type': 'dir', 'contents': {}} parent = parent['contents'][directory] return parent, path @@ -425,15 +404,10 @@ class FileTree2(object): if path[-1] == '/': path = path[:-1] parent, path = get_parent(path) - parent['contents'][path] = { - 'type': 'dir', - 'contents': {}, - } + parent['contents'][path] = {'type': 'dir', 'contents': {}} else: parent, path = get_parent(path) - parent['contents'][path] = { - 'type': 'file', - } + parent['contents'][path] = {'type': 'file'} def get_tree(self): """ @@ -454,18 +428,22 @@ class FileTree2(object): and `dict` for a directory. :type callback: function """ + def walk(directory, parent_path): for path in list(directory['contents']): full_path = os.path.join(parent_path, path).replace('\\', '/') if directory['contents'][path]['type'] == 'dir': - directory['contents'][path] = callback( - full_path, directory['contents'][path], - ) or directory['contents'][path] + directory['contents'][path] = ( + callback(full_path, directory['contents'][path]) + or directory['contents'][path] + ) walk(directory['contents'][path], full_path) else: - directory['contents'][path] = callback( - full_path, directory['contents'][path], - ) or directory['contents'][path] + directory['contents'][path] = ( + callback(full_path, directory['contents'][path]) + or directory['contents'][path] + ) + walk(self.tree, '') def __str__(self): @@ -476,6 +454,7 @@ class FileTree2(object): path = os.path.basename(path) path = path + '/' if item['type'] == 'dir' else path lines.append(' ' * depth + path) + self.walk(write) return '\n'.join(lines) @@ -517,10 +496,12 @@ class FileTree(object): :returns: the file tree. :rtype: dictionary """ + def to_tuple(path, item): if isinstance(item, dict): return item return tuple(item) + self.walk(to_tuple) return self.tree @@ -534,14 +515,20 @@ class FileTree(object): and `dict` for a directory. :type callback: function """ + def walk(directory, parent_path): for path in list(directory): full_path = os.path.join(parent_path, path) if isinstance(directory[path], dict): - directory[path] = callback(full_path, directory[path]) or directory[path] + directory[path] = ( + callback(full_path, directory[path]) or directory[path] + ) walk(directory[path], full_path) else: - directory[path] = callback(full_path, directory[path]) or directory[path] + directory[path] = ( + callback(full_path, directory[path]) or directory[path] + ) + walk(self.tree, '') def __str__(self): @@ -552,5 +539,6 @@ class FileTree(object): path = os.path.basename(path) path = isinstance(item, dict) and path + '/' or path lines.append(' ' * depth + path) + self.walk(write) return '\n'.join(lines) diff --git a/deluge/ui/console/cmdline/command.py b/deluge/ui/console/cmdline/command.py index 0b59aca7b..2ff32dff9 100644 --- a/deluge/ui/console/cmdline/command.py +++ b/deluge/ui/console/cmdline/command.py @@ -24,7 +24,6 @@ log = logging.getLogger(__name__) class Commander(object): - def __init__(self, cmds, interactive=False): self._commands = cmds self.interactive = interactive @@ -85,6 +84,7 @@ class Commander(object): self.write(parser.format_help()) else: parser._print_help(f) + parser.print_help = print_help # Only these commands can be run when not connected to a daemon @@ -95,7 +95,9 @@ class Commander(object): not_connected_cmds.extend(aliases) if not client.connected() and cmd not in not_connected_cmds: - self.write('{!error!}Not connected to a daemon, please use the connect command first.') + self.write( + '{!error!}Not connected to a daemon, please use the connect command first.' + ) return try: @@ -104,10 +106,12 @@ class Commander(object): except TypeError as ex: self.write('{!error!}Error parsing options: %s' % ex) import traceback + self.write('%s' % traceback.format_exc()) return except OptionParserError as ex: import traceback + log.warning('Error parsing command "%s": %s', args, ex) self.write('{!error!} %s' % ex) parser.print_help() @@ -133,6 +137,7 @@ class Commander(object): self.write('{!error!} %s' % ex) log.exception(ex) import traceback + self.write('%s' % traceback.format_exc()) return defer.succeed(True) else: @@ -174,7 +179,11 @@ class BaseCommand(object): return result def create_parser(self): - opts = {'prog': self.name_with_alias, 'description': self.__doc__, 'epilog': self.epilog} + opts = { + 'prog': self.name_with_alias, + 'description': self.__doc__, + 'epilog': self.epilog, + } if self.usage: opts['usage'] = self.usage parser = OptionParser(**opts) @@ -184,7 +193,11 @@ class BaseCommand(object): return parser def add_subparser(self, subparsers): - opts = {'prog': self.name_with_alias, 'help': self.__doc__, 'description': self.__doc__} + opts = { + 'prog': self.name_with_alias, + 'help': self.__doc__, + 'description': self.__doc__, + } if self.usage: opts['usage'] = self.usage diff --git a/deluge/ui/console/cmdline/commands/add.py b/deluge/ui/console/cmdline/commands/add.py index df0da2db3..e2baf2740 100644 --- a/deluge/ui/console/cmdline/commands/add.py +++ b/deluge/ui/console/cmdline/commands/add.py @@ -34,9 +34,13 @@ class Command(BaseCommand): """Add torrents""" def add_arguments(self, parser): - parser.add_argument('-p', '--path', dest='path', help=_('download folder for torrent')) parser.add_argument( - 'torrents', metavar='<torrent>', nargs='+', + '-p', '--path', dest='path', help=_('download folder for torrent') + ) + parser.add_argument( + 'torrents', + metavar='<torrent>', + nargs='+', help=_('One or more torrent files, URLs or magnet URIs'), ) @@ -45,7 +49,9 @@ class Command(BaseCommand): t_options = {} if options.path: - t_options['download_location'] = os.path.abspath(os.path.expanduser(options.path)) + t_options['download_location'] = os.path.abspath( + os.path.expanduser(options.path) + ) def on_success(result): if not result: @@ -62,15 +68,23 @@ class Command(BaseCommand): if not torrent.strip(): continue if deluge.common.is_url(torrent): - self.console.write('{!info!}Attempting to add torrent from url: %s' % torrent) - deferreds.append(client.core.add_torrent_url(torrent, t_options).addCallback(on_success).addErrback( - on_fail, - )) + self.console.write( + '{!info!}Attempting to add torrent from url: %s' % torrent + ) + deferreds.append( + client.core.add_torrent_url(torrent, t_options) + .addCallback(on_success) + .addErrback(on_fail) + ) elif deluge.common.is_magnet(torrent): - self.console.write('{!info!}Attempting to add torrent from magnet uri: %s' % torrent) - deferreds.append(client.core.add_torrent_magnet(torrent, t_options).addCallback(on_success).addErrback( - on_fail, - )) + self.console.write( + '{!info!}Attempting to add torrent from magnet uri: %s' % torrent + ) + deferreds.append( + client.core.add_torrent_magnet(torrent, t_options) + .addCallback(on_success) + .addErrback(on_fail) + ) else: # Just a file if urlparse(torrent).scheme == 'file': @@ -87,12 +101,14 @@ class Command(BaseCommand): with open(path, 'rb') as _file: filedump = b64encode(_file.read()) deferreds.append( - client.core.add_torrent_file_async( - filename, filedump, t_options, - ).addCallback(on_success).addErrback(on_fail), + client.core.add_torrent_file_async(filename, filedump, t_options) + .addCallback(on_success) + .addErrback(on_fail) ) return defer.DeferredList(deferreds) def complete(self, line): - return component.get('ConsoleUI').tab_complete_path(line, ext='.torrent', sort='date') + return component.get('ConsoleUI').tab_complete_path( + line, ext='.torrent', sort='date' + ) diff --git a/deluge/ui/console/cmdline/commands/cache.py b/deluge/ui/console/cmdline/commands/cache.py index cbe61bace..e427f085f 100644 --- a/deluge/ui/console/cmdline/commands/cache.py +++ b/deluge/ui/console/cmdline/commands/cache.py @@ -26,4 +26,6 @@ class Command(BaseCommand): for key, value in sorted(status.items()): self.console.write('{!info!}%s: {!input!}%s' % (key, value)) - return client.core.get_session_status(DISK_CACHE_KEYS).addCallback(on_cache_status) + return client.core.get_session_status(DISK_CACHE_KEYS).addCallback( + on_cache_status + ) diff --git a/deluge/ui/console/cmdline/commands/config.py b/deluge/ui/console/cmdline/commands/config.py index d9132d739..1cbccba88 100644 --- a/deluge/ui/console/cmdline/commands/config.py +++ b/deluge/ui/console/cmdline/commands/config.py @@ -80,10 +80,23 @@ class Command(BaseCommand): def add_arguments(self, parser): set_group = parser.add_argument_group('setting a value') - set_group.add_argument('-s', '--set', action='store', metavar='<key>', help=_('set value for this key')) - set_group.add_argument('values', metavar='<value>', nargs='+', help=_('Value to set')) + set_group.add_argument( + '-s', + '--set', + action='store', + metavar='<key>', + help=_('set value for this key'), + ) + set_group.add_argument( + 'values', metavar='<value>', nargs='+', help=_('Value to set') + ) get_group = parser.add_argument_group('getting values') - get_group.add_argument('keys', metavar='<keys>', nargs='*', help=_('one or more keys separated by space')) + get_group.add_argument( + 'keys', + metavar='<keys>', + nargs='*', + help=_('one or more keys separated by space'), + ) def handle(self, options): self.console = component.get('ConsoleUI') @@ -109,6 +122,7 @@ class Command(BaseCommand): # We need to format dicts for printing if isinstance(value, dict): import pprint + value = pprint.pformat(value, 2, 80) new_value = [] for line in value.splitlines(): @@ -139,7 +153,9 @@ class Command(BaseCommand): try: val = type(config[key])(val) except TypeError: - self.config.write('{!error!}Configuration value provided has incorrect type.') + self.config.write( + '{!error!}Configuration value provided has incorrect type.' + ) return def on_set_config(result): diff --git a/deluge/ui/console/cmdline/commands/connect.py b/deluge/ui/console/cmdline/commands/connect.py index 356db6d9f..6588f7a04 100644 --- a/deluge/ui/console/cmdline/commands/connect.py +++ b/deluge/ui/console/cmdline/commands/connect.py @@ -26,12 +26,20 @@ class Command(BaseCommand): usage = _('Usage: connect <host[:port]> [<username>] [<password>]') def add_arguments(self, parser): - parser.add_argument('host', help=_('Daemon host and port'), metavar='<host[:port]>') - parser.add_argument('username', help=_('Username'), metavar='<username>', nargs='?', default='') - parser.add_argument('password', help=_('Password'), metavar='<password>', nargs='?', default='') + parser.add_argument( + 'host', help=_('Daemon host and port'), metavar='<host[:port]>' + ) + parser.add_argument( + 'username', help=_('Username'), metavar='<username>', nargs='?', default='' + ) + parser.add_argument( + 'password', help=_('Password'), metavar='<password>', nargs='?', default='' + ) def add_parser(self, subparsers): - parser = subparsers.add_parser(self.name, help=self.__doc__, description=self.__doc__, prog='connect') + parser = subparsers.add_parser( + self.name, help=self.__doc__, description=self.__doc__, prog='connect' + ) self.add_arguments(parser) def handle(self, options): @@ -57,7 +65,10 @@ class Command(BaseCommand): msg = result.value.exception_msg except AttributeError: msg = result.value.message - self.console.write('{!error!}Failed to connect to %s:%s with reason: %s' % (host, port, msg)) + self.console.write( + '{!error!}Failed to connect to %s:%s with reason: %s' + % (host, port, msg) + ) return result d.addCallbacks(on_connect, on_connect_fail) @@ -69,6 +80,7 @@ class Command(BaseCommand): if self.console.statusbars: self.console.statusbars.update_statusbars() return do_connect() + return client.disconnect().addCallback(on_disconnect) else: return do_connect() diff --git a/deluge/ui/console/cmdline/commands/debug.py b/deluge/ui/console/cmdline/commands/debug.py index f65d172b5..3ca06ed15 100644 --- a/deluge/ui/console/cmdline/commands/debug.py +++ b/deluge/ui/console/cmdline/commands/debug.py @@ -22,7 +22,9 @@ class Command(BaseCommand): """Enable and disable debugging""" def add_arguments(self, parser): - parser.add_argument('state', metavar='<on|off>', choices=['on', 'off'], help=_('The new state')) + parser.add_argument( + 'state', metavar='<on|off>', choices=['on', 'off'], help=_('The new state') + ) def handle(self, options): if options.state == 'on': diff --git a/deluge/ui/console/cmdline/commands/gui.py b/deluge/ui/console/cmdline/commands/gui.py index 88c8ca5a6..10e4c499b 100644 --- a/deluge/ui/console/cmdline/commands/gui.py +++ b/deluge/ui/console/cmdline/commands/gui.py @@ -20,6 +20,7 @@ log = logging.getLogger(__name__) class Command(BaseCommand): """Enable interactive mode""" + interactive_only = True def handle(self, options): diff --git a/deluge/ui/console/cmdline/commands/halt.py b/deluge/ui/console/cmdline/commands/halt.py index ef22873e9..635595898 100644 --- a/deluge/ui/console/cmdline/commands/halt.py +++ b/deluge/ui/console/cmdline/commands/halt.py @@ -28,4 +28,8 @@ class Command(BaseCommand): def on_shutdown_fail(reason): self.console.write('{!error!}Unable to shutdown daemon: %s' % reason) - return client.daemon.shutdown().addCallback(on_shutdown).addErrback(on_shutdown_fail) + return ( + client.daemon.shutdown() + .addCallback(on_shutdown) + .addErrback(on_shutdown_fail) + ) diff --git a/deluge/ui/console/cmdline/commands/help.py b/deluge/ui/console/cmdline/commands/help.py index df917f177..2711eea99 100644 --- a/deluge/ui/console/cmdline/commands/help.py +++ b/deluge/ui/console/cmdline/commands/help.py @@ -25,7 +25,9 @@ class Command(BaseCommand): """Displays help on other commands""" def add_arguments(self, parser): - parser.add_argument('commands', metavar='<command>', nargs='*', help=_('One or more commands')) + parser.add_argument( + 'commands', metavar='<command>', nargs='*', help=_('One or more commands') + ) def handle(self, options): self.console = component.get('ConsoleUI') @@ -51,8 +53,15 @@ class Command(BaseCommand): if cmd in self._commands[cmd].aliases: continue parser = self._commands[cmd].create_parser() - cmd_doc = '{!info!}' + '%-9s' % self._commands[cmd].name_with_alias + '{!input!} - '\ - + self._commands[cmd].__doc__ + '\n ' + parser.format_usage() or '' + cmd_doc = ( + '{!info!}' + + '%-9s' % self._commands[cmd].name_with_alias + + '{!input!} - ' + + self._commands[cmd].__doc__ + + '\n ' + + parser.format_usage() + or '' + ) cmds_doc += parser.formatter.format_colors(cmd_doc) self.console.write(cmds_doc) self.console.write(' ') diff --git a/deluge/ui/console/cmdline/commands/info.py b/deluge/ui/console/cmdline/commands/info.py index 44b1e4f27..02455da69 100644 --- a/deluge/ui/console/cmdline/commands/info.py +++ b/deluge/ui/console/cmdline/commands/info.py @@ -17,9 +17,19 @@ import deluge.ui.console.utils.colors as colors from deluge.common import TORRENT_STATE, fsize, fspeed from deluge.ui.client import client from deluge.ui.common import FILE_PRIORITY -from deluge.ui.console.utils.format_utils import (f_progressbar, f_seedrank_dash, format_date_never, format_progress, - format_time, ftotal_sized, pad_string, remove_formatting, - shorten_hash, strwidth, trim_string) +from deluge.ui.console.utils.format_utils import ( + f_progressbar, + f_seedrank_dash, + format_date_never, + format_progress, + format_time, + ftotal_sized, + pad_string, + remove_formatting, + shorten_hash, + strwidth, + trim_string, +) from . import BaseCommand @@ -83,31 +93,58 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - '-v', '--verbose', action='store_true', default=False, dest='verbose', + '-v', + '--verbose', + action='store_true', + default=False, + dest='verbose', help=_('Show more information per torrent.'), ) parser.add_argument( - '-d', '--detailed', action='store_true', default=False, dest='detailed', + '-d', + '--detailed', + action='store_true', + default=False, + dest='detailed', help=_('Show more detailed information including files and peers.'), ) parser.add_argument( - '-s', '--state', action='store', dest='state', + '-s', + '--state', + action='store', + dest='state', help=_('Show torrents with state STATE: %s.' % (', '.join(STATES))), ) - parser.add_argument('--sort', action='store', type=str, default='', dest='sort', help=self.sort_help) parser.add_argument( - '--sort-reverse', action='store', type=str, default='', dest='sort_rev', + '--sort', + action='store', + type=str, + default='', + dest='sort', + help=self.sort_help, + ) + parser.add_argument( + '--sort-reverse', + action='store', + type=str, + default='', + dest='sort_rev', help=_('Same as --sort but items are in reverse order.'), ) parser.add_argument( - 'torrent_ids', metavar='<torrent-id>', nargs='*', + 'torrent_ids', + metavar='<torrent-id>', + nargs='*', help=_('One or more torrent ids. If none is given, list all'), ) def add_subparser(self, subparsers): parser = subparsers.add_parser( - self.name, prog=self.name, help=self.__doc__, - description=self.__doc__, epilog=self.epilog, + self.name, + prog=self.name, + help=self.__doc__, + description=self.__doc__, + epilog=self.epilog, ) self.add_arguments(parser) @@ -134,10 +171,16 @@ class Command(BaseCommand): sort_reverse = False if sort_key not in STATUS_KEYS: self.console.write('') - self.console.write('{!error!}Unknown sort key: ' + sort_key + ', will sort on name') + self.console.write( + '{!error!}Unknown sort key: ' + sort_key + ', will sort on name' + ) sort_key = 'name' sort_reverse = False - for key, value in sorted(list(status.items()), key=lambda x: x[1].get(sort_key), reverse=sort_reverse): + for key, value in sorted( + list(status.items()), + key=lambda x: x[1].get(sort_key), + reverse=sort_reverse, + ): self.show_info(key, status[key], options.verbose, options.detailed) def on_torrents_status_fail(reason): @@ -189,7 +232,9 @@ class Command(BaseCommand): col_priority = ' {!info!}Priority: ' - file_priority = FILE_PRIORITY[status['file_priorities'][index]].replace('Priority', '') + file_priority = FILE_PRIORITY[status['file_priorities'][index]].replace( + 'Priority', '' + ) if status['file_progress'][index] != 1.0: if file_priority == 'Do Not Download': col_priority += '{!error!}' @@ -206,7 +251,9 @@ class Command(BaseCommand): # Check how much space we've got left after writing all the info space_left = cols - tlen(col_all_info) # And how much we will potentially have with the longest possible column - maxlen_space_left = cols - tlen(' (1000.0 MiB) 100.00% Priority: Do Not Download') + maxlen_space_left = cols - tlen( + ' (1000.0 MiB) 100.00% Priority: Do Not Download' + ) if maxlen_space_left > tlen(col_filename) + 1: # If there is enough space, pad it all nicely col_all_info = '' @@ -221,7 +268,9 @@ class Command(BaseCommand): col_all_info += col_priority col_all_info += ' ' * spaces_to_add # And remember to put it to the left! - col_filename = pad_string(col_filename, maxlen_space_left - 2, side='right') + col_filename = pad_string( + col_filename, maxlen_space_left - 2, side='right' + ) elif space_left > tlen(col_filename) + 1: # If there is enough space, put the info to the right col_filename = pad_string(col_filename, space_left - 2, side='right') @@ -250,7 +299,10 @@ class Command(BaseCommand): s += peer['ip'] else: # IPv6 - s += '[%s]:%s' % (':'.join(peer['ip'].split(':')[:-1]), peer['ip'].split(':')[-1]) + s += '[%s]:%s' % ( + ':'.join(peer['ip'].split(':')[:-1]), + peer['ip'].split(':')[-1], + ) c = peer['client'] s += '\t' + c @@ -287,29 +339,40 @@ class Command(BaseCommand): if verbose or detailed: self.console.write('{!info!}Name: {!input!}%s' % (status['name'])) self.console.write('{!info!}ID: {!input!}%s' % (torrent_id)) - s = '{!info!}State: %s%s' % (colors.state_color[status['state']], status['state']) + s = '{!info!}State: %s%s' % ( + colors.state_color[status['state']], + status['state'], + ) # Only show speed if active if status['state'] in ('Seeding', 'Downloading'): if status['state'] != 'Seeding': s += sep s += '{!info!}Down Speed: {!input!}%s' % fspeed( - status['download_payload_rate'], shortform=True, + status['download_payload_rate'], shortform=True ) s += sep s += '{!info!}Up Speed: {!input!}%s' % fspeed( - status['upload_payload_rate'], shortform=True, + status['upload_payload_rate'], shortform=True ) self.console.write(s) if status['state'] in ('Seeding', 'Downloading', 'Queued'): - s = '{!info!}Seeds: {!input!}%s (%s)' % (status['num_seeds'], status['total_seeds']) + s = '{!info!}Seeds: {!input!}%s (%s)' % ( + status['num_seeds'], + status['total_seeds'], + ) s += sep - s += '{!info!}Peers: {!input!}%s (%s)' % (status['num_peers'], status['total_peers']) + s += '{!info!}Peers: {!input!}%s (%s)' % ( + status['num_peers'], + status['total_peers'], + ) s += sep - s += '{!info!}Availability: {!input!}%.2f' % status['distributed_copies'] + s += ( + '{!info!}Availability: {!input!}%.2f' % status['distributed_copies'] + ) s += sep s += '{!info!}Seed Rank: {!input!}%s' % f_seedrank_dash( - status['seed_rank'], status['seeding_time'], + status['seed_rank'], status['seeding_time'] ) self.console.write(s) @@ -320,9 +383,13 @@ class Command(BaseCommand): else: s = '{!info!}Size: {!input!}%s/%s' % (total_done, total_size) s += sep - s += '{!info!}Downloaded: {!input!}%s' % fsize(status['all_time_download'], shortform=True) + s += '{!info!}Downloaded: {!input!}%s' % fsize( + status['all_time_download'], shortform=True + ) s += sep - s += '{!info!}Uploaded: {!input!}%s' % fsize(status['total_uploaded'], shortform=True) + s += '{!info!}Uploaded: {!input!}%s' % fsize( + status['total_uploaded'], shortform=True + ) s += sep s += '{!info!}Share Ratio: {!input!}%.2f' % status['ratio'] self.console.write(s) @@ -334,20 +401,26 @@ class Command(BaseCommand): s += '{!info!}Active: {!input!}%s' % format_time(status['active_time']) self.console.write(s) - s = '{!info!}Last Transfer: {!input!}%s' % format_time(status['time_since_transfer']) + s = '{!info!}Last Transfer: {!input!}%s' % format_time( + status['time_since_transfer'] + ) s += sep s += '{!info!}Complete Seen: {!input!}%s' % format_date_never( - status['last_seen_complete'], + status['last_seen_complete'] ) self.console.write(s) s = '{!info!}Tracker: {!input!}%s' % status['tracker_host'] self.console.write(s) - self.console.write('{!info!}Tracker status: {!input!}%s' % status['tracker_status']) + self.console.write( + '{!info!}Tracker status: {!input!}%s' % status['tracker_status'] + ) if not status['is_finished']: - pbar = f_progressbar(status['progress'], cols - (13 + len('%.2f%%' % status['progress']))) + pbar = f_progressbar( + status['progress'], cols - (13 + len('%.2f%%' % status['progress'])) + ) s = '{!info!}Progress: {!input!}%.2f%% %s' % (status['progress'], pbar) self.console.write(s) @@ -363,7 +436,10 @@ class Command(BaseCommand): up_color = colors.state_color['Seeding'] down_color = colors.state_color['Downloading'] - s = '%s%s' % (colors.state_color[status['state']], '[' + status['state'][0] + ']') + s = '%s%s' % ( + colors.state_color[status['state']], + '[' + status['state'][0] + ']', + ) s += ' {!info!}' + format_progress(status['progress']).rjust(6, ' ') s += ' {!input!}%s' % (status['name']) @@ -380,7 +456,9 @@ class Command(BaseCommand): self.console.write(s) dl_info = '{!info!}DL: {!input!}' - dl_info += '%s' % ftotal_sized(status['all_time_download'], status['total_payload_download']) + dl_info += '%s' % ftotal_sized( + status['all_time_download'], status['total_payload_download'] + ) if status['download_payload_rate'] > 0: dl_info += ' @ %s%s' % ( @@ -389,7 +467,9 @@ class Command(BaseCommand): ) ul_info = ' {!info!}UL: {!input!}' - ul_info += '%s' % ftotal_sized(status['total_uploaded'], status['total_payload_upload']) + ul_info += '%s' % ftotal_sized( + status['total_uploaded'], status['total_payload_upload'] + ) if status['upload_payload_rate'] > 0: ul_info += ' @ %s%s' % ( up_color, diff --git a/deluge/ui/console/cmdline/commands/manage.py b/deluge/ui/console/cmdline/commands/manage.py index 8f8349679..2de921969 100644 --- a/deluge/ui/console/cmdline/commands/manage.py +++ b/deluge/ui/console/cmdline/commands/manage.py @@ -44,14 +44,28 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - 'torrent', metavar='<torrent>', + 'torrent', + metavar='<torrent>', help=_('an expression matched against torrent ids and torrent names'), ) set_group = parser.add_argument_group('setting a value') - set_group.add_argument('-s', '--set', action='store', metavar='<key>', help=_('set value for this key')) - set_group.add_argument('values', metavar='<value>', nargs='+', help=_('Value to set')) + set_group.add_argument( + '-s', + '--set', + action='store', + metavar='<key>', + help=_('set value for this key'), + ) + set_group.add_argument( + 'values', metavar='<value>', nargs='+', help=_('Value to set') + ) get_group = parser.add_argument_group('getting values') - get_group.add_argument('keys', metavar='<keys>', nargs='*', help=_('one or more keys separated by space')) + get_group.add_argument( + 'keys', + metavar='<keys>', + nargs='*', + help=_('one or more keys separated by space'), + ) def handle(self, options): self.console = component.get('ConsoleUI') @@ -61,7 +75,6 @@ class Command(BaseCommand): return self._get_option(options) def _get_option(self, options): - def on_torrents_status(status): for torrentid, data in status.items(): self.console.write('') @@ -94,7 +107,7 @@ class Command(BaseCommand): def _set_option(self, options): deferred = defer.Deferred() key = options.set - val = ' ' .join(options.values) + val = ' '.join(options.values) torrent_ids = self.console.match_torrent(options.torrent) if key not in torrent_options: @@ -107,8 +120,12 @@ class Command(BaseCommand): self.console.write('{!success!}Torrent option successfully updated.') deferred.callback(True) - self.console.write('Setting %s to %s for torrents %s..' % (key, val, torrent_ids)) - client.core.set_torrent_options(torrent_ids, {key: val}).addCallback(on_set_config) + self.console.write( + 'Setting %s to %s for torrents %s..' % (key, val, torrent_ids) + ) + client.core.set_torrent_options(torrent_ids, {key: val}).addCallback( + on_set_config + ) return deferred def complete(self, line): diff --git a/deluge/ui/console/cmdline/commands/move.py b/deluge/ui/console/cmdline/commands/move.py index 637adbd54..13e475e6f 100644 --- a/deluge/ui/console/cmdline/commands/move.py +++ b/deluge/ui/console/cmdline/commands/move.py @@ -24,14 +24,24 @@ class Command(BaseCommand): """Move torrents' storage location""" def add_arguments(self, parser): - parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='+', help=_('One or more torrent ids')) - parser.add_argument('path', metavar='<path>', help=_('The path to move the torrents to')) + parser.add_argument( + 'torrent_ids', + metavar='<torrent-id>', + nargs='+', + help=_('One or more torrent ids'), + ) + parser.add_argument( + 'path', metavar='<path>', help=_('The path to move the torrents to') + ) def handle(self, options): self.console = component.get('ConsoleUI') if os.path.exists(options.path) and not os.path.isdir(options.path): - self.console.write('{!error!}Cannot Move Download Folder: %s exists and is not a directory' % options.path) + self.console.write( + '{!error!}Cannot Move Download Folder: %s exists and is not a directory' + % options.path + ) return ids = [] diff --git a/deluge/ui/console/cmdline/commands/pause.py b/deluge/ui/console/cmdline/commands/pause.py index fa5ba7d13..1f7ef31a0 100644 --- a/deluge/ui/console/cmdline/commands/pause.py +++ b/deluge/ui/console/cmdline/commands/pause.py @@ -18,11 +18,14 @@ from . import BaseCommand class Command(BaseCommand): """Pause torrents""" + usage = 'pause [ * | <torrent-id> [<torrent-id> ...] ]' def add_arguments(self, parser): parser.add_argument( - 'torrent_ids', metavar='<torrent-id>', nargs='+', + 'torrent_ids', + metavar='<torrent-id>', + nargs='+', help=_('One or more torrent ids. Use "*" to pause all torrents'), ) diff --git a/deluge/ui/console/cmdline/commands/plugin.py b/deluge/ui/console/cmdline/commands/plugin.py index b9d59173a..fafc77afd 100644 --- a/deluge/ui/console/cmdline/commands/plugin.py +++ b/deluge/ui/console/cmdline/commands/plugin.py @@ -21,20 +21,38 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - '-l', '--list', action='store_true', default=False, dest='list', + '-l', + '--list', + action='store_true', + default=False, + dest='list', help=_('Lists available plugins'), ) parser.add_argument( - '-s', '--show', action='store_true', default=False, dest='show', + '-s', + '--show', + action='store_true', + default=False, + dest='show', help=_('Shows enabled plugins'), ) - parser.add_argument('-e', '--enable', dest='enable', nargs='+', help=_('Enables a plugin')) - parser.add_argument('-d', '--disable', dest='disable', nargs='+', help=_('Disables a plugin')) parser.add_argument( - '-r', '--reload', action='store_true', default=False, dest='reload', + '-e', '--enable', dest='enable', nargs='+', help=_('Enables a plugin') + ) + parser.add_argument( + '-d', '--disable', dest='disable', nargs='+', help=_('Disables a plugin') + ) + parser.add_argument( + '-r', + '--reload', + action='store_true', + default=False, + dest='reload', help=_('Reload list of available plugins'), ) - parser.add_argument('-i', '--install', help=_('Install a plugin from an .egg file')) + parser.add_argument( + '-i', '--install', help=_('Install a plugin from an .egg file') + ) def handle(self, options): self.console = component.get('ConsoleUI') @@ -45,6 +63,7 @@ class Command(BaseCommand): return elif options.list: + def on_available_plugins(result): self.console.write('{!info!}Available Plugins:') for p in result: @@ -53,6 +72,7 @@ class Command(BaseCommand): return client.core.get_available_plugins().addCallback(on_available_plugins) elif options.show: + def on_enabled_plugins(result): self.console.write('{!info!}Enabled Plugins:') for p in result: @@ -61,6 +81,7 @@ class Command(BaseCommand): return client.core.get_enabled_plugins().addCallback(on_enabled_plugins) elif options.enable: + def on_available_plugins(result): plugins = {} for p in result: @@ -72,6 +93,7 @@ class Command(BaseCommand): return client.core.get_available_plugins().addCallback(on_available_plugins) elif options.disable: + def on_enabled_plugins(result): plugins = {} for p in result: @@ -107,9 +129,15 @@ class Command(BaseCommand): client.core.upload_plugin(filename, filedump) client.core.rescan_plugins() except Exception: - self.console.write('{!error!}An error occurred, plugin was not installed') + self.console.write( + '{!error!}An error occurred, plugin was not installed' + ) - self.console.write('{!green!}Plugin was successfully installed: %s' % filename) + self.console.write( + '{!green!}Plugin was successfully installed: %s' % filename + ) def complete(self, line): - return component.get('ConsoleUI').tab_complete_path(line, ext='.egg', sort='name', dirs_first=-1) + return component.get('ConsoleUI').tab_complete_path( + line, ext='.egg', sort='name', dirs_first=-1 + ) diff --git a/deluge/ui/console/cmdline/commands/quit.py b/deluge/ui/console/cmdline/commands/quit.py index 6c8895b00..261a01a9b 100644 --- a/deluge/ui/console/cmdline/commands/quit.py +++ b/deluge/ui/console/cmdline/commands/quit.py @@ -17,6 +17,7 @@ from . import BaseCommand class Command(BaseCommand): """Exit the client""" + aliases = ['exit'] interactive_only = True diff --git a/deluge/ui/console/cmdline/commands/recheck.py b/deluge/ui/console/cmdline/commands/recheck.py index 49f196626..c9b6360c9 100644 --- a/deluge/ui/console/cmdline/commands/recheck.py +++ b/deluge/ui/console/cmdline/commands/recheck.py @@ -17,10 +17,16 @@ from . import BaseCommand class Command(BaseCommand): """Forces a recheck of the torrent data""" + usage = 'recheck [ * | <torrent-id> [<torrent-id> ...] ]' def add_arguments(self, parser): - parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='+', help=_('One or more torrent ids')) + parser.add_argument( + 'torrent_ids', + metavar='<torrent-id>', + nargs='+', + help=_('One or more torrent ids'), + ) def handle(self, options): self.console = component.get('ConsoleUI') diff --git a/deluge/ui/console/cmdline/commands/resume.py b/deluge/ui/console/cmdline/commands/resume.py index 8a2632264..1f62c5f00 100644 --- a/deluge/ui/console/cmdline/commands/resume.py +++ b/deluge/ui/console/cmdline/commands/resume.py @@ -18,11 +18,14 @@ from . import BaseCommand class Command(BaseCommand): """Resume torrents""" + usage = _('Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]') def add_arguments(self, parser): parser.add_argument( - 'torrent_ids', metavar='<torrent-id>', nargs='+', + 'torrent_ids', + metavar='<torrent-id>', + nargs='+', help=_('One or more torrent ids. Use "*" to resume all torrents'), ) diff --git a/deluge/ui/console/cmdline/commands/rm.py b/deluge/ui/console/cmdline/commands/rm.py index a2d431c4d..ff3125d81 100644 --- a/deluge/ui/console/cmdline/commands/rm.py +++ b/deluge/ui/console/cmdline/commands/rm.py @@ -22,33 +22,50 @@ log = logging.getLogger(__name__) class Command(BaseCommand): """Remove a torrent""" + aliases = ['del'] def add_arguments(self, parser): parser.add_argument( - '--remove_data', action='store_true', default=False, + '--remove_data', + action='store_true', + default=False, help=_('Also removes the torrent data'), ) parser.add_argument( - '-c', '--confirm', action='store_true', default=False, + '-c', + '--confirm', + action='store_true', + default=False, help=_('List the matching torrents without removing.'), ) - parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='+', help=_('One or more torrent ids')) + parser.add_argument( + 'torrent_ids', + metavar='<torrent-id>', + nargs='+', + help=_('One or more torrent ids'), + ) def handle(self, options): self.console = component.get('ConsoleUI') torrent_ids = self.console.match_torrents(options.torrent_ids) if not options.confirm: - self.console.write('{!info!}%d %s %s{!info!}' % ( - len(torrent_ids), - _n('torrent', 'torrents', len(torrent_ids)), - _n('match', 'matches', len(torrent_ids)), - )) + self.console.write( + '{!info!}%d %s %s{!info!}' + % ( + len(torrent_ids), + _n('torrent', 'torrents', len(torrent_ids)), + _n('match', 'matches', len(torrent_ids)), + ) + ) for t_id in torrent_ids: name = self.console.get_torrent_name(t_id) self.console.write('* %-50s (%s)' % (name, t_id)) - self.console.write(_('Confirm with -c to remove the listed torrents (Count: %d)') % len(torrent_ids)) + self.console.write( + _('Confirm with -c to remove the listed torrents (Count: %d)') + % len(torrent_ids) + ) return def on_removed_finished(errors): diff --git a/deluge/ui/console/cmdline/commands/status.py b/deluge/ui/console/cmdline/commands/status.py index 2f34ede95..948ad6b94 100644 --- a/deluge/ui/console/cmdline/commands/status.py +++ b/deluge/ui/console/cmdline/commands/status.py @@ -27,14 +27,22 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - '-r', '--raw', action='store_true', default=False, dest='raw', + '-r', + '--raw', + action='store_true', + default=False, + dest='raw', help=_( 'Raw values for upload/download rates (without KiB/s suffix)' - '(useful for scripts that want to do their own parsing)', + '(useful for scripts that want to do their own parsing)' ), ) parser.add_argument( - '-n', '--no-torrents', action='store_false', default=True, dest='show_torrents', + '-n', + '--no-torrents', + action='store_false', + default=True, + dest='show_torrents', help=_('Do not show torrent status (Improves command speed)'), ) @@ -56,7 +64,9 @@ class Command(BaseCommand): deferreds = [] - ds = client.core.get_session_status(['num_peers', 'payload_upload_rate', 'payload_download_rate', 'dht_nodes']) + ds = client.core.get_session_status( + ['num_peers', 'payload_upload_rate', 'payload_download_rate', 'dht_nodes'] + ) ds.addCallback(on_session_status) deferreds.append(ds) @@ -71,11 +81,20 @@ class Command(BaseCommand): def print_status(self, *args): self.console.set_batch_write(True) if self.raw: - self.console.write('{!info!}Total upload: %f' % self.status['payload_upload_rate']) - self.console.write('{!info!}Total download: %f' % self.status['payload_download_rate']) + self.console.write( + '{!info!}Total upload: %f' % self.status['payload_upload_rate'] + ) + self.console.write( + '{!info!}Total download: %f' % self.status['payload_download_rate'] + ) else: - self.console.write('{!info!}Total upload: %s' % fspeed(self.status['payload_upload_rate'])) - self.console.write('{!info!}Total download: %s' % fspeed(self.status['payload_download_rate'])) + self.console.write( + '{!info!}Total upload: %s' % fspeed(self.status['payload_upload_rate']) + ) + self.console.write( + '{!info!}Total download: %s' + % fspeed(self.status['payload_download_rate']) + ) self.console.write('{!info!}DHT Nodes: %i' % self.status['dht_nodes']) if isinstance(self.torrents, int): diff --git a/deluge/ui/console/cmdline/commands/update_tracker.py b/deluge/ui/console/cmdline/commands/update_tracker.py index f0848bced..591b95192 100644 --- a/deluge/ui/console/cmdline/commands/update_tracker.py +++ b/deluge/ui/console/cmdline/commands/update_tracker.py @@ -18,12 +18,15 @@ from . import BaseCommand class Command(BaseCommand): """Update tracker for torrent(s)""" + usage = 'update_tracker [ * | <torrent-id> [<torrent-id> ...] ]' aliases = ['reannounce'] def add_arguments(self, parser): parser.add_argument( - 'torrent_ids', metavar='<torrent-id>', nargs='+', + 'torrent_ids', + metavar='<torrent-id>', + nargs='+', help='One or more torrent ids. "*" updates all torrents', ) diff --git a/deluge/ui/console/console.py b/deluge/ui/console/console.py index 893caf548..99d6ea72c 100644 --- a/deluge/ui/console/console.py +++ b/deluge/ui/console/console.py @@ -28,13 +28,12 @@ log = logging.getLogger(__name__) def load_commands(command_dir): - def get_command(name): command = getattr( __import__( - 'deluge.ui.console.cmdline.commands.%s' % name, - {}, {}, ['Command'], - ), 'Command', + 'deluge.ui.console.cmdline.commands.%s' % name, {}, {}, ['Command'] + ), + 'Command', )() command._name = name return command @@ -69,48 +68,74 @@ class Console(UI): cmd_description = """Console or command-line user interface""" def __init__(self, *args, **kwargs): - super(Console, self).__init__('console', *args, log_stream=LogStream(), **kwargs) + super(Console, self).__init__( + 'console', *args, log_stream=LogStream(), **kwargs + ) group = self.parser.add_argument_group( _('Console Options'), _( 'These daemon connect options will be ' - 'used for commands, or if console ui autoconnect is enabled.', + 'used for commands, or if console ui autoconnect is enabled.' ), ) group.add_argument( - '-d', '--daemon', metavar='<ip_addr>', dest='daemon_addr', - help=_('Deluge daemon IP address to connect to (default 127.0.0.1)'), default='127.0.0.1', + '-d', + '--daemon', + metavar='<ip_addr>', + dest='daemon_addr', + help=_('Deluge daemon IP address to connect to (default 127.0.0.1)'), + default='127.0.0.1', ) group.add_argument( - '-p', '--port', metavar='<port>', dest='daemon_port', type=int, - help=_('Deluge daemon port to connect to (default 58846)'), default='58846', + '-p', + '--port', + metavar='<port>', + dest='daemon_port', + type=int, + help=_('Deluge daemon port to connect to (default 58846)'), + default='58846', ) group.add_argument( - '-U', '--username', metavar='<user>', dest='daemon_user', + '-U', + '--username', + metavar='<user>', + dest='daemon_user', help=_('Deluge daemon username to use when connecting'), ) group.add_argument( - '-P', '--password', metavar='<pass>', dest='daemon_pass', + '-P', + '--password', + metavar='<pass>', + dest='daemon_pass', help=_('Deluge daemon password to use when connecting'), ) # To properly print help message for the console commands ( e.g. deluge-console info -h), # we add a subparser for each command which will trigger the help/usage when given - from deluge.ui.console.parser import ConsoleCommandParser # import here because (see top) + from deluge.ui.console.parser import ( + ConsoleCommandParser, + ) # import here because (see top) + self.console_parser = ConsoleCommandParser( - parents=[self.parser], add_help=False, prog=self.parser.prog, + parents=[self.parser], + add_help=False, + prog=self.parser.prog, description='Starts the Deluge console interface', - formatter_class=lambda prog: - DelugeTextHelpFormatter(prog, max_help_position=33, width=90), + formatter_class=lambda prog: DelugeTextHelpFormatter( + prog, max_help_position=33, width=90 + ), ) self.parser.subparser = self.console_parser self.console_parser.base_parser = self.parser subparsers = self.console_parser.add_subparsers( - title=_('Console Commands'), help=_('Description'), + title=_('Console Commands'), + help=_('Description'), description=_('The following console commands are available:'), - metavar=_('Command'), dest='command', + metavar=_('Command'), + dest='command', ) from deluge.ui.console import UI_PATH # Must import here + self.console_cmds = load_commands(os.path.join(UI_PATH, 'cmdline', 'commands')) for cmd in sorted(self.console_cmds): self.console_cmds[cmd].add_subparser(subparsers) @@ -137,6 +162,8 @@ class Console(UI): raise return deluge.common.run_profiled( - run, self.options, output_file=self.options.profile, + run, + self.options, + output_file=self.options.profile, do_profile=self.options.profile, ) diff --git a/deluge/ui/console/main.py b/deluge/ui/console/main.py index 0edbe6034..23965bbb2 100644 --- a/deluge/ui/console/main.py +++ b/deluge/ui/console/main.py @@ -68,7 +68,6 @@ DEFAULT_CONSOLE_PREFS = { class ConsoleUI(component.Component, TermResizeHandler): - def __init__(self, options, cmds, log_stream): component.Component.__init__(self, 'ConsoleUI') TermResizeHandler.__init__(self) @@ -126,14 +125,17 @@ class ConsoleUI(component.Component, TermResizeHandler): else: # Interactive if deluge.common.windows_check(): - print("""\nDeluge-console does not run in interactive mode on Windows. \n + print( + """\nDeluge-console does not run in interactive mode on Windows. \n Please use commands from the command line, e.g.:\n deluge-console.exe help deluge-console.exe info deluge-console.exe "add --help" deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent" -""") +""" + ) else: + class ConsoleLog(object): def write(self, data): pass @@ -153,12 +155,15 @@ Please use commands from the command line, e.g.:\n # We use the curses.wrapper function to prevent the console from getting # messed up if an uncaught exception is experienced. from curses import wrapper + wrapper(self.run) def quit(self): if client.connected(): + def on_disconnect(result): reactor.stop() + return client.disconnect().addCallback(on_disconnect) else: try: @@ -169,6 +174,7 @@ Please use commands from the command line, e.g.:\n def exec_args(self, options): """Execute console commands from command line.""" from deluge.ui.console.cmdline.command import Commander + commander = Commander(self._commands) def on_connect(result): @@ -179,6 +185,7 @@ Please use commands from the command line, e.g.:\n def exec_command(result, cmd): return commander.exec_command(cmd) + d = defer.succeed(None) for command in options.parsed_cmds: if command.command in ('quit', 'exit'): @@ -191,6 +198,7 @@ Please use commands from the command line, e.g.:\n # any of the commands. self.started_deferred.addCallback(on_started) return self.started_deferred + d = self.start_console() d.addCallback(on_components_started) return d @@ -200,7 +208,10 @@ Please use commands from the command line, e.g.:\n rm = reason.getErrorMessage() else: rm = reason.value.message - print('Could not connect to daemon: %s:%s\n %s' % (options.daemon_addr, options.daemon_port, rm)) + print( + 'Could not connect to daemon: %s:%s\n %s' + % (options.daemon_addr, options.daemon_port, rm) + ) commander.do_command('quit') d = None @@ -209,9 +220,17 @@ Please use commands from the command line, e.g.:\n else: log.info( 'connect: host=%s, port=%s, username=%s, password=%s', - options.daemon_addr, options.daemon_port, options.daemon_user, options.daemon_pass, + options.daemon_addr, + options.daemon_port, + options.daemon_user, + options.daemon_pass, + ) + d = client.connect( + options.daemon_addr, + options.daemon_port, + options.daemon_user, + options.daemon_pass, ) - d = client.connect(options.daemon_addr, options.daemon_port, options.daemon_user, options.daemon_pass) d.addCallback(on_connect) d.addErrback(on_connect_fail) return d @@ -227,23 +246,34 @@ Please use commands from the command line, e.g.:\n # pass it the function that handles commands colors.init_colors() self.stdscr = stdscr - self.config = ConfigManager('console.conf', defaults=DEFAULT_CONSOLE_PREFS, file_version=2) + self.config = ConfigManager( + 'console.conf', defaults=DEFAULT_CONSOLE_PREFS, file_version=2 + ) self.config.run_converter((0, 1), 2, self._migrate_config_1_to_2) self.statusbars = StatusBars() from deluge.ui.console.modes.connectionmanager import ConnectionManager + self.register_mode(ConnectionManager(stdscr, self.encoding), set_mode=True) torrentlist = self.register_mode(TorrentList(self.stdscr, self.encoding)) self.register_mode(CmdLine(self.stdscr, self.encoding)) self.register_mode(EventView(torrentlist, self.stdscr, self.encoding)) - self.register_mode(TorrentDetail(torrentlist, self.stdscr, self.config, self.encoding)) - self.register_mode(Preferences(torrentlist, self.stdscr, self.config, self.encoding)) - self.register_mode(AddTorrents(torrentlist, self.stdscr, self.config, self.encoding)) + self.register_mode( + TorrentDetail(torrentlist, self.stdscr, self.config, self.encoding) + ) + self.register_mode( + Preferences(torrentlist, self.stdscr, self.config, self.encoding) + ) + self.register_mode( + AddTorrents(torrentlist, self.stdscr, self.config, self.encoding) + ) self.eventlog = EventLog() - self.active_mode.topbar = '{!status!}Deluge ' + deluge.common.get_version() + ' Console' + self.active_mode.topbar = ( + '{!status!}Deluge ' + deluge.common.get_version() + ' Console' + ) self.active_mode.bottombar = '{!status!}' self.active_mode.refresh() # Start the twisted mainloop @@ -275,6 +305,7 @@ Please use commands from the command line, e.g.:\n def on_mode_paused(result, mode, *args): from deluge.ui.console.widgets.popup import PopupsHandler + if isinstance(mode, PopupsHandler): if mode.popup is not None: # If popups are not removed, they are still referenced in the memory @@ -283,8 +314,10 @@ Please use commands from the command line, e.g.:\n # while the current modes' screen is repainted. log.error( 'Mode "%s" still has popups available after being paused.' - ' Ensure all popups are removed on pause!', mode.popup.title, + ' Ensure all popups are removed on pause!', + mode.popup.title, ) + d.addCallback(on_mode_paused, self.active_mode) reactor.removeReader(self.active_mode) @@ -312,6 +345,7 @@ Please use commands from the command line, e.g.:\n func() else: self.messages.append(('Error', error_smg)) + component.stop(['TorrentList']).addCallback(on_stop) def is_active_mode(self, mode): @@ -319,7 +353,15 @@ Please use commands from the command line, e.g.:\n def start_components(self): def on_started(result): - component.pause(['TorrentList', 'EventView', 'AddTorrents', 'TorrentDetail', 'Preferences']) + component.pause( + [ + 'TorrentList', + 'EventView', + 'AddTorrents', + 'TorrentDetail', + 'Preferences', + ] + ) if self.interactive: d = component.start().addCallback(on_started) @@ -335,8 +377,10 @@ Please use commands from the command line, e.g.:\n self.initialized = True d = self.start_components() else: + def on_stopped(result): return component.start(['SessionProxy']) + d = component.stop(['SessionProxy']).addCallback(on_stopped) return d @@ -350,18 +394,23 @@ Please use commands from the command line, e.g.:\n self.torrents.append((torrent_id, status['name'])) self.started_deferred.callback(True) - client.core.get_torrents_status({'id': result}, ['name']).addCallback(on_torrents_status) + client.core.get_torrents_status({'id': result}, ['name']).addCallback( + on_torrents_status + ) d = client.core.get_session_state().addCallback(on_session_state) # Register event handlers to keep the torrent list up-to-date client.register_event_handler('TorrentAddedEvent', self.on_torrent_added_event) - client.register_event_handler('TorrentRemovedEvent', self.on_torrent_removed_event) + client.register_event_handler( + 'TorrentRemovedEvent', self.on_torrent_removed_event + ) return d def on_torrent_added_event(self, event, from_state=False): def on_torrent_status(status): self.torrents.append((event, status['name'])) + client.core.get_torrent_status(event, ['name']).addCallback(on_torrent_status) def on_torrent_removed_event(self, event): @@ -402,7 +451,9 @@ Please use commands from the command line, e.g.:\n matches = [] for tid, name in self.torrents: deluge.common.decode_bytes(name, self.encoding) - if getattr(tid, match_func, None)(string) or getattr(name, match_func, None)(string): + if getattr(tid, match_func, None)(string) or getattr( + name, match_func, None + )(string): matches.append(tid) return matches @@ -413,18 +464,25 @@ Please use commands from the command line, e.g.:\n return None def set_batch_write(self, batch): - if self.interactive and isinstance(self.active_mode, deluge.ui.console.modes.cmdline.CmdLine): + if self.interactive and isinstance( + self.active_mode, deluge.ui.console.modes.cmdline.CmdLine + ): return self.active_mode.set_batch_write(batch) def tab_complete_torrent(self, line): - if self.interactive and isinstance(self.active_mode, deluge.ui.console.modes.cmdline.CmdLine): + if self.interactive and isinstance( + self.active_mode, deluge.ui.console.modes.cmdline.CmdLine + ): return self.active_mode.tab_complete_torrent(line) - def tab_complete_path(self, line, path_type='file', ext='', sort='name', dirs_first=True): - if self.interactive and isinstance(self.active_mode, deluge.ui.console.modes.cmdline.CmdLine): + def tab_complete_path( + self, line, path_type='file', ext='', sort='name', dirs_first=True + ): + if self.interactive and isinstance( + self.active_mode, deluge.ui.console.modes.cmdline.CmdLine + ): return self.active_mode.tab_complete_path( - line, path_type=path_type, ext=ext, - sort=sort, dirs_first=dirs_first, + line, path_type=path_type, ext=ext, sort=sort, dirs_first=dirs_first ) def on_client_disconnect(self): @@ -456,6 +514,7 @@ Please use commands from the command line, e.g.:\n and into sub categories. Some keys are also renamed to be consistent with other UIs. """ + def move_key(source, dest, source_key, dest_key=None): if dest_key is None: dest_key = source_key @@ -463,28 +522,71 @@ Please use commands from the command line, e.g.:\n del source[source_key] # These are moved to 'torrentview' sub dict - for k in ['sort_primary', 'sort_secondary', 'move_selection', 'separate_complete']: + for k in [ + 'sort_primary', + 'sort_secondary', + 'move_selection', + 'separate_complete', + ]: move_key(config, config['torrentview'], k) # These are moved to 'addtorrents' sub dict - for k in ['show_misc_files', 'show_hidden_folders', 'sort_column', 'reverse_sort', 'last_path']: + for k in [ + 'show_misc_files', + 'show_hidden_folders', + 'sort_column', + 'reverse_sort', + 'last_path', + ]: move_key(config, config['addtorrents'], 'addtorrents_%s' % k, dest_key=k) # These are moved to 'cmdline' sub dict - for k in ['ignore_duplicate_lines', 'torrents_per_tab_press', 'third_tab_lists_all']: + for k in [ + 'ignore_duplicate_lines', + 'torrents_per_tab_press', + 'third_tab_lists_all', + ]: move_key(config, config['cmdline'], k) - move_key(config, config['cmdline'], 'save_legacy_history', dest_key='save_command_history') + move_key( + config, + config['cmdline'], + 'save_legacy_history', + dest_key='save_command_history', + ) # Add key for localization config['language'] = DEFAULT_CONSOLE_PREFS['language'] # Migrate column settings columns = [ - 'queue', 'size', 'state', 'progress', 'seeds', 'peers', 'downspeed', 'upspeed', - 'eta', 'ratio', 'avail', 'added', 'tracker', 'savepath', 'downloaded', 'uploaded', - 'remaining', 'owner', 'downloading_time', 'seeding_time', 'completed', 'seeds_peers_ratio', - 'complete_seen', 'down_limit', 'up_limit', 'shared', 'name', + 'queue', + 'size', + 'state', + 'progress', + 'seeds', + 'peers', + 'downspeed', + 'upspeed', + 'eta', + 'ratio', + 'avail', + 'added', + 'tracker', + 'savepath', + 'downloaded', + 'uploaded', + 'remaining', + 'owner', + 'downloading_time', + 'seeding_time', + 'completed', + 'seeds_peers_ratio', + 'complete_seen', + 'down_limit', + 'up_limit', + 'shared', + 'name', ] column_name_mapping = { 'downspeed': 'download_speed', @@ -499,6 +601,7 @@ Please use commands from the command line, e.g.:\n } from deluge.ui.console.modes.torrentlist.torrentview import default_columns + # These are moved to 'torrentview.columns' sub dict for k in columns: column_name = column_name_mapping.get(k, k) @@ -506,9 +609,21 @@ Please use commands from the command line, e.g.:\n if k == 'name': config['torrentview']['columns'][column_name]['visible'] = True else: - move_key(config, config['torrentview']['columns'][column_name], 'show_%s' % k, dest_key='visible') - move_key(config, config['torrentview']['columns'][column_name], '%s_width' % k, dest_key='width') - config['torrentview']['columns'][column_name]['order'] = default_columns[column_name]['order'] + move_key( + config, + config['torrentview']['columns'][column_name], + 'show_%s' % k, + dest_key='visible', + ) + move_key( + config, + config['torrentview']['columns'][column_name], + '%s_width' % k, + dest_key='width', + ) + config['torrentview']['columns'][column_name]['order'] = default_columns[ + column_name + ]['order'] return config @@ -517,6 +632,7 @@ class EventLog(component.Component): """ Prints out certain events as they are received from the core. """ + def __init__(self): component.Component.__init__(self, 'EventLog') self.console = component.get('ConsoleUI') @@ -524,15 +640,33 @@ class EventLog(component.Component): self.date_change_format = 'On {!yellow!}%a, %d %b %Y{!input!} %Z:' client.register_event_handler('TorrentAddedEvent', self.on_torrent_added_event) - client.register_event_handler('PreTorrentRemovedEvent', self.on_torrent_removed_event) - client.register_event_handler('TorrentStateChangedEvent', self.on_torrent_state_changed_event) - client.register_event_handler('TorrentFinishedEvent', self.on_torrent_finished_event) - client.register_event_handler('NewVersionAvailableEvent', self.on_new_version_available_event) - client.register_event_handler('SessionPausedEvent', self.on_session_paused_event) - client.register_event_handler('SessionResumedEvent', self.on_session_resumed_event) - client.register_event_handler('ConfigValueChangedEvent', self.on_config_value_changed_event) - client.register_event_handler('PluginEnabledEvent', self.on_plugin_enabled_event) - client.register_event_handler('PluginDisabledEvent', self.on_plugin_disabled_event) + client.register_event_handler( + 'PreTorrentRemovedEvent', self.on_torrent_removed_event + ) + client.register_event_handler( + 'TorrentStateChangedEvent', self.on_torrent_state_changed_event + ) + client.register_event_handler( + 'TorrentFinishedEvent', self.on_torrent_finished_event + ) + client.register_event_handler( + 'NewVersionAvailableEvent', self.on_new_version_available_event + ) + client.register_event_handler( + 'SessionPausedEvent', self.on_session_paused_event + ) + client.register_event_handler( + 'SessionResumedEvent', self.on_session_resumed_event + ) + client.register_event_handler( + 'ConfigValueChangedEvent', self.on_config_value_changed_event + ) + client.register_event_handler( + 'PluginEnabledEvent', self.on_plugin_enabled_event + ) + client.register_event_handler( + 'PluginDisabledEvent', self.on_plugin_disabled_event + ) self.previous_time = time.localtime(0) @@ -541,16 +675,22 @@ class EventLog(component.Component): return def on_torrent_status(status): - self.write('{!green!}Torrent Added: {!info!}%s ({!cyan!}%s{!info!})' % - (status['name'], torrent_id)) + self.write( + '{!green!}Torrent Added: {!info!}%s ({!cyan!}%s{!info!})' + % (status['name'], torrent_id) + ) # Write out what state the added torrent took self.on_torrent_state_changed_event(torrent_id, status['state']) - client.core.get_torrent_status(torrent_id, ['name', 'state']).addCallback(on_torrent_status) + client.core.get_torrent_status(torrent_id, ['name', 'state']).addCallback( + on_torrent_status + ) def on_torrent_removed_event(self, torrent_id): - self.write('{!red!}Torrent Removed: {!info!}%s ({!cyan!}%s{!info!})' % - (self.console.get_torrent_name(torrent_id), torrent_id)) + self.write( + '{!red!}Torrent Removed: {!info!}%s ({!cyan!}%s{!info!})' + % (self.console.get_torrent_name(torrent_id), torrent_id) + ) def on_torrent_state_changed_event(self, torrent_id, state): # It's probably a new torrent, ignore it @@ -566,19 +706,20 @@ class EventLog(component.Component): if not t_name: return - self.write('%s: {!info!}%s ({!cyan!}%s{!info!})' % - (state, t_name, torrent_id)) + self.write('%s: {!info!}%s ({!cyan!}%s{!info!})' % (state, t_name, torrent_id)) def on_torrent_finished_event(self, torrent_id): if component.get('TorrentList').config['ring_bell']: import curses.beep + curses.beep() - self.write('{!info!}Torrent Finished: %s ({!cyan!}%s{!info!})' % - (self.console.get_torrent_name(torrent_id), torrent_id)) + self.write( + '{!info!}Torrent Finished: %s ({!cyan!}%s{!info!})' + % (self.console.get_torrent_name(torrent_id), torrent_id) + ) def on_new_version_available_event(self, version): - self.write('{!input!}New Deluge version available: {!info!}%s' % - (version)) + self.write('{!input!}New Deluge version available: {!info!}%s' % (version)) def on_session_paused_event(self): self.write('{!input!}Session Paused') diff --git a/deluge/ui/console/modes/add_util.py b/deluge/ui/console/modes/add_util.py index 4138a2f5f..6ebfd9957 100644 --- a/deluge/ui/console/modes/add_util.py +++ b/deluge/ui/console/modes/add_util.py @@ -30,7 +30,9 @@ def _bracket_fixup(path): while path.find(unichr(sentinal)) != -1: sentinal += 1 if sentinal > 65535: - log.error('Cannot fix brackets in path, path contains all possible sentinal characters') + log.error( + 'Cannot fix brackets in path, path contains all possible sentinal characters' + ) return path newpath = path.replace(']', unichr(sentinal)) newpath = newpath.replace('[', '[[]') @@ -44,8 +46,12 @@ def add_torrent(t_file, options, success_cb, fail_cb, ress): t_options['download_location'] = os.path.expanduser(options['path']) t_options['add_paused'] = options['add_paused'] - is_url = (options['path_type'] != 1) and (deluge.common.is_url(t_file) or options['path_type'] == 2) - is_magnet = not(is_url) and (options['path_type'] != 1) and deluge.common.is_magnet(t_file) + is_url = (options['path_type'] != 1) and ( + deluge.common.is_url(t_file) or options['path_type'] == 2 + ) + is_magnet = ( + not (is_url) and (options['path_type'] != 1) and deluge.common.is_magnet(t_file) + ) if is_url or is_magnet: files = [t_file] @@ -59,9 +65,13 @@ def add_torrent(t_file, options, success_cb, fail_cb, ress): for f in files: if is_url: - client.core.add_torrent_url(f, t_options).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress) + client.core.add_torrent_url(f, t_options).addCallback( + success_cb, f, ress + ).addErrback(fail_cb, f, ress) elif is_magnet: - client.core.add_torrent_magnet(f, t_options).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress) + client.core.add_torrent_magnet(f, t_options).addCallback( + success_cb, f, ress + ).addErrback(fail_cb, f, ress) else: if not os.path.exists(f): fail_cb('Does not exist', f, ress) @@ -81,5 +91,5 @@ def add_torrent(t_file, options, success_cb, fail_cb, ress): filedump = b64encode(_file.read()) client.core.add_torrent_file_async( - filename, filedump, t_options, + filename, filedump, t_options ).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress) diff --git a/deluge/ui/console/modes/addtorrents.py b/deluge/ui/console/modes/addtorrents.py index 32892a60d..6b2c105d9 100644 --- a/deluge/ui/console/modes/addtorrents.py +++ b/deluge/ui/console/modes/addtorrents.py @@ -66,7 +66,6 @@ if a file is highlighted class AddTorrents(BaseMode): - def __init__(self, parent_mode, stdscr, console_config, encoding=None): self.console_config = console_config self.parent_mode = parent_mode @@ -108,7 +107,7 @@ class AddTorrents(BaseMode): pass def __refresh_listing(self): - path = os.path.join(*self.path_stack[:self.path_stack_pos]) + path = os.path.join(*self.path_stack[: self.path_stack_pos]) listing = os.listdir(path) @@ -177,7 +176,9 @@ class AddTorrents(BaseMode): self.raw_rows_dirs.sort(key=lambda r: r[0].lower()) if self.sort_column == 'name': - self.raw_rows_files.sort(key=lambda r: r[0].lower(), reverse=self.reverse_sort) + self.raw_rows_files.sort( + key=lambda r: r[0].lower(), reverse=self.reverse_sort + ) elif self.sort_column == 'date': self.raw_rows_files.sort(key=lambda r: r[2], reverse=self.reverse_sort) self.raw_rows = self.raw_rows_dirs + self.raw_rows_files @@ -287,7 +288,7 @@ class AddTorrents(BaseMode): off += 1 # Render files and folders - for i, row in enumerate(self.formatted_rows[self.view_offset:]): + for i, row in enumerate(self.formatted_rows[self.view_offset :]): i += self.view_offset # It's a folder color_string = '' @@ -347,14 +348,16 @@ class AddTorrents(BaseMode): new_dir = self.path_stack_pos >= len(self.path_stack) new_dir = new_dir or (dirname != self.path_stack[self.path_stack_pos]) if new_dir: - self.path_stack = self.path_stack[:self.path_stack_pos] + self.path_stack = self.path_stack[: self.path_stack_pos] self.path_stack.append(dirname) - path = os.path.join(*self.path_stack[:self.path_stack_pos + 1]) + path = os.path.join(*self.path_stack[: self.path_stack_pos + 1]) if not os.access(path, os.R_OK): - self.path_stack = self.path_stack[:self.path_stack_pos] - self.popup = MessagePopup(self, 'Error', '{!error!}Access denied: %s' % path) + self.path_stack = self.path_stack[: self.path_stack_pos] + self.popup = MessagePopup( + self, 'Error', '{!error!}Access denied: %s' % path + ) self.__refresh_listing() return @@ -368,7 +371,6 @@ class AddTorrents(BaseMode): self.__refresh_listing() def _show_add_dialog(self): - def _do_add(result, **kwargs): ress = {'succ': 0, 'fail': 0, 'total': len(self.marked), 'fmsg': []} @@ -377,20 +379,30 @@ class AddTorrents(BaseMode): ress['fail'] += 1 ress['fmsg'].append('{!input!} * %s: {!error!}%s' % (t_file, msg)) if (ress['succ'] + ress['fail']) >= ress['total']: - report_add_status(component.get('TorrentList'), ress['succ'], ress['fail'], ress['fmsg']) + report_add_status( + component.get('TorrentList'), + ress['succ'], + ress['fail'], + ress['fmsg'], + ) def success_cb(tid, t_file, ress): if tid: log.debug('added torrent: %s (%s)', t_file, tid) ress['succ'] += 1 if (ress['succ'] + ress['fail']) >= ress['total']: - report_add_status(component.get('TorrentList'), ress['succ'], ress['fail'], ress['fmsg']) + report_add_status( + component.get('TorrentList'), + ress['succ'], + ress['fail'], + ress['fmsg'], + ) else: fail_cb('Already in session (probably)', t_file, ress) for m in self.marked: filename = m - directory = os.path.join(*self.path_stack[:self.path_stack_pos]) + directory = os.path.join(*self.path_stack[: self.path_stack_pos]) path = os.path.join(directory, filename) with open(path, 'rb') as _file: filedump = b64encode(_file.read()) @@ -403,7 +415,9 @@ class AddTorrents(BaseMode): d.addCallback(success_cb, filename, ress) d.addErrback(fail_cb, filename, ress) - self.console_config['addtorrents']['last_path'] = os.path.join(*self.path_stack[:self.path_stack_pos]) + self.console_config['addtorrents']['last_path'] = os.path.join( + *self.path_stack[: self.path_stack_pos] + ) self.console_config.save() self.back_to_overview() @@ -413,7 +427,9 @@ class AddTorrents(BaseMode): ap = 0 else: ap = 1 - self.popup = InputPopup(self, 'Add Torrents (Esc to cancel)', close_cb=_do_add, height_req=17) + self.popup = InputPopup( + self, 'Add Torrents (Esc to cancel)', close_cb=_do_add, height_req=17 + ) msg = 'Adding torrent files:' for i, m in enumerate(self.marked): @@ -426,8 +442,12 @@ class AddTorrents(BaseMode): self.popup.add_text(msg) self.popup.add_spaces(1) - self.popup.add_text_input('location', 'Download Folder:', config['download_location'], complete=True) - self.popup.add_select_input('add_paused', 'Add Paused:', ['Yes', 'No'], [True, False], ap) + self.popup.add_text_input( + 'location', 'Download Folder:', config['download_location'], complete=True + ) + self.popup.add_select_input( + 'add_paused', 'Add Paused:', ['Yes', 'No'], [True, False], ap + ) def _go_up(self): # Go up in directory hierarchy diff --git a/deluge/ui/console/modes/basemode.py b/deluge/ui/console/modes/basemode.py index 6226d1bc1..6ad2a2c36 100644 --- a/deluge/ui/console/modes/basemode.py +++ b/deluge/ui/console/modes/basemode.py @@ -37,7 +37,6 @@ log = logging.getLogger(__name__) class InputKeyHandler(object): - def __init__(self): self._input_result = None @@ -64,7 +63,6 @@ class InputKeyHandler(object): class TermResizeHandler(object): - def __init__(self): try: signal.signal(signal.SIGWINCH, self.on_terminal_size) @@ -73,10 +71,9 @@ class TermResizeHandler(object): def on_terminal_size(self, *args): # Get the new rows and cols value - rows, cols = struct.unpack( - 'hhhh', - ioctl(0, termios.TIOCGWINSZ, b'\000' * 8), - )[0:2] + rows, cols = struct.unpack('hhhh', ioctl(0, termios.TIOCGWINSZ, b'\000' * 8))[ + 0:2 + ] curses.resizeterm(rows, cols) return rows, cols @@ -100,8 +97,9 @@ class CursesStdIO(object): class BaseMode(CursesStdIO, component.Component): - - def __init__(self, stdscr, encoding=None, do_refresh=True, mode_name=None, depend=None): + def __init__( + self, stdscr, encoding=None, do_refresh=True, mode_name=None, depend=None + ): """ A mode that provides a curses screen designed to run as a reader in a twisted reactor. This mode doesn't do much, just shows status bars and "Base Mode" on the screen @@ -160,16 +158,28 @@ class BaseMode(CursesStdIO, component.Component): return add_string(row, string, screen, self.encoding, **kwargs) def draw_statusbars( - self, top_row=0, bottom_row=-1, topbar=None, bottombar=None, - bottombar_help=True, scr=None, + self, + top_row=0, + bottom_row=-1, + topbar=None, + bottombar=None, + bottombar_help=True, + scr=None, ): self.add_string(top_row, topbar if topbar else self.statusbars.topbar, scr=scr) bottombar = bottombar if bottombar else self.statusbars.bottombar if bottombar_help: if bottombar_help is True: bottombar_help = self.help_hstr - bottombar += ' ' * (self.cols - len(remove_formatting(bottombar)) - - len(remove_formatting(bottombar_help))) + bottombar_help + bottombar += ( + ' ' + * ( + self.cols + - len(remove_formatting(bottombar)) + - len(remove_formatting(bottombar_help)) + ) + + bottombar_help + ) self.add_string(self.rows + bottom_row, bottombar, scr=scr) # This mode doesn't do anything with popups @@ -226,7 +236,9 @@ class BaseMode(CursesStdIO, component.Component): curses.endwin() -def add_string(row, fstring, screen, encoding, col=0, pad=True, pad_char=' ', trim='..', leaveok=0): +def add_string( + row, fstring, screen, encoding, col=0, pad=True, pad_char=' ', trim='..', leaveok=0 +): """ Adds a string to the desired `:param:row`. @@ -282,7 +294,7 @@ def add_string(row, fstring, screen, encoding, col=0, pad=True, pad_char=' ', tr if col + len(string) > max_x: remaining_chrs = max(0, max_x - col) if trim: - string = string[0:max(0, remaining_chrs - len(trim))] + trim + string = string[0 : max(0, remaining_chrs - len(trim))] + trim else: string = string[0:remaining_chrs] @@ -333,7 +345,13 @@ def move_cursor(screen, row, col): screen.move(row, col) except curses.error as ex: import traceback + log.warning( 'Error on screen.move(%s, %s): (curses.LINES: %s, curses.COLS: %s) Error: %s\nStack: %s', - row, col, curses.LINES, curses.COLS, ex, ''.join(traceback.format_stack()), + row, + col, + curses.LINES, + curses.COLS, + ex, + ''.join(traceback.format_stack()), ) diff --git a/deluge/ui/console/modes/cmdline.py b/deluge/ui/console/modes/cmdline.py index 683f6be17..21236c3c7 100644 --- a/deluge/ui/console/modes/cmdline.py +++ b/deluge/ui/console/modes/cmdline.py @@ -22,7 +22,11 @@ from deluge.ui.console.cmdline.command import Commander from deluge.ui.console.modes.basemode import BaseMode, move_cursor from deluge.ui.console.utils import colors from deluge.ui.console.utils import curses_util as util -from deluge.ui.console.utils.format_utils import delete_alt_backspace, remove_formatting, strwidth +from deluge.ui.console.utils.format_utils import ( + delete_alt_backspace, + remove_formatting, + strwidth, +) try: import curses @@ -95,7 +99,6 @@ def commonprefix(m): class CmdLine(BaseMode, Commander): - def __init__(self, stdscr, encoding=None): # Get a handle to the main console self.console = component.get('ConsoleUI') @@ -246,8 +249,13 @@ class CmdLine(BaseMode, Commander): if self.tab_completer: # We only call the tab completer function if we're at the end of # the input string on the cursor is on a space - if self.input_cursor == len(self.input) or self.input[self.input_cursor] == ' ': - self.input, self.input_cursor = self.tab_completer(self.input, self.input_cursor, self.tab_count) + if ( + self.input_cursor == len(self.input) + or self.input[self.input_cursor] == ' ' + ): + self.input, self.input_cursor = self.tab_completer( + self.input, self.input_cursor, self.tab_count + ) # We use the UP and DOWN keys to cycle through input history elif c == curses.KEY_UP: @@ -301,14 +309,25 @@ class CmdLine(BaseMode, Commander): # Delete a character in the input string based on cursor position elif c in [curses.KEY_BACKSPACE, util.KEY_BACKSPACE2]: if self.input and self.input_cursor > 0: - self.input = self.input[:self.input_cursor - 1] + self.input[self.input_cursor:] + self.input = ( + self.input[: self.input_cursor - 1] + + self.input[self.input_cursor :] + ) self.input_cursor -= 1 # Delete a word when alt+backspace is pressed - elif c == [util.KEY_ESC, util.KEY_BACKSPACE2] or c == [util.KEY_ESC, curses.KEY_BACKSPACE]: - self.input, self.input_cursor = delete_alt_backspace(self.input, self.input_cursor) + elif c == [util.KEY_ESC, util.KEY_BACKSPACE2] or c == [ + util.KEY_ESC, + curses.KEY_BACKSPACE, + ]: + self.input, self.input_cursor = delete_alt_backspace( + self.input, self.input_cursor + ) elif c == curses.KEY_DC: if self.input and self.input_cursor < len(self.input): - self.input = self.input[:self.input_cursor] + self.input[self.input_cursor + 1:] + self.input = ( + self.input[: self.input_cursor] + + self.input[self.input_cursor + 1 :] + ) # A key to add to the input string else: @@ -328,7 +347,11 @@ class CmdLine(BaseMode, Commander): self.input += uchar else: # Insert into string - self.input = self.input[:self.input_cursor] + uchar + self.input[self.input_cursor:] + self.input = ( + self.input[: self.input_cursor] + + uchar + + self.input[self.input_cursor :] + ) # Move the cursor forward self.input_cursor += 1 @@ -363,7 +386,7 @@ class CmdLine(BaseMode, Commander): if len(self.lines) > available_lines: # Get the lines to display based on the offset offset = len(self.lines) - self.display_lines_offset - lines = self.lines[-(available_lines - offset):offset] + lines = self.lines[-(available_lines - offset) : offset] elif len(self.lines) == available_lines: lines = self.lines else: @@ -411,7 +434,10 @@ class CmdLine(BaseMode, Commander): if self.console_config['cmdline']['save_command_history']: # Determine which file is the active one # If both are under maximum, it's first, otherwise it's the one not full - if self._hf_lines[0] < MAX_HISTFILE_SIZE and self._hf_lines[1] < MAX_HISTFILE_SIZE: + if ( + self._hf_lines[0] < MAX_HISTFILE_SIZE + and self._hf_lines[1] < MAX_HISTFILE_SIZE + ): active_file = 0 elif self._hf_lines[0] == MAX_HISTFILE_SIZE: active_file = 1 @@ -429,7 +455,9 @@ class CmdLine(BaseMode, Commander): # therefore swapping the currently active file if self._hf_lines[active_file] == MAX_HISTFILE_SIZE: self._hf_lines[1 - active_file] = 0 - with open(self.history_file[1 - active_file], 'w', encoding='utf8') as _file: + with open( + self.history_file[1 - active_file], 'w', encoding='utf8' + ) as _file: _file.truncate(0) def get_line_chunks(line): @@ -454,7 +482,7 @@ class CmdLine(BaseMode, Commander): next_color = line.find('{!', end_color) if next_color == -1: next_color = len(line) - chunks.append((line[:end_color + 2], line[end_color + 2:next_color])) + chunks.append((line[: end_color + 2], line[end_color + 2 : next_color])) line = line[next_color:] return chunks @@ -612,7 +640,9 @@ class CmdLine(BaseMode, Commander): except IndexError: l_arg = '' - new_line = ' '.join([p, complete_line(l_arg, possible_matches)]).lstrip() + new_line = ' '.join( + [p, complete_line(l_arg, possible_matches)] + ).lstrip() if len(remove_formatting(new_line)) > len(line): line = new_line @@ -634,7 +664,10 @@ class CmdLine(BaseMode, Commander): for i in range(listed, listed + max_list): match = possible_matches[i] self.write(match.replace(r'\ ', ' ')) - self.write('{!error!}And %i more. Press <tab> to list them' % (left - max_list)) + self.write( + '{!error!}And %i more. Press <tab> to list them' + % (left - max_list) + ) else: self.tab_count = 0 for match in possible_matches[listed:]: @@ -644,22 +677,28 @@ class CmdLine(BaseMode, Commander): for i in range(listed, listed + max_list): match = possible_matches[i] self.write(match.replace(r'\ ', ' ')) - self.write('{!error!}And %i more (%i/%i). Press <tab> to view more' % ( - left - max_list, hits - 1, pages, - )) + self.write( + '{!error!}And %i more (%i/%i). Press <tab> to view more' + % (left - max_list, hits - 1, pages) + ) else: self.tab_count = 0 for match in possible_matches[listed:]: self.write(match.replace(r'\ ', ' ')) if hits > 2: - self.write('{!green!}Finished listing %i torrents (%i/%i)' % (match_count, hits - 1, pages)) + self.write( + '{!green!}Finished listing %i torrents (%i/%i)' + % (match_count, hits - 1, pages) + ) # We only want to print eventual colors or other control characters, not return them line = remove_formatting(line) cursor = len(line) return (line, cursor) - def tab_complete_path(self, line, path_type='file', ext='', sort='name', dirs_first=1): + def tab_complete_path( + self, line, path_type='file', ext='', sort='name', dirs_first=1 + ): self.console = component.get('ConsoleUI') line = line.replace('\\ ', ' ') @@ -787,17 +826,23 @@ class CmdLine(BaseMode, Commander): if not empty and torrent_id.startswith(line): # Highlight the matching part text = '{!info!}%s{!input!}%s - "%s"' % ( - torrent_id[:line_len], torrent_id[line_len:], torrent_name, + torrent_id[:line_len], + torrent_id[line_len:], + torrent_name, ) possible_matches.append(text) if torrent_name.startswith(line): text = '{!info!}%s{!input!}%s ({!cyan!}%s{!input!})' % ( - escaped_name[:line_len], escaped_name[line_len:], torrent_id, + escaped_name[:line_len], + escaped_name[line_len:], + torrent_id, ) possible_matches.append(text) elif torrent_name.lower().startswith(line.lower()): text = '{!info!}%s{!input!}%s ({!cyan!}%s{!input!})' % ( - escaped_name[:line_len], escaped_name[line_len:], torrent_id, + escaped_name[:line_len], + escaped_name[line_len:], + torrent_id, ) possible_matches2.append(text) diff --git a/deluge/ui/console/modes/connectionmanager.py b/deluge/ui/console/modes/connectionmanager.py index 000360390..84a3fbc14 100644 --- a/deluge/ui/console/modes/connectionmanager.py +++ b/deluge/ui/console/modes/connectionmanager.py @@ -27,7 +27,6 @@ log = logging.getLogger(__name__) class ConnectionManager(BaseMode, PopupsHandler): - def __init__(self, stdscr, encoding=None): PopupsHandler.__init__(self) self.statuses = {} @@ -41,11 +40,16 @@ class ConnectionManager(BaseMode, PopupsHandler): selected_index = self.popup.current_selection() if self.popup else None popup = SelectablePopup( - self, _('Select Host'), self._host_selected, border_off_west=1, active_wrap=True, + self, + _('Select Host'), + self._host_selected, + border_off_west=1, + active_wrap=True, ) popup.add_header( - "{!white,black,bold!}'Q'=%s, 'a'=%s, 'D'=%s" % - (_('Quit'), _('Add Host'), _('Delete Host')), space_below=True, + "{!white,black,bold!}'Q'=%s, 'a'=%s, 'D'=%s" + % (_('Quit'), _('Add Host'), _('Delete Host')), + space_below=True, ) self.push_popup(popup, clear=True) @@ -57,7 +61,9 @@ class ConnectionManager(BaseMode, PopupsHandler): state = 'Online' args.update({'data': self.statuses[host_id], 'foreground': 'green'}) host_str = '%s:%d [%s]' % (hostname, port, state) - self.popup.add_line(host_id, host_str, selectable=True, use_underline=True, **args) + self.popup.add_line( + host_id, host_str, selectable=True, use_underline=True, **args + ) if selected_index: self.popup.set_selection(selected_index) @@ -66,14 +72,17 @@ class ConnectionManager(BaseMode, PopupsHandler): def update_hosts_status(self): for host_entry in self.hostlist.get_hosts_info(): + def on_host_status(status_info): self.statuses[status_info[0]] = status_info self.update_select_host_popup() + self.hostlist.get_host_status(host_entry[0]).addCallback(on_host_status) def _on_connected(self, result): def on_console_start(result): component.get('ConsoleUI').set_mode('TorrentList') + d = component.get('ConsoleUI').start_console() d.addCallback(on_console_start) @@ -94,8 +103,10 @@ class ConnectionManager(BaseMode, PopupsHandler): self.pop_popup() else: self.add_host( - result['hostname']['value'], result['port']['value'], - result['username']['value'], result['password']['value'], + result['hostname']['value'], + result['port']['value'], + result['username']['value'], + result['password']['value'], ) def add_popup(self): diff --git a/deluge/ui/console/modes/eventview.py b/deluge/ui/console/modes/eventview.py index a908058ef..cd3308cf9 100644 --- a/deluge/ui/console/modes/eventview.py +++ b/deluge/ui/console/modes/eventview.py @@ -25,7 +25,6 @@ log = logging.getLogger(__name__) class EventView(BaseMode): - def __init__(self, parent_mode, stdscr, encoding=None): BaseMode.__init__(self, stdscr, encoding) self.parent_mode = parent_mode diff --git a/deluge/ui/console/modes/preferences/preference_panes.py b/deluge/ui/console/modes/preferences/preference_panes.py index fd989fdbd..5781fc117 100644 --- a/deluge/ui/console/modes/preferences/preference_panes.py +++ b/deluge/ui/console/modes/preferences/preference_panes.py @@ -23,11 +23,17 @@ log = logging.getLogger(__name__) class BasePreferencePane(BaseInputPane, BaseWindow, PopupsHandler): - def __init__(self, name, preferences): PopupsHandler.__init__(self) self.preferences = preferences - BaseWindow.__init__(self, '%s' % name, self.pane_width, preferences.height, posy=1, posx=self.pane_x_pos) + BaseWindow.__init__( + self, + '%s' % name, + self.pane_width, + preferences.height, + posy=1, + posx=self.pane_x_pos, + ) BaseInputPane.__init__(self, preferences, border_off_east=1) self.name = name @@ -76,9 +82,15 @@ class BasePreferencePane(BaseInputPane, BaseWindow, PopupsHandler): if ipt.has_input(): # Need special cases for in/out ports or proxy since they are tuples or dicts. if ipt.name == 'listen_ports_to' or ipt.name == 'listen_ports_from': - conf_dict['listen_ports'] = (self.infrom.get_value(), self.into.get_value()) + conf_dict['listen_ports'] = ( + self.infrom.get_value(), + self.into.get_value(), + ) elif ipt.name == 'out_ports_to' or ipt.name == 'out_ports_from': - conf_dict['outgoing_ports'] = (self.outfrom.get_value(), self.outto.get_value()) + conf_dict['outgoing_ports'] = ( + self.outfrom.get_value(), + self.outto.get_value(), + ) elif ipt.name == 'listen_interface': listen_interface = ipt.get_value().strip() if is_ip(listen_interface) or not listen_interface: @@ -99,15 +111,23 @@ class BasePreferencePane(BaseInputPane, BaseWindow, PopupsHandler): elif ipt.name == 'proxy_port': conf_dict.setdefault('proxy', {})['port'] = ipt.get_value() elif ipt.name == 'proxy_hostnames': - conf_dict.setdefault('proxy', {})['proxy_hostnames'] = ipt.get_value() + conf_dict.setdefault('proxy', {})[ + 'proxy_hostnames' + ] = ipt.get_value() elif ipt.name == 'proxy_peer_connections': - conf_dict.setdefault('proxy', {})['proxy_peer_connections'] = ipt.get_value() + conf_dict.setdefault('proxy', {})[ + 'proxy_peer_connections' + ] = ipt.get_value() elif ipt.name == 'proxy_tracker_connections': - conf_dict.setdefault('proxy', {})['proxy_tracker_connections'] = ipt.get_value() + conf_dict.setdefault('proxy', {})[ + 'proxy_tracker_connections' + ] = ipt.get_value() elif ipt.name == 'force_proxy': conf_dict.setdefault('proxy', {})['force_proxy'] = ipt.get_value() elif ipt.name == 'anonymous_mode': - conf_dict.setdefault('proxy', {})['anonymous_mode'] = ipt.get_value() + conf_dict.setdefault('proxy', {})[ + 'anonymous_mode' + ] = ipt.get_value() else: conf_dict[ipt.name] = ipt.get_value() @@ -150,7 +170,6 @@ class BasePreferencePane(BaseInputPane, BaseWindow, PopupsHandler): class InterfacePane(BasePreferencePane): - def __init__(self, preferences): BasePreferencePane.__init__(self, ' %s ' % _('Interface'), preferences) @@ -159,7 +178,8 @@ class InterfacePane(BasePreferencePane): self.add_header(_('General options')) self.add_checked_input( - 'ring_bell', _('Ring system bell when a download finishes'), + 'ring_bell', + _('Ring system bell when a download finishes'), console_config['ring_bell'], ) self.add_header('Console UI', space_above=True) @@ -169,38 +189,44 @@ class InterfacePane(BasePreferencePane): console_config['torrentview']['separate_complete'], ) self.add_checked_input( - 'move_selection', _('Move selection when moving torrents in the queue'), + 'move_selection', + _('Move selection when moving torrents in the queue'), console_config['torrentview']['move_selection'], ) from deluge.ui.translations_util import get_languages + langs = get_languages() langs.insert(0, ('', 'System Default')) self.add_combo_input( - 'language', _('Language'), - langs, default=console_config['language'], + 'language', _('Language'), langs, default=console_config['language'] ) self.add_header(_('Command Line Mode'), space_above=True) self.add_checked_input( - 'ignore_duplicate_lines', _('Do not store duplicate input in history'), + 'ignore_duplicate_lines', + _('Do not store duplicate input in history'), console_config['cmdline']['ignore_duplicate_lines'], ) self.add_checked_input( - 'save_command_history', _('Store and load command line history in command line mode'), + 'save_command_history', + _('Store and load command line history in command line mode'), console_config['cmdline']['save_command_history'], ) self.add_header('') self.add_checked_input( - 'third_tab_lists_all', _('Third tab lists all remaining torrents in command line mode'), + 'third_tab_lists_all', + _('Third tab lists all remaining torrents in command line mode'), console_config['cmdline']['third_tab_lists_all'], ) self.add_int_spin_input( - 'torrents_per_tab_press', _('Torrents per tab press'), - console_config['cmdline']['torrents_per_tab_press'], min_val=5, max_val=10000, + 'torrents_per_tab_press', + _('Torrents per tab press'), + console_config['cmdline']['torrents_per_tab_press'], + min_val=5, + max_val=10000, ) class DownloadsPane(BasePreferencePane): - def __init__(self, preferences): BasePreferencePane.__init__(self, ' %s ' % _('Downloads'), preferences) @@ -208,48 +234,69 @@ class DownloadsPane(BasePreferencePane): def create_pane(self, core_conf, console_config): self.add_header(_('Folders')) self.add_text_input( - 'download_location', '%s:' % _('Download To'), core_conf['download_location'], - complete=True, activate_input=True, col='+1', + 'download_location', + '%s:' % _('Download To'), + core_conf['download_location'], + complete=True, + activate_input=True, + col='+1', ) cmptxt = TextInput( - self.preferences, 'move_completed_path', None, self.move, self.pane_width, - core_conf['move_completed_path'], False, + self.preferences, + 'move_completed_path', + None, + self.move, + self.pane_width, + core_conf['move_completed_path'], + False, ) self.add_checkedplus_input( - 'move_completed', '%s:' % _('Move completed to'), - cmptxt, core_conf['move_completed'], + 'move_completed', + '%s:' % _('Move completed to'), + cmptxt, + core_conf['move_completed'], ) copytxt = TextInput( - self.preferences, 'torrentfiles_location', None, self.move, self.pane_width, - core_conf['torrentfiles_location'], False, + self.preferences, + 'torrentfiles_location', + None, + self.move, + self.pane_width, + core_conf['torrentfiles_location'], + False, ) self.add_checkedplus_input( - 'copy_torrent_file', '%s:' % _('Copy of .torrent files to'), copytxt, + 'copy_torrent_file', + '%s:' % _('Copy of .torrent files to'), + copytxt, core_conf['copy_torrent_file'], ) self.add_checked_input( - 'del_copy_torrent_file', _('Delete copy of torrent file on remove'), + 'del_copy_torrent_file', + _('Delete copy of torrent file on remove'), core_conf['del_copy_torrent_file'], ) self.add_header(_('Options'), space_above=True) self.add_checked_input( - 'prioritize_first_last_pieces', ('Prioritize first and last pieces of torrent'), + 'prioritize_first_last_pieces', + ('Prioritize first and last pieces of torrent'), core_conf['prioritize_first_last_pieces'], ) self.add_checked_input( - 'sequential_download', _('Sequential download'), + 'sequential_download', + _('Sequential download'), core_conf['sequential_download'], ) self.add_checked_input('add_paused', _('Add Paused'), core_conf['add_paused']) self.add_checked_input( - 'pre_allocate_storage', _('Pre-Allocate disk space'), + 'pre_allocate_storage', + _('Pre-Allocate disk space'), core_conf['pre_allocate_storage'], ) class NetworkPane(BasePreferencePane): - def __init__(self, preferences): BasePreferencePane.__init__(self, ' %s ' % _('Network'), preferences) @@ -257,36 +304,49 @@ class NetworkPane(BasePreferencePane): def create_pane(self, core_conf, console_config): self.add_header(_('Incomming Ports')) inrand = self.add_checked_input( - 'random_port', 'Use Random Ports Active Port: %d' - % self.preferences.active_port, + 'random_port', + 'Use Random Ports Active Port: %d' % self.preferences.active_port, core_conf['random_port'], ) listen_ports = core_conf['listen_ports'] self.infrom = self.add_int_spin_input( - 'listen_ports_from', ' %s:' % _('From'), - value=listen_ports[0], min_val=0, max_val=65535, + 'listen_ports_from', + ' %s:' % _('From'), + value=listen_ports[0], + min_val=0, + max_val=65535, ) self.infrom.set_depend(inrand, inverse=True) self.into = self.add_int_spin_input( - 'listen_ports_to', ' %s:' % _('To'), - value=listen_ports[1], min_val=0, max_val=65535, + 'listen_ports_to', + ' %s:' % _('To'), + value=listen_ports[1], + min_val=0, + max_val=65535, ) self.into.set_depend(inrand, inverse=True) self.add_header(_('Outgoing Ports'), space_above=True) outrand = self.add_checked_input( - 'random_outgoing_ports', _('Use Random Ports'), + 'random_outgoing_ports', + _('Use Random Ports'), core_conf['random_outgoing_ports'], ) out_ports = core_conf['outgoing_ports'] self.outfrom = self.add_int_spin_input( - 'out_ports_from', ' %s:' % _('From'), - value=out_ports[0], min_val=0, max_val=65535, + 'out_ports_from', + ' %s:' % _('From'), + value=out_ports[0], + min_val=0, + max_val=65535, ) self.outfrom.set_depend(outrand, inverse=True) self.outto = self.add_int_spin_input( - 'out_ports_to', ' %s:' % _('To'), - value=out_ports[1], min_val=0, max_val=65535, + 'out_ports_to', + ' %s:' % _('To'), + value=out_ports[1], + min_val=0, + max_val=65535, ) self.outto.set_depend(outrand, inverse=True) @@ -300,7 +360,9 @@ class NetworkPane(BasePreferencePane): self.add_header(_('Outgoing Interface'), space_above=True) self.add_text_input( 'outgoing_interface', - _('The interface adapter name for outgoing BitTorrent connections. (Leave empty for default.):'), + _( + 'The interface adapter name for outgoing BitTorrent connections. (Leave empty for default.):' + ), core_conf['outgoing_interface'], ) @@ -316,21 +378,33 @@ class NetworkPane(BasePreferencePane): self.add_header(_('Encryption'), space_above=True) self.add_select_input( - 'enc_in_policy', '%s:' % _('Inbound'), [_('Forced'), _('Enabled'), _('Disabled')], - [0, 1, 2], core_conf['enc_in_policy'], active_default=True, col='+1', + 'enc_in_policy', + '%s:' % _('Inbound'), + [_('Forced'), _('Enabled'), _('Disabled')], + [0, 1, 2], + core_conf['enc_in_policy'], + active_default=True, + col='+1', ) self.add_select_input( - 'enc_out_policy', '%s:' % _('Outbound'), [_('Forced'), _('Enabled'), _('Disabled')], - [0, 1, 2], core_conf['enc_out_policy'], active_default=True, + 'enc_out_policy', + '%s:' % _('Outbound'), + [_('Forced'), _('Enabled'), _('Disabled')], + [0, 1, 2], + core_conf['enc_out_policy'], + active_default=True, ) self.add_select_input( - 'enc_level', '%s:' % _('Level'), [_('Handshake'), _('Full Stream'), _('Either')], - [0, 1, 2], core_conf['enc_level'], active_default=True, + 'enc_level', + '%s:' % _('Level'), + [_('Handshake'), _('Full Stream'), _('Either')], + [0, 1, 2], + core_conf['enc_level'], + active_default=True, ) class BandwidthPane(BasePreferencePane): - def __init__(self, preferences): BasePreferencePane.__init__(self, ' %s ' % _('Bandwidth'), preferences) @@ -338,58 +412,89 @@ class BandwidthPane(BasePreferencePane): def create_pane(self, core_conf, console_config): self.add_header(_('Global Bandwidth Usage')) self.add_int_spin_input( - 'max_connections_global', '%s:' % _('Maximum Connections'), - core_conf['max_connections_global'], min_val=-1, max_val=9000, + 'max_connections_global', + '%s:' % _('Maximum Connections'), + core_conf['max_connections_global'], + min_val=-1, + max_val=9000, ) self.add_int_spin_input( - 'max_upload_slots_global', '%s:' % _('Maximum Upload Slots'), - core_conf['max_upload_slots_global'], min_val=-1, max_val=9000, + 'max_upload_slots_global', + '%s:' % _('Maximum Upload Slots'), + core_conf['max_upload_slots_global'], + min_val=-1, + max_val=9000, ) self.add_float_spin_input( - 'max_download_speed', '%s:' % _('Maximum Download Speed (KiB/s)'), - core_conf['max_download_speed'], min_val=-1.0, max_val=60000.0, + 'max_download_speed', + '%s:' % _('Maximum Download Speed (KiB/s)'), + core_conf['max_download_speed'], + min_val=-1.0, + max_val=60000.0, ) self.add_float_spin_input( - 'max_upload_speed', '%s:' % _('Maximum Upload Speed (KiB/s)'), - core_conf['max_upload_speed'], min_val=-1.0, max_val=60000.0, + 'max_upload_speed', + '%s:' % _('Maximum Upload Speed (KiB/s)'), + core_conf['max_upload_speed'], + min_val=-1.0, + max_val=60000.0, ) self.add_int_spin_input( - 'max_half_open_connections', '%s:' % _('Maximum Half-Open Connections'), - core_conf['max_half_open_connections'], min_val=-1, max_val=9999, + 'max_half_open_connections', + '%s:' % _('Maximum Half-Open Connections'), + core_conf['max_half_open_connections'], + min_val=-1, + max_val=9999, ) self.add_int_spin_input( - 'max_connections_per_second', '%s:' % _('Maximum Connection Attempts per Second'), - core_conf['max_connections_per_second'], min_val=-1, max_val=9999, + 'max_connections_per_second', + '%s:' % _('Maximum Connection Attempts per Second'), + core_conf['max_connections_per_second'], + min_val=-1, + max_val=9999, ) self.add_checked_input( - 'ignore_limits_on_local_network', _('Ignore limits on local network'), + 'ignore_limits_on_local_network', + _('Ignore limits on local network'), core_conf['ignore_limits_on_local_network'], ) self.add_checked_input( - 'rate_limit_ip_overhead', _('Rate Limit IP Overhead'), + 'rate_limit_ip_overhead', + _('Rate Limit IP Overhead'), core_conf['rate_limit_ip_overhead'], ) self.add_header(_('Per Torrent Bandwidth Usage'), space_above=True) self.add_int_spin_input( - 'max_connections_per_torrent', '%s:' % _('Maximum Connections'), - core_conf['max_connections_per_torrent'], min_val=-1, max_val=9000, + 'max_connections_per_torrent', + '%s:' % _('Maximum Connections'), + core_conf['max_connections_per_torrent'], + min_val=-1, + max_val=9000, ) self.add_int_spin_input( - 'max_upload_slots_per_torrent', '%s:' % _('Maximum Upload Slots'), - core_conf['max_upload_slots_per_torrent'], min_val=-1, max_val=9000, + 'max_upload_slots_per_torrent', + '%s:' % _('Maximum Upload Slots'), + core_conf['max_upload_slots_per_torrent'], + min_val=-1, + max_val=9000, ) self.add_float_spin_input( - 'max_download_speed_per_torrent', '%s:' % _('Maximum Download Speed (KiB/s)'), - core_conf['max_download_speed_per_torrent'], min_val=-1.0, max_val=60000.0, + 'max_download_speed_per_torrent', + '%s:' % _('Maximum Download Speed (KiB/s)'), + core_conf['max_download_speed_per_torrent'], + min_val=-1.0, + max_val=60000.0, ) self.add_float_spin_input( - 'max_upload_speed_per_torrent', '%s:' % _('Maximum Upload Speed (KiB/s)'), - core_conf['max_upload_speed_per_torrent'], min_val=-1.0, max_val=60000.0, + 'max_upload_speed_per_torrent', + '%s:' % _('Maximum Upload Speed (KiB/s)'), + core_conf['max_upload_speed_per_torrent'], + min_val=-1.0, + max_val=60000.0, ) class OtherPane(BasePreferencePane): - def __init__(self, preferences): BasePreferencePane.__init__(self, ' %s ' % _('Other'), preferences) @@ -397,15 +502,24 @@ class OtherPane(BasePreferencePane): def create_pane(self, core_conf, console_config): self.add_header(_('System Information')) self.add_info_field('info1', ' Help us improve Deluge by sending us your', '') - self.add_info_field('info2', ' Python version, PyGTK version, OS and processor', '') - self.add_info_field('info3', ' types. Absolutely no other information is sent.', '') - self.add_checked_input('send_info', _('Yes, please send anonymous statistics.'), core_conf['send_info']) + self.add_info_field( + 'info2', ' Python version, PyGTK version, OS and processor', '' + ) + self.add_info_field( + 'info3', ' types. Absolutely no other information is sent.', '' + ) + self.add_checked_input( + 'send_info', + _('Yes, please send anonymous statistics.'), + core_conf['send_info'], + ) self.add_header(_('GeoIP Database'), space_above=True) - self.add_text_input('geoip_db_location', 'Location:', core_conf['geoip_db_location']) + self.add_text_input( + 'geoip_db_location', 'Location:', core_conf['geoip_db_location'] + ) class DaemonPane(BasePreferencePane): - def __init__(self, preferences): BasePreferencePane.__init__(self, ' %s ' % _('Daemon'), preferences) @@ -413,77 +527,115 @@ class DaemonPane(BasePreferencePane): def create_pane(self, core_conf, console_config): self.add_header('Port') self.add_int_spin_input( - 'daemon_port', '%s:' % _('Daemon Port'), core_conf['daemon_port'], - min_val=0, max_val=65535, + 'daemon_port', + '%s:' % _('Daemon Port'), + core_conf['daemon_port'], + min_val=0, + max_val=65535, ) self.add_header('Connections', space_above=True) - self.add_checked_input('allow_remote', _('Allow remote connections'), core_conf['allow_remote']) + self.add_checked_input( + 'allow_remote', _('Allow remote connections'), core_conf['allow_remote'] + ) self.add_header('Other', space_above=True) self.add_checked_input( - 'new_release_check', _('Periodically check the website for new releases'), + 'new_release_check', + _('Periodically check the website for new releases'), core_conf['new_release_check'], ) class QueuePane(BasePreferencePane): - def __init__(self, preferences): BasePreferencePane.__init__(self, ' %s ' % _('Queue'), preferences) @overrides(BasePreferencePane) def create_pane(self, core_conf, console_config): self.add_header(_('New Torrents')) - self.add_checked_input('queue_new_to_top', _('Queue to top'), core_conf['queue_new_to_top']) + self.add_checked_input( + 'queue_new_to_top', _('Queue to top'), core_conf['queue_new_to_top'] + ) self.add_header(_('Active Torrents'), True) self.add_int_spin_input( - 'max_active_limit', '%s:' % _('Total'), core_conf['max_active_limit'], - min_val=-1, max_val=9999, + 'max_active_limit', + '%s:' % _('Total'), + core_conf['max_active_limit'], + min_val=-1, + max_val=9999, ) self.add_int_spin_input( - 'max_active_downloading', '%s:' % _('Downloading'), - core_conf['max_active_downloading'], min_val=-1, max_val=9999, + 'max_active_downloading', + '%s:' % _('Downloading'), + core_conf['max_active_downloading'], + min_val=-1, + max_val=9999, ) self.add_int_spin_input( - 'max_active_seeding', '%s:' % _('Seeding'), - core_conf['max_active_seeding'], min_val=-1, max_val=9999, + 'max_active_seeding', + '%s:' % _('Seeding'), + core_conf['max_active_seeding'], + min_val=-1, + max_val=9999, ) self.add_checked_input( - 'dont_count_slow_torrents', 'Ignore slow torrents', + 'dont_count_slow_torrents', + 'Ignore slow torrents', core_conf['dont_count_slow_torrents'], ) self.add_checked_input( - 'auto_manage_prefer_seeds', 'Prefer seeding torrents', + 'auto_manage_prefer_seeds', + 'Prefer seeding torrents', core_conf['auto_manage_prefer_seeds'], ) self.add_header(_('Seeding Rotation'), space_above=True) self.add_float_spin_input( - 'share_ratio_limit', '%s:' % _('Share Ratio'), - core_conf['share_ratio_limit'], precision=2, min_val=-1.0, max_val=100.0, + 'share_ratio_limit', + '%s:' % _('Share Ratio'), + core_conf['share_ratio_limit'], + precision=2, + min_val=-1.0, + max_val=100.0, ) self.add_float_spin_input( - 'seed_time_ratio_limit', '%s:' % _('Time Ratio'), - core_conf['seed_time_ratio_limit'], precision=2, min_val=-1.0, max_val=100.0, + 'seed_time_ratio_limit', + '%s:' % _('Time Ratio'), + core_conf['seed_time_ratio_limit'], + precision=2, + min_val=-1.0, + max_val=100.0, ) self.add_int_spin_input( - 'seed_time_limit', '%s:' % _('Time (m)'), core_conf['seed_time_limit'], - min_val=1, max_val=10000, + 'seed_time_limit', + '%s:' % _('Time (m)'), + core_conf['seed_time_limit'], + min_val=1, + max_val=10000, ) seedratio = FloatSpinInput( - self.mode, 'stop_seed_ratio', '', self.move, core_conf['stop_seed_ratio'], - precision=2, inc_amt=0.1, min_val=0.5, max_val=100.0, + self.mode, + 'stop_seed_ratio', + '', + self.move, + core_conf['stop_seed_ratio'], + precision=2, + inc_amt=0.1, + min_val=0.5, + max_val=100.0, ) self.add_checkedplus_input( - 'stop_seed_at_ratio', '%s:' % _('Share Ratio Reached'), seedratio, + 'stop_seed_at_ratio', + '%s:' % _('Share Ratio Reached'), + seedratio, core_conf['stop_seed_at_ratio'], ) self.add_checked_input( - 'remove_seed_at_ratio', _('Remove torrent (Unchecked pauses torrent)'), + 'remove_seed_at_ratio', + _('Remove torrent (Unchecked pauses torrent)'), core_conf['remove_seed_at_ratio'], ) class ProxyPane(BasePreferencePane): - def __init__(self, preferences): BasePreferencePane.__init__(self, ' %s ' % _('Proxy'), preferences) @@ -493,17 +645,31 @@ class ProxyPane(BasePreferencePane): self.add_header(_('Proxy Settings')) self.add_header(_('Proxy'), space_above=True) - self.add_int_spin_input('proxy_type', '%s:' % _('Type'), proxy['type'], min_val=0, max_val=5) + self.add_int_spin_input( + 'proxy_type', '%s:' % _('Type'), proxy['type'], min_val=0, max_val=5 + ) self.add_text_input('proxy_username', '%s:' % _('Username'), proxy['username']) self.add_text_input('proxy_password', '%s:' % _('Password'), proxy['password']) self.add_text_input('proxy_hostname', '%s:' % _('Hostname'), proxy['hostname']) - self.add_int_spin_input('proxy_port', '%s:' % _('Port'), proxy['port'], min_val=0, max_val=65535) - self.add_checked_input('proxy_hostnames', _('Proxy Hostnames'), proxy['proxy_hostnames']) - self.add_checked_input('proxy_peer_connections', _('Proxy Peers'), proxy['proxy_peer_connections']) - self.add_checked_input('proxy_tracker_connections', _('Proxy Trackers'), proxy['proxy_tracker_connections']) + self.add_int_spin_input( + 'proxy_port', '%s:' % _('Port'), proxy['port'], min_val=0, max_val=65535 + ) + self.add_checked_input( + 'proxy_hostnames', _('Proxy Hostnames'), proxy['proxy_hostnames'] + ) + self.add_checked_input( + 'proxy_peer_connections', _('Proxy Peers'), proxy['proxy_peer_connections'] + ) + self.add_checked_input( + 'proxy_tracker_connections', + _('Proxy Trackers'), + proxy['proxy_tracker_connections'], + ) self.add_header('%s' % _('Force Proxy'), space_above=True) self.add_checked_input('force_proxy', _('Force Proxy'), proxy['force_proxy']) - self.add_checked_input('anonymous_mode', _('Hide Client Identity'), proxy['anonymous_mode']) + self.add_checked_input( + 'anonymous_mode', _('Hide Client Identity'), proxy['anonymous_mode'] + ) self.add_header('%s' % _('Proxy Type Help'), space_above=True) self.add_text_area( 'proxy_text_area', @@ -515,7 +681,6 @@ class ProxyPane(BasePreferencePane): class CachePane(BasePreferencePane): - def __init__(self, preferences): BasePreferencePane.__init__(self, ' %s ' % _('Cache'), preferences) self.created = False @@ -529,52 +694,65 @@ class CachePane(BasePreferencePane): self.add_header(_('Settings'), space_below=True) self.add_int_spin_input( 'cache_size', - '%s:' % _('Cache Size (16 KiB blocks)'), core_conf['cache_size'], - min_val=0, max_val=99999, + '%s:' % _('Cache Size (16 KiB blocks)'), + core_conf['cache_size'], + min_val=0, + max_val=99999, ) self.add_int_spin_input( 'cache_expiry', - '%s:' % _('Cache Expiry (seconds)'), core_conf['cache_expiry'], - min_val=1, max_val=32000, + '%s:' % _('Cache Expiry (seconds)'), + core_conf['cache_expiry'], + min_val=1, + max_val=32000, ) self.add_header(' %s' % _('Write'), space_above=True) - self.add_info_field('blocks_written', ' %s:' % _('Blocks Written'), status['disk.num_blocks_written']) - self.add_info_field('writes', ' %s:' % _('Writes'), status['disk.num_write_ops']) + self.add_info_field( + 'blocks_written', + ' %s:' % _('Blocks Written'), + status['disk.num_blocks_written'], + ) + self.add_info_field( + 'writes', ' %s:' % _('Writes'), status['disk.num_write_ops'] + ) self.add_info_field( 'write_hit_ratio', - ' %s:' % _('Write Cache Hit Ratio'), '%.2f' % status['write_hit_ratio'], + ' %s:' % _('Write Cache Hit Ratio'), + '%.2f' % status['write_hit_ratio'], ) self.add_header(' %s' % _('Read')) self.add_info_field( - 'blocks_read', - ' %s:' % _('Blocks Read'), status['disk.num_blocks_read'], + 'blocks_read', ' %s:' % _('Blocks Read'), status['disk.num_blocks_read'] ) self.add_info_field( 'blocks_read_hit', - ' %s:' % _('Blocks Read hit'), status['disk.num_blocks_cache_hits'], - ) - self.add_info_field( - 'reads', - ' %s:' % _('Reads'), status['disk.num_read_ops'], + ' %s:' % _('Blocks Read hit'), + status['disk.num_blocks_cache_hits'], ) + self.add_info_field('reads', ' %s:' % _('Reads'), status['disk.num_read_ops']) self.add_info_field( 'read_hit_ratio', - ' %s:' % _('Read Cache Hit Ratio'), '%.2f' % status['read_hit_ratio'], + ' %s:' % _('Read Cache Hit Ratio'), + '%.2f' % status['read_hit_ratio'], ) self.add_header(' %s' % _('Size')) self.add_info_field( 'cache_size_info', - ' %s:' % _('Cache Size'), status['disk.disk_blocks_in_use'], + ' %s:' % _('Cache Size'), + status['disk.disk_blocks_in_use'], ) self.add_info_field( 'read_cache_size', - ' %s:' % _('Read Cache Size'), status['disk.read_cache_blocks'], + ' %s:' % _('Read Cache Size'), + status['disk.read_cache_blocks'], ) @overrides(BasePreferencePane) def update(self, active): if active: - client.core.get_session_status(DISK_CACHE_KEYS).addCallback(self.update_cache_status_fields) + client.core.get_session_status(DISK_CACHE_KEYS).addCallback( + self.update_cache_status_fields + ) def update_cache_status_fields(self, status): if not self.created: diff --git a/deluge/ui/console/modes/preferences/preferences.py b/deluge/ui/console/modes/preferences/preferences.py index 00c124cd6..45a39a621 100644 --- a/deluge/ui/console/modes/preferences/preferences.py +++ b/deluge/ui/console/modes/preferences/preferences.py @@ -16,9 +16,17 @@ import deluge.component as component from deluge.decorators import overrides from deluge.ui.client import client from deluge.ui.console.modes.basemode import BaseMode -from deluge.ui.console.modes.preferences.preference_panes import (BandwidthPane, CachePane, DaemonPane, DownloadsPane, - InterfacePane, NetworkPane, OtherPane, ProxyPane, - QueuePane) +from deluge.ui.console.modes.preferences.preference_panes import ( + BandwidthPane, + CachePane, + DaemonPane, + DownloadsPane, + InterfacePane, + NetworkPane, + OtherPane, + ProxyPane, + QueuePane, +) from deluge.ui.console.utils import curses_util as util from deluge.ui.console.widgets.fields import SelectInput from deluge.ui.console.widgets.popup import MessagePopup, PopupsHandler @@ -72,17 +80,28 @@ class ZONE(object): class PreferenceSidebar(Sidebar): - def __init__(self, torrentview, width): height = curses.LINES - 2 - Sidebar.__init__(self, torrentview, width, height, title=None, border_off_north=1) + Sidebar.__init__( + self, torrentview, width, height, title=None, border_off_north=1 + ) self.categories = [ - _('Interface'), _('Downloads'), _('Network'), _('Bandwidth'), - _('Other'), _('Daemon'), _('Queue'), _('Proxy'), _('Cache'), + _('Interface'), + _('Downloads'), + _('Network'), + _('Bandwidth'), + _('Other'), + _('Daemon'), + _('Queue'), + _('Proxy'), + _('Cache'), ] for name in self.categories: self.add_text_field( - name, name, selectable=True, font_unfocused_active='bold', + name, + name, + selectable=True, + font_unfocused_active='bold', color_unfocused_active='white,black', ) @@ -91,7 +110,6 @@ class PreferenceSidebar(Sidebar): class Preferences(BaseMode, PopupsHandler): - def __init__(self, parent_mode, stdscr, console_config, encoding=None): BaseMode.__init__(self, stdscr, encoding=encoding, do_refresh=False) PopupsHandler.__init__(self) @@ -123,7 +141,9 @@ class Preferences(BaseMode, PopupsHandler): CachePane(self), ] - self.action_input = SelectInput(self, None, None, [_('Cancel'), _('Apply'), _('OK')], [0, 1, 2], 0) + self.action_input = SelectInput( + self, None, None, [_('Cancel'), _('Apply'), _('OK')], [0, 1, 2], 0 + ) def load_config(self): if self.config_loaded: @@ -135,10 +155,12 @@ class Preferences(BaseMode, PopupsHandler): for p in self.panes: p.create_pane(core_config, self.console_config) self.refresh() + client.core.get_config().addCallback(on_get_config) def on_get_listen_port(port): self.active_port = port + client.core.get_listen_port().addCallback(on_get_listen_port) @property @@ -161,15 +183,21 @@ class Preferences(BaseMode, PopupsHandler): def _draw_preferences(self): self.cur_cat = self.sidebar.active_input - self.panes[self.cur_cat].render(self, self.stdscr, self.prefs_width, self.active_zone == ZONE.PREFRENCES) + self.panes[self.cur_cat].render( + self, self.stdscr, self.prefs_width, self.active_zone == ZONE.PREFRENCES + ) self.panes[self.cur_cat].refresh() def _draw_actions(self): selected = self.active_zone == ZONE.ACTIONS self.stdscr.hline(self.rows - 3, self.sidebar_width, b'_', self.cols) self.action_input.render( - self.stdscr, self.rows - 2, width=self.cols, - active=selected, focus=True, col=self.cols - 22, + self.stdscr, + self.rows - 2, + width=self.cols, + active=selected, + focus=True, + col=self.cols - 22, ) @overrides(BaseMode) @@ -195,7 +223,10 @@ class Preferences(BaseMode, PopupsHandler): @overrides(BaseMode) def refresh(self): - if not component.get('ConsoleUI').is_active_mode(self) or not self.config_loaded: + if ( + not component.get('ConsoleUI').is_active_mode(self) + or not self.config_loaded + ): return if self.popup is None and self.messages: @@ -257,14 +288,25 @@ class Preferences(BaseMode, PopupsHandler): if isinstance(pane, InterfacePane): pane.add_config_values(new_console_config) for k in ['ring_bell', 'language']: - didupdate = update_conf_value(k, new_console_config, self.console_config, didupdate) + didupdate = update_conf_value( + k, new_console_config, self.console_config, didupdate + ) for k in ['separate_complete', 'move_selection']: - didupdate = update_conf_value(k, new_console_config, self.console_config['torrentview'], didupdate) + didupdate = update_conf_value( + k, + new_console_config, + self.console_config['torrentview'], + didupdate, + ) for k in [ - 'ignore_duplicate_lines', 'save_command_history', - 'third_tab_lists_all', 'torrents_per_tab_press', + 'ignore_duplicate_lines', + 'save_command_history', + 'third_tab_lists_all', + 'torrents_per_tab_press', ]: - didupdate = update_conf_value(k, new_console_config, self.console_config['cmdline'], didupdate) + didupdate = update_conf_value( + k, new_console_config, self.console_config['cmdline'], didupdate + ) if didupdate: self.parent_mode.on_config_changed() diff --git a/deluge/ui/console/modes/torrentdetail.py b/deluge/ui/console/modes/torrentdetail.py index 9316585ad..cbe3d16c9 100644 --- a/deluge/ui/console/modes/torrentdetail.py +++ b/deluge/ui/console/modes/torrentdetail.py @@ -17,12 +17,24 @@ from deluge.decorators import overrides from deluge.ui.client import client from deluge.ui.common import FILE_PRIORITY from deluge.ui.console.modes.basemode import BaseMode -from deluge.ui.console.modes.torrentlist.torrentactions import ACTION, torrent_actions_popup +from deluge.ui.console.modes.torrentlist.torrentactions import ( + ACTION, + torrent_actions_popup, +) from deluge.ui.console.utils import colors from deluge.ui.console.utils import curses_util as util from deluge.ui.console.utils.column import get_column_value, torrent_data_fields -from deluge.ui.console.utils.format_utils import format_priority, format_progress, format_row -from deluge.ui.console.widgets.popup import InputPopup, MessagePopup, PopupsHandler, SelectablePopup +from deluge.ui.console.utils.format_utils import ( + format_priority, + format_progress, + format_row, +) +from deluge.ui.console.widgets.popup import ( + InputPopup, + MessagePopup, + PopupsHandler, + SelectablePopup, +) try: import curses @@ -66,7 +78,6 @@ download priority of selected files and folders. class TorrentDetail(BaseMode, PopupsHandler): - def __init__(self, parent_mode, stdscr, console_config, encoding=None): PopupsHandler.__init__(self) self.console_config = console_config @@ -74,13 +85,37 @@ class TorrentDetail(BaseMode, PopupsHandler): self.torrentid = None self.torrent_state = None self._status_keys = [ - 'files', 'name', 'state', 'download_payload_rate', 'upload_payload_rate', - 'progress', 'eta', 'all_time_download', 'total_uploaded', 'ratio', - 'num_seeds', 'total_seeds', 'num_peers', 'total_peers', 'active_time', - 'seeding_time', 'time_added', 'distributed_copies', 'num_pieces', - 'piece_length', 'download_location', 'file_progress', 'file_priorities', 'message', - 'total_wanted', 'tracker_host', 'owner', 'seed_rank', 'last_seen_complete', - 'completed_time', 'time_since_transfer', + 'files', + 'name', + 'state', + 'download_payload_rate', + 'upload_payload_rate', + 'progress', + 'eta', + 'all_time_download', + 'total_uploaded', + 'ratio', + 'num_seeds', + 'total_seeds', + 'num_peers', + 'total_peers', + 'active_time', + 'seeding_time', + 'time_added', + 'distributed_copies', + 'num_pieces', + 'piece_length', + 'download_location', + 'file_progress', + 'file_priorities', + 'message', + 'total_wanted', + 'tracker_host', + 'owner', + 'seed_rank', + 'last_seen_complete', + 'completed_time', + 'time_since_transfer', ] self.file_list = None self.current_file = None @@ -99,9 +134,15 @@ class TorrentDetail(BaseMode, PopupsHandler): self._listing_start = self.rows // 2 self._listing_space = self._listing_start - self._listing_start - client.register_event_handler('TorrentFileRenamedEvent', self._on_torrentfilerenamed_event) - client.register_event_handler('TorrentFolderRenamedEvent', self._on_torrentfolderrenamed_event) - client.register_event_handler('TorrentRemovedEvent', self._on_torrentremoved_event) + client.register_event_handler( + 'TorrentFileRenamedEvent', self._on_torrentfilerenamed_event + ) + client.register_event_handler( + 'TorrentFolderRenamedEvent', self._on_torrentfolderrenamed_event + ) + client.register_event_handler( + 'TorrentRemovedEvent', self._on_torrentremoved_event + ) util.safe_curs_set(util.Curser.INVISIBLE) self.stdscr.notimeout(0) @@ -124,8 +165,7 @@ class TorrentDetail(BaseMode, PopupsHandler): if self.torrentid: component.get('SessionProxy').get_torrent_status( - self.torrentid, - self._status_keys, + self.torrentid, self._status_keys ).addCallback(self.set_state) @overrides(BaseMode) @@ -152,14 +192,17 @@ class TorrentDetail(BaseMode, PopupsHandler): # don't keep getting the files once we've got them once if state.get('files'): self.files_sep = '{!green,black,bold,underline!}%s' % ( - ('Files (torrent has %d files)' % len(state['files'])).center(self.cols) + ('Files (torrent has %d files)' % len(state['files'])).center( + self.cols + ) ) self.file_list, self.file_dict = self.build_file_list( - state['files'], state['file_progress'], - state['file_priorities'], + state['files'], state['file_progress'], state['file_priorities'] ) else: - self.files_sep = '{!green,black,bold,underline!}%s' % (('Files (File list unknown)').center(self.cols)) + self.files_sep = '{!green,black,bold,underline!}%s' % ( + ('Files (File list unknown)').center(self.cols) + ) need_prio_update = True self.__fill_progress(self.file_list, state['file_progress']) @@ -199,10 +242,17 @@ class TorrentDetail(BaseMode, PopupsHandler): if not cur or path != cur[-1][0]: child_list = [] if path == paths[-1]: - file_progress = format_progress(progress[torrent_file['index']] * 100) + file_progress = format_progress( + progress[torrent_file['index']] * 100 + ) entry = [ - path, torrent_file['index'], torrent_file['size'], child_list, - False, file_progress, priority[torrent_file['index']], + path, + torrent_file['index'], + torrent_file['size'], + child_list, + False, + file_progress, + priority[torrent_file['index']], ] file_dict[torrent_file['index']] = entry else: @@ -265,18 +315,26 @@ class TorrentDetail(BaseMode, PopupsHandler): self.column_widths[i] = cw else: rem = self.cols - req - var_cols = len([col_width for col_width in self.column_widths if col_width < 0]) + var_cols = len( + [col_width for col_width in self.column_widths if col_width < 0] + ) vw = rem // var_cols for i in range(0, len(self.column_widths)): if self.column_widths[i] < 0: self.column_widths[i] = vw self.column_string = '{!green,black,bold!}%s' % ( - ''.join(['%s%s' % ( - self.column_names[i], ' ' * ( - self.column_widths[i] - len(self.column_names[i]) - ), - ) for i in range(0, len(self.column_names))])) + ''.join( + [ + '%s%s' + % ( + self.column_names[i], + ' ' * (self.column_widths[i] - len(self.column_names[i])), + ) + for i in range(0, len(self.column_names)) + ] + ) + ) def _on_torrentremoved_event(self, torrent_id): if torrent_id == self.torrentid: @@ -286,7 +344,7 @@ class TorrentDetail(BaseMode, PopupsHandler): if torrent_id == self.torrentid: self.file_dict[index][0] = new_name.split('/')[-1] component.get('SessionProxy').get_torrent_status( - self.torrentid, self._status_keys, + self.torrentid, self._status_keys ).addCallback(self.set_state) def _on_torrentfolderrenamed_event(self, torrent_id, old_folder, new_folder): @@ -303,7 +361,7 @@ class TorrentDetail(BaseMode, PopupsHandler): # self.__get_file_by_name(old_folder, self.file_list)[0] = new_folder.strip('/') component.get('SessionProxy').get_torrent_status( - self.torrentid, self._status_keys, + self.torrentid, self._status_keys ).addCallback(self.set_state) def draw_files(self, files, depth, off, idx): @@ -333,7 +391,7 @@ class TorrentDetail(BaseMode, PopupsHandler): 4: 'white', # Normal 5: 'green', 6: 'green', - 7: 'green', # High + 7: 'green', # High } fg = priority_fg_color[fl[6]] @@ -344,7 +402,9 @@ class TorrentDetail(BaseMode, PopupsHandler): if fl[1] in self.marked: bg = color_selected if fl[3]: - if self.marked[fl[1]] < self.__get_contained_files_count(file_list=fl[3]): + if self.marked[fl[1]] < self.__get_contained_files_count( + file_list=fl[3] + ): bg = color_partially_selected attr = 'bold' @@ -354,7 +414,9 @@ class TorrentDetail(BaseMode, PopupsHandler): if fl[1] in self.marked: fg = color_selected if fl[3]: - if self.marked[fl[1]] < self.__get_contained_files_count(file_list=fl[3]): + if self.marked[fl[1]] < self.__get_contained_files_count( + file_list=fl[3] + ): fg = color_partially_selected else: if fg == 'white': @@ -377,7 +439,8 @@ class TorrentDetail(BaseMode, PopupsHandler): r = format_row( [ '%s%s %s' % (' ' * depth, xchar, fl[0]), - fsize(fl[2]), fl[5], + fsize(fl[2]), + fl[5], format_priority(fl[6]), ], self.column_widths, @@ -439,8 +502,10 @@ class TorrentDetail(BaseMode, PopupsHandler): def add_field(name, row, pre_color='{!info!}', post_color='{!input!}'): s = '%s%s: %s%s' % ( - pre_color, torrent_data_fields[name]['name'], - post_color, get_column_value(name, status), + pre_color, + torrent_data_fields[name]['name'], + post_color, + get_column_value(name, status), ) if row: row = self.add_string(row, s) @@ -457,7 +522,10 @@ class TorrentDetail(BaseMode, PopupsHandler): if status['progress'] != 100.0: s += '/%s' % fsize(status['total_wanted']) if status['download_payload_rate'] > 0: - s += ' {!yellow!}@ %s%s' % (download_color, fsize(status['download_payload_rate'])) + s += ' {!yellow!}@ %s%s' % ( + download_color, + fsize(status['download_payload_rate']), + ) s += add_field('eta', 0) if s: row = self.add_string(row, s) @@ -465,35 +533,48 @@ class TorrentDetail(BaseMode, PopupsHandler): # Print UL info and ratio s = add_field('uploaded', 0, download_color) if status['upload_payload_rate'] > 0: - s += ' {!yellow!}@ %s%s' % (colors.state_color['Seeding'], fsize(status['upload_payload_rate'])) + s += ' {!yellow!}@ %s%s' % ( + colors.state_color['Seeding'], + fsize(status['upload_payload_rate']), + ) s += ' ' + add_field('ratio', 0) row = self.add_string(row, s) # Seed/peer info s = '{!info!}%s:{!green!} %s {!input!}(%s)' % ( torrent_data_fields['seeds']['name'], - status['num_seeds'], status['total_seeds'], + status['num_seeds'], + status['total_seeds'], ) row = self.add_string(row, s) s = '{!info!}%s:{!red!} %s {!input!}(%s)' % ( torrent_data_fields['peers']['name'], - status['num_peers'], status['total_peers'], + status['num_peers'], + status['total_peers'], ) row = self.add_string(row, s) # Tracker tracker_color = '{!green!}' if status['message'] == 'OK' else '{!red!}' s = '{!info!}%s: {!magenta!}%s{!input!} says "%s%s{!input!}"' % ( - torrent_data_fields['tracker']['name'], status['tracker_host'], tracker_color, status['message'], + torrent_data_fields['tracker']['name'], + status['tracker_host'], + tracker_color, + status['message'], ) row = self.add_string(row, s) # Pieces and availability s = '{!info!}%s: {!yellow!}%s {!input!}x {!yellow!}%s' % ( - torrent_data_fields['pieces']['name'], status['num_pieces'], fsize(status['piece_length']), + torrent_data_fields['pieces']['name'], + status['num_pieces'], + fsize(status['piece_length']), ) if status['distributed_copies']: - s += '{!info!}%s: {!input!}%s' % (torrent_data_fields['seed_rank']['name'], status['seed_rank']) + s += '{!info!}%s: {!input!}%s' % ( + torrent_data_fields['seed_rank']['name'], + status['seed_rank'], + ) row = self.add_string(row, s) # Time added @@ -599,7 +680,9 @@ class TorrentDetail(BaseMode, PopupsHandler): self.build_prio_list(self.file_list, plist, -1, data) plist.sort() priorities = [p[1] for p in plist] - client.core.set_torrent_options([self.torrent_id], {'file_priorities': priorities}) + client.core.set_torrent_options( + [self.torrent_id], {'file_priorities': priorities} + ) if was_empty: self.marked = {} @@ -607,29 +690,37 @@ class TorrentDetail(BaseMode, PopupsHandler): # show popup for priority selections def show_priority_popup(self, was_empty): - def popup_func(data, *args, **kwargs): if data is None: return return self.do_priority(data, kwargs[data], was_empty) if self.marked: - popup = SelectablePopup(self, 'Set File Priority', popup_func, border_off_north=1) + popup = SelectablePopup( + self, 'Set File Priority', popup_func, border_off_north=1 + ) popup.add_line( - 'ignore_priority', '_Ignore', - cb_arg=FILE_PRIORITY['Ignore'], foreground='red', + 'ignore_priority', + '_Ignore', + cb_arg=FILE_PRIORITY['Ignore'], + foreground='red', ) popup.add_line( - 'low_priority', '_Low Priority', - cb_arg=FILE_PRIORITY['Low Priority'], foreground='yellow', + 'low_priority', + '_Low Priority', + cb_arg=FILE_PRIORITY['Low Priority'], + foreground='yellow', ) popup.add_line( - 'normal_priority', '_Normal Priority', + 'normal_priority', + '_Normal Priority', cb_arg=FILE_PRIORITY['Normal Priority'], ) popup.add_line( - 'high_priority', '_High Priority', - cb_arg=FILE_PRIORITY['High Priority'], foreground='green', + 'high_priority', + '_High Priority', + cb_arg=FILE_PRIORITY['High Priority'], + foreground='green', ) popup._selected = 1 self.push_popup(popup) @@ -758,7 +849,9 @@ class TorrentDetail(BaseMode, PopupsHandler): # It's a folder if element[3]: - i = self._selection_to_file_idx(element[3], idx + 1, true_idx, closed or not element[4]) + i = self._selection_to_file_idx( + element[3], idx + 1, true_idx, closed or not element[4] + ) if isinstance(i, tuple): idx, true_idx = i if element[4]: @@ -786,7 +879,9 @@ class TorrentDetail(BaseMode, PopupsHandler): if num == idx: return '%s%s/' % (path, element[0]) if element[4]: - i = self._get_full_folder_path(num, element[3], path + element[0] + '/', idx + 1) + i = self._get_full_folder_path( + num, element[3], path + element[0] + '/', idx + 1 + ) if not isinstance(i, int): return i idx = i @@ -819,27 +914,50 @@ class TorrentDetail(BaseMode, PopupsHandler): if _file[3]: def do_rename(result, **kwargs): - if not result or not result['new_foldername']['value'] or kwargs.get('close', False): + if ( + not result + or not result['new_foldername']['value'] + or kwargs.get('close', False) + ): self.popup.close(None, call_cb=False) return old_fname = self._get_full_folder_path(self.current_file_idx) - new_fname = '%s/%s/' % (old_fname.strip('/').rpartition('/')[0], result['new_foldername']['value']) + new_fname = '%s/%s/' % ( + old_fname.strip('/').rpartition('/')[0], + result['new_foldername']['value'], + ) self._do_rename_folder(tid, old_fname, new_fname) - popup = InputPopup(self, 'Rename folder (Esc to cancel)', close_cb=do_rename) - popup.add_text_input('new_foldername', 'Enter new folder name:', old_filename.strip('/'), complete=True) + popup = InputPopup( + self, 'Rename folder (Esc to cancel)', close_cb=do_rename + ) + popup.add_text_input( + 'new_foldername', + 'Enter new folder name:', + old_filename.strip('/'), + complete=True, + ) self.push_popup(popup) else: def do_rename(result, **kwargs): - if not result or not result['new_filename']['value'] or kwargs.get('close', False): + if ( + not result + or not result['new_filename']['value'] + or kwargs.get('close', False) + ): self.popup.close(None, call_cb=False) return - fname = '%s/%s' % (self.full_names[idx].rpartition('/')[0], result['new_filename']['value']) + fname = '%s/%s' % ( + self.full_names[idx].rpartition('/')[0], + result['new_filename']['value'], + ) self._do_rename_file(tid, idx, fname) popup = InputPopup(self, ' Rename file ', close_cb=do_rename) - popup.add_text_input('new_filename', 'Enter new filename:', old_filename, complete=True) + popup.add_text_input( + 'new_filename', 'Enter new filename:', old_filename, complete=True + ) self.push_popup(popup) @overrides(BaseMode) @@ -879,7 +997,7 @@ class TorrentDetail(BaseMode, PopupsHandler): elif c == curses.KEY_DC: torrent_actions_popup(self, [self.torrentid], action=ACTION.REMOVE) elif c in [curses.KEY_ENTER, util.KEY_ENTER2]: - was_empty = (self.marked == {}) + was_empty = self.marked == {} self.__mark_tree(self.file_list, self.current_file[1]) self.show_priority_popup(was_empty) elif c == util.KEY_SPACE: diff --git a/deluge/ui/console/modes/torrentlist/__init__.py b/deluge/ui/console/modes/torrentlist/__init__.py index 9a41a6636..18c4db377 100644 --- a/deluge/ui/console/modes/torrentlist/__init__.py +++ b/deluge/ui/console/modes/torrentlist/__init__.py @@ -11,7 +11,7 @@ class ACTION(object): REMOVE_DATA = 6 REMOVE_NODATA = 7 DETAILS = 'torrent_details' - MOVE_STORAGE = ('move_download_folder') + MOVE_STORAGE = 'move_download_folder' QUEUE = 'queue' QUEUE_TOP = 'queue_top' QUEUE_UP = 'queue_up' diff --git a/deluge/ui/console/modes/torrentlist/add_torrents_popup.py b/deluge/ui/console/modes/torrentlist/add_torrents_popup.py index 3cc7ee253..b0ac483a0 100644 --- a/deluge/ui/console/modes/torrentlist/add_torrents_popup.py +++ b/deluge/ui/console/modes/torrentlist/add_torrents_popup.py @@ -20,16 +20,19 @@ log = logging.getLogger(__name__) def report_add_status(torrentlist, succ_cnt, fail_cnt, fail_msgs): if fail_cnt == 0: - torrentlist.report_message('Torrents Added', '{!success!}Successfully added %d torrent(s)' % succ_cnt) + torrentlist.report_message( + 'Torrents Added', '{!success!}Successfully added %d torrent(s)' % succ_cnt + ) else: - msg = ('{!error!}Failed to add the following %d torrent(s):\n {!input!}' % fail_cnt) + '\n '.join(fail_msgs) + msg = ( + '{!error!}Failed to add the following %d torrent(s):\n {!input!}' % fail_cnt + ) + '\n '.join(fail_msgs) if succ_cnt != 0: msg += '\n \n{!success!}Successfully added %d torrent(s)' % succ_cnt torrentlist.report_message('Torrent Add Report', msg) def show_torrent_add_popup(torrentlist): - def do_add_from_url(data=None, **kwargs): torrentlist.pop_popup() if not data or kwargs.get('close', False): @@ -51,30 +54,47 @@ def show_torrent_add_popup(torrentlist): if not url: return - t_options = {'download_location': data['path']['value'], 'add_paused': data['add_paused']['value']} + t_options = { + 'download_location': data['path']['value'], + 'add_paused': data['add_paused']['value'], + } if deluge.common.is_magnet(url): - client.core.add_torrent_magnet(url, t_options).addCallback(success_cb, url).addErrback(fail_cb, url) + client.core.add_torrent_magnet(url, t_options).addCallback( + success_cb, url + ).addErrback(fail_cb, url) elif deluge.common.is_url(url): - client.core.add_torrent_url(url, t_options).addCallback(success_cb, url).addErrback(fail_cb, url) + client.core.add_torrent_url(url, t_options).addCallback( + success_cb, url + ).addErrback(fail_cb, url) else: - torrentlist.report_message('Error', '{!error!}Invalid URL or magnet link: %s' % url) + torrentlist.report_message( + 'Error', '{!error!}Invalid URL or magnet link: %s' % url + ) return log.debug( 'Adding Torrent(s): %s (dl path: %s) (paused: %d)', - url, data['path']['value'], data['add_paused']['value'], + url, + data['path']['value'], + data['add_paused']['value'], ) def show_add_url_popup(): add_paused = 1 if 'add_paused' in torrentlist.coreconfig else 0 - popup = InputPopup(torrentlist, 'Add Torrent (Esc to cancel)', close_cb=do_add_from_url) + popup = InputPopup( + torrentlist, 'Add Torrent (Esc to cancel)', close_cb=do_add_from_url + ) popup.add_text_input('url', 'Enter torrent URL or Magnet link:') popup.add_text_input( - 'path', 'Enter save path:', torrentlist.coreconfig.get('download_location', ''), + 'path', + 'Enter save path:', + torrentlist.coreconfig.get('download_location', ''), complete=True, ) - popup.add_select_input('add_paused', 'Add Paused:', ['Yes', 'No'], [True, False], add_paused) + popup.add_select_input( + 'add_paused', 'Add Paused:', ['Yes', 'No'], [True, False], add_paused + ) torrentlist.push_popup(popup) def option_chosen(selected, *args, **kwargs): diff --git a/deluge/ui/console/modes/torrentlist/filtersidebar.py b/deluge/ui/console/modes/torrentlist/filtersidebar.py index 49e26ed89..0f39b5c3c 100644 --- a/deluge/ui/console/modes/torrentlist/filtersidebar.py +++ b/deluge/ui/console/modes/torrentlist/filtersidebar.py @@ -29,36 +29,54 @@ class FilterSidebar(Sidebar, Component): torrents based on state. """ + def __init__(self, torrentlist, config): self.config = config height = curses.LINES - 2 width = self.config['torrentview']['sidebar_width'] Sidebar.__init__( - self, torrentlist, width, height, title=' Filter ', border_off_north=1, + self, + torrentlist, + width, + height, + title=' Filter ', + border_off_north=1, allow_resize=True, ) Component.__init__(self, 'FilterSidebar') self.checked_index = 0 - kwargs = {'checked_char': '*', 'unchecked_char': '-', 'checkbox_format': ' %s ', 'col': 0} + kwargs = { + 'checked_char': '*', + 'unchecked_char': '-', + 'checkbox_format': ' %s ', + 'col': 0, + } self.add_checked_input('All', 'All', checked=True, **kwargs) self.add_checked_input('Active', 'Active', **kwargs) - self.add_checked_input('Downloading', 'Downloading', color='green,black', **kwargs) + self.add_checked_input( + 'Downloading', 'Downloading', color='green,black', **kwargs + ) self.add_checked_input('Seeding', 'Seeding', color='cyan,black', **kwargs) self.add_checked_input('Paused', 'Paused', **kwargs) self.add_checked_input('Error', 'Error', color='red,black', **kwargs) self.add_checked_input('Checking', 'Checking', color='blue,black', **kwargs) self.add_checked_input('Queued', 'Queued', **kwargs) - self.add_checked_input('Allocating', 'Allocating', color='yellow,black', **kwargs) + self.add_checked_input( + 'Allocating', 'Allocating', color='yellow,black', **kwargs + ) self.add_checked_input('Moving', 'Moving', color='green,black', **kwargs) @overrides(Component) def update(self): if not self.hidden() and client.connected(): - d = client.core.get_filter_tree(True, []).addCallback(self._cb_update_filter_tree) + d = client.core.get_filter_tree(True, []).addCallback( + self._cb_update_filter_tree + ) def on_filter_tree_updated(changed): if changed: self.refresh() + d.addCallback(on_filter_tree_updated) def _cb_update_filter_tree(self, filter_items): @@ -78,7 +96,11 @@ class FilterSidebar(Sidebar, Component): for state in states: field = self.get_input(state[0]) if field: - txt = ('%%-%ds%%%ds' % (filter_state_width, filter_count_width) % (state[0], state[1])) + txt = ( + '%%-%ds%%%ds' + % (filter_state_width, filter_count_width) + % (state[0], state[1]) + ) if field.set_message(txt): changed = True return changed @@ -86,7 +108,9 @@ class FilterSidebar(Sidebar, Component): @overrides(BaseInputPane) def immediate_action_cb(self, state_changed=True): if state_changed: - self.parent.torrentview.set_torrent_filter(self.inputs[self.active_input].name) + self.parent.torrentview.set_torrent_filter( + self.inputs[self.active_input].name + ) @overrides(Sidebar) def handle_read(self, c): diff --git a/deluge/ui/console/modes/torrentlist/queue_mode.py b/deluge/ui/console/modes/torrentlist/queue_mode.py index 9a98e86c3..0c44aafdf 100644 --- a/deluge/ui/console/modes/torrentlist/queue_mode.py +++ b/deluge/ui/console/modes/torrentlist/queue_mode.py @@ -39,14 +39,15 @@ Change queue position of selected torrents class QueueMode(object): - def __init__(self, torrentslist, torrent_ids): self.torrentslist = torrentslist self.torrentview = torrentslist.torrentview self.torrent_ids = torrent_ids def set_statusbar_args(self, statusbar_args): - statusbar_args['bottombar'] = '{!black,white!}Queue mode: change queue position of selected torrents.' + statusbar_args[ + 'bottombar' + ] = '{!black,white!}Queue mode: change queue position of selected torrents.' statusbar_args['bottombar_help'] = ' Press [h] for help' def update_cursor(self): @@ -59,9 +60,22 @@ class QueueMode(object): if c in [util.KEY_ESC, util.KEY_BELL]: # If Escape key or CTRL-g, we abort self.torrentslist.set_minor_mode(None) elif c == ord('h'): - popup = MessagePopup(self.torrentslist, 'Help', QUEUE_MODE_HELP_STR, width_req=0.65, border_off_west=1) + popup = MessagePopup( + self.torrentslist, + 'Help', + QUEUE_MODE_HELP_STR, + width_req=0.65, + border_off_west=1, + ) self.torrentslist.push_popup(popup, clear=True) - elif c in [curses.KEY_UP, curses.KEY_DOWN, curses.KEY_HOME, curses.KEY_END, curses.KEY_NPAGE, curses.KEY_PPAGE]: + elif c in [ + curses.KEY_UP, + curses.KEY_DOWN, + curses.KEY_HOME, + curses.KEY_END, + curses.KEY_NPAGE, + curses.KEY_PPAGE, + ]: action = key_to_action[c] self.do_queue(action) @@ -78,38 +92,64 @@ class QueueMode(object): selected_num += 1 if qact == ACTION.QUEUE_TOP: if self.torrentview.marked: - self.torrentview.cursel = 1 + sorted(self.torrentview.marked).index(self.torrentview.cursel) + self.torrentview.cursel = 1 + sorted(self.torrentview.marked).index( + self.torrentview.cursel + ) else: self.torrentview.cursel = 1 self.torrentview.marked = list(range(1, selected_num + 1)) elif qact == ACTION.QUEUE_UP: self.torrentview.cursel = max(1, self.torrentview.cursel - 1) self.torrentview.marked = [marked - 1 for marked in self.torrentview.marked] - self.torrentview.marked = [marked for marked in self.torrentview.marked if marked > 0] + self.torrentview.marked = [ + marked for marked in self.torrentview.marked if marked > 0 + ] elif qact == ACTION.QUEUE_DOWN: self.torrentview.cursel = min(queue_length, self.torrentview.cursel + 1) self.torrentview.marked = [marked + 1 for marked in self.torrentview.marked] - self.torrentview.marked = [marked for marked in self.torrentview.marked if marked <= queue_length] + self.torrentview.marked = [ + marked for marked in self.torrentview.marked if marked <= queue_length + ] elif qact == ACTION.QUEUE_BOTTOM: if self.torrentview.marked: - self.torrentview.cursel = (queue_length - selected_num + 1 + - sorted(self.torrentview.marked).index(self.torrentview.cursel)) + self.torrentview.cursel = ( + queue_length + - selected_num + + 1 + + sorted(self.torrentview.marked).index(self.torrentview.cursel) + ) else: self.torrentview.cursel = queue_length - self.torrentview.marked = list(range(queue_length - selected_num + 1, queue_length + 1)) + self.torrentview.marked = list( + range(queue_length - selected_num + 1, queue_length + 1) + ) def do_queue(self, qact, *args, **kwargs): if qact == ACTION.QUEUE_TOP: - client.core.queue_top(self.torrent_ids).addCallback(self.move_selection, qact) + client.core.queue_top(self.torrent_ids).addCallback( + self.move_selection, qact + ) elif qact == ACTION.QUEUE_BOTTOM: - client.core.queue_bottom(self.torrent_ids).addCallback(self.move_selection, qact) + client.core.queue_bottom(self.torrent_ids).addCallback( + self.move_selection, qact + ) elif qact == ACTION.QUEUE_UP: - client.core.queue_up(self.torrent_ids).addCallback(self.move_selection, qact) + client.core.queue_up(self.torrent_ids).addCallback( + self.move_selection, qact + ) elif qact == ACTION.QUEUE_DOWN: - client.core.queue_down(self.torrent_ids).addCallback(self.move_selection, qact) + client.core.queue_down(self.torrent_ids).addCallback( + self.move_selection, qact + ) def popup(self, **kwargs): - popup = SelectablePopup(self.torrentslist, 'Queue Action', self.do_queue, cb_args=kwargs, border_off_west=1) + popup = SelectablePopup( + self.torrentslist, + 'Queue Action', + self.do_queue, + cb_args=kwargs, + border_off_west=1, + ) popup.add_line(ACTION.QUEUE_TOP, '_Top') popup.add_line(ACTION.QUEUE_UP, '_Up') popup.add_line(ACTION.QUEUE_DOWN, '_Down') diff --git a/deluge/ui/console/modes/torrentlist/search_mode.py b/deluge/ui/console/modes/torrentlist/search_mode.py index f28bf75cb..6720c925a 100644 --- a/deluge/ui/console/modes/torrentlist/search_mode.py +++ b/deluge/ui/console/modes/torrentlist/search_mode.py @@ -41,14 +41,12 @@ SEARCH_FORMAT = { SEARCH_EMPTY: '{!black,white!}Search torrents: %s{!black,white!}', SEARCH_SUCCESS: '{!black,white!}Search torrents: {!black,green!}%s{!black,white!}', SEARCH_FAILING: '{!black,white!}Search torrents: {!black,red!}%s{!black,white!}', - SEARCH_START_REACHED: - '{!black,white!}Search torrents: {!black,yellow!}%s{!black,white!} (start reached)', + SEARCH_START_REACHED: '{!black,white!}Search torrents: {!black,yellow!}%s{!black,white!} (start reached)', SEARCH_END_REACHED: '{!black,white!}Search torrents: {!black,yellow!}%s{!black,white!} (end reached)', } class SearchMode(InputKeyHandler): - def __init__(self, torrentlist): super(SearchMode, self).__init__() self.torrentlist = torrentlist @@ -58,10 +56,16 @@ class SearchMode(InputKeyHandler): def update_cursor(self): util.safe_curs_set(util.Curser.VERY_VISIBLE) - move_cursor(self.torrentlist.stdscr, self.torrentlist.rows - 1, len(self.search_string) + 17) + move_cursor( + self.torrentlist.stdscr, + self.torrentlist.rows - 1, + len(self.search_string) + 17, + ) def set_statusbar_args(self, statusbar_args): - statusbar_args['bottombar'] = SEARCH_FORMAT[self.search_state] % self.search_string + statusbar_args['bottombar'] = ( + SEARCH_FORMAT[self.search_state] % self.search_string + ) statusbar_args['bottombar_help'] = False def update_colors(self, tidx, colors): @@ -92,18 +96,23 @@ class SearchMode(InputKeyHandler): if direction == 'last': search_space = reversed(search_space) elif direction == 'next': - search_space = search_space[self.torrentview.cursel + 1:] + search_space = search_space[self.torrentview.cursel + 1 :] elif direction == 'previous': - search_space = reversed(search_space[:self.torrentview.cursel]) + search_space = reversed(search_space[: self.torrentview.cursel]) search_string = self.search_string.lower() for i, n in search_space: n = n.lower() if n.find(search_string) != -1: self.torrentview.cursel = i - if (self.torrentview.curoff + self.torrentview.torrent_rows - self.torrentview.torrentlist_offset)\ - < self.torrentview.cursel: - self.torrentview.curoff = self.torrentview.cursel - self.torrentview.torrent_rows + 1 + if ( + self.torrentview.curoff + + self.torrentview.torrent_rows + - self.torrentview.torrentlist_offset + ) < self.torrentview.cursel: + self.torrentview.curoff = ( + self.torrentview.cursel - self.torrentview.torrent_rows + 1 + ) elif (self.torrentview.curoff + 1) > self.torrentview.cursel: self.torrentview.curoff = max(0, self.torrentview.cursel) self.search_state = SEARCH_SUCCESS @@ -120,7 +129,10 @@ class SearchMode(InputKeyHandler): cname = self.torrentview.torrent_names[self.torrentview.cursel] refresh = True - if c in [util.KEY_ESC, util.KEY_BELL]: # If Escape key or CTRL-g, we abort search + if c in [ + util.KEY_ESC, + util.KEY_BELL, + ]: # If Escape key or CTRL-g, we abort search self.torrentlist.set_minor_mode(None) self.search_state = SEARCH_EMPTY elif c in [curses.KEY_BACKSPACE, util.KEY_BACKSPACE2]: @@ -175,9 +187,9 @@ class SearchMode(InputKeyHandler): self.search_string += uchar still_matching = ( - cname.lower().find(self.search_string.lower()) == - cname.lower().find(old_search_string.lower()) and - cname.lower().find(self.search_string.lower()) != -1 + cname.lower().find(self.search_string.lower()) + == cname.lower().find(old_search_string.lower()) + and cname.lower().find(self.search_string.lower()) != -1 ) if self.search_string and not still_matching: diff --git a/deluge/ui/console/modes/torrentlist/torrentactions.py b/deluge/ui/console/modes/torrentlist/torrentactions.py index f7376f273..1d49929eb 100644 --- a/deluge/ui/console/modes/torrentlist/torrentactions.py +++ b/deluge/ui/console/modes/torrentlist/torrentactions.py @@ -26,9 +26,18 @@ from . import ACTION log = logging.getLogger(__name__) torrent_options = [ - 'max_download_speed', 'max_upload_speed', 'max_connections', 'max_upload_slots', - 'prioritize_first_last', 'sequential_download', 'is_auto_managed', 'stop_at_ratio', - 'stop_ratio', 'remove_at_ratio', 'move_completed', 'move_completed_path', + 'max_download_speed', + 'max_upload_speed', + 'max_connections', + 'max_upload_slots', + 'prioritize_first_last', + 'sequential_download', + 'is_auto_managed', + 'stop_at_ratio', + 'stop_ratio', + 'remove_at_ratio', + 'move_completed', + 'move_completed_path', ] @@ -38,7 +47,6 @@ def action_error(error, mode): def action_remove(mode=None, torrent_ids=None, **kwargs): - def do_remove(*args, **kwargs): data = args[0] if args else None if data is None or kwargs.get('close', False): @@ -53,7 +61,9 @@ def action_remove(mode=None, torrent_ids=None, **kwargs): error_msgs = '' for t_id, e_msg in errors: error_msgs += 'Error removing torrent %s : %s\n' % (t_id, e_msg) - mode.report_message('Error(s) occured when trying to delete torrent(s).', error_msgs) + mode.report_message( + 'Error(s) occured when trying to delete torrent(s).', error_msgs + ) mode.refresh() d = client.core.remove_torrents(torrent_ids, remove_data) @@ -73,7 +83,9 @@ def action_remove(mode=None, torrent_ids=None, **kwargs): if len(torrent_ids) == 1: rem_msg = '{!info!}Remove the following torrent?{!input!}' else: - rem_msg = '{!info!}Remove the following %d torrents?{!input!}' % len(torrent_ids) + rem_msg = '{!info!}Remove the following %d torrents?{!input!}' % len( + torrent_ids + ) show_max = 6 for i, (name, state) in enumerate(status): @@ -85,16 +97,23 @@ def action_remove(mode=None, torrent_ids=None, **kwargs): break popup = InputPopup( - mode, '(Esc to cancel, Enter to remove)', close_cb=do_remove, - border_off_west=1, border_off_north=1, + mode, + '(Esc to cancel, Enter to remove)', + close_cb=do_remove, + border_off_west=1, + border_off_north=1, ) popup.add_text(rem_msg) popup.add_spaces(1) popup.add_select_input( - 'remove_files', '{!info!}Torrent files:', - ['Keep', 'Remove'], [False, True], False, + 'remove_files', + '{!info!}Torrent files:', + ['Keep', 'Remove'], + [False, True], + False, ) mode.push_popup(popup) + defer.DeferredList(callbacks).addCallback(remove_dialog) @@ -128,9 +147,13 @@ def action_torrent_info(mode=None, torrent_ids=None, **kwargs): if kwargs.get('close', False): mode.pop_popup() return True + option_popup = InputPopup( - mode, ' Set Torrent Options ', close_cb=cb, - border_off_west=1, border_off_north=1, + mode, + ' Set Torrent Options ', + close_cb=cb, + border_off_west=1, + border_off_north=1, base_popup=kwargs.get('base_popup', None), ) for field in torrent_options: @@ -140,9 +163,13 @@ def action_torrent_info(mode=None, torrent_ids=None, **kwargs): option_popup.add_text_input(field, caption, value) elif isinstance(value, bool): choices = (['Yes', 'No'], [True, False], [True, False].index(value)) - option_popup.add_select_input(field, caption, choices[0], choices[1], choices[2]) + option_popup.add_select_input( + field, caption, choices[0], choices[1], choices[2] + ) elif isinstance(value, float): - option_popup.add_float_spin_input(field, caption, value=value, min_val=-1) + option_popup.add_float_spin_input( + field, caption, value=value, min_val=-1 + ) elif isinstance(value, int): option_popup.add_int_spin_input(field, caption, value=value, min_val=-1) @@ -150,7 +177,9 @@ def action_torrent_info(mode=None, torrent_ids=None, **kwargs): callbacks = [] for tid in torrents: - deferred = component.get('SessionProxy').get_torrent_status(tid, torrent_options) + deferred = component.get('SessionProxy').get_torrent_status( + tid, torrent_options + ) callbacks.append(deferred.addCallback(on_torrent_status)) callbacks = defer.DeferredList(callbacks) @@ -181,20 +210,28 @@ def torrent_action(action, *args, **kwargs): action_remove(**kwargs) return False elif action == ACTION.MOVE_STORAGE: + def do_move(res, **kwargs): if res is None or kwargs.get('close', False): mode.pop_popup() return True - if os.path.exists(res['path']['value']) and not os.path.isdir(res['path']['value']): + if os.path.exists(res['path']['value']) and not os.path.isdir( + res['path']['value'] + ): mode.report_message( 'Cannot Move Download Folder', '{!error!}%s exists and is not a directory' % res['path']['value'], ) else: log.debug('Moving %s to: %s', torrent_ids, res['path']['value']) - client.core.move_storage(torrent_ids, res['path']['value']).addErrback(action_error, mode) - popup = InputPopup(mode, 'Move Download Folder', close_cb=do_move, border_off_east=1) + client.core.move_storage(torrent_ids, res['path']['value']).addErrback( + action_error, mode + ) + + popup = InputPopup( + mode, 'Move Download Folder', close_cb=do_move, border_off_east=1 + ) popup.add_text_input('path', 'Enter path to move to:', complete=True) mode.push_popup(popup) elif action == ACTION.RECHECK: @@ -226,10 +263,14 @@ def torrent_actions_popup(mode, torrent_ids, details=False, action=None, close_c return popup = SelectablePopup( - mode, 'Torrent Actions', torrent_action, + mode, + 'Torrent Actions', + torrent_action, cb_args={'mode': mode, 'torrent_ids': torrent_ids}, - close_cb=close_cb, border_off_north=1, - border_off_west=1, border_off_east=1, + close_cb=close_cb, + border_off_north=1, + border_off_west=1, + border_off_east=1, ) popup.add_line(ACTION.PAUSE, '_Pause') popup.add_line(ACTION.RESUME, '_Resume') diff --git a/deluge/ui/console/modes/torrentlist/torrentlist.py b/deluge/ui/console/modes/torrentlist/torrentlist.py index 0af01a633..a427d65b0 100644 --- a/deluge/ui/console/modes/torrentlist/torrentlist.py +++ b/deluge/ui/console/modes/torrentlist/torrentlist.py @@ -18,7 +18,9 @@ from deluge.decorators import overrides from deluge.ui.client import client from deluge.ui.console.modes.basemode import BaseMode, mkwin from deluge.ui.console.modes.torrentlist import torrentview, torrentviewcolumns -from deluge.ui.console.modes.torrentlist.add_torrents_popup import show_torrent_add_popup +from deluge.ui.console.modes.torrentlist.add_torrents_popup import ( + show_torrent_add_popup, +) from deluge.ui.console.modes.torrentlist.filtersidebar import FilterSidebar from deluge.ui.console.modes.torrentlist.queue_mode import QueueMode from deluge.ui.console.modes.torrentlist.search_mode import SearchMode @@ -102,9 +104,10 @@ where you input something class TorrentList(BaseMode, PopupsHandler): - def __init__(self, stdscr, encoding=None): - BaseMode.__init__(self, stdscr, encoding=encoding, do_refresh=False, depend=['SessionProxy']) + BaseMode.__init__( + self, stdscr, encoding=encoding, do_refresh=False, depend=['SessionProxy'] + ) PopupsHandler.__init__(self) self.messages = deque() self.last_mark = -1 @@ -116,8 +119,11 @@ class TorrentList(BaseMode, PopupsHandler): self.config = self.consoleui.config self.sidebar = FilterSidebar(self, self.config) self.torrentview_panel = mkwin( - curses.COLOR_GREEN, curses.LINES - 1, - curses.COLS - self.sidebar.width, 0, self.sidebar.width, + curses.COLOR_GREEN, + curses.LINES - 1, + curses.COLS - self.sidebar.width, + 0, + self.sidebar.width, ) self.torrentview = torrentview.TorrentView(self, self.config) @@ -135,7 +141,9 @@ class TorrentList(BaseMode, PopupsHandler): if self.config['torrentview']['show_sidebar']: self.sidebar.show() self.sidebar.resize_window(curses.LINES - 2, self.sidebar.width) - self.torrentview_panel.resize(curses.LINES - 1, curses.COLS - self.sidebar.width) + self.torrentview_panel.resize( + curses.LINES - 1, curses.COLS - self.sidebar.width + ) self.torrentview_panel.mvwin(0, self.sidebar.width) else: self.sidebar.hide() @@ -152,7 +160,9 @@ class TorrentList(BaseMode, PopupsHandler): self.toggle_sidebar() if self.config['first_run']: - self.push_popup(MessagePopup(self, 'Welcome to Deluge', HELP_STR, width_req=0.65)) + self.push_popup( + MessagePopup(self, 'Welcome to Deluge', HELP_STR, width_req=0.65) + ) self.config['first_run'] = False self.config.save() @@ -212,8 +222,10 @@ class TorrentList(BaseMode, PopupsHandler): # Update the status bars statusbar_args = {'scr': self.stdscr, 'bottombar_help': True} if self.torrentview.curr_filter is not None: - statusbar_args['topbar'] = ('%s {!filterstatus!}Current filter: %s' - % (self.statusbars.topbar, self.torrentview.curr_filter)) + statusbar_args['topbar'] = '%s {!filterstatus!}Current filter: %s' % ( + self.statusbars.topbar, + self.torrentview.curr_filter, + ) if self.minor_mode: self.minor_mode.set_statusbar_args(statusbar_args) @@ -269,7 +281,9 @@ class TorrentList(BaseMode, PopupsHandler): return elif chr(c) == 'q': self.torrentview.update_marked(self.torrentview.cursel) - self.set_minor_mode(QueueMode(self, self.torrentview._selected_torrent_ids())) + self.set_minor_mode( + QueueMode(self, self.torrentview._selected_torrent_ids()) + ) return elif chr(c) == '/': self.set_minor_mode(SearchMode(self)) @@ -322,7 +336,9 @@ class TorrentList(BaseMode, PopupsHandler): self.consoleui.set_mode('EventView') return elif chr(c) == 'S': - self.config['torrentview']['show_sidebar'] = self.config['torrentview']['show_sidebar'] is False + self.config['torrentview']['show_sidebar'] = ( + self.config['torrentview']['show_sidebar'] is False + ) self.config.save() self.toggle_sidebar() elif chr(c) == 'l': diff --git a/deluge/ui/console/modes/torrentlist/torrentview.py b/deluge/ui/console/modes/torrentlist/torrentview.py index e81ba6c99..67de3e786 100644 --- a/deluge/ui/console/modes/torrentlist/torrentview.py +++ b/deluge/ui/console/modes/torrentlist/torrentview.py @@ -16,7 +16,11 @@ from deluge.ui.console.modes.torrentlist import torrentviewcolumns from deluge.ui.console.modes.torrentlist.torrentactions import torrent_actions_popup from deluge.ui.console.utils import curses_util as util from deluge.ui.console.utils import format_utils -from deluge.ui.console.utils.column import get_column_value, get_required_fields, torrent_data_fields +from deluge.ui.console.utils.column import ( + get_column_value, + get_required_fields, + torrent_data_fields, +) from . import ACTION @@ -85,7 +89,6 @@ for col_i, col_name in enumerate(torrentviewcolumns.column_pref_names): class TorrentView(InputKeyHandler): - def __init__(self, torrentlist, config): super(TorrentView, self).__init__() self.torrentlist = torrentlist @@ -226,6 +229,7 @@ class TorrentView(InputKeyHandler): # and if it's a string first_element = state[list(state)[0]] if field in first_element: + def sort_key(s): try: # Sort case-insensitively but preserve A>a order. @@ -250,7 +254,9 @@ class TorrentView(InputKeyHandler): result = sort_by_field(state, result, s_primary) if self.config['torrentview']['separate_complete']: - result = sorted(result, key=lambda s: state.get(s).get('progress', 0) == 100.0) + result = sorted( + result, key=lambda s: state.get(s).get('progress', 0) == 100.0 + ) return result @@ -278,7 +284,9 @@ class TorrentView(InputKeyHandler): if self.numtorrents == 0: cols = self.torrentlist.torrentview_columns() msg = 'No torrents match filter'.center(cols) - self.torrentlist.add_string(3, '{!info!}%s' % msg, scr=self.torrentlist.torrentview_panel) + self.torrentlist.add_string( + 3, '{!info!}%s' % msg, scr=self.torrentlist.torrentview_panel + ) elif self.numtorrents == 0: self.torrentlist.add_string(1, 'Waiting for torrents from core...') return @@ -290,7 +298,8 @@ class TorrentView(InputKeyHandler): format_utils.format_row( [get_column_value(name, ts) for name in self.cols_to_show], self.column_widths, - ), ts['state'], + ), + ts['state'], ) return self.cached_rows[index] @@ -321,14 +330,15 @@ class TorrentView(InputKeyHandler): colorstr = '{!%(fg)s,%(bg)s!}' % colors self.torrentlist.add_string( - currow + self.torrentlist_offset, '%s%s' % (colorstr, row[0]), - trim=False, scr=self.torrentlist.torrentview_panel, + currow + self.torrentlist_offset, + '%s%s' % (colorstr, row[0]), + trim=False, + scr=self.torrentlist.torrentview_panel, ) def update(self, refresh=False): d = component.get('SessionProxy').get_torrents_status( - self.filter_dict, - self.status_fields, + self.filter_dict, self.status_fields ) d.addCallback(self.update_state, refresh=refresh) @@ -338,15 +348,19 @@ class TorrentView(InputKeyHandler): changed = None for col in default_columns: if col not in self.config['torrentview']['columns']: - changed = self.config['torrentview']['columns'][col] = default_columns[col] + changed = self.config['torrentview']['columns'][col] = default_columns[ + col + ] if changed: self.config.save() self.cols_to_show = [ - col for col in sorted( + col + for col in sorted( self.config['torrentview']['columns'], key=lambda k: self.config['torrentview']['columns'][k]['order'], - ) if self.config['torrentview']['columns'][col]['visible'] + ) + if self.config['torrentview']['columns'][col]['visible'] ] self.status_fields = get_required_fields(self.cols_to_show) @@ -364,7 +378,10 @@ class TorrentView(InputKeyHandler): self.update_columns() def update_columns(self): - self.column_widths = [self.config['torrentview']['columns'][col]['width'] for col in self.cols_to_show] + self.column_widths = [ + self.config['torrentview']['columns'][col]['width'] + for col in self.cols_to_show + ] requested_width = sum(width for width in self.column_widths if width >= 0) cols = self.torrentlist.torrentview_columns() @@ -391,7 +408,7 @@ class TorrentView(InputKeyHandler): # Trim the column if it's too long to fit if len(ccol) > width: - ccol = ccol[:width - 1] + ccol = ccol[: width - 1] # Padding ccol += ' ' * (width - len(ccol)) @@ -428,9 +445,12 @@ class TorrentView(InputKeyHandler): def on_close(**kwargs): if added: self.marked.pop() + torrent_actions_popup( - self.torrentlist, self._selected_torrent_ids(), - action=ACTION.REMOVE, close_cb=on_close, + self.torrentlist, + self._selected_torrent_ids(), + action=ACTION.REMOVE, + close_cb=on_close, ) elif c in [curses.KEY_ENTER, util.KEY_ENTER2] and self.numtorrents: added = self.update_marked(self.cursel) @@ -439,7 +459,12 @@ class TorrentView(InputKeyHandler): if added: self.marked.remove(self.cursel) - torrent_actions_popup(self.torrentlist, self._selected_torrent_ids(), details=True, close_cb=on_close) + torrent_actions_popup( + self.torrentlist, + self._selected_torrent_ids(), + details=True, + close_cb=on_close, + ) self.torrentlist.refresh() elif c == ord('j'): affected_lines = self._scroll_up(1) @@ -466,7 +491,11 @@ class TorrentView(InputKeyHandler): added = self.update_marked(self.cursel, clear=True) else: self.last_mark = -1 - torrent_actions_popup(self.torrentlist, self._selected_torrent_ids(), action=ACTION.TORRENT_OPTIONS) + torrent_actions_popup( + self.torrentlist, + self._selected_torrent_ids(), + action=ACTION.TORRENT_OPTIONS, + ) elif c in [ord('>'), ord('<')]: try: i = self.cols_to_show.index(self.config['torrentview']['sort_primary']) diff --git a/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py b/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py index 96da7ffad..9dff84306 100644 --- a/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py +++ b/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py @@ -26,17 +26,39 @@ Control column visibilty with the following actions: """ column_pref_names = [ - 'queue', 'name', 'size', 'downloaded', 'uploaded', 'remaining', 'state', - 'progress', 'seeds', 'peers', 'seeds_peers_ratio', - 'download_speed', 'upload_speed', 'max_download_speed', 'max_upload_speed', - 'eta', 'ratio', 'avail', 'time_added', 'completed_time', 'last_seen_complete', - 'tracker', 'download_location', 'active_time', 'seeding_time', 'finished_time', - 'time_since_transfer', 'shared', 'owner', + 'queue', + 'name', + 'size', + 'downloaded', + 'uploaded', + 'remaining', + 'state', + 'progress', + 'seeds', + 'peers', + 'seeds_peers_ratio', + 'download_speed', + 'upload_speed', + 'max_download_speed', + 'max_upload_speed', + 'eta', + 'ratio', + 'avail', + 'time_added', + 'completed_time', + 'last_seen_complete', + 'tracker', + 'download_location', + 'active_time', + 'seeding_time', + 'finished_time', + 'time_since_transfer', + 'shared', + 'owner', ] class ColumnAndWidth(CheckedPlusInput): - def __init__(self, parent, name, message, child, on_width_func, **kwargs): CheckedPlusInput.__init__(self, parent, name, message, child, **kwargs) self.on_width_func = on_width_func @@ -53,19 +75,19 @@ class ColumnAndWidth(CheckedPlusInput): class TorrentViewColumns(InputPopup): - def __init__(self, torrentlist): self.torrentlist = torrentlist self.torrentview = torrentlist.torrentview title = 'Visible columns (Esc to exit)' InputPopup.__init__( - self, torrentlist, title, close_cb=self._do_set_column_visibility, + self, + torrentlist, + title, + close_cb=self._do_set_column_visibility, immediate_action=True, height_req=len(column_pref_names) - 5, - width_req=max( - len(col) for col in column_pref_names + [title] - ) + 14, + width_req=max(len(col) for col in column_pref_names + [title]) + 14, border_off_west=1, allow_rearrange=True, ) @@ -76,21 +98,37 @@ class TorrentViewColumns(InputPopup): for colpref_name in column_pref_names: col = self.torrentview.config['torrentview']['columns'][colpref_name] width_spin = IntSpinInput( - self, colpref_name + '_ width', '', self.move, col['width'], - min_val=-1, max_val=99, fmt='%2d', + self, + colpref_name + '_ width', + '', + self.move, + col['width'], + min_val=-1, + max_val=99, + fmt='%2d', ) def on_width_func(name, width): self.torrentview.config['torrentview']['columns'][name]['width'] = width - self._add_input(ColumnAndWidth( - self, colpref_name, torrent_data_fields[colpref_name]['name'], width_spin, - on_width_func, - checked=col['visible'], checked_char='*', msg_fmt=msg_fmt, - show_usage_hints=False, child_always_visible=True, - )) + self._add_input( + ColumnAndWidth( + self, + colpref_name, + torrent_data_fields[colpref_name]['name'], + width_spin, + on_width_func, + checked=col['visible'], + checked_char='*', + msg_fmt=msg_fmt, + show_usage_hints=False, + child_always_visible=True, + ) + ) - def _do_set_column_visibility(self, data=None, state_changed=True, close=True, **kwargs): + def _do_set_column_visibility( + self, data=None, state_changed=True, close=True, **kwargs + ): if close: self.torrentlist.pop_popup() return @@ -98,8 +136,12 @@ class TorrentViewColumns(InputPopup): return for key, value in data.items(): - self.torrentview.config['torrentview']['columns'][key]['visible'] = value['value'] - self.torrentview.config['torrentview']['columns'][key]['order'] = value['order'] + self.torrentview.config['torrentview']['columns'][key]['visible'] = value[ + 'value' + ] + self.torrentview.config['torrentview']['columns'][key]['order'] = value[ + 'order' + ] self.torrentview.config.save() self.torrentview.on_config_changed() @@ -108,7 +150,13 @@ class TorrentViewColumns(InputPopup): @overrides(InputPopup) def handle_read(self, c): if c == ord('h'): - popup = MessagePopup(self.torrentlist, 'Help', COLUMN_VIEW_HELP_STR, width_req=70, border_off_west=1) + popup = MessagePopup( + self.torrentlist, + 'Help', + COLUMN_VIEW_HELP_STR, + width_req=70, + border_off_west=1, + ) self.torrentlist.push_popup(popup) return util.ReadState.READ return InputPopup.handle_read(self, c) diff --git a/deluge/ui/console/parser.py b/deluge/ui/console/parser.py index 35477c5bc..27f248564 100644 --- a/deluge/ui/console/parser.py +++ b/deluge/ui/console/parser.py @@ -21,7 +21,6 @@ class OptionParserError(Exception): class ConsoleBaseParser(argparse.ArgumentParser): - def format_help(self): """Differs from ArgumentParser.format_help by adding the raw epilog as formatted in the string. Default bahavior mangles the formatting. @@ -38,7 +37,6 @@ class ConsoleBaseParser(argparse.ArgumentParser): class ConsoleCommandParser(ConsoleBaseParser): - def _split_args(self, args): command_options = [] for a in args: @@ -72,6 +70,7 @@ class ConsoleCommandParser(ConsoleBaseParser): argparse.Namespace: The parsed arguments. """ from deluge.ui.ui_entry import AMBIGUOUS_CMD_ARGS + self.base_parser.parse_known_ui_args(args, withhold=AMBIGUOUS_CMD_ARGS) multi_command = self._split_args(args) @@ -107,7 +106,6 @@ class ConsoleCommandParser(ConsoleBaseParser): class OptionParser(ConsoleBaseParser): - def __init__(self, **kwargs): super(OptionParser, self).__init__(**kwargs) self.formatter = ConsoleColorFormatter() diff --git a/deluge/ui/console/utils/colors.py b/deluge/ui/console/utils/colors.py index a6523fd06..c07f3e4d4 100644 --- a/deluge/ui/console/utils/colors.py +++ b/deluge/ui/console/utils/colors.py @@ -33,9 +33,7 @@ colors = [ ] # {(fg, bg): pair_number, ...} -color_pairs = { - ('white', 'black'): 0, # Special case, can't be changed -} +color_pairs = {('white', 'black'): 0} # Special case, can't be changed # Some default color schemes schemes = { @@ -101,7 +99,13 @@ def init_colors(): counter = 1 for fg in colors: for bg in colors: - counter = define_pair(counter, fg[6:].lower(), bg[6:].lower(), getattr(curses, fg), getattr(curses, bg)) + counter = define_pair( + counter, + fg[6:].lower(), + bg[6:].lower(), + getattr(curses, fg), + getattr(curses, bg), + ) counter = define_pair(counter, 'white', 'grey', curses.COLOR_WHITE, 241) counter = define_pair(counter, 'black', 'whitegrey', curses.COLOR_BLACK, 249) @@ -124,7 +128,7 @@ def replace_tabs(line): """ for i in range(line.count(tab_char)): - tab_length = 8 - (len(line[:line.find(tab_char)]) % 8) + tab_length = 8 - (len(line[: line.find(tab_char)]) % 8) line = line.replace(tab_char, b' ' * tab_length, 1) return line @@ -187,14 +191,21 @@ def parse_color_string(s, encoding='UTF-8'): while s.find(color_tag_start) != -1: begin = s.find(color_tag_start) if begin > 0: - ret.append((curses.color_pair(color_pairs[(schemes['input'][0], schemes['input'][1])]), s[:begin])) + ret.append( + ( + curses.color_pair( + color_pairs[(schemes['input'][0], schemes['input'][1])] + ), + s[:begin], + ) + ) end = s.find(color_tag_end) if end == -1: raise BadColorString('Missing closing "!}"') # Get a list of attributes in the bracketed section - attrs = s[begin + 2:end].split(',') + attrs = s[begin + 2 : end].split(',') if len(attrs) == 1 and not attrs[0].strip(' '): raise BadColorString('No description in {! !}') @@ -230,7 +241,10 @@ def parse_color_string(s, encoding='UTF-8'): if attrs[0][0] in ['+', '-']: # Color is not given, so use last color if last_color_attr is None: - raise BadColorString('No color value given when no previous color was used!: %s' % (attrs[0])) + raise BadColorString( + 'No color value given when no previous color was used!: %s' + % (attrs[0]) + ) color_pair = last_color_attr for i, attr in enumerate(attrs): if attr[1:] not in attrlist: @@ -268,10 +282,10 @@ def parse_color_string(s, encoding='UTF-8'): next_begin = s.find(color_tag_start, end) if next_begin == -1: - ret.append((color_pair, replace_tabs(s[end + 2:]))) + ret.append((color_pair, replace_tabs(s[end + 2 :]))) break else: - ret.append((color_pair, replace_tabs(s[end + 2:next_begin]))) + ret.append((color_pair, replace_tabs(s[end + 2 : next_begin]))) s = s[next_begin:] if not ret: @@ -289,7 +303,6 @@ class ConsoleColorFormatter(object): '<torrent-id>': '{!green!}%s{!input!}', '<torrent>': '{!green!}%s{!input!}', '<command>': '{!green!}%s{!input!}', - '<state>': '{!yellow!}%s{!input!}', '\\.\\.\\.': '{!yellow!}%s{!input!}', '\\s\\*\\s': '{!blue!}%s{!input!}', @@ -297,7 +310,6 @@ class ConsoleColorFormatter(object): # "(\-[a-zA-Z0-9])": "{!red!}%s{!input!}", '--[_\\-a-zA-Z0-9]+': '{!green!}%s{!input!}', '(\\[|\\])': '{!info!}%s{!input!}', - '<tab>': '{!white!}%s{!input!}', '[_A-Z]{3,}': '{!cyan!}%s{!input!}', '<key>': '{!cyan!}%s{!input!}', @@ -305,12 +317,12 @@ class ConsoleColorFormatter(object): 'usage:': '{!info!}%s{!input!}', '<download-folder>': '{!yellow!}%s{!input!}', '<torrent-file>': '{!green!}%s{!input!}', - } def format_colors(self, string): def r(repl): return lambda s: repl % s.group() + for key, replacement in self.replace_dict.items(): string = re.sub(key, r(replacement), string) return string diff --git a/deluge/ui/console/utils/column.py b/deluge/ui/console/utils/column.py index cf3d64be9..47b0b2433 100644 --- a/deluge/ui/console/utils/column.py +++ b/deluge/ui/console/utils/column.py @@ -30,33 +30,26 @@ formatters = { 'tracker': None, 'download_location': None, 'owner': None, - 'progress_state': format_utils.format_progress, 'progress': format_utils.format_progress, - 'size': format_utils.format_size, 'downloaded': format_utils.format_size, 'uploaded': format_utils.format_size, 'remaining': format_utils.format_size, - 'ratio': format_utils.format_float, 'avail': format_utils.format_float, 'seeds_peers_ratio': format_utils.format_float, - 'download_speed': format_utils.format_speed, 'upload_speed': format_utils.format_speed, 'max_download_speed': format_utils.format_speed, 'max_upload_speed': format_utils.format_speed, - 'peers': format_utils.format_seeds_peers, 'seeds': format_utils.format_seeds_peers, - 'time_added': deluge.common.fdate, 'seeding_time': format_utils.format_time, 'active_time': format_utils.format_time, 'time_since_transfer': format_utils.format_date_dash, 'finished_time': deluge.common.ftime, - 'last_seen_complete': format_utils.format_date_never, 'completed_time': format_utils.format_date_dash, 'eta': format_utils.format_time, diff --git a/deluge/ui/console/utils/format_utils.py b/deluge/ui/console/utils/format_utils.py index 9d609c4fb..6b6d8d483 100644 --- a/deluge/ui/console/utils/format_utils.py +++ b/deluge/ui/console/utils/format_utils.py @@ -142,7 +142,7 @@ def trim_string(string, w, have_dbls): chrs.append('.') return '%s ' % (''.join(chrs)) else: - return '%s ' % (string[0:w - 1]) + return '%s ' % (string[0 : w - 1]) def format_column(col, lim): @@ -161,7 +161,9 @@ def format_column(col, lim): def format_row(row, column_widths): - return ''.join([format_column(row[i], column_widths[i]) for i in range(0, len(row))]) + return ''.join( + [format_column(row[i], column_widths[i]) for i in range(0, len(row))] + ) _strip_re = re.compile(r'\{!.*?!\}') @@ -183,7 +185,7 @@ def shorten_hash(tid, space_left, min_width=13, placeholder='...'): if space_left >= min_width: mid = len(tid) // 2 trim, remain = divmod(len(tid) + len(placeholder) - space_left, 2) - return tid[0: mid - trim] + placeholder + tid[mid + trim + remain:] + return tid[0 : mid - trim] + placeholder + tid[mid + trim + remain :] else: # Justity the tid so it is completely on the next line. return tid.rjust(len(tid) + space_left) @@ -218,7 +220,7 @@ def wrap_string(string, width, min_lines=0, strip_colors=True): m = _format_code.search(remove_formatting(s)) if m: if m.group(1).startswith('indent:'): - indent = m.group(1)[len('indent:'):] + indent = m.group(1)[len('indent:') :] elif m.group(1).startswith('indent_pos:'): begin = m.start(0) indent = ' ' * begin diff --git a/deluge/ui/console/widgets/fields.py b/deluge/ui/console/widgets/fields.py index e5b9ece76..ae6da34e9 100644 --- a/deluge/ui/console/widgets/fields.py +++ b/deluge/ui/console/widgets/fields.py @@ -18,7 +18,11 @@ from deluge.decorators import overrides from deluge.ui.console.modes.basemode import InputKeyHandler from deluge.ui.console.utils import colors from deluge.ui.console.utils import curses_util as util -from deluge.ui.console.utils.format_utils import delete_alt_backspace, remove_formatting, wrap_string +from deluge.ui.console.utils.format_utils import ( + delete_alt_backspace, + remove_formatting, + wrap_string, +) try: import curses @@ -29,7 +33,6 @@ log = logging.getLogger(__name__) class BaseField(InputKeyHandler): - def __init__(self, parent=None, name=None, selectable=True, **kwargs): super(BaseField, self).__init__() self.name = name @@ -70,7 +73,12 @@ class BaseField(InputKeyHandler): def build_fmt_string(self, focused, active, value_key='msg', **kwargs): color_key, font_key = self.get_fmt_keys(focused, active, **kwargs) - return '{!%%(%s)s,%%(%s)s!}%%(%s)s{!%%(%s)s!}' % (color_key, font_key, value_key, 'color_end') + return '{!%%(%s)s,%%(%s)s!}%%(%s)s{!%%(%s)s!}' % ( + color_key, + font_key, + value_key, + 'color_end', + ) def depend_skip(self): return False @@ -97,14 +105,12 @@ class BaseField(InputKeyHandler): class NoInputField(BaseField): - @overrides(BaseField) def has_input(self): return False class InputField(BaseField): - def __init__(self, parent, name, message, format_default=None, **kwargs): BaseField.__init__(self, parent=parent, name=name, **kwargs) self.format_default = format_default @@ -140,7 +146,6 @@ class InputField(BaseField): class Header(NoInputField): - def __init__(self, parent, header, space_above, space_below, **kwargs): if 'name' not in kwargs: kwargs['name'] = header @@ -166,7 +171,6 @@ class Header(NoInputField): class InfoField(NoInputField): - def __init__(self, parent, name, label, value, **kwargs): NoInputField.__init__(self, parent=parent, name=name, **kwargs) self.label = label @@ -188,17 +192,27 @@ class InfoField(NoInputField): class CheckedInput(InputField): - def __init__( - self, parent, name, message, checked=False, checked_char='X', unchecked_char=' ', - checkbox_format='[%s] ', **kwargs + self, + parent, + name, + message, + checked=False, + checked_char='X', + unchecked_char=' ', + checkbox_format='[%s] ', + **kwargs ): InputField.__init__(self, parent, name, message, **kwargs) self.set_value(checked) - self.fmt_keys.update({ - 'msg': message, 'checkbox_format': checkbox_format, - 'unchecked_char': unchecked_char, 'checked_char': checked_char, - }) + self.fmt_keys.update( + { + 'msg': message, + 'checkbox_format': checkbox_format, + 'unchecked_char': unchecked_char, + 'checked_char': checked_char, + } + ) self.set_fmt_key('font_checked', 'font', kwargs) self.set_fmt_key('font_unfocused_checked', 'font_checked', kwargs) self.set_fmt_key('font_active_checked', 'font_active', kwargs) @@ -206,7 +220,9 @@ class CheckedInput(InputField): self.set_fmt_key('color_checked', 'color', kwargs) self.set_fmt_key('color_active_checked', 'color_active', kwargs) self.set_fmt_key('color_unfocused_checked', 'color_checked', kwargs) - self.set_fmt_key('color_unfocused_active_checked', 'color_unfocused_active', kwargs) + self.set_fmt_key( + 'color_unfocused_active_checked', 'color_unfocused_active', kwargs + ) @property def checked(self): @@ -214,7 +230,9 @@ class CheckedInput(InputField): @overrides(BaseField) def get_fmt_keys(self, focused, active, **kwargs): - color_key, font_key = super(CheckedInput, self).get_fmt_keys(focused, active, **kwargs) + color_key, font_key = super(CheckedInput, self).get_fmt_keys( + focused, active, **kwargs + ) if self.checked: color_key += '_checked' font_key += '_checked' @@ -256,10 +274,16 @@ class CheckedInput(InputField): class CheckedPlusInput(CheckedInput): - def __init__( - self, parent, name, message, child, child_always_visible=False, - show_usage_hints=True, msg_fmt='%s ', **kwargs + self, + parent, + name, + message, + child, + child_always_visible=False, + show_usage_hints=True, + msg_fmt='%s ', + **kwargs ): CheckedInput.__init__(self, parent, name, message, **kwargs) self.child = child @@ -273,28 +297,42 @@ class CheckedPlusInput(CheckedInput): return max(2 if self.show_usage_hints else 1, self.child.height) @overrides(CheckedInput) - def render(self, screen, row, width=None, active=False, focused=False, col=0, **kwargs): + def render( + self, screen, row, width=None, active=False, focused=False, col=0, **kwargs + ): isact = active and not self.child_active - CheckedInput.render(self, screen, row, width=width, active=isact, focused=focused, col=col) + CheckedInput.render( + self, screen, row, width=width, active=isact, focused=focused, col=col + ) rows = 1 - if self.show_usage_hints and (self.child_always_visible or (active and self.checked)): + if self.show_usage_hints and ( + self.child_always_visible or (active and self.checked) + ): msg = '(esc to leave)' if self.child_active else '(right arrow to edit)' self.parent.add_string(row + 1, msg, scr=screen, col=col, pad=False) rows += 1 - msglen = len(self.msg_fmt % colors.strip_colors(self.build_msg_string(focused, active))) + msglen = len( + self.msg_fmt % colors.strip_colors(self.build_msg_string(focused, active)) + ) # show child if self.checked or self.child_always_visible: crows = self.child.render( - screen, row, width=width - msglen, + screen, + row, + width=width - msglen, active=self.child_active and active, - col=col + msglen, cursor_offset=msglen, + col=col + msglen, + cursor_offset=msglen, ) rows = max(rows, crows) else: self.parent.add_string( - row, '(enable to view/edit value)', scr=screen, - col=col + msglen, pad=False, + row, + '(enable to view/edit value)', + scr=screen, + col=col + msglen, + pad=False, ) return rows @@ -320,10 +358,20 @@ class CheckedPlusInput(CheckedInput): class IntSpinInput(InputField): - def __init__( - self, parent, name, message, move_func, value, min_val=None, max_val=None, - inc_amt=1, incr_large=10, strict_validation=False, fmt='%d', **kwargs + self, + parent, + name, + message, + move_func, + value, + min_val=None, + max_val=None, + inc_amt=1, + incr_large=10, + strict_validation=False, + fmt='%d', + **kwargs ): InputField.__init__(self, parent, name, message, **kwargs) self.convert_func = int @@ -335,7 +383,9 @@ class IntSpinInput(InputField): self.last_valid_value = self.value self.last_active = False self.cursor = len(self.valstr) - self.cursoff = colors.get_line_width(self.message) + 3 # + 4 for the " [ " in the rendered string + self.cursoff = ( + colors.get_line_width(self.message) + 3 + ) # + 4 for the " [ " in the rendered string self.move_func = move_func self.strict_validation = strict_validation self.min_val = min_val @@ -351,11 +401,15 @@ class IntSpinInput(InputField): return value @overrides(InputField) - def render(self, screen, row, active=False, focused=True, col=0, cursor_offset=0, **kwargs): + def render( + self, screen, row, active=False, focused=True, col=0, cursor_offset=0, **kwargs + ): if active: self.last_active = True elif self.last_active: - self.set_value(self.valstr, validate=True, value_on_fail=self.last_valid_value) + self.set_value( + self.valstr, validate=True, value_on_fail=self.last_valid_value + ) self.last_active = False fmt_str = self.build_fmt_string(focused, active, value_key='value') @@ -368,11 +422,12 @@ class IntSpinInput(InputField): value_format += '[ ' + fmt_str + ' ]' self.parent.add_string( - row, value_format % dict( - {'msg': self.message, 'value': '%s' % self.valstr}, - **self.fmt_keys - ), - scr=screen, col=col, pad=False, + row, + value_format + % dict({'msg': self.message, 'value': '%s' % self.valstr}, **self.fmt_keys), + scr=screen, + col=col, + pad=False, ) if active: if focused: @@ -404,9 +459,12 @@ class IntSpinInput(InputField): self.cursor = len(self.valstr) elif c == curses.KEY_BACKSPACE or c == util.KEY_BACKSPACE2: if self.valstr and self.cursor > 0: - new_val = self.valstr[:self.cursor - 1] + self.valstr[self.cursor:] + new_val = self.valstr[: self.cursor - 1] + self.valstr[self.cursor :] self.set_value( - new_val, validate=False, cursor=self.cursor - 1, cursor_on_fail=True, + new_val, + validate=False, + cursor=self.cursor - 1, + cursor_on_fail=True, value_on_fail=self.valstr if self.strict_validation else None, ) elif c == curses.KEY_DC: # Del @@ -414,39 +472,74 @@ class IntSpinInput(InputField): if self.cursor == 0: new_val = self.valstr[1:] else: - new_val = self.valstr[:self.cursor] + self.valstr[self.cursor + 1:] + new_val = ( + self.valstr[: self.cursor] + self.valstr[self.cursor + 1 :] + ) self.set_value( - new_val, validate=False, cursor=False, - value_on_fail=self.valstr if self.strict_validation else None, cursor_on_fail=True, + new_val, + validate=False, + cursor=False, + value_on_fail=self.valstr if self.strict_validation else None, + cursor_on_fail=True, ) elif c == ord('-'): # minus self.set_value( - self.value - 1, validate=True, cursor=True, cursor_on_fail=True, - value_on_fail=self.value, on_invalid=self.value, + self.value - 1, + validate=True, + cursor=True, + cursor_on_fail=True, + value_on_fail=self.value, + on_invalid=self.value, ) elif c == ord('+'): # plus self.set_value( - self.value + 1, validate=True, cursor=True, cursor_on_fail=True, - value_on_fail=self.value, on_invalid=self.value, + self.value + 1, + validate=True, + cursor=True, + cursor_on_fail=True, + value_on_fail=self.value, + on_invalid=self.value, ) elif util.is_int_chr(c): if self.strict_validation: - new_val = self.valstr[:self.cursor - 1] + chr(c) + self.valstr[self.cursor - 1:] + new_val = ( + self.valstr[: self.cursor - 1] + + chr(c) + + self.valstr[self.cursor - 1 :] + ) self.set_value( - new_val, validate=True, cursor=self.cursor + 1, - value_on_fail=self.valstr, on_invalid=self.value, + new_val, + validate=True, + cursor=self.cursor + 1, + value_on_fail=self.valstr, + on_invalid=self.value, ) else: minus_place = self.valstr.find('-') if self.cursor > minus_place: - new_val = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:] - self.set_value(new_val, validate=True, cursor=self.cursor + 1, on_invalid=self.value) + new_val = ( + self.valstr[: self.cursor] + chr(c) + self.valstr[self.cursor :] + ) + self.set_value( + new_val, + validate=True, + cursor=self.cursor + 1, + on_invalid=self.value, + ) else: return util.ReadState.IGNORED return util.ReadState.READ @overrides(BaseField) - def set_value(self, val, cursor=True, validate=False, cursor_on_fail=False, value_on_fail=None, on_invalid=None): + def set_value( + self, + val, + cursor=True, + validate=False, + cursor_on_fail=False, + value_on_fail=None, + on_invalid=None, + ): value = None try: value = self.convert_func(val) @@ -467,8 +560,11 @@ class IntSpinInput(InputField): except ValueError: if value_on_fail is not None: self.set_value( - value_on_fail, cursor=cursor, cursor_on_fail=cursor_on_fail, - validate=validate, on_invalid=on_invalid, + value_on_fail, + cursor=cursor, + cursor_on_fail=cursor_on_fail, + validate=validate, + on_invalid=on_invalid, ) return self.value = None @@ -477,6 +573,7 @@ class IntSpinInput(InputField): self.cursor = cursor except TypeError: import traceback + log.warning('TypeError: %s', ''.join(traceback.format_exc())) else: if cursor is True: @@ -486,7 +583,6 @@ class IntSpinInput(InputField): class FloatSpinInput(IntSpinInput): - def __init__(self, parent, message, name, move_func, value, precision=1, **kwargs): self.precision = precision IntSpinInput.__init__(self, parent, message, name, move_func, value, **kwargs) @@ -504,17 +600,24 @@ class FloatSpinInput(IntSpinInput): point_place = self.valstr.find('.') if point_place >= 0: return util.ReadState.READ - new_val = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:] + new_val = self.valstr[: self.cursor] + chr(c) + self.valstr[self.cursor :] self.set_value(new_val, validate=True, cursor=self.cursor + 1) else: return IntSpinInput.handle_read(self, c) class SelectInput(InputField): - def __init__( - self, parent, name, message, opts, vals, active_index, active_default=False, - require_select_action=True, **kwargs + self, + parent, + name, + message, + opts, + vals, + active_index, + active_default=False, + require_select_action=True, + **kwargs ): InputField.__init__(self, parent, name, message, **kwargs) self.opts = opts @@ -529,21 +632,33 @@ class SelectInput(InputField): self.set_fmt_key('font_selected', font_selected, kwargs) self.set_fmt_key('font_active_selected', 'font_selected', kwargs) self.set_fmt_key('font_unfocused_selected', 'font_selected', kwargs) - self.set_fmt_key('font_unfocused_active_selected', 'font_active_selected', kwargs) + self.set_fmt_key( + 'font_unfocused_active_selected', 'font_active_selected', kwargs + ) self.set_fmt_key('color_selected', 'color', kwargs) self.set_fmt_key('color_active_selected', 'color_active', kwargs) self.set_fmt_key('color_unfocused_selected', 'color_selected', kwargs) - self.set_fmt_key('color_unfocused_active_selected', 'color_unfocused_active', kwargs) + self.set_fmt_key( + 'color_unfocused_active_selected', 'color_unfocused_active', kwargs + ) self.set_fmt_key('color_default_value', 'magenta,black', kwargs) self.set_fmt_key('color_default_value', 'magenta,black') self.set_fmt_key('color_default_value_active', 'magentadark,white') self.set_fmt_key('color_default_value_selected', 'color_default_value', kwargs) self.set_fmt_key('color_default_value_unfocused', 'color_default_value', kwargs) - self.set_fmt_key('color_default_value_unfocused_selected', 'color_default_value_selected', kwargs) + self.set_fmt_key( + 'color_default_value_unfocused_selected', + 'color_default_value_selected', + kwargs, + ) self.set_fmt_key('color_default_value_active_selected', 'magentadark,white') - self.set_fmt_key('color_default_value_unfocused_active_selected', 'color_unfocused_active', kwargs) + self.set_fmt_key( + 'color_default_value_unfocused_active_selected', + 'color_unfocused_active', + kwargs, + ) @property def height(self): @@ -551,7 +666,9 @@ class SelectInput(InputField): @overrides(BaseField) def get_fmt_keys(self, focused, active, selected=False, **kwargs): - color_key, font_key = super(SelectInput, self).get_fmt_keys(focused, active, **kwargs) + color_key, font_key = super(SelectInput, self).get_fmt_keys( + focused, active, **kwargs + ) if selected: color_key += '_selected' font_key += '_selected' @@ -569,7 +686,9 @@ class SelectInput(InputField): fmt_args = {'selected': i == self.selected_index} if i == self.default_option: fmt_args['color_key'] = 'color_default_value' - fmt = self.build_fmt_string(focused, (i == self.active_index) and active, **fmt_args) + fmt = self.build_fmt_string( + focused, (i == self.active_index) and active, **fmt_args + ) string = '[%s]' % (fmt % self.fmt_keys) self.parent.add_string(row, string, scr=screen, col=off, pad=False) off += len(opt) + 3 @@ -609,10 +728,17 @@ class SelectInput(InputField): class TextInput(InputField): - def __init__( - self, parent, name, message, move_func, width, value, complete=False, - activate_input=False, **kwargs + self, + parent, + name, + message, + move_func, + width, + value, + complete=False, + activate_input=False, + **kwargs ): InputField.__init__(self, parent, name, message, **kwargs) self.move_func = move_func @@ -649,7 +775,7 @@ class TextInput(InputField): c_pos_abs = len(self.value) - cursor_width new_cur = c_pos_abs + 1 self.value_offset = new_cur - vstr = self.value[self.value_offset:] + vstr = self.value[self.value_offset :] if len(vstr) > cursor_width: vstr = vstr[:cursor_width] @@ -660,7 +786,7 @@ class TextInput(InputField): vstr = self.value.ljust(cursor_width) else: self.value_offset = min(self.value_offset, self.cursor) - vstr = self.value[self.value_offset:] + vstr = self.value[self.value_offset :] if len(vstr) > cursor_width: vstr = vstr[:cursor_width] vstr = vstr.ljust(cursor_width) @@ -679,7 +805,17 @@ class TextInput(InputField): return min(width - 1 + col, x_pos) @overrides(InputField) - def render(self, screen, row, width=None, active=False, focused=True, col=0, cursor_offset=0, **kwargs): + def render( + self, + screen, + row, + width=None, + active=False, + focused=True, + col=0, + cursor_offset=0, + **kwargs + ): if not self.value and not active and len(self.default_value) != 0: self.value = self.default_value self.cursor = len(self.value) @@ -692,20 +828,30 @@ class TextInput(InputField): if active: if self.opts: - self.parent.add_string(row + 1, self.opts[self.opt_off:], scr=screen, col=col, pad=False) + self.parent.add_string( + row + 1, self.opts[self.opt_off :], scr=screen, col=col, pad=False + ) if focused and self.input_active: - util.safe_curs_set(util.Curser.NORMAL) # Make cursor visible when text field is focused + util.safe_curs_set( + util.Curser.NORMAL + ) # Make cursor visible when text field is focused x_pos = self.calculate_cursor_pos(width, col) self.move_func(row, x_pos) fmt = '{!black,white,bold!}%s' - if self.format_default and len(self.value) != 0 and self.value == self.default_value: + if ( + self.format_default + and len(self.value) != 0 + and self.value == self.default_value + ): fmt = '{!magenta,white!}%s' if not active or not focused or self.input_active: fmt = '{!white,grey,bold!}%s' - self.parent.add_string(row, fmt % vstr, scr=screen, col=col, pad=False, trim=False) + self.parent.add_string( + row, fmt % vstr, scr=screen, col=col, pad=False, trim=False + ) return self.height @overrides(BaseField) @@ -723,8 +869,12 @@ class TextInput(InputField): if self.activate_input: if not self.input_active: if c in [ - curses.KEY_LEFT, curses.KEY_RIGHT, curses.KEY_HOME, - curses.KEY_END, curses.KEY_ENTER, util.KEY_ENTER2, + curses.KEY_LEFT, + curses.KEY_RIGHT, + curses.KEY_HOME, + curses.KEY_END, + curses.KEY_ENTER, + util.KEY_ENTER2, ]: self.input_active = True return util.ReadState.READ @@ -752,7 +902,9 @@ class TextInput(InputField): # now find previous double space, best guess at a split point # in future could keep opts unjoined to get this really right self.opt_off = self.opts.rfind(' ', 0, self.opt_off) + 2 - if second_hit and self.opt_off == prev: # double tap and we're at the end + if ( + second_hit and self.opt_off == prev + ): # double tap and we're at the end self.opt_off = 0 else: opts = self.do_complete(self.value) @@ -766,7 +918,9 @@ class TextInput(InputField): self.value = prefix self.cursor = len(prefix) - if len(opts) > 1 and second_hit: # display multiple options on second tab hit + if ( + len(opts) > 1 and second_hit + ): # display multiple options on second tab hit sp = self.value.rfind(os.sep) + 1 self.opts = ' '.join([o[sp:] for o in opts]) @@ -783,13 +937,16 @@ class TextInput(InputField): # Delete a character in the input string based on cursor position elif c == curses.KEY_BACKSPACE or c == util.KEY_BACKSPACE2: if self.value and self.cursor > 0: - self.value = self.value[:self.cursor - 1] + self.value[self.cursor:] + self.value = self.value[: self.cursor - 1] + self.value[self.cursor :] self.cursor -= 1 - elif c == [util.KEY_ESC, util.KEY_BACKSPACE2] or c == [util.KEY_ESC, curses.KEY_BACKSPACE]: + elif c == [util.KEY_ESC, util.KEY_BACKSPACE2] or c == [ + util.KEY_ESC, + curses.KEY_BACKSPACE, + ]: self.value, self.cursor = delete_alt_backspace(self.value, self.cursor) elif c == curses.KEY_DC: if self.value and self.cursor < len(self.value): - self.value = self.value[:self.cursor] + self.value[self.cursor + 1:] + self.value = self.value[: self.cursor] + self.value[self.cursor + 1 :] elif c > 31 and c < 256: # Emulate getwch stroke = chr(c) @@ -805,7 +962,9 @@ class TextInput(InputField): self.value += uchar else: # Insert into string - self.value = self.value[:self.cursor] + uchar + self.value[self.cursor:] + self.value = ( + self.value[: self.cursor] + uchar + self.value[self.cursor :] + ) # Move the cursor forward self.cursor += 1 @@ -853,8 +1012,9 @@ class TextInput(InputField): class ComboInput(InputField): - - def __init__(self, parent, name, message, choices, default=None, searchable=True, **kwargs): + def __init__( + self, parent, name, message, choices, default=None, searchable=True, **kwargs + ): InputField.__init__(self, parent, name, message, **kwargs) self.choices = choices self.default = default @@ -900,17 +1060,27 @@ class ComboInput(InputField): # No match, so start at beginning select_in_range(0, selected) - from deluge.ui.console.widgets.popup import SelectablePopup # Must import here + from deluge.ui.console.widgets.popup import ( + SelectablePopup, + ) # Must import here + select_popup = SelectablePopup( - self.parent, ' %s ' % _('Select Language'), self._lang_selected, + self.parent, + ' %s ' % _('Select Language'), + self._lang_selected, input_cb=search_handler if self.searchable else None, - border_off_west=1, active_wrap=False, width_req=self.choices_width + 12, + border_off_west=1, + active_wrap=False, + width_req=self.choices_width + 12, ) for choice in self.choices: args = {'data': choice[0]} select_popup.add_line( - choice[0], choice[1], selectable=True, - selected=choice[0] == self.get_value(), **args + choice[0], + choice[1], + selectable=True, + selected=choice[0] == self.get_value(), + **args ) self.parent.push_popup(select_popup) return util.ReadState.CHANGED @@ -925,14 +1095,17 @@ class ComboInput(InputField): msg = c[1] break if msg is None: - log.warning('Setting value "%s" found nothing in choices: %s', val, self.choices) + log.warning( + 'Setting value "%s" found nothing in choices: %s', val, self.choices + ) self.fmt_keys.update({'msg': msg}) class TextField(BaseField): - def __init__(self, parent, name, value, selectable=True, value_fmt='%s', **kwargs): - BaseField.__init__(self, parent=parent, name=name, selectable=selectable, **kwargs) + BaseField.__init__( + self, parent=parent, name=name, selectable=selectable, **kwargs + ) self.value = value self.value_fmt = value_fmt self.set_value(value) @@ -948,7 +1121,9 @@ class TextField(BaseField): @overrides(BaseField) def render(self, screen, row, active=False, focused=False, col=0, **kwargs): - util.safe_curs_set(util.Curser.INVISIBLE) # Make cursor invisible when text field is active + util.safe_curs_set( + util.Curser.INVISIBLE + ) # Make cursor invisible when text field is active fmt = self.build_fmt_string(focused, active) self.fmt_keys['msg'] = self.txt string = fmt % self.fmt_keys @@ -957,18 +1132,28 @@ class TextField(BaseField): class TextArea(TextField): - def __init__(self, parent, name, value, value_fmt='%s', **kwargs): - TextField.__init__(self, parent, name, value, selectable=False, value_fmt=value_fmt, **kwargs) + TextField.__init__( + self, parent, name, value, selectable=False, value_fmt=value_fmt, **kwargs + ) @overrides(TextField) def render(self, screen, row, col=0, **kwargs): - util.safe_curs_set(util.Curser.INVISIBLE) # Make cursor invisible when text field is active + util.safe_curs_set( + util.Curser.INVISIBLE + ) # Make cursor invisible when text field is active color = '{!white,black!}' lines = wrap_string(self.txt, self.parent.width - 3, 3, True) for i, line in enumerate(lines): - self.parent.add_string(row + i, '%s%s' % (color, line), scr=screen, col=col, pad=False, trim=False) + self.parent.add_string( + row + i, + '%s%s' % (color, line), + scr=screen, + col=col, + pad=False, + trim=False, + ) return len(lines) @property @@ -982,9 +1167,19 @@ class TextArea(TextField): class DividerField(NoInputField): - - def __init__(self, parent, name, value, selectable=False, fill_width=True, value_fmt='%s', **kwargs): - NoInputField.__init__(self, parent=parent, name=name, selectable=selectable, **kwargs) + def __init__( + self, + parent, + name, + value, + selectable=False, + fill_width=True, + value_fmt='%s', + **kwargs + ): + NoInputField.__init__( + self, parent=parent, name=name, selectable=selectable, **kwargs + ) self.value = value self.value_fmt = value_fmt self.set_value(value) @@ -996,8 +1191,12 @@ class DividerField(NoInputField): self.txt = self.value_fmt % (value) @overrides(BaseField) - def render(self, screen, row, active=False, focused=False, col=0, width=None, **kwargs): - util.safe_curs_set(util.Curser.INVISIBLE) # Make cursor invisible when text field is active + def render( + self, screen, row, active=False, focused=False, col=0, width=None, **kwargs + ): + util.safe_curs_set( + util.Curser.INVISIBLE + ) # Make cursor invisible when text field is active fmt = self.build_fmt_string(focused, active) self.fmt_keys['msg'] = self.txt if self.fill_width: diff --git a/deluge/ui/console/widgets/inputpane.py b/deluge/ui/console/widgets/inputpane.py index 2aba998fd..097a6cb8d 100644 --- a/deluge/ui/console/widgets/inputpane.py +++ b/deluge/ui/console/widgets/inputpane.py @@ -16,9 +16,21 @@ import logging from deluge.decorators import overrides from deluge.ui.console.modes.basemode import InputKeyHandler, move_cursor from deluge.ui.console.utils import curses_util as util -from deluge.ui.console.widgets.fields import (CheckedInput, CheckedPlusInput, ComboInput, DividerField, FloatSpinInput, - Header, InfoField, IntSpinInput, NoInputField, SelectInput, TextArea, - TextField, TextInput) +from deluge.ui.console.widgets.fields import ( + CheckedInput, + CheckedPlusInput, + ComboInput, + DividerField, + FloatSpinInput, + Header, + InfoField, + IntSpinInput, + NoInputField, + SelectInput, + TextArea, + TextField, + TextInput, +) try: import curses @@ -29,11 +41,18 @@ log = logging.getLogger(__name__) class BaseInputPane(InputKeyHandler): - def __init__( - self, mode, allow_rearrange=False, immediate_action=False, set_first_input_active=True, - border_off_west=0, border_off_north=0, border_off_east=0, border_off_south=0, - active_wrap=False, **kwargs + self, + mode, + allow_rearrange=False, + immediate_action=False, + set_first_input_active=True, + border_off_west=0, + border_off_north=0, + border_off_east=0, + border_off_south=0, + active_wrap=False, + **kwargs ): InputKeyHandler.__init__(self) self.inputs = [] @@ -54,7 +73,9 @@ class BaseInputPane(InputKeyHandler): if not hasattr(self, 'visible_content_pane_height'): log.error( 'The class "%s" does not have the attribute "%s" required by super class "%s"', - self.__class__.__name__, 'visible_content_pane_height', BaseInputPane.__name__, + self.__class__.__name__, + 'visible_content_pane_height', + BaseInputPane.__name__, ) raise AttributeError('visible_content_pane_height') @@ -87,9 +108,12 @@ class BaseInputPane(InputKeyHandler): continue if e.name == input_element.name: import traceback + log.warning( 'Input element with name "%s" already exists in input pane (%s):\n%s', - input_element.name, e, ''.join(traceback.format_stack(limit=5)), + input_element.name, + e, + ''.join(traceback.format_stack(limit=5)), ) return @@ -106,7 +130,9 @@ class BaseInputPane(InputKeyHandler): return self._add_input(InfoField(self, name, label, value)) def add_text_field(self, name, message, selectable=True, col='+1', **kwargs): - return self._add_input(TextField(self, name, message, selectable=selectable, col=col, **kwargs)) + return self._add_input( + TextField(self, name, message, selectable=selectable, col=col, **kwargs) + ) def add_text_area(self, name, message, **kwargs): return self._add_input(TextArea(self, name, message, **kwargs)) @@ -123,28 +149,52 @@ class BaseInputPane(InputKeyHandler): :param value: initial value of the field :param complete: should completion be run when tab is hit and this field is active """ - return self._add_input(TextInput( - self, name, message, self.move, self.visible_content_pane_width, value, - col=col, **kwargs - )) + return self._add_input( + TextInput( + self, + name, + message, + self.move, + self.visible_content_pane_width, + value, + col=col, + **kwargs + ) + ) def add_select_input(self, name, message, opts, vals, default_index=0, **kwargs): - return self._add_input(SelectInput(self, name, message, opts, vals, default_index, **kwargs)) + return self._add_input( + SelectInput(self, name, message, opts, vals, default_index, **kwargs) + ) def add_checked_input(self, name, message, checked=False, col='+1', **kwargs): - return self._add_input(CheckedInput(self, name, message, checked=checked, col=col, **kwargs)) + return self._add_input( + CheckedInput(self, name, message, checked=checked, col=col, **kwargs) + ) - def add_checkedplus_input(self, name, message, child, checked=False, col='+1', **kwargs): - return self._add_input(CheckedPlusInput(self, name, message, child, checked=checked, col=col, **kwargs)) + def add_checkedplus_input( + self, name, message, child, checked=False, col='+1', **kwargs + ): + return self._add_input( + CheckedPlusInput( + self, name, message, child, checked=checked, col=col, **kwargs + ) + ) def add_float_spin_input(self, name, message, value=0.0, col='+1', **kwargs): - return self._add_input(FloatSpinInput(self, name, message, self.move, value, col=col, **kwargs)) + return self._add_input( + FloatSpinInput(self, name, message, self.move, value, col=col, **kwargs) + ) def add_int_spin_input(self, name, message, value=0, col='+1', **kwargs): - return self._add_input(IntSpinInput(self, name, message, self.move, value, col=col, **kwargs)) + return self._add_input( + IntSpinInput(self, name, message, self.move, value, col=col, **kwargs) + ) def add_combo_input(self, name, message, choices, col='+1', **kwargs): - return self._add_input(ComboInput(self, name, message, choices, col=col, **kwargs)) + return self._add_input( + ComboInput(self, name, message, choices, col=col, **kwargs) + ) @overrides(InputKeyHandler) def handle_read(self, c): @@ -153,7 +203,9 @@ class BaseInputPane(InputKeyHandler): ret = self.inputs[self.active_input].handle_read(c) if ret != util.ReadState.IGNORED: if self.immediate_action: - self.immediate_action_cb(state_changed=False if ret == util.ReadState.READ else True) + self.immediate_action_cb( + state_changed=False if ret == util.ReadState.READ else True + ) return ret ret = util.ReadState.READ @@ -174,7 +226,9 @@ class BaseInputPane(InputKeyHandler): self.lineoff = max(self.lineoff - 1, 0) elif c == util.KEY_ALT_AND_ARROW_DOWN: tot_height = self.get_content_height() - self.lineoff = min(self.lineoff + 1, tot_height - self.visible_content_pane_height) + self.lineoff = min( + self.lineoff + 1, tot_height - self.visible_content_pane_height + ) elif c == util.KEY_CTRL_AND_ARROW_UP: if not self.allow_rearrange: return ret @@ -200,7 +254,11 @@ class BaseInputPane(InputKeyHandler): for i, ipt in enumerate(self.inputs): if not ipt.has_input(): continue - vals[ipt.name] = {'value': ipt.get_value(), 'order': i, 'active': self.active_input == i} + vals[ipt.name] = { + 'value': ipt.get_value(), + 'order': i, + 'active': self.active_input == i, + } return vals def immediate_action_cb(self, state_changed=True): @@ -246,9 +304,15 @@ class BaseInputPane(InputKeyHandler): cur_sel = next_sel next_sel = next_move(next_sel, direction, limit) if cur_sel == next_sel: - tot_height = self.get_content_height() + self.border_off_north + self.border_off_south + tot_height = ( + self.get_content_height() + + self.border_off_north + + self.border_off_south + ) if direction > 0: - self.lineoff = min(self.lineoff + 1, tot_height - self.visible_content_pane_height) + self.lineoff = min( + self.lineoff + 1, tot_height - self.visible_content_pane_height + ) else: self.lineoff = max(self.lineoff - 1, 0) @@ -285,9 +349,11 @@ class BaseInputPane(InputKeyHandler): continue height = self.visible_content_pane_height if end_row > height + self.lineoff: - self.lineoff += end_row - (height + self.lineoff) # Correct result depends on paranthesis + self.lineoff += end_row - ( + height + self.lineoff + ) # Correct result depends on paranthesis elif start_row < self.lineoff: - self.lineoff -= (self.lineoff - start_row) + self.lineoff -= self.lineoff - start_row break def render_inputs(self, focused=False): @@ -307,15 +373,23 @@ class BaseInputPane(InputKeyHandler): if ipt.default_col != -1: default_col = int(ipt.default_col) - if isinstance(ipt.default_col, ''.__class__) and ipt.default_col[0] in ['+', '-']: + if isinstance(ipt.default_col, ''.__class__) and ipt.default_col[0] in [ + '+', + '-', + ]: col += default_col cursor_offset += default_col field_width -= default_col # Increase to col must be reflected here else: col = default_col crow += ipt.render( - self.screen, crow, width=field_width, active=i == self.active_input, - focused=focused, col=col, cursor_offset=cursor_offset, + self.screen, + crow, + width=field_width, + active=i == self.active_input, + focused=focused, + col=col, + cursor_offset=cursor_offset, ) if self._cursor_row >= 0: diff --git a/deluge/ui/console/widgets/popup.py b/deluge/ui/console/widgets/popup.py index 4553ab257..d588bbb24 100644 --- a/deluge/ui/console/widgets/popup.py +++ b/deluge/ui/console/widgets/popup.py @@ -39,7 +39,6 @@ class ALIGN(object): class PopupsHandler(object): - def __init__(self): self._popups = [] @@ -63,10 +62,17 @@ class PopupsHandler(object): class Popup(BaseWindow, InputKeyHandler): - def __init__( - self, parent_mode, title, width_req=0, height_req=0, align=ALIGN.DEFAULT, - close_cb=None, encoding=None, base_popup=None, **kwargs + self, + parent_mode, + title, + width_req=0, + height_req=0, + align=ALIGN.DEFAULT, + close_cb=None, + encoding=None, + base_popup=None, + **kwargs ): """ Init a new popup. The default constructor will handle sizing and borders and the like. @@ -120,7 +126,9 @@ class Popup(BaseWindow, InputKeyHandler): def refresh(self): self.screen.erase() height = self.get_content_height() - self.ensure_content_pane_height(height + self.border_off_north + self.border_off_south) + self.ensure_content_pane_height( + height + self.border_off_north + self.border_off_south + ) BaseInputPane.render_inputs(self, focused=True) BaseWindow.refresh(self) @@ -198,9 +206,17 @@ class SelectablePopup(BaseInputPane, Popup): """ A popup which will let the user select from some of the lines that are added. """ + def __init__( - self, parent_mode, title, selection_cb, close_cb=None, input_cb=None, - allow_rearrange=False, immediate_action=False, **kwargs + self, + parent_mode, + title, + selection_cb, + close_cb=None, + input_cb=None, + allow_rearrange=False, + immediate_action=False, + **kwargs ): """ Args: @@ -215,7 +231,9 @@ class SelectablePopup(BaseInputPane, Popup): """ Popup.__init__(self, parent_mode, title, close_cb=close_cb, **kwargs) - kwargs.update({'allow_rearrange': allow_rearrange, 'immediate_action': immediate_action}) + kwargs.update( + {'allow_rearrange': allow_rearrange, 'immediate_action': immediate_action} + ) BaseInputPane.__init__(self, self, **kwargs) self.selection_cb = selection_cb self.input_cb = input_cb @@ -240,8 +258,15 @@ class SelectablePopup(BaseInputPane, Popup): self.active_input = index def add_line( - self, name, string, use_underline=True, cb_arg=None, foreground=None, selectable=True, - selected=False, **kwargs + self, + name, + string, + use_underline=True, + cb_arg=None, + foreground=None, + selectable=True, + selected=False, + **kwargs ): hotkey = None self.cb_arg[name] = cb_arg @@ -249,7 +274,13 @@ class SelectablePopup(BaseInputPane, Popup): udx = string.find('_') if udx >= 0: hotkey = string[udx].lower() - string = string[:udx] + '{!+underline!}' + string[udx + 1] + '{!-underline!}' + string[udx + 2:] + string = ( + string[:udx] + + '{!+underline!}' + + string[udx + 1] + + '{!-underline!}' + + string[udx + 2 :] + ) kwargs['selectable'] = selectable if foreground: @@ -299,14 +330,25 @@ class MessagePopup(Popup, BaseInputPane): """ Popup that just displays a message """ + def __init__( - self, parent_mode, title, message, align=ALIGN.DEFAULT, - height_req=0.75, width_req=0.5, **kwargs + self, + parent_mode, + title, + message, + align=ALIGN.DEFAULT, + height_req=0.75, + width_req=0.5, + **kwargs ): self.message = message Popup.__init__( - self, parent_mode, title, align=align, - height_req=height_req, width_req=width_req, + self, + parent_mode, + title, + align=align, + height_req=height_req, + width_req=width_req, ) BaseInputPane.__init__(self, self, immediate_action=True, **kwargs) lns = format_utils.wrap_string(self.message, self.width - 3, 3, True) @@ -327,7 +369,6 @@ class MessagePopup(Popup, BaseInputPane): class InputPopup(Popup, BaseInputPane): - def __init__(self, parent_mode, title, **kwargs): Popup.__init__(self, parent_mode, title, **kwargs) BaseInputPane.__init__(self, self, **kwargs) diff --git a/deluge/ui/console/widgets/sidebar.py b/deluge/ui/console/widgets/sidebar.py index 410d92216..cc237174d 100644 --- a/deluge/ui/console/widgets/sidebar.py +++ b/deluge/ui/console/widgets/sidebar.py @@ -29,7 +29,9 @@ class Sidebar(BaseInputPane, BaseWindow): """ - def __init__(self, torrentlist, width, height, title=None, allow_resize=False, **kwargs): + def __init__( + self, torrentlist, width, height, title=None, allow_resize=False, **kwargs + ): BaseWindow.__init__(self, title, width, height, posy=1) BaseInputPane.__init__(self, self, immediate_action=True, **kwargs) self.parent = torrentlist @@ -49,7 +51,7 @@ class Sidebar(BaseInputPane, BaseWindow): elif c == curses.KEY_DOWN: self.move_active_down(1) elif self.allow_resize and c in [ord('+'), ord('-')]: - width = self.visible_content_pane_width + (1 if c == ord('+') else - 1) + width = self.visible_content_pane_width + (1 if c == ord('+') else -1) self.on_resize(width) else: return BaseInputPane.handle_read(self, c) @@ -61,14 +63,18 @@ class Sidebar(BaseInputPane, BaseWindow): @overrides(BaseWindow) def refresh(self): height = self.get_content_height() - self.ensure_content_pane_height(height + self.border_off_north + self.border_off_south) + self.ensure_content_pane_height( + height + self.border_off_north + self.border_off_south + ) BaseInputPane.render_inputs(self, focused=self.has_focus()) BaseWindow.refresh(self) def _refresh(self): self.screen.erase() height = self.get_content_height() - self.ensure_content_pane_height(height + self.border_off_north + self.border_off_south) + self.ensure_content_pane_height( + height + self.border_off_north + self.border_off_south + ) BaseInputPane.render_inputs(self, focused=True) BaseWindow.refresh(self) diff --git a/deluge/ui/console/widgets/statusbars.py b/deluge/ui/console/widgets/statusbars.py index e1d9c4ccf..fcf4f2f41 100644 --- a/deluge/ui/console/widgets/statusbars.py +++ b/deluge/ui/console/widgets/statusbars.py @@ -100,7 +100,9 @@ class StatusBars(component.Component): self.bottombar += ' D: {!white,blue!}%s{!status!}' % self.download if self.config['max_download_speed'] > -1: - self.bottombar += ' (%s ' % self.config['max_download_speed'] + _('KiB/s') + ')' + self.bottombar += ( + ' (%s ' % self.config['max_download_speed'] + _('KiB/s') + ')' + ) if self.upload != '0.0 KiB': self.bottombar += ' U: {!green,blue,bold!}%s{!status!}' % self.upload @@ -108,7 +110,9 @@ class StatusBars(component.Component): self.bottombar += ' U: {!white,blue!}%s{!status!}' % self.upload if self.config['max_upload_speed'] > -1: - self.bottombar += ' (%s ' % self.config['max_upload_speed'] + _('KiB/s') + ')' + self.bottombar += ( + ' (%s ' % self.config['max_upload_speed'] + _('KiB/s') + ')' + ) if self.config['dht']: self.bottombar += ' ' + _('DHT') + ': {!white,blue!}%s{!status!}' % self.dht diff --git a/deluge/ui/console/widgets/window.py b/deluge/ui/console/widgets/window.py index eeb0de4e5..2ef35281e 100644 --- a/deluge/ui/console/widgets/window.py +++ b/deluge/ui/console/widgets/window.py @@ -28,6 +28,7 @@ class BaseWindow(object): """ BaseWindow creates a curses screen to be used for showing panels and popup dialogs """ + def __init__(self, title, width, height, posy=0, posx=0, encoding=None): """ Args: @@ -42,6 +43,7 @@ class BaseWindow(object): self.posy, self.posx = posy, posx if encoding is None: from deluge import component + encoding = component.get('ConsoleUI').encoding self.encoding = encoding @@ -117,7 +119,9 @@ class BaseWindow(object): if content_height <= self.visible_content_pane_height: return - percent_scroll = float(self.lineoff) / (content_height - self.visible_content_pane_height) + percent_scroll = float(self.lineoff) / ( + content_height - self.visible_content_pane_height + ) indicator_row = int(self.visible_content_pane_height * percent_scroll) + 1 # Never greater than height @@ -125,8 +129,13 @@ class BaseWindow(object): indicator_col = self.width + 1 add_string( - indicator_row, '{!red,black,bold!}#', screen, self.encoding, - col=indicator_col, pad=False, trim=False, + indicator_row, + '{!red,black,bold!}#', + screen, + self.encoding, + col=indicator_col, + pad=False, + trim=False, ) def refresh(self): @@ -136,7 +145,13 @@ class BaseWindow(object): if self.title: toff = max(1, (self.width // 2) - (len(self.title) // 2)) - self.add_string(0, '{!white,black,bold!}%s' % self.title, scr=self.outer_screen, col=toff, pad=False) + self.add_string( + 0, + '{!white,black,bold!}%s' % self.title, + scr=self.outer_screen, + col=toff, + pad=False, + ) self.draw_scroll_indicator(self.outer_screen) self.outer_screen.noutrefresh() @@ -151,10 +166,20 @@ class BaseWindow(object): smincol = self.posx + 1 smaxrow = height + self.posy smaxcol = width + self.posx - self.screen.noutrefresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) + self.screen.noutrefresh( + pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol + ) except curses.error as ex: import traceback + log.warning( 'Error on screen.noutrefresh(%s, %s, %s, %s, %s, %s) Error: %s\nStack: %s', - pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol, ex, ''.join(traceback.format_stack()), + pminrow, + pmincol, + sminrow, + smincol, + smaxrow, + smaxcol, + ex, + ''.join(traceback.format_stack()), ) diff --git a/deluge/ui/coreconfig.py b/deluge/ui/coreconfig.py index 0462d098a..ed6b614a2 100644 --- a/deluge/ui/coreconfig.py +++ b/deluge/ui/coreconfig.py @@ -25,7 +25,10 @@ class CoreConfig(component.Component): def on_configvaluechanged_event(key, value): self.config[key] = value - client.register_event_handler('ConfigValueChangedEvent', on_configvaluechanged_event) + + client.register_event_handler( + 'ConfigValueChangedEvent', on_configvaluechanged_event + ) def start(self): def on_get_config(config): diff --git a/deluge/ui/gtkui/__init__.py b/deluge/ui/gtkui/__init__.py index 4a5f4e040..c57c468f0 100644 --- a/deluge/ui/gtkui/__init__.py +++ b/deluge/ui/gtkui/__init__.py @@ -22,7 +22,9 @@ class Gtk(UI): cmd_description = """GTK-based graphical user interface""" def __init__(self, *args, **kwargs): - super(Gtk, self).__init__('gtk', *args, description='Starts the Deluge GTK+ interface', **kwargs) + super(Gtk, self).__init__( + 'gtk', *args, description='Starts the Deluge GTK+ interface', **kwargs + ) group = self.parser.add_argument_group(_('GTK Options')) group.add_argument( @@ -32,7 +34,7 @@ class Gtk(UI): default=None, help=_( 'Add one or more torrent files, torrent URLs or magnet URIs' - ' to a currently running Deluge GTK instance', + ' to a currently running Deluge GTK instance' ), ) diff --git a/deluge/ui/gtkui/aboutdialog.py b/deluge/ui/gtkui/aboutdialog.py index 355f8f9d3..abb3135f1 100644 --- a/deluge/ui/gtkui/aboutdialog.py +++ b/deluge/ui/gtkui/aboutdialog.py @@ -24,234 +24,797 @@ class AboutDialog(object): self.about.set_position(gtk.WIN_POS_CENTER) self.about.set_program_name(_('Deluge')) if windows_check(): + def url_hook(dialog, url): """Url hook for Windows OS which has no default browser.""" open_url_in_browser(url) return True + self.about.connect('activate-link', url_hook) version = get_version() self.about.set_copyright( - _('Copyright %(year_start)s-%(year_end)s Deluge Team') % {'year_start': 2007, 'year_end': 2015}, + _('Copyright %(year_start)s-%(year_end)s Deluge Team') + % {'year_start': 2007, 'year_end': 2015} ) self.about.set_comments( - _('A peer-to-peer file sharing program\nutilizing the BitTorrent protocol.') + - '\n\n' + _('Client:') + ' %s\n' % version, + _('A peer-to-peer file sharing program\nutilizing the BitTorrent protocol.') + + '\n\n' + + _('Client:') + + ' %s\n' % version ) self.about.set_version(version) - self.about.set_authors([ - _('Current Developers:'), 'Andrew Resch', 'Damien Churchill', - 'John Garland', 'Calum Lind', '', 'libtorrent (libtorrent.org):', - 'Arvid Norberg', '', _('Past Developers or Contributors:'), - 'Zach Tibbitts', 'Alon Zakai', 'Marcos Mobley', 'Alex Dedul', - 'Sadrul Habib Chowdhury', 'Ido Abramovich', 'Martijn Voncken', - ]) + self.about.set_authors( + [ + _('Current Developers:'), + 'Andrew Resch', + 'Damien Churchill', + 'John Garland', + 'Calum Lind', + '', + 'libtorrent (libtorrent.org):', + 'Arvid Norberg', + '', + _('Past Developers or Contributors:'), + 'Zach Tibbitts', + 'Alon Zakai', + 'Marcos Mobley', + 'Alex Dedul', + 'Sadrul Habib Chowdhury', + 'Ido Abramovich', + 'Martijn Voncken', + ] + ) self.about.set_artists(['Andrew Wedderburn', 'Andrew Resch']) - self.about.set_translator_credits('\n'.join([ - 'Aaron Wang Shi', 'abbigss', 'ABCdatos', 'Abcx', 'Actam', 'Adam', - 'adaminikisi', 'adi_oporanu', 'Adrian Goll', 'afby', 'Ahmades', - 'Ahmad Farghal', 'Ahmad Gharbeia أحمد غربية', 'akira', 'Aki Sivula', - 'Alan Pepelko', 'Alberto', 'Alberto Ferrer', 'alcatr4z', 'AlckO', - 'Aleksej Korgenkov', 'Alessio Treglia', 'Alexander Ilyashov', - 'Alexander Matveev', 'Alexander Saltykov', 'Alexander Taubenkorb', - 'Alexander Telenga', 'Alexander Yurtsev', 'Alexandre Martani', - 'Alexandre Rosenfeld', 'Alexandre Sapata Carbonell', - 'Alexey Osipov', 'Alin Claudiu Radut', 'allah', 'AlSim', - 'Alvaro Carrillanca P.', 'A.Matveev', 'Andras Hipsag', - 'András Kárász', 'Andrea Ratto', 'Andreas Johansson', 'Andreas Str', - 'André F. Oliveira', 'AndreiF', 'andrewh', 'Angel Guzman Maeso', - 'Aníbal Deboni Neto', 'animarval', 'Antonio Cono', 'antoniojreyes', - 'Anton Shestakov', 'Anton Yakutovich', 'antou', - 'Arkadiusz Kalinowski', 'Artin', 'artir', 'Astur', - 'Athanasios Lefteris', 'Athmane MOKRAOUI (ButterflyOfFire)', - 'Augusta Carla Klug', 'Avoledo Marco', 'axaard', 'AxelRafn', - 'Axezium', 'Ayont', 'b3rx', 'Bae Taegil', 'Bajusz Tamás', - "Balaam's Miracle", 'Ballestein', 'Bent Ole Fosse', 'berto89', - 'bigx', 'Bjorn Inge Berg', 'blackbird', 'Blackeyed', 'blackmx', - 'BlueSky', 'Blutheo', 'bmhm', 'bob00work', 'boenki', - 'Bogdan Bădic-Spătariu', 'bonpu', 'Boone', 'boss01', - 'Branislav Jovanović', 'bronze', 'brownie', 'Brus46', 'bumper', - 'butely', 'BXCracer', 'c0nfidencal', 'Can Kaya', - 'Carlos Alexandro Becker', 'cassianoleal', 'Cédric.h', - 'César Rubén', 'chaoswizard', 'Chen Tao', 'chicha', - 'Chien Cheng Wei', 'Christian Kopac', 'Christian Widell', - 'Christoffer Brodd-Reijer', 'christooss', 'CityAceE', 'Clopy', - 'Clusty', 'cnu', 'Commandant', 'Constantinos Koniaris', 'Coolmax', - 'cosmix', 'Costin Chirvasuta', 'CoVaLiDiTy', 'cow_2001', - 'Crispin Kirchner', 'crom', 'Cruster', 'Cybolic', 'Dan Bishop', - 'Danek', 'Dani', 'Daniel Demarco', 'Daniel Ferreira', - 'Daniel Frank', 'Daniel Holm', 'Daniel Høyer Iversen', - 'Daniel Marynicz', 'Daniel Nylander', 'Daniel Patriche', - 'Daniel Schildt', 'Daniil Sorokin', 'Dante Díaz', 'Daria Michalska', - 'DarkenCZ', 'Darren', 'Daspah', 'David Eurenius', 'davidhjelm', - 'David Machakhelidze', 'Dawid Dziurdzia', 'Daya Adianto ', 'dcruz', - 'Deady', 'Dereck Wonnacott', 'Devgru', 'Devid Antonio Filoni' - 'DevilDogTG', 'di0rz`', 'Dialecti Valsamou', 'Diego Medeiros', - 'Dkzoffy', 'Dmitrij D. Czarkoff', 'Dmitriy Geels', - 'Dmitry Olyenyov', 'Dominik Kozaczko', 'Dominik Lübben', 'doomster', - 'Dorota Król', 'Doyen Philippe', 'Dread Knight', 'DreamSonic', - 'duan', 'Duong Thanh An', 'DvoglavaZver', 'dwori', 'dylansmrjones', - 'Ebuntor', 'Edgar Alejandro Jarquin Flores', 'Eetu', 'ekerazha', - 'Elias Julkunen', 'elparia', 'Emberke', 'Emiliano Goday Caneda', - 'EndelWar', 'eng.essam', 'enubuntu', 'ercangun', 'Erdal Ronahi', - 'ergin üresin', 'Eric', 'Éric Lassauge', 'Erlend Finvåg', 'Errdil', - 'ethan shalev', 'Evgeni Spasov', 'ezekielnin', 'Fabian Ordelmans', - 'Fabio Mazanatti', 'Fábio Nogueira', 'FaCuZ', 'Felipe Lerena', - 'Fernando Pereira', 'fjetland', 'Florian Schäfer', 'FoBoS', 'Folke', - 'Force', 'fosk', 'fragarray', 'freddeg', 'Frédéric Perrin', - 'Fredrik Kilegran', 'FreeAtMind', 'Fulvio Ciucci', 'Gabor Kelemen', - 'Galatsanos Panagiotis', 'Gaussian', 'gdevitis', 'Georg Brzyk', - 'George Dumitrescu', 'Georgi Arabadjiev', 'Georg Sieber', - 'Gerd Radecke', 'Germán Heusdens', 'Gianni Vialetto', - 'Gigih Aji Ibrahim', 'Giorgio Wicklein', 'Giovanni Rapagnani', - 'Giuseppe', 'gl', 'glen', 'granjerox', 'Green Fish', 'greentea', - 'Greyhound', 'G. U.', 'Guillaume BENOIT', 'Guillaume Pelletier', - 'Gustavo Henrique Klug', 'gutocarvalho', 'Guybrush88', - 'Hans Rødtang', 'HardDisk', 'Hargas Gábor', - 'Heitor Thury Barreiros Barbosa', 'helios91940', 'helix84', - 'Helton Rodrigues', 'Hendrik Luup', 'Henrique Ferreiro', - 'Henry Goury-Laffont', 'Hezy Amiel', 'hidro', 'hoball', 'hokten', - 'Holmsss', 'hristo.num', 'Hubert Życiński', 'Hyo', 'Iarwain', 'ibe', - 'ibear', 'Id2ndR', 'Igor Zubarev', 'IKON (Ion)', 'imen', - 'Ionuț Jula', 'Isabelle STEVANT', 'István Nyitrai', 'Ivan Petrovic', - 'Ivan Prignano', 'IvaSerge', 'jackmc', 'Jacks0nxD', 'Jack Shen', - 'Jacky Yeung', 'Jacques Stadler', 'Janek Thomaschewski', 'Jan Kaláb', - 'Jan Niklas Hasse', 'Jasper Groenewegen', 'Javi Rodríguez', - 'Jayasimha (ಜಯಸಿಂಹ)', 'jeannich', 'Jeff Bailes', 'Jesse Zilstorff', - 'Joan Duran', 'João Santos', 'Joar Bagge', 'Joe Anderson', - 'Joel Calado', 'Johan Linde', 'John Garland', 'Jojan', 'jollyr0ger', - 'Jonas Bo Grimsgaard', 'Jonas Granqvist', 'Jonas Slivka', - 'Jonathan Zeppettini', 'Jørgen', 'Jørgen Tellnes', 'josé', - 'José Geraldo Gouvêa', 'José Iván León Islas', 'José Lou C.', - 'Jose Sun', 'Jr.', 'Jukka Kauppinen', 'Julián Alarcón', - 'julietgolf', 'Jusic', 'Justzupi', 'Kaarel', 'Kai Thomsen', - 'Kalman Tarnay', 'Kamil Páral', 'Kane_F', 'kaotiks@gmail.com', - 'Kateikyoushii', 'kaxhinaz', 'Kazuhiro NISHIYAMA', 'Kerberos', - 'Keresztes Ákos', 'kevintyk', 'kiersie', 'Kimbo^', 'Kim Lübbe', - 'kitzOgen', 'Kjetil Rydland', 'kluon', 'kmikz', 'Knedlyk', - 'koleoptero', 'Kőrösi Krisztián', 'Kouta', 'Krakatos', - 'Krešo Kunjas', 'kripken', 'Kristaps', 'Kristian Øllegaard', - 'Kristoffer Egil Bonarjee', 'Krzysztof Janowski', - 'Krzysztof Zawada', 'Larry Wei Liu', 'laughterwym', 'Laur Mõtus', - 'lazka', 'leandrud', 'lê bình', 'Le Coz Florent', 'Leo', 'liorda', - 'LKRaider', 'LoLo_SaG', 'Long Tran', 'Lorenz', 'Low Kian Seong', - 'Luca Andrea Rossi', 'Luca Ferretti', 'Lucky LIX', 'Luis Gomes', - 'Luis Reis', 'Łukasz Wyszyński', 'luojie-dune', 'maaark', - 'Maciej Chojnacki', 'Maciej Meller', 'Mads Peter Rommedahl', - 'Major Kong', 'Malaki', 'malde', 'Malte Lenz', 'Mantas Kriaučiūnas', - 'Mara Sorella', 'Marcin', 'Marcin Falkiewicz', 'marcobra', - 'Marco da Silva', 'Marco de Moulin', 'Marco Rodrigues', 'Marcos', - 'Marcos Escalier', 'Marcos Mobley', 'Marcus Ekstrom', - 'Marek Dębowski', 'Mário Buči', 'Mario Munda', 'Marius Andersen', - 'Marius Hudea', 'Marius Mihai', 'Mariusz Cielecki', - 'Mark Krapivner', 'marko-markovic', 'Markus Brummer', - 'Markus Sutter', 'Martin', 'Martin Dybdal', 'Martin Iglesias', - 'Martin Lettner', 'Martin Pihl', 'Masoud Kalali', 'mat02', - 'Matej Urbančič', 'Mathias-K', 'Mathieu Arès', - 'Mathieu D. (MatToufoutu)', 'Mathijs', 'Matrik', 'Matteo Renzulli', - 'Matteo Settenvini', 'Matthew Gadd', 'Matthias Benkard', - 'Matthias Mailänder', 'Mattias Ohlsson', 'Mauro de Carvalho', - 'Max Molchanov', 'Me', 'MercuryCC', 'Mert Bozkurt', 'Mert Dirik', - 'MFX', 'mhietar', 'mibtha', 'Michael Budde', 'Michael Kaliszka', - 'Michalis Makaronides', 'Michał Tokarczyk', 'Miguel Pires da Rosa', - 'Mihai Capotă', 'Miika Metsälä', 'Mikael Fernblad', 'Mike Sierra', - 'mikhalek', 'Milan Prvulović', 'Milo Casagrande', 'Mindaugas', - 'Miroslav Matejaš', 'misel', 'mithras', 'Mitja Pagon', 'M.Kitchen', - 'Mohamed Magdy', 'moonkey', 'MrBlonde', 'muczy', 'Münir Ekinci', - 'Mustafa Temizel', 'mvoncken', 'Mytonn', 'NagyMarton', 'neaion', - 'Neil Lin', 'Nemo', 'Nerijus Arlauskas', 'Nicklas Larsson', - 'Nicolaj Wyke', 'Nicola Piovesan', 'Nicolas Sabatier', - 'Nicolas Velin', 'Nightfall', 'NiKoB', 'Nikolai M. Riabov', - 'Niko_Thien', 'niska', 'Nithir', 'noisemonkey', 'nomemohes', - 'nosense', 'null', 'Nuno Estêvão', 'Nuno Santos', 'nxxs', 'nyo', - 'obo', 'Ojan', 'Olav Andreas Lindekleiv', 'oldbeggar', - 'Olivier FAURAX', 'orphe', 'osantana', 'Osman Tosun', 'OssiR', - 'otypoks', 'ounn', 'Oz123', 'Özgür BASKIN', 'Pablo Carmona A.', - 'Pablo Ledesma', 'Pablo Navarro Castillo', 'Paco Molinero', - 'Pål-Eivind Johnsen', 'pano', 'Paolo Naldini', 'Paracelsus', - 'Patryk13_03', 'Patryk Skorupa', 'PattogoTehen', 'Paul Lange', - 'Pavcio', 'Paweł Wysocki', 'Pedro Brites Moita', - 'Pedro Clemente Pereira Neto', 'Pekka "PEXI" Niemistö', 'Penegal', - 'Penzo', 'perdido', 'Peter Kotrcka', 'Peter Skov', - 'Peter Van den Bosch', 'Petter Eklund', 'Petter Viklund', - 'phatsphere', 'Phenomen', 'Philipi', 'Philippides Homer', 'phoenix', - 'pidi', 'Pierre Quillery', 'Pierre Rudloff', 'Pierre Slamich', - 'Pietrao', 'Piotr Strębski', 'Piotr Wicijowski', 'Pittmann Tamás', - 'Playmolas', 'Prescott', 'Prescott_SK', 'pronull', - 'Przemysław Kulczycki', 'Pumy', 'pushpika', 'PY', 'qubicllj', - 'r21vo', 'Rafał Barański', 'rainofchaos', 'Rajbir', 'ras0ir', 'Rat', - 'rd1381', 'Renato', 'Rene Hennig', 'Rene Pärts', 'Ricardo Duarte', - 'Richard', 'Robert Hrovat', 'Roberth Sjonøy', 'Robert Lundmark', - 'Robin Jakobsson', 'Robin Kåveland', 'Rodrigo Donado', - 'Roel Groeneveld', 'rohmaru', 'Rolf Christensen', 'Rolf Leggewie', - 'Roni Kantis', 'Ronmi', 'Rostislav Raykov', 'royto', 'RuiAmaro', - 'Rui Araújo', 'Rui Moura', 'Rune Svendsen', 'Rusna', 'Rytis', - 'Sabirov Mikhail', 'salseeg', 'Sami Koskinen', 'Samir van de Sand', - 'Samuel Arroyo Acuña', 'Samuel R. C. Vale', 'Sanel', 'Santi', - 'Santi Martínez Cantelli', 'Sardan', 'Sargate Kanogan', - 'Sarmad Jari', 'Saša Bodiroža', 'sat0shi', 'Saulius Pranckevičius', - 'Savvas Radevic', 'Sebastian Krauß', 'Sebastián Porta', 'Sedir', - 'Sefa Denizoğlu', 'sekolands', 'Selim Suerkan', 'semsomi', - 'Sergii Golovatiuk', 'setarcos', 'Sheki', 'Shironeko', 'Shlomil', - 'silfiriel', 'Simone Tolotti', 'Simone Vendemia', 'sirkubador', - 'Sławomir Więch', 'slip', 'slyon', 'smoke', 'Sonja', 'spectral', - 'spin_555', 'spitf1r3', 'Spiziuz', 'Spyros Theodoritsis', 'SqUe', - 'Squigly', 'srtck', 'Stefan Horning', 'Stefano Maggiolo', - 'Stefano Roberto Soleti', 'steinberger', 'Stéphane Travostino', - 'Stephan Klein', 'Steven De Winter', 'Stevie', 'Stian24', 'stylius', - 'Sukarn Maini', 'Sunjae Park', 'Susana Pereira', 'szymon siglowy', - 'takercena', 'TAS', 'Taygeto', 'temy4', 'texxxxxx', 'thamood', - 'Thanos Chatziathanassiou', 'Tharawut Paripaiboon', 'Theodoor', - 'Théophane Anestis', 'Thor Marius K. Høgås', 'Tiago Silva', - 'Tiago Sousa', 'Tikkel', 'tim__b', 'Tim Bordemann', 'Tim Fuchs', - 'Tim Kornhammar', 'Timo', 'Timo Jyrinki', 'Timothy Babych', - 'TitkosRejtozo', 'Tom', 'Tomas Gustavsson', 'Tomas Valentukevičius', - 'Tomasz Dominikowski', 'Tomislav Plavčić', 'Tom Mannerhagen', - 'Tommy Mikkelsen', 'Tom Verdaat', 'Tony Manco', - 'Tor Erling H. Opsahl', 'Toudi', 'tqm_z', 'Trapanator', 'Tribaal', - 'Triton', 'TuniX12', 'Tuomo Sipola', 'turbojugend_gr', 'Turtle.net', - 'twilight', 'tymmej', 'Ulrik', 'Umarzuki Mochlis', 'unikob', - 'Vadim Gusev', 'Vagi', 'Valentin Bora', 'Valmantas Palikša', - 'VASKITTU', 'Vassilis Skoullis', 'vetal17', 'vicedo', 'viki', - 'villads hamann', 'Vincent Garibal', 'Vincent Ortalda', 'vinchi007', - 'Vinícius de Figueiredo Silva', 'Vinzenz Vietzke', 'virtoo', - 'virtual_spirit', 'Vitor Caike', 'Vitor Lamas Gatti', - 'Vladimir Lazic', 'Vladimir Sharshov', 'Wanderlust', 'Wander Nauta', - 'Ward De Ridder', 'WebCrusader', 'webdr', 'Wentao Tang', 'wilana', - 'Wilfredo Ernesto Guerrero Campos', 'Wim Champagne', 'World Sucks', - 'Xabi Ezpeleta', 'Xavi de Moner', 'XavierToo', 'XChesser', - 'Xiaodong Xu', 'xyb', 'Yaron', 'Yasen Pramatarov', 'YesPoX', - 'Yuren Ju', 'Yves MATHIEU', 'zekopeko', 'zhuqin', 'Zissan', - 'Γιάννης Κατσαμπίρης', 'Артём Попов', 'Миша', 'Шаймарданов Максим', - '蔡查理', - ])) + self.about.set_translator_credits( + '\n'.join( + [ + 'Aaron Wang Shi', + 'abbigss', + 'ABCdatos', + 'Abcx', + 'Actam', + 'Adam', + 'adaminikisi', + 'adi_oporanu', + 'Adrian Goll', + 'afby', + 'Ahmades', + 'Ahmad Farghal', + 'Ahmad Gharbeia أحمد غربية', + 'akira', + 'Aki Sivula', + 'Alan Pepelko', + 'Alberto', + 'Alberto Ferrer', + 'alcatr4z', + 'AlckO', + 'Aleksej Korgenkov', + 'Alessio Treglia', + 'Alexander Ilyashov', + 'Alexander Matveev', + 'Alexander Saltykov', + 'Alexander Taubenkorb', + 'Alexander Telenga', + 'Alexander Yurtsev', + 'Alexandre Martani', + 'Alexandre Rosenfeld', + 'Alexandre Sapata Carbonell', + 'Alexey Osipov', + 'Alin Claudiu Radut', + 'allah', + 'AlSim', + 'Alvaro Carrillanca P.', + 'A.Matveev', + 'Andras Hipsag', + 'András Kárász', + 'Andrea Ratto', + 'Andreas Johansson', + 'Andreas Str', + 'André F. Oliveira', + 'AndreiF', + 'andrewh', + 'Angel Guzman Maeso', + 'Aníbal Deboni Neto', + 'animarval', + 'Antonio Cono', + 'antoniojreyes', + 'Anton Shestakov', + 'Anton Yakutovich', + 'antou', + 'Arkadiusz Kalinowski', + 'Artin', + 'artir', + 'Astur', + 'Athanasios Lefteris', + 'Athmane MOKRAOUI (ButterflyOfFire)', + 'Augusta Carla Klug', + 'Avoledo Marco', + 'axaard', + 'AxelRafn', + 'Axezium', + 'Ayont', + 'b3rx', + 'Bae Taegil', + 'Bajusz Tamás', + "Balaam's Miracle", + 'Ballestein', + 'Bent Ole Fosse', + 'berto89', + 'bigx', + 'Bjorn Inge Berg', + 'blackbird', + 'Blackeyed', + 'blackmx', + 'BlueSky', + 'Blutheo', + 'bmhm', + 'bob00work', + 'boenki', + 'Bogdan Bădic-Spătariu', + 'bonpu', + 'Boone', + 'boss01', + 'Branislav Jovanović', + 'bronze', + 'brownie', + 'Brus46', + 'bumper', + 'butely', + 'BXCracer', + 'c0nfidencal', + 'Can Kaya', + 'Carlos Alexandro Becker', + 'cassianoleal', + 'Cédric.h', + 'César Rubén', + 'chaoswizard', + 'Chen Tao', + 'chicha', + 'Chien Cheng Wei', + 'Christian Kopac', + 'Christian Widell', + 'Christoffer Brodd-Reijer', + 'christooss', + 'CityAceE', + 'Clopy', + 'Clusty', + 'cnu', + 'Commandant', + 'Constantinos Koniaris', + 'Coolmax', + 'cosmix', + 'Costin Chirvasuta', + 'CoVaLiDiTy', + 'cow_2001', + 'Crispin Kirchner', + 'crom', + 'Cruster', + 'Cybolic', + 'Dan Bishop', + 'Danek', + 'Dani', + 'Daniel Demarco', + 'Daniel Ferreira', + 'Daniel Frank', + 'Daniel Holm', + 'Daniel Høyer Iversen', + 'Daniel Marynicz', + 'Daniel Nylander', + 'Daniel Patriche', + 'Daniel Schildt', + 'Daniil Sorokin', + 'Dante Díaz', + 'Daria Michalska', + 'DarkenCZ', + 'Darren', + 'Daspah', + 'David Eurenius', + 'davidhjelm', + 'David Machakhelidze', + 'Dawid Dziurdzia', + 'Daya Adianto ', + 'dcruz', + 'Deady', + 'Dereck Wonnacott', + 'Devgru', + 'Devid Antonio Filoni' 'DevilDogTG', + 'di0rz`', + 'Dialecti Valsamou', + 'Diego Medeiros', + 'Dkzoffy', + 'Dmitrij D. Czarkoff', + 'Dmitriy Geels', + 'Dmitry Olyenyov', + 'Dominik Kozaczko', + 'Dominik Lübben', + 'doomster', + 'Dorota Król', + 'Doyen Philippe', + 'Dread Knight', + 'DreamSonic', + 'duan', + 'Duong Thanh An', + 'DvoglavaZver', + 'dwori', + 'dylansmrjones', + 'Ebuntor', + 'Edgar Alejandro Jarquin Flores', + 'Eetu', + 'ekerazha', + 'Elias Julkunen', + 'elparia', + 'Emberke', + 'Emiliano Goday Caneda', + 'EndelWar', + 'eng.essam', + 'enubuntu', + 'ercangun', + 'Erdal Ronahi', + 'ergin üresin', + 'Eric', + 'Éric Lassauge', + 'Erlend Finvåg', + 'Errdil', + 'ethan shalev', + 'Evgeni Spasov', + 'ezekielnin', + 'Fabian Ordelmans', + 'Fabio Mazanatti', + 'Fábio Nogueira', + 'FaCuZ', + 'Felipe Lerena', + 'Fernando Pereira', + 'fjetland', + 'Florian Schäfer', + 'FoBoS', + 'Folke', + 'Force', + 'fosk', + 'fragarray', + 'freddeg', + 'Frédéric Perrin', + 'Fredrik Kilegran', + 'FreeAtMind', + 'Fulvio Ciucci', + 'Gabor Kelemen', + 'Galatsanos Panagiotis', + 'Gaussian', + 'gdevitis', + 'Georg Brzyk', + 'George Dumitrescu', + 'Georgi Arabadjiev', + 'Georg Sieber', + 'Gerd Radecke', + 'Germán Heusdens', + 'Gianni Vialetto', + 'Gigih Aji Ibrahim', + 'Giorgio Wicklein', + 'Giovanni Rapagnani', + 'Giuseppe', + 'gl', + 'glen', + 'granjerox', + 'Green Fish', + 'greentea', + 'Greyhound', + 'G. U.', + 'Guillaume BENOIT', + 'Guillaume Pelletier', + 'Gustavo Henrique Klug', + 'gutocarvalho', + 'Guybrush88', + 'Hans Rødtang', + 'HardDisk', + 'Hargas Gábor', + 'Heitor Thury Barreiros Barbosa', + 'helios91940', + 'helix84', + 'Helton Rodrigues', + 'Hendrik Luup', + 'Henrique Ferreiro', + 'Henry Goury-Laffont', + 'Hezy Amiel', + 'hidro', + 'hoball', + 'hokten', + 'Holmsss', + 'hristo.num', + 'Hubert Życiński', + 'Hyo', + 'Iarwain', + 'ibe', + 'ibear', + 'Id2ndR', + 'Igor Zubarev', + 'IKON (Ion)', + 'imen', + 'Ionuț Jula', + 'Isabelle STEVANT', + 'István Nyitrai', + 'Ivan Petrovic', + 'Ivan Prignano', + 'IvaSerge', + 'jackmc', + 'Jacks0nxD', + 'Jack Shen', + 'Jacky Yeung', + 'Jacques Stadler', + 'Janek Thomaschewski', + 'Jan Kaláb', + 'Jan Niklas Hasse', + 'Jasper Groenewegen', + 'Javi Rodríguez', + 'Jayasimha (ಜಯಸಿಂಹ)', + 'jeannich', + 'Jeff Bailes', + 'Jesse Zilstorff', + 'Joan Duran', + 'João Santos', + 'Joar Bagge', + 'Joe Anderson', + 'Joel Calado', + 'Johan Linde', + 'John Garland', + 'Jojan', + 'jollyr0ger', + 'Jonas Bo Grimsgaard', + 'Jonas Granqvist', + 'Jonas Slivka', + 'Jonathan Zeppettini', + 'Jørgen', + 'Jørgen Tellnes', + 'josé', + 'José Geraldo Gouvêa', + 'José Iván León Islas', + 'José Lou C.', + 'Jose Sun', + 'Jr.', + 'Jukka Kauppinen', + 'Julián Alarcón', + 'julietgolf', + 'Jusic', + 'Justzupi', + 'Kaarel', + 'Kai Thomsen', + 'Kalman Tarnay', + 'Kamil Páral', + 'Kane_F', + 'kaotiks@gmail.com', + 'Kateikyoushii', + 'kaxhinaz', + 'Kazuhiro NISHIYAMA', + 'Kerberos', + 'Keresztes Ákos', + 'kevintyk', + 'kiersie', + 'Kimbo^', + 'Kim Lübbe', + 'kitzOgen', + 'Kjetil Rydland', + 'kluon', + 'kmikz', + 'Knedlyk', + 'koleoptero', + 'Kőrösi Krisztián', + 'Kouta', + 'Krakatos', + 'Krešo Kunjas', + 'kripken', + 'Kristaps', + 'Kristian Øllegaard', + 'Kristoffer Egil Bonarjee', + 'Krzysztof Janowski', + 'Krzysztof Zawada', + 'Larry Wei Liu', + 'laughterwym', + 'Laur Mõtus', + 'lazka', + 'leandrud', + 'lê bình', + 'Le Coz Florent', + 'Leo', + 'liorda', + 'LKRaider', + 'LoLo_SaG', + 'Long Tran', + 'Lorenz', + 'Low Kian Seong', + 'Luca Andrea Rossi', + 'Luca Ferretti', + 'Lucky LIX', + 'Luis Gomes', + 'Luis Reis', + 'Łukasz Wyszyński', + 'luojie-dune', + 'maaark', + 'Maciej Chojnacki', + 'Maciej Meller', + 'Mads Peter Rommedahl', + 'Major Kong', + 'Malaki', + 'malde', + 'Malte Lenz', + 'Mantas Kriaučiūnas', + 'Mara Sorella', + 'Marcin', + 'Marcin Falkiewicz', + 'marcobra', + 'Marco da Silva', + 'Marco de Moulin', + 'Marco Rodrigues', + 'Marcos', + 'Marcos Escalier', + 'Marcos Mobley', + 'Marcus Ekstrom', + 'Marek Dębowski', + 'Mário Buči', + 'Mario Munda', + 'Marius Andersen', + 'Marius Hudea', + 'Marius Mihai', + 'Mariusz Cielecki', + 'Mark Krapivner', + 'marko-markovic', + 'Markus Brummer', + 'Markus Sutter', + 'Martin', + 'Martin Dybdal', + 'Martin Iglesias', + 'Martin Lettner', + 'Martin Pihl', + 'Masoud Kalali', + 'mat02', + 'Matej Urbančič', + 'Mathias-K', + 'Mathieu Arès', + 'Mathieu D. (MatToufoutu)', + 'Mathijs', + 'Matrik', + 'Matteo Renzulli', + 'Matteo Settenvini', + 'Matthew Gadd', + 'Matthias Benkard', + 'Matthias Mailänder', + 'Mattias Ohlsson', + 'Mauro de Carvalho', + 'Max Molchanov', + 'Me', + 'MercuryCC', + 'Mert Bozkurt', + 'Mert Dirik', + 'MFX', + 'mhietar', + 'mibtha', + 'Michael Budde', + 'Michael Kaliszka', + 'Michalis Makaronides', + 'Michał Tokarczyk', + 'Miguel Pires da Rosa', + 'Mihai Capotă', + 'Miika Metsälä', + 'Mikael Fernblad', + 'Mike Sierra', + 'mikhalek', + 'Milan Prvulović', + 'Milo Casagrande', + 'Mindaugas', + 'Miroslav Matejaš', + 'misel', + 'mithras', + 'Mitja Pagon', + 'M.Kitchen', + 'Mohamed Magdy', + 'moonkey', + 'MrBlonde', + 'muczy', + 'Münir Ekinci', + 'Mustafa Temizel', + 'mvoncken', + 'Mytonn', + 'NagyMarton', + 'neaion', + 'Neil Lin', + 'Nemo', + 'Nerijus Arlauskas', + 'Nicklas Larsson', + 'Nicolaj Wyke', + 'Nicola Piovesan', + 'Nicolas Sabatier', + 'Nicolas Velin', + 'Nightfall', + 'NiKoB', + 'Nikolai M. Riabov', + 'Niko_Thien', + 'niska', + 'Nithir', + 'noisemonkey', + 'nomemohes', + 'nosense', + 'null', + 'Nuno Estêvão', + 'Nuno Santos', + 'nxxs', + 'nyo', + 'obo', + 'Ojan', + 'Olav Andreas Lindekleiv', + 'oldbeggar', + 'Olivier FAURAX', + 'orphe', + 'osantana', + 'Osman Tosun', + 'OssiR', + 'otypoks', + 'ounn', + 'Oz123', + 'Özgür BASKIN', + 'Pablo Carmona A.', + 'Pablo Ledesma', + 'Pablo Navarro Castillo', + 'Paco Molinero', + 'Pål-Eivind Johnsen', + 'pano', + 'Paolo Naldini', + 'Paracelsus', + 'Patryk13_03', + 'Patryk Skorupa', + 'PattogoTehen', + 'Paul Lange', + 'Pavcio', + 'Paweł Wysocki', + 'Pedro Brites Moita', + 'Pedro Clemente Pereira Neto', + 'Pekka "PEXI" Niemistö', + 'Penegal', + 'Penzo', + 'perdido', + 'Peter Kotrcka', + 'Peter Skov', + 'Peter Van den Bosch', + 'Petter Eklund', + 'Petter Viklund', + 'phatsphere', + 'Phenomen', + 'Philipi', + 'Philippides Homer', + 'phoenix', + 'pidi', + 'Pierre Quillery', + 'Pierre Rudloff', + 'Pierre Slamich', + 'Pietrao', + 'Piotr Strębski', + 'Piotr Wicijowski', + 'Pittmann Tamás', + 'Playmolas', + 'Prescott', + 'Prescott_SK', + 'pronull', + 'Przemysław Kulczycki', + 'Pumy', + 'pushpika', + 'PY', + 'qubicllj', + 'r21vo', + 'Rafał Barański', + 'rainofchaos', + 'Rajbir', + 'ras0ir', + 'Rat', + 'rd1381', + 'Renato', + 'Rene Hennig', + 'Rene Pärts', + 'Ricardo Duarte', + 'Richard', + 'Robert Hrovat', + 'Roberth Sjonøy', + 'Robert Lundmark', + 'Robin Jakobsson', + 'Robin Kåveland', + 'Rodrigo Donado', + 'Roel Groeneveld', + 'rohmaru', + 'Rolf Christensen', + 'Rolf Leggewie', + 'Roni Kantis', + 'Ronmi', + 'Rostislav Raykov', + 'royto', + 'RuiAmaro', + 'Rui Araújo', + 'Rui Moura', + 'Rune Svendsen', + 'Rusna', + 'Rytis', + 'Sabirov Mikhail', + 'salseeg', + 'Sami Koskinen', + 'Samir van de Sand', + 'Samuel Arroyo Acuña', + 'Samuel R. C. Vale', + 'Sanel', + 'Santi', + 'Santi Martínez Cantelli', + 'Sardan', + 'Sargate Kanogan', + 'Sarmad Jari', + 'Saša Bodiroža', + 'sat0shi', + 'Saulius Pranckevičius', + 'Savvas Radevic', + 'Sebastian Krauß', + 'Sebastián Porta', + 'Sedir', + 'Sefa Denizoğlu', + 'sekolands', + 'Selim Suerkan', + 'semsomi', + 'Sergii Golovatiuk', + 'setarcos', + 'Sheki', + 'Shironeko', + 'Shlomil', + 'silfiriel', + 'Simone Tolotti', + 'Simone Vendemia', + 'sirkubador', + 'Sławomir Więch', + 'slip', + 'slyon', + 'smoke', + 'Sonja', + 'spectral', + 'spin_555', + 'spitf1r3', + 'Spiziuz', + 'Spyros Theodoritsis', + 'SqUe', + 'Squigly', + 'srtck', + 'Stefan Horning', + 'Stefano Maggiolo', + 'Stefano Roberto Soleti', + 'steinberger', + 'Stéphane Travostino', + 'Stephan Klein', + 'Steven De Winter', + 'Stevie', + 'Stian24', + 'stylius', + 'Sukarn Maini', + 'Sunjae Park', + 'Susana Pereira', + 'szymon siglowy', + 'takercena', + 'TAS', + 'Taygeto', + 'temy4', + 'texxxxxx', + 'thamood', + 'Thanos Chatziathanassiou', + 'Tharawut Paripaiboon', + 'Theodoor', + 'Théophane Anestis', + 'Thor Marius K. Høgås', + 'Tiago Silva', + 'Tiago Sousa', + 'Tikkel', + 'tim__b', + 'Tim Bordemann', + 'Tim Fuchs', + 'Tim Kornhammar', + 'Timo', + 'Timo Jyrinki', + 'Timothy Babych', + 'TitkosRejtozo', + 'Tom', + 'Tomas Gustavsson', + 'Tomas Valentukevičius', + 'Tomasz Dominikowski', + 'Tomislav Plavčić', + 'Tom Mannerhagen', + 'Tommy Mikkelsen', + 'Tom Verdaat', + 'Tony Manco', + 'Tor Erling H. Opsahl', + 'Toudi', + 'tqm_z', + 'Trapanator', + 'Tribaal', + 'Triton', + 'TuniX12', + 'Tuomo Sipola', + 'turbojugend_gr', + 'Turtle.net', + 'twilight', + 'tymmej', + 'Ulrik', + 'Umarzuki Mochlis', + 'unikob', + 'Vadim Gusev', + 'Vagi', + 'Valentin Bora', + 'Valmantas Palikša', + 'VASKITTU', + 'Vassilis Skoullis', + 'vetal17', + 'vicedo', + 'viki', + 'villads hamann', + 'Vincent Garibal', + 'Vincent Ortalda', + 'vinchi007', + 'Vinícius de Figueiredo Silva', + 'Vinzenz Vietzke', + 'virtoo', + 'virtual_spirit', + 'Vitor Caike', + 'Vitor Lamas Gatti', + 'Vladimir Lazic', + 'Vladimir Sharshov', + 'Wanderlust', + 'Wander Nauta', + 'Ward De Ridder', + 'WebCrusader', + 'webdr', + 'Wentao Tang', + 'wilana', + 'Wilfredo Ernesto Guerrero Campos', + 'Wim Champagne', + 'World Sucks', + 'Xabi Ezpeleta', + 'Xavi de Moner', + 'XavierToo', + 'XChesser', + 'Xiaodong Xu', + 'xyb', + 'Yaron', + 'Yasen Pramatarov', + 'YesPoX', + 'Yuren Ju', + 'Yves MATHIEU', + 'zekopeko', + 'zhuqin', + 'Zissan', + 'Γιάννης Κατσαμπίρης', + 'Артём Попов', + 'Миша', + 'Шаймарданов Максим', + '蔡查理', + ] + ) + ) self.about.set_wrap_license(True) - self.about.set_license(_( - 'This program is free software; you can redistribute it and/or ' - 'modify it under the terms of the GNU General Public License as ' - 'published by the Free Software Foundation; either version 3 of ' - 'the License, or (at your option) any later version. \n\n' - 'This program ' - 'is distributed in the hope that it will be useful, but WITHOUT ' - 'ANY WARRANTY; without even the implied warranty of ' - 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ' - 'General Public License for more details. \n\n' - 'You should have received ' - 'a copy of the GNU General Public License along with this program; ' - 'if not, see <http://www.gnu.org/licenses>. \n\n' - 'In addition, as a ' - 'special exception, the copyright holders give permission to link ' - 'the code of portions of this program with the OpenSSL library. ' - 'You must obey the GNU General Public License in all respects for ' - 'all of the code used other than OpenSSL. \n\n' - 'If you modify file(s) ' - 'with this exception, you may extend this exception to your ' - 'version of the file(s), but you are not obligated to do so. If ' - 'you do not wish to do so, delete this exception statement from ' - 'your version. If you delete this exception statement from all ' - 'source files in the program, then also delete it here.', - )) + self.about.set_license( + _( + 'This program is free software; you can redistribute it and/or ' + 'modify it under the terms of the GNU General Public License as ' + 'published by the Free Software Foundation; either version 3 of ' + 'the License, or (at your option) any later version. \n\n' + 'This program ' + 'is distributed in the hope that it will be useful, but WITHOUT ' + 'ANY WARRANTY; without even the implied warranty of ' + 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ' + 'General Public License for more details. \n\n' + 'You should have received ' + 'a copy of the GNU General Public License along with this program; ' + 'if not, see <http://www.gnu.org/licenses>. \n\n' + 'In addition, as a ' + 'special exception, the copyright holders give permission to link ' + 'the code of portions of this program with the OpenSSL library. ' + 'You must obey the GNU General Public License in all respects for ' + 'all of the code used other than OpenSSL. \n\n' + 'If you modify file(s) ' + 'with this exception, you may extend this exception to your ' + 'version of the file(s), but you are not obligated to do so. If ' + 'you do not wish to do so, delete this exception statement from ' + 'your version. If you delete this exception statement from all ' + 'source files in the program, then also delete it here.' + ) + ) self.about.set_website('http://deluge-torrent.org') self.about.set_website_label('deluge-torrent.org') @@ -261,11 +824,11 @@ class AboutDialog(object): if client.connected(): if not client.is_standalone(): self.about.set_comments( - self.about.get_comments() + _('Server:') + ' %coreversion%\n', + self.about.get_comments() + _('Server:') + ' %coreversion%\n' ) self.about.set_comments( - self.about.get_comments() + '\n' + _('libtorrent:') + ' %ltversion%\n', + self.about.get_comments() + '\n' + _('libtorrent:') + ' %ltversion%\n' ) def on_lt_version(result): diff --git a/deluge/ui/gtkui/addtorrentdialog.py b/deluge/ui/gtkui/addtorrentdialog.py index ff002d019..365d0f301 100644 --- a/deluge/ui/gtkui/addtorrentdialog.py +++ b/deluge/ui/gtkui/addtorrentdialog.py @@ -24,7 +24,11 @@ from deluge.configmanager import ConfigManager from deluge.httpdownloader import download_file from deluge.ui.client import client from deluge.ui.common import TorrentInfo -from deluge.ui.gtkui.common import get_clipboard_text, listview_replace_treestore, reparent_iter +from deluge.ui.gtkui.common import ( + get_clipboard_text, + listview_replace_treestore, + reparent_iter, +) from deluge.ui.gtkui.dialogs import ErrorDialog from deluge.ui.gtkui.edittrackersdialog import trackers_tiers_from_text from deluge.ui.gtkui.path_chooser import PathChooser @@ -38,17 +42,24 @@ class AddTorrentDialog(component.Component): component.Component.__init__(self, 'AddTorrentDialog') self.builder = gtk.Builder() # The base dialog - self.builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'add_torrent_dialog.ui'), - )) + self.builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'add_torrent_dialog.ui') + ) + ) # The infohash dialog - self.builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'add_torrent_dialog.infohash.ui'), - )) + self.builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', + os.path.join('glade', 'add_torrent_dialog.infohash.ui'), + ) + ) # The url dialog - self.builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'add_torrent_dialog.url.ui'), - )) + self.builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'add_torrent_dialog.url.ui') + ) + ) self.dialog = self.builder.get_object('dialog_add_torrent') @@ -58,7 +69,7 @@ class AddTorrentDialog(component.Component): # download?, path, filesize, sequence number, inconsistent? self.files_treestore = gtk.TreeStore( - bool, str, TYPE_UINT64, TYPE_INT64, bool, str, + bool, str, TYPE_UINT64, TYPE_INT64, bool, str ) self.files_treestore.set_sort_column_id(1, gtk.SORT_ASCENDING) @@ -110,11 +121,11 @@ class AddTorrentDialog(component.Component): self.listview_files.set_model(self.files_treestore) self.listview_files.get_selection().set_mode(gtk.SELECTION_MULTIPLE) - self.listview_torrents.get_selection().connect('changed', self._on_torrent_changed) - self.torrent_liststore.connect( - 'row-inserted', self.update_dialog_title_count) - self.torrent_liststore.connect( - 'row-deleted', self.update_dialog_title_count) + self.listview_torrents.get_selection().connect( + 'changed', self._on_torrent_changed + ) + self.torrent_liststore.connect('row-inserted', self.update_dialog_title_count) + self.torrent_liststore.connect('row-deleted', self.update_dialog_title_count) self.setup_move_completed_path_chooser() self.setup_download_location_path_chooser() @@ -136,7 +147,9 @@ class AddTorrentDialog(component.Component): 'move_completed_paths_list', ] # self.core_keys += self.move_completed_path_chooser.get_config_keys() - self.builder.get_object('notebook1').connect('switch-page', self._on_switch_page) + self.builder.get_object('notebook1').connect( + 'switch-page', self._on_switch_page + ) def start(self): self.update_core_config() @@ -177,9 +190,7 @@ class AddTorrentDialog(component.Component): d = client.core.get_config_values(self.core_keys) d.addCallback(self._on_config_values, show, focus) - def _add_torrent_liststore( - self, info_hash, name, filename, files, filedata, - ): + def _add_torrent_liststore(self, info_hash, name, filename, files, filedata): """Add a torrent to torrent_liststore.""" if info_hash in self.files: return False @@ -197,8 +208,7 @@ class AddTorrentDialog(component.Component): def update_dialog_title_count(self, *args): """Update the AddTorrent dialog title with current torrent count.""" - self.dialog.set_title( - _('Add Torrents (%d)') % len(self.torrent_liststore)) + self.dialog.set_title(_('Add Torrents (%d)') % len(self.torrent_liststore)) def show_already_added_dialog(self, count): """Show a message about trying to add duplicate torrents.""" @@ -207,7 +217,7 @@ class AddTorrentDialog(component.Component): _('Duplicate torrent(s)'), _( 'You cannot add the same torrent twice.' - ' %d torrents were already added.' % count, + ' %d torrents were already added.' % count ), self.dialog, ).run() @@ -225,11 +235,7 @@ class AddTorrentDialog(component.Component): continue if not self._add_torrent_liststore( - info.info_hash, - info.name, - filename, - info.files, - info.filedata, + info.info_hash, info.name, filename, info.files, info.filedata ): already_added += 1 @@ -257,12 +263,14 @@ class AddTorrentDialog(component.Component): """Show magnet files fetching or failed message above files list.""" if torrent_id in self.prefetching_magnets: self.builder.get_object('prefetch_label').set_text( - _('Please wait for files...')) + _('Please wait for files...') + ) self.builder.get_object('prefetch_spinner').show() self.builder.get_object('prefetch_hbox').show() elif not files: self.builder.get_object('prefetch_label').set_text( - _('Unable to download files for this magnet')) + _('Unable to download files for this magnet') + ) self.builder.get_object('prefetch_spinner').hide() self.builder.get_object('prefetch_hbox').show() else: @@ -281,10 +289,7 @@ class AddTorrentDialog(component.Component): torrent_id = magnet['info_hash'] files = magnet['files_tree'] if not self._add_torrent_liststore( - torrent_id, magnet['name'], - xml_escape(uri), - files, - None, + torrent_id, magnet['name'], xml_escape(uri), files, None ): already_added += 1 continue @@ -343,11 +348,7 @@ class AddTorrentDialog(component.Component): split_files = {} for idx, _file in enumerate(files): self.prepare_file( - _file, - _file['path'], - idx, - _file.get('download', True), - split_files, + _file, _file['path'], idx, _file.get('download', True), split_files ) self.add_files(None, split_files) self.listview_files.expand_row(b'0', False) @@ -357,12 +358,15 @@ class AddTorrentDialog(component.Component): if first_slash_index == -1: files_storage[file_name] = (file_num, _file, download) else: - file_name_chunk = file_name[:first_slash_index + 1] + file_name_chunk = file_name[: first_slash_index + 1] if file_name_chunk not in files_storage: files_storage[file_name_chunk] = {} self.prepare_file( - _file, file_name[first_slash_index + 1:], - file_num, download, files_storage[file_name_chunk], + _file, + file_name[first_slash_index + 1 :], + file_num, + download, + files_storage[file_name_chunk], ) def add_files(self, parent_iter, split_files): @@ -370,17 +374,15 @@ class AddTorrentDialog(component.Component): for key, value in split_files.items(): if key.endswith(os.path.sep): chunk_iter = self.files_treestore.append( - parent_iter, [True, key, 0, -1, False, gtk.STOCK_DIRECTORY], + parent_iter, [True, key, 0, -1, False, gtk.STOCK_DIRECTORY] ) chunk_size = self.add_files(chunk_iter, value) self.files_treestore.set(chunk_iter, 2, chunk_size) ret += chunk_size else: self.files_treestore.append( - parent_iter, [ - value[2], key, value[1]['size'], - value[0], False, gtk.STOCK_FILE, - ], + parent_iter, + [value[2], key, value[1]['size'], value[0], False, gtk.STOCK_FILE], ) ret += value[1]['size'] if parent_iter and self.files_treestore.iter_has_child(parent_iter): @@ -407,24 +409,30 @@ class AddTorrentDialog(component.Component): def load_path_choosers_data(self): self.move_completed_path_chooser.set_text( - self.core_config['move_completed_path'], - cursor_end=False, default_text=True, + self.core_config['move_completed_path'], cursor_end=False, default_text=True ) self.download_location_path_chooser.set_text( - self.core_config['download_location'], - cursor_end=False, default_text=True, + self.core_config['download_location'], cursor_end=False, default_text=True + ) + self.builder.get_object('chk_move_completed').set_active( + self.core_config['move_completed'] ) - self.builder.get_object('chk_move_completed').set_active(self.core_config['move_completed']) def setup_move_completed_path_chooser(self): - self.move_completed_hbox = self.builder.get_object('hbox_move_completed_chooser') + self.move_completed_hbox = self.builder.get_object( + 'hbox_move_completed_chooser' + ) self.move_completed_path_chooser = PathChooser('move_completed_paths_list') self.move_completed_hbox.add(self.move_completed_path_chooser) self.move_completed_hbox.show_all() def setup_download_location_path_chooser(self): - self.download_location_hbox = self.builder.get_object('hbox_download_location_chooser') - self.download_location_path_chooser = PathChooser('download_location_paths_list') + self.download_location_hbox = self.builder.get_object( + 'hbox_download_location_chooser' + ) + self.download_location_path_chooser = PathChooser( + 'download_location_paths_list' + ) self.download_location_hbox.add(self.download_location_path_chooser) self.download_location_hbox.show_all() @@ -435,43 +443,42 @@ class AddTorrentDialog(component.Component): options = self.options[torrent_id] - self.download_location_path_chooser.set_text(options['download_location'], cursor_end=True) - self.move_completed_path_chooser.set_text(options['move_completed_path'], cursor_end=True) - - self.builder.get_object('spin_maxdown').set_value( - options['max_download_speed'], + self.download_location_path_chooser.set_text( + options['download_location'], cursor_end=True ) - self.builder.get_object('spin_maxup').set_value( - options['max_upload_speed'], + self.move_completed_path_chooser.set_text( + options['move_completed_path'], cursor_end=True ) + + self.builder.get_object('spin_maxdown').set_value(options['max_download_speed']) + self.builder.get_object('spin_maxup').set_value(options['max_upload_speed']) self.builder.get_object('spin_maxconnections').set_value( - options['max_connections'], + options['max_connections'] ) self.builder.get_object('spin_maxupslots').set_value( - options['max_upload_slots'], - ) - self.builder.get_object('chk_paused').set_active( - options['add_paused'], + options['max_upload_slots'] ) + self.builder.get_object('chk_paused').set_active(options['add_paused']) self.builder.get_object('chk_pre_alloc').set_active( - options['pre_allocate_storage'], + options['pre_allocate_storage'] ) self.builder.get_object('chk_prioritize').set_active( - options['prioritize_first_last_pieces'], + options['prioritize_first_last_pieces'] ) self.builder.get_object('chk_sequential_download').set_active( - options['sequential_download'], + options['sequential_download'] ) self.builder.get_object('chk_move_completed').set_active( - options['move_completed'], + options['move_completed'] ) def save_torrent_options(self, row=None): # Keeps the torrent options dictionary up-to-date with what the user has # selected. if row is None: - if self.previous_selected_torrent and \ - self.torrent_liststore.iter_is_valid(self.previous_selected_torrent): + if self.previous_selected_torrent and self.torrent_liststore.iter_is_valid( + self.previous_selected_torrent + ): row = self.previous_selected_torrent else: return @@ -485,25 +492,39 @@ class AddTorrentDialog(component.Component): options['download_location'] = self.download_location_path_chooser.get_text() options['move_completed_path'] = self.move_completed_path_chooser.get_text() - options['pre_allocate_storage'] = self.builder.get_object('chk_pre_alloc').get_active() - options['move_completed'] = self.builder.get_object('chk_move_completed').get_active() - options['max_download_speed'] = self.builder.get_object('spin_maxdown').get_value() + options['pre_allocate_storage'] = self.builder.get_object( + 'chk_pre_alloc' + ).get_active() + options['move_completed'] = self.builder.get_object( + 'chk_move_completed' + ).get_active() + options['max_download_speed'] = self.builder.get_object( + 'spin_maxdown' + ).get_value() options['max_upload_speed'] = self.builder.get_object('spin_maxup').get_value() - options['max_connections'] = self.builder.get_object('spin_maxconnections').get_value_as_int() - options['max_upload_slots'] = self.builder.get_object('spin_maxupslots').get_value_as_int() + options['max_connections'] = self.builder.get_object( + 'spin_maxconnections' + ).get_value_as_int() + options['max_upload_slots'] = self.builder.get_object( + 'spin_maxupslots' + ).get_value_as_int() options['add_paused'] = self.builder.get_object('chk_paused').get_active() - options['prioritize_first_last_pieces'] = self.builder.get_object('chk_prioritize').get_active() - options['sequential_download'] = self.builder.get_object( - 'chk_sequential_download', - ).get_active() or False - options['move_completed'] = self.builder.get_object('chk_move_completed').get_active() + options['prioritize_first_last_pieces'] = self.builder.get_object( + 'chk_prioritize' + ).get_active() + options['sequential_download'] = ( + self.builder.get_object('chk_sequential_download').get_active() or False + ) + options['move_completed'] = self.builder.get_object( + 'chk_move_completed' + ).get_active() options['seed_mode'] = self.builder.get_object('chk_seed_mode').get_active() self.options[torrent_id] = options # Save the file priorities files_priorities = self.build_priorities( - self.files_treestore.get_iter_first(), {}, + self.files_treestore.get_iter_first(), {} ) if len(files_priorities) > 0: @@ -513,9 +534,13 @@ class AddTorrentDialog(component.Component): def build_priorities(self, _iter, priorities): while _iter is not None: if self.files_treestore.iter_has_child(_iter): - self.build_priorities(self.files_treestore.iter_children(_iter), priorities) + self.build_priorities( + self.files_treestore.iter_children(_iter), priorities + ) elif not self.files_treestore.get_value(_iter, 1).endswith(os.path.sep): - priorities[self.files_treestore.get_value(_iter, 3)] = self.files_treestore.get_value(_iter, 0) + priorities[ + self.files_treestore.get_value(_iter, 3) + ] = self.files_treestore.get_value(_iter, 0) _iter = self.files_treestore.iter_next(_iter) return priorities @@ -528,31 +553,29 @@ class AddTorrentDialog(component.Component): self.load_path_choosers_data() self.builder.get_object('chk_pre_alloc').set_active( - self.core_config['pre_allocate_storage'], + self.core_config['pre_allocate_storage'] ) self.builder.get_object('spin_maxdown').set_value( - self.core_config['max_download_speed_per_torrent'], + self.core_config['max_download_speed_per_torrent'] ) self.builder.get_object('spin_maxup').set_value( - self.core_config['max_upload_speed_per_torrent'], + self.core_config['max_upload_speed_per_torrent'] ) self.builder.get_object('spin_maxconnections').set_value( - self.core_config['max_connections_per_torrent'], + self.core_config['max_connections_per_torrent'] ) self.builder.get_object('spin_maxupslots').set_value( - self.core_config['max_upload_slots_per_torrent'], - ) - self.builder.get_object('chk_paused').set_active( - self.core_config['add_paused'], + self.core_config['max_upload_slots_per_torrent'] ) + self.builder.get_object('chk_paused').set_active(self.core_config['add_paused']) self.builder.get_object('chk_prioritize').set_active( - self.core_config['prioritize_first_last_pieces'], + self.core_config['prioritize_first_last_pieces'] ) self.builder.get_object('chk_sequential_download').set_active( - self.core_config['sequential_download'], + self.core_config['sequential_download'] ) self.builder.get_object('chk_move_completed').set_active( - self.core_config['move_completed'], + self.core_config['move_completed'] ) self.builder.get_object('chk_seed_mode').set_active(False) @@ -595,7 +618,9 @@ class AddTorrentDialog(component.Component): this_level_toggle = None while _iter is not None: if self.files_treestore.iter_has_child(_iter): - toggle = self.update_treeview_toggles(self.files_treestore.iter_children(_iter)) + toggle = self.update_treeview_toggles( + self.files_treestore.iter_children(_iter) + ) if toggle == toggle_inconsistent: self.files_treestore.set_value(_iter, 4, True) else: @@ -619,7 +644,9 @@ class AddTorrentDialog(component.Component): None, gtk.FILE_CHOOSER_ACTION_OPEN, buttons=( - gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, + gtk.STOCK_CANCEL, + gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, gtk.RESPONSE_OK, ), ) @@ -699,7 +726,9 @@ class AddTorrentDialog(component.Component): def add_from_url(self, url): dialog = gtk.Dialog( _('Downloading...'), - flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, + flags=gtk.DIALOG_MODAL + | gtk.DIALOG_DESTROY_WITH_PARENT + | gtk.DIALOG_NO_SEPARATOR, parent=self.dialog, ) dialog.set_transient_for(self.dialog) @@ -710,17 +739,21 @@ class AddTorrentDialog(component.Component): # Create a tmp file path import tempfile + tmp_fd, tmp_file = tempfile.mkstemp(prefix='deluge_url.', suffix='.torrent') def on_part(data, current_length, total_length): if total_length: percent = current_length / total_length pb.set_fraction(percent) - pb.set_text('%.2f%% (%s / %s)' % ( - percent * 100, - deluge.common.fsize(current_length), - deluge.common.fsize(total_length), - )) + pb.set_text( + '%.2f%% (%s / %s)' + % ( + percent * 100, + deluge.common.fsize(current_length), + deluge.common.fsize(total_length), + ) + ) else: pb.pulse() pb.set_text('%s' % deluge.common.fsize(current_length)) @@ -733,8 +766,10 @@ class AddTorrentDialog(component.Component): log.debug('Download failed: %s', result) dialog.destroy() ErrorDialog( - _('Download Failed'), '%s %s' % (_('Failed to download:'), url), - details=result.getErrorMessage(), parent=self.dialog, + _('Download Failed'), + '%s %s' % (_('Failed to download:'), url), + details=result.getErrorMessage(), + parent=self.dialog, ).run() return result @@ -822,11 +857,13 @@ class AddTorrentDialog(component.Component): options['file_priorities'] = file_priorities if self.infos[torrent_id]: - torrents_to_add.append(( - os.path.split(filename)[-1], - b64encode(self.infos[torrent_id]), - options, - )) + torrents_to_add.append( + ( + os.path.split(filename)[-1], + b64encode(self.infos[torrent_id]), + options, + ) + ) elif deluge.common.is_magnet(filename): client.core.add_torrent_magnet(filename, options) @@ -834,11 +871,16 @@ class AddTorrentDialog(component.Component): def on_torrents_added(errors): if errors: - log.info('Failed to add %d out of %d torrents.', len(errors), len(torrents_to_add)) + log.info( + 'Failed to add %d out of %d torrents.', + len(errors), + len(torrents_to_add), + ) for e in errors: log.info('Torrent add failed: %s', e) else: log.info('Successfully added %d torrents.', len(torrents_to_add)) + client.core.add_torrent_files(torrents_to_add).addCallback(on_torrents_added) def on_button_apply_clicked(self, widget): @@ -919,7 +961,9 @@ class AddTorrentDialog(component.Component): # and then move the file iter to top split_text = new_text.split(os.path.sep) for s in split_text[:-1]: - parent = self.files_treestore.append(parent, [True, s, 0, -1, False, gtk.STOCK_DIRECTORY]) + parent = self.files_treestore.append( + parent, [True, s, 0, -1, False, gtk.STOCK_DIRECTORY] + ) self.files_treestore[itr][1] = split_text[-1] reparent_iter(self.files_treestore, itr, parent) @@ -942,7 +986,7 @@ class AddTorrentDialog(component.Component): # Get the file path base once, since it will be the same for # all siblings file_path_base = self.get_file_path( - self.files_treestore.iter_parent(row), + self.files_treestore.iter_parent(row) ) # Iterate through all the siblings at this level @@ -977,9 +1021,8 @@ class AddTorrentDialog(component.Component): # We don't iterate over the last item because we'll just use # the existing itr and change the text parent = self.files_treestore.append( - parent, [ - True, s + os.path.sep, 0, -1, False, gtk.STOCK_DIRECTORY, - ], + parent, + [True, s + os.path.sep, 0, -1, False, gtk.STOCK_DIRECTORY], ) self.files_treestore[itr][1] = split_text[-1] + os.path.sep diff --git a/deluge/ui/gtkui/common.py b/deluge/ui/gtkui/common.py index a8701b49a..a02eace0a 100644 --- a/deluge/ui/gtkui/common.py +++ b/deluge/ui/gtkui/common.py @@ -17,8 +17,22 @@ import sys import six.moves.cPickle as pickle from gobject import GError -from gtk import SORT_ASCENDING, Menu, MenuItem, RadioMenuItem, SeparatorMenuItem, clipboard_get, icon_theme_get_default -from gtk.gdk import COLORSPACE_RGB, SELECTION_PRIMARY, Pixbuf, pixbuf_new_from_file, pixbuf_new_from_file_at_size +from gtk import ( + SORT_ASCENDING, + Menu, + MenuItem, + RadioMenuItem, + SeparatorMenuItem, + clipboard_get, + icon_theme_get_default, +) +from gtk.gdk import ( + COLORSPACE_RGB, + SELECTION_PRIMARY, + Pixbuf, + pixbuf_new_from_file, + pixbuf_new_from_file_at_size, +) from deluge.common import get_pixmap, osx_check, windows_check @@ -73,8 +87,14 @@ def get_logo(size): def build_menu_radio_list( - value_list, callback, pref_value=None, suffix=None, show_notset=False, - notset_label='∞', notset_lessthan=0, show_other=False, + value_list, + callback, + pref_value=None, + suffix=None, + show_notset=False, + notset_label='∞', + notset_lessthan=0, + show_other=False, ): """Build a menu with radio menu items from a list and connect them to the callback. @@ -145,7 +165,9 @@ def reparent_iter(treestore, itr, parent, move_siblings=False): def move_children(i, dest): while i: - n_cols = treestore.append(dest, treestore.get(i, *range(treestore.get_n_columns()))) + n_cols = treestore.append( + dest, treestore.get(i, *range(treestore.get_n_columns())) + ) to_remove = i if treestore.iter_children(i): move_children(treestore.iter_children(i), n_cols) @@ -208,13 +230,22 @@ def associate_magnet_links(overwrite=False): magnet_key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT, 'Magnet') except WindowsError: # Could not create for all users, falling back to current user - magnet_key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, 'Software\\Classes\\Magnet') + magnet_key = winreg.CreateKey( + winreg.HKEY_CURRENT_USER, 'Software\\Classes\\Magnet' + ) winreg.SetValue(magnet_key, '', winreg.REG_SZ, 'URL:Magnet Protocol') winreg.SetValueEx(magnet_key, 'URL Protocol', 0, winreg.REG_SZ, '') winreg.SetValueEx(magnet_key, 'BrowserFlags', 0, winreg.REG_DWORD, 0x8) - winreg.SetValue(magnet_key, 'DefaultIcon', winreg.REG_SZ, '{},0'.format(deluge_exe)) - winreg.SetValue(magnet_key, r'shell\open\command', winreg.REG_SZ, '"{}" "%1"'.format(deluge_exe)) + winreg.SetValue( + magnet_key, 'DefaultIcon', winreg.REG_SZ, '{},0'.format(deluge_exe) + ) + winreg.SetValue( + magnet_key, + r'shell\open\command', + winreg.REG_SZ, + '"{}" "%1"'.format(deluge_exe), + ) winreg.CloseKey(magnet_key) # Don't try associate magnet on OSX see: #2420 @@ -223,7 +254,9 @@ def associate_magnet_links(overwrite=False): try: import gconf except ImportError: - log.debug('gconf not available, so will not attempt to register magnet uri handler') + log.debug( + 'gconf not available, so will not attempt to register magnet uri handler' + ) return False else: key = '/desktop/gnome/url-handlers/magnet/command' @@ -231,12 +264,18 @@ def associate_magnet_links(overwrite=False): if (gconf_client.get(key) and overwrite) or not gconf_client.get(key): # We are either going to overwrite the key, or do it if it hasn't been set yet if gconf_client.set_string(key, 'deluge "%s"'): - gconf_client.set_bool('/desktop/gnome/url-handlers/magnet/needs_terminal', False) - gconf_client.set_bool('/desktop/gnome/url-handlers/magnet/enabled', True) + gconf_client.set_bool( + '/desktop/gnome/url-handlers/magnet/needs_terminal', False + ) + gconf_client.set_bool( + '/desktop/gnome/url-handlers/magnet/enabled', True + ) log.info('Deluge registered as default magnet uri handler!') return True else: - log.error('Unable to register Deluge as default magnet uri handler.') + log.error( + 'Unable to register Deluge as default magnet uri handler.' + ) return False return False @@ -249,6 +288,7 @@ def save_pickled_state_file(filename, state): state (state): The data to be pickled and written to file """ from deluge.configmanager import get_config_dir + filepath = os.path.join(get_config_dir(), 'gtkui_state', filename) filepath_bak = filepath + '.bak' filepath_tmp = filepath + '.tmp' @@ -285,6 +325,7 @@ def load_pickled_state_file(filename): state: the unpickled state """ from deluge.configmanager import get_config_dir + filepath = os.path.join(get_config_dir(), 'gtkui_state', filename) filepath_bak = filepath + '.bak' old_data_filepath = os.path.join(get_config_dir(), filename) @@ -328,6 +369,9 @@ def listview_replace_treestore(listview): def get_clipboard_text(): - text = clipboard_get(selection=SELECTION_PRIMARY).wait_for_text() or clipboard_get().wait_for_text() + text = ( + clipboard_get(selection=SELECTION_PRIMARY).wait_for_text() + or clipboard_get().wait_for_text() + ) if text: return text.strip() diff --git a/deluge/ui/gtkui/connectionmanager.py b/deluge/ui/gtkui/connectionmanager.py index 5b860e699..6320f649a 100644 --- a/deluge/ui/gtkui/connectionmanager.py +++ b/deluge/ui/gtkui/connectionmanager.py @@ -45,11 +45,7 @@ HOSTLIST_PIXBUFS = [ # This is populated in ConnectionManager.show ] -HOSTLIST_STATUS = [ - 'Offline', - 'Online', - 'Connected', -] +HOSTLIST_STATUS = ['Offline', 'Online', 'Connected'] def cell_render_host(column, cell, model, row, data): @@ -92,9 +88,11 @@ class ConnectionManager(component.Component): def show(self): """Show the ConnectionManager dialog.""" self.builder = gtk.Builder() - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.ui'), - )) + self.builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.ui') + ) + ) self.connection_manager = self.builder.get_object('connection_manager') self.connection_manager.set_transient_for(component.get('MainWindow').window) @@ -102,7 +100,7 @@ class ConnectionManager(component.Component): if not HOSTLIST_PIXBUFS: for stock_id in (gtk.STOCK_NO, gtk.STOCK_YES, gtk.STOCK_CONNECT): HOSTLIST_PIXBUFS.append( - self.connection_manager.render_icon(stock_id, gtk.ICON_SIZE_MENU), + self.connection_manager.render_icon(stock_id, gtk.ICON_SIZE_MENU) ) # Setup the hostlist liststore and treeview @@ -121,7 +119,9 @@ class ConnectionManager(component.Component): column.set_expand(True) self.treeview.append_column(column) - column = gtk.TreeViewColumn(_('Version'), gtk.CellRendererText(), text=HOSTLIST_COL_VERSION) + column = gtk.TreeViewColumn( + _('Version'), gtk.CellRendererText(), text=HOSTLIST_COL_VERSION + ) self.treeview.append_column(column) # Load any saved host entries @@ -132,7 +132,9 @@ class ConnectionManager(component.Component): # Connect the signals to the handlers self.builder.connect_signals(self) - self.treeview.get_selection().connect('changed', self.on_hostlist_selection_changed) + self.treeview.get_selection().connect( + 'changed', self.on_hostlist_selection_changed + ) # Set running True before update status call. self.running = True @@ -167,13 +169,13 @@ class ConnectionManager(component.Component): def _load_widget_config(self): """Set the widgets to show the correct options from the config.""" self.builder.get_object('chk_autoconnect').set_active( - self.gtkui_config['autoconnect'], + self.gtkui_config['autoconnect'] ) self.builder.get_object('chk_autostart').set_active( - self.gtkui_config['autostart_localhost'], + self.gtkui_config['autostart_localhost'] ) self.builder.get_object('chk_donotshow').set_active( - not self.gtkui_config['show_connection_manager_on_start'], + not self.gtkui_config['show_connection_manager_on_start'] ) def _update_host_status(self): @@ -210,9 +212,11 @@ class ConnectionManager(component.Component): self.builder.get_object('button_removehost').set_sensitive(False) self.builder.get_object('button_startdaemon').set_sensitive(False) self.builder.get_object('image_startdaemon').set_from_stock( - gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU, + gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU + ) + self.builder.get_object('label_startdaemon').set_text_with_mnemonic( + '_Start Daemon' ) - self.builder.get_object('label_startdaemon').set_text_with_mnemonic('_Start Daemon') model, row = self.treeview.get_selection().get_selected() if row: @@ -226,7 +230,9 @@ class ConnectionManager(component.Component): try: gethostbyname(host) except gaierror as ex: - log.error('Error resolving host %s to ip: %s', row[HOSTLIST_COL_HOST], ex.args[1]) + log.error( + 'Error resolving host %s to ip: %s', row[HOSTLIST_COL_HOST], ex.args[1] + ) self.builder.get_object('button_connect').set_sensitive(False) return @@ -236,9 +242,11 @@ class ConnectionManager(component.Component): if status == 'Connected' or status == 'Online': self.builder.get_object('button_connect').set_sensitive(True) self.builder.get_object('image_startdaemon').set_from_stock( - gtk.STOCK_STOP, gtk.ICON_SIZE_MENU, + gtk.STOCK_STOP, gtk.ICON_SIZE_MENU + ) + self.builder.get_object('label_startdaemon').set_text_with_mnemonic( + _('_Stop Daemon') ) - self.builder.get_object('label_startdaemon').set_text_with_mnemonic(_('_Stop Daemon')) self.builder.get_object('button_startdaemon').set_sensitive(False) if status == 'Connected': # Display a disconnect button if we're connected to this host @@ -315,6 +323,7 @@ class ConnectionManager(component.Component): def dialog_finished(response_id): if response_id == gtk.RESPONSE_OK: self._connect(host_id, dialog.get_username(), dialog.get_password()) + return dialog.run().addCallback(dialog_finished) elif reason.check(IncompatibleClient): @@ -322,13 +331,15 @@ class ConnectionManager(component.Component): if try_counter: log.info('Retrying connection.. Retries left: %s', try_counter) - return reactor.callLater(0.5, self._connect, host_id, try_counter=try_counter - 1) + return reactor.callLater( + 0.5, self._connect, host_id, try_counter=try_counter - 1 + ) msg = str(reason.value) if not self.gtkui_config['autostart_localhost']: msg += '\n' + _( 'Auto-starting the daemon locally is not enabled. ' - 'See "Options" on the "Connection Manager".', + 'See "Options" on the "Connection Manager".' ) ErrorDialog(_('Failed To Connect'), msg).run() @@ -341,8 +352,10 @@ class ConnectionManager(component.Component): host_id, host, port, __, __, status, __ = model[row] # If status is connected then connect button disconnects instead. if status == 'Connected': + def on_disconnect(reason): self._update_host_status() + return client.disconnect().addCallback(on_disconnect) try_counter = 0 @@ -372,9 +385,12 @@ class ConnectionManager(component.Component): list: The new host info values (host, port, user, pass). """ - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.addhost.ui'), - )) + self.builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', + os.path.join('glade', 'connection_manager.addhost.ui'), + ) + ) dialog = self.builder.get_object('addhost_dialog') dialog.set_transient_for(self.connection_manager) hostname_entry = self.builder.get_object('entry_hostname') @@ -410,7 +426,9 @@ class ConnectionManager(component.Component): except ValueError as ex: ErrorDialog(_('Error Adding Host'), ex).run() else: - self.liststore.append([host_id, hostname, port, username, password, 'Offline', '']) + self.liststore.append( + [host_id, hostname, port, username, password, 'Offline', ''] + ) self._update_host_status() def on_button_edithost_clicked(self, widget=None): @@ -420,8 +438,10 @@ class ConnectionManager(component.Component): host_id = model[row][HOSTLIST_COL_ID] if status == 'Connected': + def on_disconnect(reason): self._update_host_status() + client.disconnect().addCallback(on_disconnect) return @@ -439,7 +459,15 @@ class ConnectionManager(component.Component): except ValueError as ex: ErrorDialog(_('Error Updating Host'), ex).run() else: - self.liststore[row] = host_id, hostname, port, username, password, '', '' + self.liststore[row] = ( + host_id, + hostname, + port, + username, + password, + '', + '', + ) self._update_host_status() def on_button_removehost_clicked(self, widget): @@ -502,11 +530,15 @@ class ConnectionManager(component.Component): self._update_widget_buttons() def on_chk_toggled(self, widget): - self.gtkui_config['autoconnect'] = self.builder.get_object('chk_autoconnect').get_active() - self.gtkui_config['autostart_localhost'] = self.builder.get_object('chk_autostart').get_active() - self.gtkui_config['show_connection_manager_on_start'] = not self.builder.get_object( - 'chk_donotshow', + self.gtkui_config['autoconnect'] = self.builder.get_object( + 'chk_autoconnect' + ).get_active() + self.gtkui_config['autostart_localhost'] = self.builder.get_object( + 'chk_autostart' ).get_active() + self.gtkui_config[ + 'show_connection_manager_on_start' + ] = not self.builder.get_object('chk_donotshow').get_active() def on_entry_host_paste_clipboard(self, widget): text = get_clipboard_text() diff --git a/deluge/ui/gtkui/createtorrentdialog.py b/deluge/ui/gtkui/createtorrentdialog.py index 5ca7586fc..8a61ee3d1 100644 --- a/deluge/ui/gtkui/createtorrentdialog.py +++ b/deluge/ui/gtkui/createtorrentdialog.py @@ -21,14 +21,16 @@ import deluge.component as component from deluge.common import get_path_size, is_url, resource_filename from deluge.configmanager import ConfigManager from deluge.ui.client import client -from deluge.ui.gtkui.edittrackersdialog import last_tier_trackers_from_liststore, trackers_tiers_from_text +from deluge.ui.gtkui.edittrackersdialog import ( + last_tier_trackers_from_liststore, + trackers_tiers_from_text, +) from deluge.ui.gtkui.torrentview_data_funcs import cell_data_size log = logging.getLogger(__name__) class CreateTorrentDialog(object): - def __init__(self): pass @@ -36,21 +38,32 @@ class CreateTorrentDialog(object): self.builder = gtk.Builder() # The main dialog - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'create_torrent_dialog.ui'), - )) + self.builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'create_torrent_dialog.ui') + ) + ) # The remote path dialog - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'create_torrent_dialog.remote_path.ui'), - )) + self.builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', + os.path.join('glade', 'create_torrent_dialog.remote_path.ui'), + ) + ) # The remote save dialog - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'create_torrent_dialog.remote_save.ui'), - )) + self.builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', + os.path.join('glade', 'create_torrent_dialog.remote_save.ui'), + ) + ) # The progress dialog - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'create_torrent_dialog.progress.ui'), - )) + self.builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', + os.path.join('glade', 'create_torrent_dialog.progress.ui'), + ) + ) self.config = ConfigManager('gtkui.conf') @@ -85,10 +98,10 @@ class CreateTorrentDialog(object): self.trackers_liststore = gtk.ListStore(int, str) self.builder.get_object('tracker_treeview').append_column( - gtk.TreeViewColumn(_('Tier'), gtk.CellRendererText(), text=0), + gtk.TreeViewColumn(_('Tier'), gtk.CellRendererText(), text=0) ) self.builder.get_object('tracker_treeview').append_column( - gtk.TreeViewColumn(_('Tracker'), gtk.CellRendererText(), text=1), + gtk.TreeViewColumn(_('Tracker'), gtk.CellRendererText(), text=1) ) self.builder.get_object('tracker_treeview').set_model(self.trackers_liststore) @@ -132,8 +145,10 @@ class CreateTorrentDialog(object): self.dialog, gtk.FILE_CHOOSER_ACTION_OPEN, buttons=( - gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, - gtk.STOCK_OPEN, gtk.RESPONSE_OK, + gtk.STOCK_CANCEL, + gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, + gtk.RESPONSE_OK, ), ) @@ -165,8 +180,10 @@ class CreateTorrentDialog(object): self.dialog, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=( - gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, - gtk.STOCK_OPEN, gtk.RESPONSE_OK, + gtk.STOCK_CANCEL, + gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, + gtk.RESPONSE_OK, ), ) @@ -207,6 +224,7 @@ class CreateTorrentDialog(object): self.files_treestore.clear() self.files_treestore.append(None, [result, gtk.STOCK_NETWORK, size]) self.adjust_piece_size() + client.core.get_path_size(result).addCallback(_on_get_path_size) client.force_call(True) @@ -243,11 +261,14 @@ class CreateTorrentDialog(object): else: # Setup the filechooserdialog chooser = gtk.FileChooserDialog( - _('Save .torrent file'), self.dialog, + _('Save .torrent file'), + self.dialog, gtk.FILE_CHOOSER_ACTION_SAVE, buttons=( - gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, - gtk.STOCK_SAVE, gtk.RESPONSE_OK, + gtk.STOCK_CANCEL, + gtk.RESPONSE_CANCEL, + gtk.STOCK_SAVE, + gtk.RESPONSE_OK, ), ) @@ -306,7 +327,9 @@ class CreateTorrentDialog(object): webseeds.append(line) # Get the piece length in bytes combo = self.builder.get_object('combo_piece_size') - piece_length = self.parse_piece_size_text(combo.get_model()[combo.get_active()][0]) + piece_length = self.parse_piece_size_text( + combo.get_model()[combo.get_active()][0] + ) author = self.builder.get_object('entry_author').get_text() comment = self.builder.get_object('entry_comments').get_text() @@ -314,17 +337,23 @@ class CreateTorrentDialog(object): add_to_session = self.builder.get_object('chk_add_to_session').get_active() if is_remote: + def torrent_created(): self.builder.get_object('progress_dialog').hide_all() - client.deregister_event_handler('CreateTorrentProgressEvent', on_create_torrent_progress_event) + client.deregister_event_handler( + 'CreateTorrentProgressEvent', on_create_torrent_progress_event + ) def on_create_torrent_progress_event(piece_count, num_pieces): self._on_create_torrent_progress(piece_count, num_pieces) if piece_count == num_pieces: from twisted.internet import reactor + reactor.callLater(0.5, torrent_created) - client.register_event_handler('CreateTorrentProgressEvent', on_create_torrent_progress_event) + client.register_event_handler( + 'CreateTorrentProgressEvent', on_create_torrent_progress_event + ) client.core.create_torrent( path, @@ -360,16 +389,29 @@ class CreateTorrentDialog(object): ).addCallback(hide_progress) # Setup progress dialog - self.builder.get_object('progress_dialog').set_transient_for(component.get('MainWindow').window) + self.builder.get_object('progress_dialog').set_transient_for( + component.get('MainWindow').window + ) self.builder.get_object('progress_dialog').show_all() self.dialog.destroy() def create_torrent( - self, path, tracker, piece_length, progress, comment, target, - webseeds, private, created_by, trackers, add_to_session, + self, + path, + tracker, + piece_length, + progress, + comment, + target, + webseeds, + private, + created_by, + trackers, + add_to_session, ): import deluge.metafile + deluge.metafile.make_meta_file( path, tracker, @@ -408,7 +450,11 @@ class CreateTorrentDialog(object): def on_button_up_clicked(self, widget): log.debug('on_button_up_clicked') - row = self.builder.get_object('tracker_treeview').get_selection().get_selected()[1] + row = ( + self.builder.get_object('tracker_treeview') + .get_selection() + .get_selected()[1] + ) if row is None: return if self.trackers_liststore[row][0] == 0: @@ -418,7 +464,11 @@ class CreateTorrentDialog(object): def on_button_down_clicked(self, widget): log.debug('on_button_down_clicked') - row = self.builder.get_object('tracker_treeview').get_selection().get_selected()[1] + row = ( + self.builder.get_object('tracker_treeview') + .get_selection() + .get_selected()[1] + ) if row is None: return self.trackers_liststore[row][0] += 1 @@ -426,14 +476,18 @@ class CreateTorrentDialog(object): def on_button_add_clicked(self, widget): log.debug('on_button_add_clicked') builder = gtk.Builder() - builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'edit_trackers.add.ui'), - )) + builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'edit_trackers.add.ui') + ) + ) dialog = builder.get_object('add_tracker_dialog') dialog.set_transient_for(self.dialog) textview = builder.get_object('textview_trackers') if self.config['createtorrent.trackers']: - textview.get_buffer().set_text('\n'.join(self.config['createtorrent.trackers'])) + textview.get_buffer().set_text( + '\n'.join(self.config['createtorrent.trackers']) + ) else: textview.get_buffer().set_text('') textview.grab_focus() @@ -447,7 +501,9 @@ class CreateTorrentDialog(object): self.config['createtorrent.trackers'] = trackers_text.split('/n') # Append trackers liststore with unique trackers and tiers starting from last tier number. - last_tier, orig_trackers = last_tier_trackers_from_liststore(self.trackers_liststore) + last_tier, orig_trackers = last_tier_trackers_from_liststore( + self.trackers_liststore + ) for tracker, tier in trackers_tiers_from_text(trackers_text).items(): if tracker not in orig_trackers: self.trackers_liststore.append([tier + last_tier, tracker]) @@ -456,7 +512,11 @@ class CreateTorrentDialog(object): def on_button_remove_clicked(self, widget): log.debug('on_button_remove_clicked') - row = self.builder.get_object('tracker_treeview').get_selection().get_selected()[1] + row = ( + self.builder.get_object('tracker_treeview') + .get_selection() + .get_selected()[1] + ) if row is None: return self.trackers_liststore.remove(row) diff --git a/deluge/ui/gtkui/details_tab.py b/deluge/ui/gtkui/details_tab.py index b6e921e04..06f9e944d 100644 --- a/deluge/ui/gtkui/details_tab.py +++ b/deluge/ui/gtkui/details_tab.py @@ -33,7 +33,9 @@ class DetailsTab(Tab): self.add_tab_widget('summary_hash', str, ('hash',)) self.add_tab_widget('summary_comments', str, ('comment',)) self.add_tab_widget('summary_creator', str, ('creator',)) - self.add_tab_widget('summary_pieces', fpieces_num_size, ('num_pieces', 'piece_length')) + self.add_tab_widget( + 'summary_pieces', fpieces_num_size, ('num_pieces', 'piece_length') + ) def update(self): # Get the first selected torrent @@ -48,7 +50,9 @@ class DetailsTab(Tab): return session = component.get('SessionProxy') - session.get_torrent_status(selected, self.status_keys).addCallback(self._on_get_torrent_status) + session.get_torrent_status(selected, self.status_keys).addCallback( + self._on_get_torrent_status + ) def _on_get_torrent_status(self, status): # Check to see if we got valid data from the core diff --git a/deluge/ui/gtkui/dialogs.py b/deluge/ui/gtkui/dialogs.py index 91b299bb3..562b2ce51 100644 --- a/deluge/ui/gtkui/dialogs.py +++ b/deluge/ui/gtkui/dialogs.py @@ -23,6 +23,7 @@ class BaseDialog(gtk.Dialog): """ Base dialog class that should be used with all dialogs. """ + def __init__(self, header, text, icon, buttons, parent=None): """ :param header: str, the header portion of the dialog @@ -35,7 +36,9 @@ class BaseDialog(gtk.Dialog): super(BaseDialog, self).__init__( title=header, parent=parent if parent else component.get('MainWindow').window, - flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, + flags=gtk.DIALOG_MODAL + | gtk.DIALOG_DESTROY_WITH_PARENT + | gtk.DIALOG_NO_SEPARATOR, buttons=buttons, ) @@ -49,7 +52,9 @@ class BaseDialog(gtk.Dialog): self.set_default_size(200, 100) hbox = gtk.HBox(spacing=5) image = gtk.Image() - if not gtk.stock_lookup(icon) and (icon.endswith('.svg') or icon.endswith('.png')): + if not gtk.stock_lookup(icon) and ( + icon.endswith('.svg') or icon.endswith('.png') + ): # Hack for Windows since it doesn't support svg if icon.endswith('.svg') and windows_check(): icon = icon.rpartition('.svg')[0] + '16.png' @@ -94,6 +99,7 @@ class YesNoDialog(BaseDialog): When run(), it will return either a gtk.RESPONSE_YES or a gtk.RESPONSE_NO. """ + def __init__(self, header, text, parent=None): """ :param header: see `:class:BaseDialog` @@ -115,6 +121,7 @@ class InformationDialog(BaseDialog): When run(), it will return a gtk.RESPONSE_CLOSE. """ + def __init__(self, header, text, parent=None): """ :param header: see `:class:BaseDialog` @@ -136,6 +143,7 @@ class ErrorDialog(BaseDialog): When run(), it will return a gtk.RESPONSE_CLOSE. """ + def __init__(self, header, text, parent=None, details=None, traceback=False): """ :param header: see `:class:BaseDialog` @@ -158,6 +166,7 @@ class ErrorDialog(BaseDialog): if traceback: import traceback import sys + tb = sys.exc_info() tb = traceback.format_exc(tb[2]) if details: @@ -188,13 +197,15 @@ class AuthenticationDialog(BaseDialog): When run(), it will return either a gtk.RESPONSE_CANCEL or a gtk.RESPONSE_OK. """ + def __init__(self, err_msg='', username=None, parent=None): """ :param err_msg: the error message we got back from the server :type err_msg: string """ super(AuthenticationDialog, self).__init__( - _('Authenticate'), err_msg, + _('Authenticate'), + err_msg, gtk.STOCK_DIALOG_AUTHENTICATION, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_CONNECT, gtk.RESPONSE_OK), parent, @@ -241,8 +252,12 @@ class AuthenticationDialog(BaseDialog): class AccountDialog(BaseDialog): def __init__( - self, username=None, password=None, authlevel=None, - levels_mapping=None, parent=None, + self, + username=None, + password=None, + authlevel=None, + levels_mapping=None, + parent=None, ): if username: super(AccountDialog, self).__init__( @@ -250,8 +265,10 @@ class AccountDialog(BaseDialog): _('Edit existing account'), gtk.STOCK_DIALOG_INFO, ( - gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, - gtk.STOCK_APPLY, gtk.RESPONSE_OK, + gtk.STOCK_CANCEL, + gtk.RESPONSE_CANCEL, + gtk.STOCK_APPLY, + gtk.RESPONSE_OK, ), parent, ) @@ -260,10 +277,7 @@ class AccountDialog(BaseDialog): _('New Account'), _('Create a new account'), gtk.STOCK_DIALOG_INFO, - ( - gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, - gtk.STOCK_ADD, gtk.RESPONSE_OK, - ), + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_ADD, gtk.RESPONSE_OK), parent, ) @@ -339,7 +353,10 @@ class OtherDialog(BaseDialog): Returns: int or float: """ - def __init__(self, header, text='', unit_text='', icon=None, default=0, parent=None): + + def __init__( + self, header, text='', unit_text='', icon=None, default=0, parent=None + ): self.value_type = type(default) if self.value_type not in (int, float): raise TypeError('default value needs to be an int or float') @@ -359,7 +376,9 @@ class OtherDialog(BaseDialog): alignment_spacer = gtk.Alignment() hbox.pack_start(alignment_spacer, True, True, 0) alignment_spin = gtk.Alignment(1, 0.5, 1, 1) - adjustment_spin = gtk.Adjustment(value=-1, lower=-1, upper=2097151, step_incr=1, page_incr=10) + adjustment_spin = gtk.Adjustment( + value=-1, lower=-1, upper=2097151, step_incr=1, page_incr=10 + ) self.spinbutton = gtk.SpinButton(adjustment_spin) self.spinbutton.set_value(default) self.spinbutton.select_region(0, -1) @@ -399,13 +418,15 @@ class PasswordDialog(BaseDialog): When run(), it will return either a gtk.RESPONSE_CANCEL or a gtk.RESPONSE_OK. """ + def __init__(self, password_msg='', parent=None): """ :param password_msg: the error message we got back from the server :type password_msg: string """ super(PasswordDialog, self).__init__( - _('Password Protected'), password_msg, + _('Password Protected'), + password_msg, gtk.STOCK_DIALOG_AUTHENTICATION, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_CONNECT, gtk.RESPONSE_OK), parent, diff --git a/deluge/ui/gtkui/edittrackersdialog.py b/deluge/ui/gtkui/edittrackersdialog.py index c3008cf44..bf1ba0ca9 100644 --- a/deluge/ui/gtkui/edittrackersdialog.py +++ b/deluge/ui/gtkui/edittrackersdialog.py @@ -83,17 +83,23 @@ class EditTrackersDialog(object): self.gtkui_config = ConfigManager('gtkui.conf') # Main dialog - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'edit_trackers.ui'), - )) + self.builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'edit_trackers.ui') + ) + ) # add tracker dialog - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'edit_trackers.add.ui'), - )) + self.builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'edit_trackers.add.ui') + ) + ) # edit tracker dialog - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'edit_trackers.edit.ui'), - )) + self.builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'edit_trackers.edit.ui') + ) + ) self.dialog = self.builder.get_object('edit_trackers_dialog') self.treeview = self.builder.get_object('tracker_treeview') @@ -116,10 +122,10 @@ class EditTrackersDialog(object): # Create the columns self.treeview.append_column( - gtk.TreeViewColumn(_('Tier'), gtk.CellRendererText(), text=0), + gtk.TreeViewColumn(_('Tier'), gtk.CellRendererText(), text=0) ) self.treeview.append_column( - gtk.TreeViewColumn(_('Tracker'), gtk.CellRendererText(), text=1), + gtk.TreeViewColumn(_('Tracker'), gtk.CellRendererText(), text=1) ) self.treeview.set_model(self.liststore) @@ -135,9 +141,9 @@ class EditTrackersDialog(object): # Get the trackers for this torrent session = component.get('SessionProxy') - session.get_torrent_status( - self.torrent_id, ['trackers'], - ).addCallback(self._on_get_torrent_status) + session.get_torrent_status(self.torrent_id, ['trackers']).addCallback( + self._on_get_torrent_status + ) client.force_call() self.deferred = defer.Deferred() @@ -169,6 +175,7 @@ class EditTrackersDialog(object): tracker['tier'] = model.get_value(_iter, 0) tracker['url'] = model.get_value(_iter, 1) self.trackers.append(tracker) + self.liststore.foreach(each, None) if self.old_trackers != self.trackers: # Set the torrens trackers diff --git a/deluge/ui/gtkui/files_tab.py b/deluge/ui/gtkui/files_tab.py index 79cb66188..bfa9c8c46 100644 --- a/deluge/ui/gtkui/files_tab.py +++ b/deluge/ui/gtkui/files_tab.py @@ -15,14 +15,23 @@ import os.path import gtk import six.moves.cPickle as pickle from gobject import TYPE_UINT64 -from gtk.gdk import ACTION_DEFAULT, ACTION_MOVE, BUTTON1_MASK, keyval_name # pylint: disable=ungrouped-imports +from gtk.gdk import ( # pylint: disable=ungrouped-imports + ACTION_DEFAULT, + ACTION_MOVE, + BUTTON1_MASK, + keyval_name, +) import deluge.component as component from deluge.common import open_file, show_file from deluge.ui.client import client from deluge.ui.common import FILE_PRIORITY -from deluge.ui.gtkui.common import (listview_replace_treestore, load_pickled_state_file, reparent_iter, - save_pickled_state_file) +from deluge.ui.gtkui.common import ( + listview_replace_treestore, + load_pickled_state_file, + reparent_iter, + save_pickled_state_file, +) from deluge.ui.gtkui.torrentdetails import Tab from deluge.ui.gtkui.torrentview_data_funcs import cell_data_size @@ -170,7 +179,7 @@ class FilesTab(Tab): self.listview.connect('button-press-event', self._on_button_press_event) self.listview.enable_model_drag_source( - BUTTON1_MASK, [('text/plain', 0, 0)], ACTION_DEFAULT | ACTION_MOVE, + BUTTON1_MASK, [('text/plain', 0, 0)], ACTION_DEFAULT | ACTION_MOVE ) self.listview.enable_model_drag_dest([('text/plain', 0, 0)], ACTION_DEFAULT) @@ -180,9 +189,15 @@ class FilesTab(Tab): component.get('MainWindow').connect_signals(self) # Connect to various events from the daemon - client.register_event_handler('TorrentFileRenamedEvent', self._on_torrentfilerenamed_event) - client.register_event_handler('TorrentFolderRenamedEvent', self._on_torrentfolderrenamed_event) - client.register_event_handler('TorrentRemovedEvent', self._on_torrentremoved_event) + client.register_event_handler( + 'TorrentFileRenamedEvent', self._on_torrentfilerenamed_event + ) + client.register_event_handler( + 'TorrentFolderRenamedEvent', self._on_torrentfolderrenamed_event + ) + client.register_event_handler( + 'TorrentRemovedEvent', self._on_torrentremoved_event + ) # Attempt to load state self.load_state() @@ -238,8 +253,13 @@ class FilesTab(Tab): # Column is in wrong position if cstate['position'] == 0: self.listview.move_column_after(column, None) - elif self.listview.get_columns()[cstate['position'] - 1].get_title() != cname: - self.listview.move_column_after(column, self.listview.get_columns()[cstate['position'] - 1]) + elif ( + self.listview.get_columns()[cstate['position'] - 1].get_title() + != cname + ): + self.listview.move_column_after( + column, self.listview.get_columns()[cstate['position'] - 1] + ) def update(self): # Get the first selected torrent @@ -264,13 +284,16 @@ class FilesTab(Tab): # We already have the files list stored, so just update the view self.update_files() - if self.torrent_id not in self.files_list or not self.files_list[self.torrent_id]: + if ( + self.torrent_id not in self.files_list + or not self.files_list[self.torrent_id] + ): # We need to get the files list log.debug('Getting file list from core..') status_keys += ['files'] component.get('SessionProxy').get_torrent_status( - self.torrent_id, status_keys, + self.torrent_id, status_keys ).addCallback(self._on_get_torrent_status, self.torrent_id) def clear(self): @@ -325,12 +348,14 @@ class FilesTab(Tab): if first_slash_index == -1: files_storage[file_name] = (file_num, torrent_file) else: - file_name_chunk = file_name[:first_slash_index + 1] + file_name_chunk = file_name[: first_slash_index + 1] if file_name_chunk not in files_storage: files_storage[file_name_chunk] = {} self.prepare_file( - torrent_file, file_name[first_slash_index + 1:], - file_num, files_storage[file_name_chunk], + torrent_file, + file_name[first_slash_index + 1 :], + file_num, + files_storage[file_name_chunk], ) def add_files(self, parent_iter, split_files): @@ -338,8 +363,7 @@ class FilesTab(Tab): for key, value in split_files.items(): if key.endswith('/'): chunk_iter = self.treestore.append( - parent_iter, - [key, 0, '', 0, 0, -1, gtk.STOCK_DIRECTORY], + parent_iter, [key, 0, '', 0, 0, -1, gtk.STOCK_DIRECTORY] ) chunk_size = self.add_files(chunk_iter, value) self.treestore.set(chunk_iter, 1, chunk_size) @@ -359,6 +383,7 @@ class FilesTab(Tab): def get_selected_files(self): """Returns a list of file indexes that are selected.""" + def get_iter_children(itr, selected): i = self.treestore.iter_children(itr) while i: @@ -398,9 +423,13 @@ class FilesTab(Tab): parent = self.treestore.iter_parent(row) while row: if self.treestore.iter_children(row): - completed_bytes += get_completed_bytes(self.treestore.iter_children(row)) + completed_bytes += get_completed_bytes( + self.treestore.iter_children(row) + ) else: - completed_bytes += self.treestore[row][1] * self.treestore[row][3] / 100 + completed_bytes += ( + self.treestore[row][1] * self.treestore[row][3] / 100 + ) row = self.treestore.iter_next(row) @@ -496,13 +525,13 @@ class FilesTab(Tab): def on_menuitem_open_file_activate(self, menuitem): if client.is_localhost: component.get('SessionProxy').get_torrent_status( - self.torrent_id, ['download_location'], + self.torrent_id, ['download_location'] ).addCallback(self._on_open_file) def on_menuitem_show_file_activate(self, menuitem): if client.is_localhost: component.get('SessionProxy').get_torrent_status( - self.torrent_id, ['download_location'], + self.torrent_id, ['download_location'] ).addCallback(self._on_show_file) def _set_file_priorities_on_user_change(self, selected, priority): @@ -520,26 +549,28 @@ class FilesTab(Tab): file_priorities.sort() priorities = [p[1] for p in file_priorities] log.debug('priorities: %s', priorities) - client.core.set_torrent_options([self.torrent_id], {'file_priorities': priorities}) + client.core.set_torrent_options( + [self.torrent_id], {'file_priorities': priorities} + ) def on_menuitem_ignore_activate(self, menuitem): self._set_file_priorities_on_user_change( - self.get_selected_files(), FILE_PRIORITY['Ignore'], + self.get_selected_files(), FILE_PRIORITY['Ignore'] ) def on_menuitem_low_activate(self, menuitem): self._set_file_priorities_on_user_change( - self.get_selected_files(), FILE_PRIORITY['Low'], + self.get_selected_files(), FILE_PRIORITY['Low'] ) def on_menuitem_normal_activate(self, menuitem): self._set_file_priorities_on_user_change( - self.get_selected_files(), FILE_PRIORITY['Normal'], + self.get_selected_files(), FILE_PRIORITY['Normal'] ) def on_menuitem_high_activate(self, menuitem): self._set_file_priorities_on_user_change( - self.get_selected_files(), FILE_PRIORITY['High'], + self.get_selected_files(), FILE_PRIORITY['High'] ) def on_menuitem_expand_all_activate(self, menuitem): @@ -586,7 +617,9 @@ class FilesTab(Tab): parent_path = self.treestore[itr][0] + parent_path itr = self.treestore.iter_parent(itr) - client.core.rename_folder(self.torrent_id, parent_path + folder, parent_path + new_text) + client.core.rename_folder( + self.torrent_id, parent_path + folder, parent_path + new_text + ) self._editing_index = None @@ -616,17 +649,28 @@ class FilesTab(Tab): if old_name_parent != parent_path: if parent_path: for i, p in enumerate(parent_path): - p_itr = self.get_iter_at_path('/'.join(parent_path[:i + 1]) + '/') + p_itr = self.get_iter_at_path('/'.join(parent_path[: i + 1]) + '/') if not p_itr: p_itr = self.get_iter_at_path('/'.join(parent_path[:i]) + '/') p_itr = self.treestore.append( - p_itr, [parent_path[i] + '/', 0, '', 0, 0, -1, gtk.STOCK_DIRECTORY], + p_itr, + [ + parent_path[i] + '/', + 0, + '', + 0, + 0, + -1, + gtk.STOCK_DIRECTORY, + ], ) p_itr = self.get_iter_at_path('/'.join(parent_path) + '/') old_name_itr = self.get_iter_at_path(old_name) self.treestore.append( p_itr, - self.treestore.get(old_name_itr, *range(self.treestore.get_n_columns())), + self.treestore.get( + old_name_itr, *range(self.treestore.get_n_columns()) + ), ) self.treestore.remove(old_name_itr) @@ -638,7 +682,7 @@ class FilesTab(Tab): parent_iter = None for f in new_folders: parent_iter = self.treestore.append( - parent_iter, [f + '/', 0, '', 0, 0, -1, gtk.STOCK_DIRECTORY], + parent_iter, [f + '/', 0, '', 0, 0, -1, gtk.STOCK_DIRECTORY] ) child = self.get_iter_at_path(old_name) self.treestore.append( @@ -653,6 +697,7 @@ class FilesTab(Tab): if model[itr][5] == index: model[itr][0] = os.path.split(name)[-1] return True + self.treestore.foreach(set_file_name, None) def get_iter_at_path(self, filepath): @@ -676,7 +721,9 @@ class FilesTab(Tab): itr = self.treestore.iter_children(itr) level += 1 continue - elif (level + 1) == len(filepath) and ipath == (filepath[level] + '/' if is_dir else filepath[level]): + elif (level + 1) == len(filepath) and ipath == ( + filepath[level] + '/' if is_dir else filepath[level] + ): # This is the iter we've been searching for path_iter = itr break @@ -734,18 +781,29 @@ class FilesTab(Tab): return if new_folder_iter: # This means that a folder by this name already exists - reparent_iter(self.treestore, self.treestore.iter_children(old_folder_iter), new_folder_iter) + reparent_iter( + self.treestore, + self.treestore.iter_children(old_folder_iter), + new_folder_iter, + ) else: parent = old_folder_iter_parent if new_split: for ns in new_split[:-1]: - parent = self.treestore.append(parent, [ns + '/', 0, '', 0, 0, -1, gtk.STOCK_DIRECTORY]) + parent = self.treestore.append( + parent, [ns + '/', 0, '', 0, 0, -1, gtk.STOCK_DIRECTORY] + ) self.treestore[old_folder_iter][0] = new_split[-1] + '/' reparent_iter(self.treestore, old_folder_iter, parent) else: child_itr = self.treestore.iter_children(old_folder_iter) - reparent_iter(self.treestore, child_itr, old_folder_iter_parent, move_siblings=True) + reparent_iter( + self.treestore, + child_itr, + old_folder_iter_parent, + move_siblings=True, + ) # We need to check if the old_folder_iter no longer has children # and if so, we delete it @@ -759,7 +817,9 @@ class FilesTab(Tab): paths = self.listview.get_selection().get_selected_rows()[1] selection.set_text(pickle.dumps(paths, protocol=2)) - def _on_drag_data_received_data(self, treeview, context, x, y, selection, info, etime): + def _on_drag_data_received_data( + self, treeview, context, x, y, selection, info, etime + ): try: selected = pickle.loads(selection.data) except pickle.UnpicklingError: @@ -789,7 +849,8 @@ class FilesTab(Tab): pp = self.treestore[itr][0] + pp itr = self.treestore.iter_parent(itr) client.core.rename_folder( - self.torrent_id, pp + model[selected[0]][0], + self.torrent_id, + pp + model[selected[0]][0], parent_path + model[selected[0]][0], ) else: diff --git a/deluge/ui/gtkui/filtertreeview.py b/deluge/ui/gtkui/filtertreeview.py index 6bec3966c..ba9d9bb9e 100644 --- a/deluge/ui/gtkui/filtertreeview.py +++ b/deluge/ui/gtkui/filtertreeview.py @@ -40,10 +40,7 @@ STATE_PIX = { 'Moving': 'checking', } -TRACKER_PIX = { - 'All': 'tracker_all', - 'Error': 'tracker_warning', -} +TRACKER_PIX = {'All': 'tracker_all', 'Error': 'tracker_warning'} FILTER_COLUMN = 5 @@ -91,8 +88,10 @@ class FilterTreeView(component.Component): self.treeview.set_headers_visible(False) self.treeview.set_level_indentation(-21) # Force theme to use expander-size so we don't cut out entries due to indentation hack. - gtk.rc_parse_string("""style "treeview-style" {GtkTreeView::expander-size = 7} - class "GtkTreeView" style "treeview-style" """) + gtk.rc_parse_string( + """style "treeview-style" {GtkTreeView::expander-size = 7} + class "GtkTreeView" style "treeview-style" """ + ) self.treeview.set_model(self.treestore) self.treeview.get_selection().connect('changed', self.on_selection_changed) @@ -107,7 +106,11 @@ class FilterTreeView(component.Component): # filtertree menu builder = gtk.Builder() - builder.add_from_file(resource_filename('deluge.ui.gtkui', os.path.join('glade', 'filtertree_menu.ui'))) + builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'filtertree_menu.ui') + ) + ) self.menu = builder.get_object('filtertree_menu') builder.connect_signals(self) @@ -119,21 +122,22 @@ class FilterTreeView(component.Component): self.filters = {} # initial order of state filter: - self.cat_nodes['state'] = self.treestore.append(None, ['cat', 'state', _('States'), 0, None, False]) + self.cat_nodes['state'] = self.treestore.append( + None, ['cat', 'state', _('States'), 0, None, False] + ) for state in ['All', 'Active'] + TORRENT_STATE: self.update_row('state', state, 0, _(state)) self.cat_nodes['tracker_host'] = self.treestore.append( - None, [ - 'cat', 'tracker_host', - _('Trackers'), 0, None, False, - ], + None, ['cat', 'tracker_host', _('Trackers'), 0, None, False] ) self.update_row('tracker_host', 'All', 0, _('All')) self.update_row('tracker_host', 'Error', 0, _('Error')) self.update_row('tracker_host', '', 0, _('None')) - self.cat_nodes['owner'] = self.treestore.append(None, ['cat', 'owner', _('Owner'), 0, None, False]) + self.cat_nodes['owner'] = self.treestore.append( + None, ['cat', 'owner', _('Owner'), 0, None, False] + ) self.update_row('owner', 'localclient', 0, _('Admin')) self.update_row('owner', '', 0, _('None')) @@ -157,7 +161,9 @@ class FilterTreeView(component.Component): label = _(cat) if cat == 'label': label = _('Labels') - self.cat_nodes[cat] = self.treestore.append(None, ['cat', cat, label, 0, None, False]) + self.cat_nodes[cat] = self.treestore.append( + None, ['cat', cat, label, 0, None, False] + ) # update rows visible_filters = [] @@ -168,7 +174,11 @@ class FilterTreeView(component.Component): # hide root-categories not returned by core-part of the plugin. for cat in self.cat_nodes: - self.treestore.set_value(self.cat_nodes[cat], FILTER_COLUMN, True if cat in filter_items else False) + self.treestore.set_value( + self.cat_nodes[cat], + FILTER_COLUMN, + True if cat in filter_items else False, + ) # hide items not returned by core-plugin. for f in self.filters: @@ -201,7 +211,9 @@ class FilterTreeView(component.Component): elif not label and value: label = _(value) - row = self.treestore.append(self.cat_nodes[cat], [cat, value, label, count, pix, True]) + row = self.treestore.append( + self.cat_nodes[cat], [cat, value, label, count, pix, True] + ) self.filters[(cat, value)] = row if cat == 'tracker_host' and value not in ('All', 'Error') and value: @@ -284,8 +296,7 @@ class FilterTreeView(component.Component): if not self.config['sidebar_show_owners']: hide_cat.append('owner') client.core.get_filter_tree( - self.config['sidebar_show_zero'], - hide_cat, + self.config['sidebar_show_zero'], hide_cat ).addCallback(self.cb_update_filter_tree) except Exception as ex: log.debug(ex) @@ -336,7 +347,7 @@ class FilterTreeView(component.Component): def set_menu_sensitivity(self): # select-all/pause/resume - sensitive = (self.cat != 'cat' and self.count != 0) + sensitive = self.cat != 'cat' and self.count != 0 for item in self.default_menu_items: item.set_sensitive(sensitive) diff --git a/deluge/ui/gtkui/gtkui.py b/deluge/ui/gtkui/gtkui.py index 0468baa86..16bde8454 100644 --- a/deluge/ui/gtkui/gtkui.py +++ b/deluge/ui/gtkui/gtkui.py @@ -17,6 +17,7 @@ import sys import time import pygtk # isort:skip (Required before gtk import). + pygtk.require('2.0') # NOQA: E402 # isort:imports-thirdparty @@ -36,7 +37,13 @@ except ReactorAlreadyInstalledError as ex: # isort:imports-firstparty import deluge.component as component -from deluge.common import fsize, fspeed, get_default_download_dir, osx_check, windows_check +from deluge.common import ( + fsize, + fspeed, + get_default_download_dir, + osx_check, + windows_check, +) from deluge.configmanager import ConfigManager, get_config_dir from deluge.error import DaemonRunningError from deluge.ui.client import client @@ -68,6 +75,7 @@ log = logging.getLogger(__name__) try: from setproctitle import setproctitle, getproctitle except ImportError: + def setproctitle(title): return @@ -149,10 +157,12 @@ class GtkUI(object): if windows_check(): from win32api import SetConsoleCtrlHandler + SetConsoleCtrlHandler(on_die, True) log.debug('Win32 "die" handler registered') elif osx_check() and WINDOWING == 'quartz': import gtkosx_application + self.osxapp = gtkosx_application.gtkosx_application_get() self.osxapp.connect('NSApplicationWillTerminate', on_die) log.debug('OSX quartz "die" handler registered') @@ -202,13 +212,16 @@ class GtkUI(object): self.addtorrentdialog = AddTorrentDialog() if osx_check() and WINDOWING == 'quartz': + def nsapp_open_file(osxapp, filename): # Ignore command name which is raised at app launch (python opening main script). if filename == sys.argv[0]: return True process_args([filename]) + self.osxapp.connect('NSApplicationOpenFile', nsapp_open_file) from deluge.ui.gtkui.menubar_osx import menubar_osx + menubar_osx(self, self.osxapp) self.osxapp.ready() @@ -286,7 +299,13 @@ class GtkUI(object): self.daemon_bps = (t, sent, recv) sent_rate = fspeed(delta_sent / delta_time) recv_rate = fspeed(delta_recv / delta_time) - log.debug('RPC: Sent %s (%s) Recv %s (%s)', fsize(sent), sent_rate, fsize(recv), recv_rate) + log.debug( + 'RPC: Sent %s (%s) Recv %s (%s)', + fsize(sent), + sent_rate, + fsize(recv), + recv_rate, + ) def _on_reactor_start(self): log.debug('_on_reactor_start') @@ -301,25 +320,25 @@ class GtkUI(object): except DaemonRunningError: err_msg = _( 'A Deluge daemon (deluged) is already running.\n' - 'To use Standalone mode, stop local daemon and restart Deluge.', + 'To use Standalone mode, stop local daemon and restart Deluge.' ) except ImportError as ex: if 'No module named libtorrent' in ex.message: err_msg = _( 'Only Thin Client mode is available because libtorrent is not installed.\n' - 'To use Standalone mode, please install libtorrent package.', + 'To use Standalone mode, please install libtorrent package.' ) else: log.exception(ex) err_msg = _( 'Only Thin Client mode is available due to unknown Import Error.\n' - 'To use Standalone mode, please see logs for error details.', + 'To use Standalone mode, please see logs for error details.' ) except Exception as ex: log.exception(ex) err_msg = _( 'Only Thin Client mode is available due to unknown Import Error.\n' - 'To use Standalone mode, please see logs for error details.', + 'To use Standalone mode, please see logs for error details.' ) else: component.start() @@ -347,6 +366,7 @@ class GtkUI(object): # Check to see if we need to start the localhost daemon if self.config['autostart_localhost']: + def on_localhost_status(status_info, port): if status_info[1] == 'Offline': log.debug('Autostarting localhost: %s', host_config[0:3]) diff --git a/deluge/ui/gtkui/ipcinterface.py b/deluge/ui/gtkui/ipcinterface.py index 373d9e39f..a2e348028 100644 --- a/deluge/ui/gtkui/ipcinterface.py +++ b/deluge/ui/gtkui/ipcinterface.py @@ -42,7 +42,6 @@ log = logging.getLogger(__name__) class IPCProtocolServer(Protocol): - def __init__(self): pass @@ -55,7 +54,6 @@ class IPCProtocolServer(Protocol): class IPCProtocolClient(Protocol): - def __init__(self): pass @@ -93,12 +91,14 @@ class IPCInterface(component.Component): import win32event import win32api import winerror + self.mutex = win32event.CreateMutex(None, False, 'deluge') if win32api.GetLastError() != winerror.ERROR_ALREADY_EXISTS: # Create listen socket self.factory = Factory() self.factory.protocol = IPCProtocolServer import random + port = random.randrange(20000, 65535) self.listener = reactor.listenTCP(port, self.factory) # Store the port number in the socket file @@ -124,6 +124,7 @@ class IPCInterface(component.Component): lockfile = socket + '.lock' log.debug('Checking if lockfile exists: %s', lockfile) if os.path.lexists(lockfile): + def delete_lockfile(): log.debug('Delete stale lockfile.') try: @@ -138,14 +139,18 @@ class IPCInterface(component.Component): delete_lockfile() else: if restart_tempfile: - log.warning('Found running PID but it is not a Deluge process, removing lockfile...') + log.warning( + 'Found running PID but it is not a Deluge process, removing lockfile...' + ) delete_lockfile() try: self.factory = Factory() self.factory.protocol = IPCProtocolServer self.listener = reactor.listenUNIX(socket, self.factory, wantPID=True) except twisted.internet.error.CannotListenError as ex: - log.info('Deluge is already running! Sending arguments to running instance...') + log.info( + 'Deluge is already running! Sending arguments to running instance...' + ) self.factory = IPCClientFactory() self.factory.args = args reactor.connectUNIX(socket, self.factory, checkPID=True) @@ -153,6 +158,7 @@ class IPCInterface(component.Component): if self.factory.stop: log.info('Success sending arguments to running Deluge.') from gtk.gdk import notify_startup_complete + notify_startup_complete() sys.exit(0) else: @@ -170,6 +176,7 @@ class IPCInterface(component.Component): def shutdown(self): if windows_check(): import win32api + win32api.CloseHandle(self.mutex) if self.listener: return self.listener.stopListening() diff --git a/deluge/ui/gtkui/listview.py b/deluge/ui/gtkui/listview.py index e1d42659c..e9a2951c4 100644 --- a/deluge/ui/gtkui/listview.py +++ b/deluge/ui/gtkui/listview.py @@ -18,7 +18,9 @@ from gtk.gdk import Event # pylint: disable=ungrouped-imports from deluge.common import decode_bytes from deluge.ui.gtkui.common import load_pickled_state_file, save_pickled_state_file -signal_new('button-press-event', gtk.TreeViewColumn, SIGNAL_RUN_LAST, TYPE_NONE, (Event,)) +signal_new( + 'button-press-event', gtk.TreeViewColumn, SIGNAL_RUN_LAST, TYPE_NONE, (Event,) +) log = logging.getLogger(__name__) @@ -29,6 +31,7 @@ class ListViewColumnState: # pylint: disable=old-style-class Note: This must be old style class to avoid breaking existing user state file. """ + def __init__(self, name, position, width, visible, sort, sort_order): self.name = name self.position = position @@ -46,6 +49,7 @@ class ListView(object): class ListViewColumn(object): """Holds information regarding a column in the ListView""" + def __init__(self, name, column_indices): # Name is how a column is identified and is also the header self.name = name @@ -79,9 +83,9 @@ class ListView(object): Most of the code of this class comes from Quod Libet (http://www.sacredchao.net/quodlibet) """ - def __init__(self, title=None, cell_renderer=None, ** args): + def __init__(self, title=None, cell_renderer=None, **args): """ Constructor, see gtk.TreeViewColumn """ - gtk.TreeViewColumn.__init__(self, title, cell_renderer, ** args) + gtk.TreeViewColumn.__init__(self, title, cell_renderer, **args) label = gtk.Label(title) self.set_widget(label) label.show() @@ -114,7 +118,9 @@ class ListView(object): # Set data function to None to prevent unecessary calls when column is hidden self.set_cell_data_func(self.cell_renderer, None, func_data=None) else: - self.set_cell_data_func(self.cell_renderer, self.data_func, self.data_func_data) + self.set_cell_data_func( + self.cell_renderer, self.data_func, self.data_func_data + ) def set_col_attributes(self, renderer, add=True, **kw): if add is True: @@ -177,9 +183,7 @@ class ListView(object): must be called after listview.create_new_liststore """ model_filter = self.liststore.filter_new() - model_filter.set_visible_column( - self.columns['filter'].column_indices[0], - ) + model_filter.set_visible_column(self.columns['filter'].column_indices[0]) self.model_filter = gtk.TreeModelSort(model_filter) self.model_filter.connect('sort-column-changed', self.on_model_sort_changed) self.model_filter.connect('row-inserted', self.on_model_row_inserted) @@ -190,12 +194,17 @@ class ListView(object): def set_model_sort(self): column_state = self.get_sort_column_from_state() if column_state: - self.treeview.get_model().set_sort_column_id(column_state.sort, column_state.sort_order) + self.treeview.get_model().set_sort_column_id( + column_state.sort, column_state.sort_order + ) # Using the default sort column elif self.default_sort_column_id: - self.model_filter.set_sort_column_id(self.default_sort_column_id, gtk.SORT_ASCENDING) + self.model_filter.set_sort_column_id( + self.default_sort_column_id, gtk.SORT_ASCENDING + ) self.model_filter.set_default_sort_func( - self.generic_sort_func, self.get_column_index('Added')[0]) + self.generic_sort_func, self.get_column_index('Added')[0] + ) def get_sort_column_from_state(self): """Find the first (should only be one) state with sort enabled""" @@ -212,12 +221,13 @@ class ListView(object): def record_position(model, path, _iter, data): self.last_sort_order[model[_iter][self.unique_column_id]] = path[0] + model.foreach(record_position, None) def on_model_row_inserted(self, model, path, _iter): if self.unique_column_id: self.last_sort_order.setdefault( - model[_iter][self.unique_column_id], len(model) - 1, + model[_iter][self.unique_column_id], len(model) - 1 ) def stabilize_sort_func(self, sort_func): @@ -228,14 +238,14 @@ class ListView(object): unique2 = model[iter2][self.unique_column_id] if unique1 in self.last_sort_order and unique2 in self.last_sort_order: result = cmp( - self.last_sort_order[unique1], - self.last_sort_order[unique2], + self.last_sort_order[unique1], self.last_sort_order[unique2] ) # If all else fails, fall back to sorting by unique column if result == 0: result = cmp(unique1, unique2) return result + return stabilized def generic_sort_func(self, model, iter1, iter2, data): @@ -245,9 +255,7 @@ class ListView(object): for column in self.columns.values(): sort_func = column.sort_func or self.generic_sort_func self.model_filter.set_sort_func( - column.sort_id, - self.stabilize_sort_func(sort_func), - column.sort_id, + column.sort_id, self.stabilize_sort_func(sort_func), column.sort_id ) def create_column_state(self, column, position=None): @@ -264,8 +272,12 @@ class ListView(object): sort = sort_id return ListViewColumnState( - column.get_title(), position, column.get_width(), - column.get_visible(), sort, int(column.get_sort_order()), + column.get_title(), + position, + column.get_width(), + column.get_visible(), + sort, + int(column.get_sort_order()), ) def save_state(self, filename): @@ -402,45 +414,50 @@ class ListView(object): if add: tree_column.pack_start(column.renderer, True) tree_column.set_col_attributes( - column.renderer, add=add, - text=column.column_indices[column.text_index], + column.renderer, add=add, text=column.column_indices[column.text_index] ) elif column.column_type == 'bool': if add: tree_column.pack_start(column.renderer, True) - tree_column.set_col_attributes(column.renderer, active=column.column_indices[0]) + tree_column.set_col_attributes( + column.renderer, active=column.column_indices[0] + ) elif column.column_type == 'func': if add: tree_column.pack_start(column.renderer, True) indice_arg = column.column_indices[0] if len(column.column_indices) > 1: indice_arg = tuple(column.column_indices) - tree_column.set_cell_data_func(column.renderer, column.data_func, indice_arg) + tree_column.set_cell_data_func( + column.renderer, column.data_func, indice_arg + ) elif column.column_type == 'progress': if add: tree_column.pack_start(column.renderer, True) if column.data_func is None: tree_column.set_col_attributes( - column.renderer, add=add, + column.renderer, + add=add, text=column.column_indices[column.text_index], value=column.column_indices[column.value_index], ) else: tree_column.set_cell_data_func( - column.renderer, column.data_func, - tuple(column.column_indices), + column.renderer, column.data_func, tuple(column.column_indices) ) elif column.column_type == 'texticon': if add: tree_column.pack_start(column.renderer[column.pixbuf_index], False) tree_column.pack_start(column.renderer[column.text_index], True) tree_column.set_col_attributes( - column.renderer[column.text_index], add=add, + column.renderer[column.text_index], + add=add, text=column.column_indices[column.text_index], ) if column.data_func is not None: tree_column.set_cell_data_func( - column.renderer[column.pixbuf_index], column.data_func, + column.renderer[column.pixbuf_index], + column.data_func, column.column_indices[column.pixbuf_index], ) return True @@ -483,10 +500,24 @@ class ListView(object): return def add_column( - self, header, render, col_types, hidden, position, - status_field, sortid, text=0, value=0, pixbuf=0, function=None, - column_type=None, sort_func=None, tooltip=None, default=True, - unique=False, default_sort=False, + self, + header, + render, + col_types, + hidden, + position, + status_field, + sortid, + text=0, + value=0, + pixbuf=0, + function=None, + column_type=None, + sort_func=None, + tooltip=None, + default=True, + unique=False, + default_sort=False, ): """Adds a column to the ListView""" # Add the column types to liststore_columns @@ -544,10 +575,7 @@ class ListView(object): column.set_min_width(20) column.set_reorderable(True) column.set_visible(not hidden) - column.connect( - 'button-press-event', - self.on_treeview_header_right_clicked, - ) + column.connect('button-press-event', self.on_treeview_header_right_clicked) if tooltip: column.get_widget().set_tooltip_markup(tooltip) @@ -586,58 +614,116 @@ class ListView(object): return True def add_text_column( - self, header, col_type=str, hidden=False, position=None, - status_field=None, sortid=0, column_type='text', - sort_func=None, tooltip=None, default=True, unique=False, + self, + header, + col_type=str, + hidden=False, + position=None, + status_field=None, + sortid=0, + column_type='text', + sort_func=None, + tooltip=None, + default=True, + unique=False, default_sort=False, ): """Add a text column to the listview. Only the header name is required. """ render = gtk.CellRendererText() self.add_column( - header, render, col_type, hidden, position, - status_field, sortid, column_type=column_type, - sort_func=sort_func, tooltip=tooltip, default=default, - unique=unique, default_sort=default_sort, + header, + render, + col_type, + hidden, + position, + status_field, + sortid, + column_type=column_type, + sort_func=sort_func, + tooltip=tooltip, + default=default, + unique=unique, + default_sort=default_sort, ) return True def add_bool_column( - self, header, col_type=bool, hidden=False, - position=None, status_field=None, sortid=0, - column_type='bool', tooltip=None, default=True, + self, + header, + col_type=bool, + hidden=False, + position=None, + status_field=None, + sortid=0, + column_type='bool', + tooltip=None, + default=True, ): """Add a bool column to the listview""" render = gtk.CellRendererToggle() self.add_column( - header, render, col_type, hidden, position, - status_field, sortid, column_type=column_type, - tooltip=tooltip, default=default, + header, + render, + col_type, + hidden, + position, + status_field, + sortid, + column_type=column_type, + tooltip=tooltip, + default=default, ) def add_func_column( - self, header, function, col_types, sortid=0, - hidden=False, position=None, status_field=None, - column_type='func', sort_func=None, tooltip=None, default=True, + self, + header, + function, + col_types, + sortid=0, + hidden=False, + position=None, + status_field=None, + column_type='func', + sort_func=None, + tooltip=None, + default=True, ): """Add a function column to the listview. Need a header name, the function and the column types.""" render = gtk.CellRendererText() self.add_column( - header, render, col_types, hidden, position, - status_field, sortid, column_type=column_type, - function=function, sort_func=sort_func, tooltip=tooltip, default=default, + header, + render, + col_types, + hidden, + position, + status_field, + sortid, + column_type=column_type, + function=function, + sort_func=sort_func, + tooltip=tooltip, + default=default, ) return True def add_progress_column( - self, header, col_types=None, sortid=0, - hidden=False, position=None, status_field=None, - function=None, column_type='progress', - tooltip=None, sort_func=None, default=True, + self, + header, + col_types=None, + sortid=0, + hidden=False, + position=None, + status_field=None, + function=None, + column_type='progress', + tooltip=None, + sort_func=None, + default=True, ): """Add a progress column to the listview.""" @@ -645,19 +731,38 @@ class ListView(object): col_types = [float, str] render = gtk.CellRendererProgress() self.add_column( - header, render, col_types, hidden, position, - status_field, sortid, function=function, - column_type=column_type, value=0, text=1, - tooltip=tooltip, sort_func=sort_func, default=default, + header, + render, + col_types, + hidden, + position, + status_field, + sortid, + function=function, + column_type=column_type, + value=0, + text=1, + tooltip=tooltip, + sort_func=sort_func, + default=default, ) return True def add_texticon_column( - self, header, col_types=None, sortid=1, - hidden=False, position=None, status_field=None, - column_type='texticon', function=None, sort_func=None, - tooltip=None, default=True, default_sort=False, + self, + header, + col_types=None, + sortid=1, + hidden=False, + position=None, + status_field=None, + column_type='texticon', + function=None, + sort_func=None, + tooltip=None, + default=True, + default_sort=False, ): """Adds a texticon column to the listview.""" if col_types is None: @@ -666,10 +771,21 @@ class ListView(object): render2 = gtk.CellRendererText() self.add_column( - header, (render1, render2), col_types, hidden, position, - status_field, sortid, column_type=column_type, - function=function, pixbuf=0, text=1, tooltip=tooltip, - sort_func=sort_func, default=default, default_sort=default_sort, + header, + (render1, render2), + col_types, + hidden, + position, + status_field, + sortid, + column_type=column_type, + function=function, + pixbuf=0, + text=1, + tooltip=tooltip, + sort_func=sort_func, + default=default, + default_sort=default_sort, ) return True @@ -708,7 +824,9 @@ class ListView(object): continue column = find_column(col_state.name) if not column: - log.debug('Could not find column matching "%s" on state.', col_state.name) + log.debug( + 'Could not find column matching "%s" on state.', col_state.name + ) # The cases where I've found that the column could not be found # is when not using the english locale, ie, the default one, or # when changing locales between runs. diff --git a/deluge/ui/gtkui/mainwindow.py b/deluge/ui/gtkui/mainwindow.py index c68d0a15e..aef39219b 100644 --- a/deluge/ui/gtkui/mainwindow.py +++ b/deluge/ui/gtkui/mainwindow.py @@ -15,7 +15,12 @@ import os.path from hashlib import sha1 as sha import gtk -from gtk.gdk import ACTION_COPY, WINDOW_STATE_ICONIFIED, WINDOW_STATE_MAXIMIZED, WINDOW_STATE_WITHDRAWN +from gtk.gdk import ( + ACTION_COPY, + WINDOW_STATE_ICONIFIED, + WINDOW_STATE_MAXIMIZED, + WINDOW_STATE_WITHDRAWN, +) from twisted.internet import reactor from twisted.internet.error import ReactorNotRunning @@ -42,8 +47,8 @@ class _GtkBuilderSignalsHolder(object): for name, handler in mapping_or_class.items(): if hasattr(self, name): raise RuntimeError( - 'A handler for signal %r has already been registered: %s' % - (name, getattr(self, name)), + 'A handler for signal %r has already been registered: %s' + % (name, getattr(self, name)) ) setattr(self, name, handler) else: @@ -51,8 +56,10 @@ class _GtkBuilderSignalsHolder(object): if not name.startswith('on_'): continue if hasattr(self, name): - raise RuntimeError('A handler for signal %r has already been registered: %s' % - (name, getattr(self, name))) + raise RuntimeError( + 'A handler for signal %r has already been registered: %s' + % (name, getattr(self, name)) + ) setattr(self, name, getattr(mapping_or_class, name)) @@ -69,22 +76,28 @@ class MainWindow(component.Component): # Think about splitting up mainwindow gtkbuilder file into the necessary parts # to avoid GtkBuilder monkey patch. Those parts would then need adding to mainwindow 'by hand'. self.gtk_builder_signals_holder = _GtkBuilderSignalsHolder() - self.main_builder.prev_connect_signals = copy.deepcopy(self.main_builder.connect_signals) + self.main_builder.prev_connect_signals = copy.deepcopy( + self.main_builder.connect_signals + ) def patched_connect_signals(*a, **k): raise RuntimeError( 'In order to connect signals to this GtkBuilder instance please use ' - '"component.get(\'MainWindow\').connect_signals()"', + '"component.get(\'MainWindow\').connect_signals()"' ) + self.main_builder.connect_signals = patched_connect_signals # Get Gtk Builder files Main Window, New release dialog, and Tabs. for filename in ( - 'main_window.ui', 'main_window.new_release.ui', 'main_window.tabs.ui', - 'main_window.tabs.menu_file.ui', 'main_window.tabs.menu_peer.ui', + 'main_window.ui', + 'main_window.new_release.ui', + 'main_window.tabs.ui', + 'main_window.tabs.menu_file.ui', + 'main_window.tabs.menu_peer.ui', ): self.main_builder.add_from_file( - resource_filename('deluge.ui.gtkui', os.path.join('glade', filename)), + resource_filename('deluge.ui.gtkui', os.path.join('glade', filename)) ) self.window = self.main_builder.get_object('main_window') @@ -102,7 +115,9 @@ class MainWindow(component.Component): self.is_minimized = False self.restart = False - self.window.drag_dest_set(gtk.DEST_DEFAULT_ALL, [('text/uri-list', 0, 80)], ACTION_COPY) + self.window.drag_dest_set( + gtk.DEST_DEFAULT_ALL, [('text/uri-list', 0, 80)], ACTION_COPY + ) # Connect events self.window.connect('window-state-event', self.on_window_state_event) @@ -112,9 +127,13 @@ class MainWindow(component.Component): self.vpaned.connect('notify::position', self.on_vpaned_position_event) self.window.connect('expose-event', self.on_expose_event) - self.config.register_set_function('show_rate_in_title', self._on_set_show_rate_in_title, apply_now=False) + self.config.register_set_function( + 'show_rate_in_title', self._on_set_show_rate_in_title, apply_now=False + ) - client.register_event_handler('NewVersionAvailableEvent', self.on_newversionavailable_event) + client.register_event_handler( + 'NewVersionAvailableEvent', self.on_newversionavailable_event + ) def connect_signals(self, mapping_or_class): self.gtk_builder_signals_holder.connect_signals(mapping_or_class) @@ -123,7 +142,7 @@ class MainWindow(component.Component): self.main_builder.prev_connect_signals(self.gtk_builder_signals_holder) self.vpaned.set_position(self.initial_vpaned_position) if not ( - self.config['start_in_tray'] and self.config['enable_system_tray'] + self.config['start_in_tray'] and self.config['enable_system_tray'] ) and not self.window.get_property('visible'): log.debug('Showing window') self.show() @@ -140,7 +159,9 @@ class MainWindow(component.Component): component.pause(self.child_components) # Store the x, y positions for when we restore the window - self.config['window_x_pos'], self.config['window_y_pos'] = self.window.get_position() + self.config['window_x_pos'], self.config[ + 'window_y_pos' + ] = self.window.get_position() self.window.hide() def present(self): @@ -155,8 +176,12 @@ class MainWindow(component.Component): def on_dialog_response(response_id): if response_id == gtk.RESPONSE_OK: - if self.config['tray_password'] == sha(dialog.get_password()).hexdigest(): + if ( + self.config['tray_password'] + == sha(dialog.get_password()).hexdigest() + ): restore() + dialog.run().addCallback(on_dialog_response) else: restore() @@ -202,14 +227,21 @@ class MainWindow(component.Component): def on_dialog_response(response_id): if response_id == gtk.RESPONSE_OK: - if self.config['tray_password'] == sha(dialog.get_password()).hexdigest(): + if ( + self.config['tray_password'] + == sha(dialog.get_password()).hexdigest() + ): quit_gtkui() + dialog.run().addCallback(on_dialog_response) else: quit_gtkui() def load_window_state(self): - if self.config['window_x_pos'] == -32000 or self.config['window_x_pos'] == -32000: + if ( + self.config['window_x_pos'] == -32000 + or self.config['window_x_pos'] == -32000 + ): self.config['window_x_pos'] = self.config['window_y_pos'] = 0 self.window.move(self.config['window_x_pos'], self.config['window_y_pos']) @@ -219,7 +251,9 @@ class MainWindow(component.Component): def on_window_configure_event(self, widget, event): if not self.config['window_maximized'] and self.visible: - self.config['window_x_pos'], self.config['window_y_pos'] = self.window.get_position() + self.config['window_x_pos'], self.config[ + 'window_y_pos' + ] = self.window.get_position() self.config['window_width'] = event.width self.config['window_height'] = event.height @@ -253,7 +287,9 @@ class MainWindow(component.Component): def on_vpaned_position_event(self, obj, param): self.config['window_pane_position'] = self.vpaned.get_position() - def on_drag_data_received_event(self, widget, drag_context, x, y, selection_data, info, timestamp): + def on_drag_data_received_event( + self, widget, drag_context, x, y, selection_data, info, timestamp + ): log.debug('Selection(s) dropped on main window %s', selection_data.get_text()) if selection_data.get_uris(): process_args(selection_data.get_uris()) @@ -270,12 +306,19 @@ class MainWindow(component.Component): def update(self): # Update the window title def _on_get_session_status(status): - download_rate = fspeed(status['payload_download_rate'], precision=0, shortform=True) - upload_rate = fspeed(status['payload_upload_rate'], precision=0, shortform=True) - self.window.set_title(_('D: %s U: %s - Deluge' % (download_rate, upload_rate))) + download_rate = fspeed( + status['payload_download_rate'], precision=0, shortform=True + ) + upload_rate = fspeed( + status['payload_upload_rate'], precision=0, shortform=True + ) + self.window.set_title( + _('D: %s U: %s - Deluge' % (download_rate, upload_rate)) + ) + if self.config['show_rate_in_title']: client.core.get_session_status( - ['payload_download_rate', 'payload_upload_rate'], + ['payload_download_rate', 'payload_upload_rate'] ).addCallback(_on_get_session_status) def _on_set_show_rate_in_title(self, key, value): @@ -287,6 +330,7 @@ class MainWindow(component.Component): def on_newversionavailable_event(self, new_version): if self.config['show_new_releases']: from deluge.ui.gtkui.new_release_dialog import NewReleaseDialog + reactor.callLater(5.0, NewReleaseDialog().show, new_version) def is_on_active_workspace(self): diff --git a/deluge/ui/gtkui/menubar.py b/deluge/ui/gtkui/menubar.py index a08ce972c..f54f81e09 100644 --- a/deluge/ui/gtkui/menubar.py +++ b/deluge/ui/gtkui/menubar.py @@ -27,7 +27,6 @@ log = logging.getLogger(__name__) class MenuBar(component.Component): - def __init__(self): log.debug('MenuBar init..') component.Component.__init__(self, 'MenuBar') @@ -37,17 +36,23 @@ class MenuBar(component.Component): self.builder = gtk.Builder() # Get the torrent menu from the gtk builder file - self.builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.ui'), - )) + self.builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.ui') + ) + ) # Get the torrent options menu from the gtk builder file - self.builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.options.ui'), - )) + self.builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.options.ui') + ) + ) # Get the torrent queue menu from the gtk builder file - self.builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.queue.ui'), - )) + self.builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.queue.ui') + ) + ) # Attach queue torrent menu torrent_queue_menu = self.builder.get_object('queue_torrent_menu') @@ -56,12 +61,18 @@ class MenuBar(component.Component): torrent_options_menu = self.builder.get_object('options_torrent_menu') self.builder.get_object('menuitem_options').set_submenu(torrent_options_menu) - self.builder.get_object('download-limit-image').set_from_file(deluge.common.get_pixmap('downloading16.png')) - self.builder.get_object('upload-limit-image').set_from_file(deluge.common.get_pixmap('seeding16.png')) + self.builder.get_object('download-limit-image').set_from_file( + deluge.common.get_pixmap('downloading16.png') + ) + self.builder.get_object('upload-limit-image').set_from_file( + deluge.common.get_pixmap('seeding16.png') + ) for menuitem in ( - 'menuitem_down_speed', 'menuitem_up_speed', - 'menuitem_max_connections', 'menuitem_upload_slots', + 'menuitem_down_speed', + 'menuitem_up_speed', + 'menuitem_max_connections', + 'menuitem_upload_slots', ): submenu = gtk.Menu() item = gtk.MenuItem(_('Set Unlimited')) @@ -103,12 +114,24 @@ class MenuBar(component.Component): self.menu_torrent.set_submenu(self.torrentmenu) # Make sure the view menuitems are showing the correct active state - self.main_builder.get_object('menuitem_toolbar').set_active(self.config['show_toolbar']) - self.main_builder.get_object('menuitem_sidebar').set_active(self.config['show_sidebar']) - self.main_builder.get_object('menuitem_statusbar').set_active(self.config['show_statusbar']) - self.main_builder.get_object('sidebar_show_zero').set_active(self.config['sidebar_show_zero']) - self.main_builder.get_object('sidebar_show_trackers').set_active(self.config['sidebar_show_trackers']) - self.main_builder.get_object('sidebar_show_owners').set_active(self.config['sidebar_show_owners']) + self.main_builder.get_object('menuitem_toolbar').set_active( + self.config['show_toolbar'] + ) + self.main_builder.get_object('menuitem_sidebar').set_active( + self.config['show_sidebar'] + ) + self.main_builder.get_object('menuitem_statusbar').set_active( + self.config['show_statusbar'] + ) + self.main_builder.get_object('sidebar_show_zero').set_active( + self.config['sidebar_show_zero'] + ) + self.main_builder.get_object('sidebar_show_trackers').set_active( + self.config['sidebar_show_trackers'] + ) + self.main_builder.get_object('sidebar_show_owners').set_active( + self.config['sidebar_show_owners'] + ) # Connect main window Signals # self.mainwindow.connect_signals(self) @@ -116,19 +139,14 @@ class MenuBar(component.Component): # Connect menubar signals self.builder.connect_signals(self) - self.change_sensitivity = [ - 'menuitem_addtorrent', - ] + self.change_sensitivity = ['menuitem_addtorrent'] def start(self): for widget in self.change_sensitivity: self.main_builder.get_object(widget).set_sensitive(True) # Only show open_folder menuitem and separator if connected to a localhost daemon. - localhost_items = [ - 'menuitem_open_folder', - 'separator4', - ] + localhost_items = ['menuitem_open_folder', 'separator4'] if client.is_localhost(): for widget in localhost_items: self.builder.get_object(widget).show() @@ -138,9 +156,15 @@ class MenuBar(component.Component): self.builder.get_object(widget).hide() self.builder.get_object(widget).set_no_show_all(True) - self.main_builder.get_object('separatormenuitem').set_visible(not self.config['standalone']) - self.main_builder.get_object('menuitem_quitdaemon').set_visible(not self.config['standalone']) - self.main_builder.get_object('menuitem_connectionmanager').set_visible(not self.config['standalone']) + self.main_builder.get_object('separatormenuitem').set_visible( + not self.config['standalone'] + ) + self.main_builder.get_object('menuitem_quitdaemon').set_visible( + not self.config['standalone'] + ) + self.main_builder.get_object('menuitem_connectionmanager').set_visible( + not self.config['standalone'] + ) # Show the Torrent menu because we're connected to a host self.menu_torrent.show() @@ -148,21 +172,35 @@ class MenuBar(component.Component): if client.get_auth_level() == deluge.common.AUTH_LEVEL_ADMIN: # Get known accounts to allow changing ownership client.core.get_known_accounts().addCallback( - self._on_known_accounts, + self._on_known_accounts ).addErrback(self._on_known_accounts_fail) - client.register_event_handler('TorrentStateChangedEvent', self.on_torrentstatechanged_event) - client.register_event_handler('TorrentResumedEvent', self.on_torrentresumed_event) + client.register_event_handler( + 'TorrentStateChangedEvent', self.on_torrentstatechanged_event + ) + client.register_event_handler( + 'TorrentResumedEvent', self.on_torrentresumed_event + ) client.register_event_handler('SessionPausedEvent', self.on_sessionpaused_event) - client.register_event_handler('SessionResumedEvent', self.on_sessionresumed_event) + client.register_event_handler( + 'SessionResumedEvent', self.on_sessionresumed_event + ) def stop(self): log.debug('MenuBar stopping') - client.deregister_event_handler('TorrentStateChangedEvent', self.on_torrentstatechanged_event) - client.deregister_event_handler('TorrentResumedEvent', self.on_torrentresumed_event) - client.deregister_event_handler('SessionPausedEvent', self.on_sessionpaused_event) - client.deregister_event_handler('SessionResumedEvent', self.on_sessionresumed_event) + client.deregister_event_handler( + 'TorrentStateChangedEvent', self.on_torrentstatechanged_event + ) + client.deregister_event_handler( + 'TorrentResumedEvent', self.on_torrentresumed_event + ) + client.deregister_event_handler( + 'SessionPausedEvent', self.on_sessionpaused_event + ) + client.deregister_event_handler( + 'SessionResumedEvent', self.on_sessionresumed_event + ) for widget in self.change_sensitivity: self.main_builder.get_object(widget).set_sensitive(False) @@ -212,6 +250,7 @@ class MenuBar(component.Component): def on_menuitem_createtorrent_activate(self, data=None): log.debug('on_menuitem_createtorrent_activate') from deluge.ui.gtkui.createtorrentdialog import CreateTorrentDialog + CreateTorrentDialog().show() def on_menuitem_quitdaemon_activate(self, data=None): @@ -234,28 +273,24 @@ class MenuBar(component.Component): # Torrent Menu # def on_menuitem_pause_activate(self, data=None): log.debug('on_menuitem_pause_activate') - client.core.pause_torrent( - component.get('TorrentView').get_selected_torrents(), - ) + client.core.pause_torrent(component.get('TorrentView').get_selected_torrents()) def on_menuitem_resume_activate(self, data=None): log.debug('on_menuitem_resume_activate') - client.core.resume_torrent( - component.get('TorrentView').get_selected_torrents(), - ) + client.core.resume_torrent(component.get('TorrentView').get_selected_torrents()) def on_menuitem_updatetracker_activate(self, data=None): log.debug('on_menuitem_updatetracker_activate') client.core.force_reannounce( - component.get('TorrentView').get_selected_torrents(), + component.get('TorrentView').get_selected_torrents() ) def on_menuitem_edittrackers_activate(self, data=None): log.debug('on_menuitem_edittrackers_activate') from deluge.ui.gtkui.edittrackersdialog import EditTrackersDialog + dialog = EditTrackersDialog( - component.get('TorrentView').get_selected_torrent(), - self.mainwindow.window, + component.get('TorrentView').get_selected_torrent(), self.mainwindow.window ) dialog.run() @@ -264,39 +299,42 @@ class MenuBar(component.Component): torrent_ids = component.get('TorrentView').get_selected_torrents() if torrent_ids: from deluge.ui.gtkui.removetorrentdialog import RemoveTorrentDialog + RemoveTorrentDialog(torrent_ids).run() def on_menuitem_recheck_activate(self, data=None): log.debug('on_menuitem_recheck_activate') - client.core.force_recheck( - component.get('TorrentView').get_selected_torrents(), - ) + client.core.force_recheck(component.get('TorrentView').get_selected_torrents()) def on_menuitem_open_folder_activate(self, data=None): log.debug('on_menuitem_open_folder') def _on_torrent_status(status): timestamp = gtk.get_current_event_time() - path = os.path.join(status['download_location'], status['files'][0]['path'].split('/')[0]) + path = os.path.join( + status['download_location'], status['files'][0]['path'].split('/')[0] + ) deluge.common.show_file(path, timestamp=timestamp) + for torrent_id in component.get('TorrentView').get_selected_torrents(): component.get('SessionProxy').get_torrent_status( - torrent_id, ['download_location', 'files'], + torrent_id, ['download_location', 'files'] ).addCallback(_on_torrent_status) def on_menuitem_move_activate(self, data=None): log.debug('on_menuitem_move_activate') component.get('SessionProxy').get_torrent_status( - component.get('TorrentView').get_selected_torrent(), - ['download_location'], + component.get('TorrentView').get_selected_torrent(), ['download_location'] ).addCallback(self.show_move_storage_dialog) def show_move_storage_dialog(self, status): log.debug('show_move_storage_dialog') builder = gtk.Builder() - builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'move_storage_dialog.ui'), - )) + builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'move_storage_dialog.ui') + ) + ) # Keep it referenced: # https://bugzilla.gnome.org/show_bug.cgi?id=546802 self.move_storage_dialog = builder.get_object('move_storage_dialog') @@ -319,12 +357,11 @@ class MenuBar(component.Component): if response_id == gtk.RESPONSE_OK: log.debug( - 'Moving torrents to %s', - self.move_storage_path_chooser.get_text(), + 'Moving torrents to %s', self.move_storage_path_chooser.get_text() ) path = self.move_storage_path_chooser.get_text() client.core.move_storage( - component.get('TorrentView').get_selected_torrents(), path, + component.get('TorrentView').get_selected_torrents(), path ).addCallback(on_core_result) self.move_storage_dialog.connect('response', on_dialog_response_event) @@ -375,6 +412,7 @@ class MenuBar(component.Component): def on_menuitem_about_activate(self, data=None): log.debug('on_menuitem_about_activate') from deluge.ui.gtkui.aboutdialog import AboutDialog + AboutDialog().run() def on_menuitem_set_unlimited(self, widget): @@ -401,22 +439,35 @@ class MenuBar(component.Component): other_dialog_info = { 'menuitem_down_speed': [ - _('Download Speed Limit'), _('Set the maximum download speed'), - _('KiB/s'), 'downloading.svg', + _('Download Speed Limit'), + _('Set the maximum download speed'), + _('KiB/s'), + 'downloading.svg', ], 'menuitem_up_speed': [ - _('Upload Speed Limit'), _('Set the maximum upload speed'), - _('KiB/s'), 'seeding.svg', + _('Upload Speed Limit'), + _('Set the maximum upload speed'), + _('KiB/s'), + 'seeding.svg', ], 'menuitem_max_connections': [ - _('Incoming Connections'), _('Set the maximum incoming connections'), - '', gtk.STOCK_NETWORK, + _('Incoming Connections'), + _('Set the maximum incoming connections'), + '', + gtk.STOCK_NETWORK, ], 'menuitem_upload_slots': [ - _('Peer Upload Slots'), _('Set the maximum upload slots'), - '', gtk.STOCK_SORT_ASCENDING, + _('Peer Upload Slots'), + _('Set the maximum upload slots'), + '', + gtk.STOCK_SORT_ASCENDING, + ], + 'menuitem_stop_seed_at_ratio': [ + _('Stop Seed At Ratio'), + 'Stop torrent seeding at ratio', + '', + None, ], - 'menuitem_stop_seed_at_ratio': [_('Stop Seed At Ratio'), 'Stop torrent seeding at ratio', '', None], } core_key = status_map[widget.get_name()][0] @@ -443,15 +494,16 @@ class MenuBar(component.Component): torrent_ids = component.get('TorrentView').get_selected_torrents() if len(torrent_ids) == 1: core_key_global = core_key - d = component.get('SessionProxy').get_torrent_status(torrent_ids[0], [core_key]) + d = component.get('SessionProxy').get_torrent_status( + torrent_ids[0], [core_key] + ) else: d = client.core.get_config_values([core_key_global]) d.addCallback(_on_torrent_status) def on_menuitem_set_automanaged_on(self, widget): client.core.set_torrent_options( - component.get('TorrentView').get_selected_torrents(), - {'auto_managed': True}, + component.get('TorrentView').get_selected_torrents(), {'auto_managed': True} ) def on_menuitem_set_automanaged_off(self, widget): @@ -510,9 +562,11 @@ class MenuBar(component.Component): 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, + 'activate', self._on_change_owner_submenu_active + ) + self.builder.get_object('menuitem_change_owner').set_submenu( + self.change_owner_submenu ) - self.builder.get_object('menuitem_change_owner').set_submenu(self.change_owner_submenu) def _on_known_accounts_fail(self, reason): self.builder.get_object('menuitem_change_owner').set_visible(False) @@ -524,7 +578,9 @@ class MenuBar(component.Component): self.change_owner_submenu_items[None].set_active(True) return - torrent_owner = component.get('TorrentView').get_torrent_status(selected[0])['owner'] + torrent_owner = component.get('TorrentView').get_torrent_status(selected[0])[ + 'owner' + ] for username, item in self.change_owner_submenu_items.items(): item.set_active(username == torrent_owner) @@ -544,8 +600,10 @@ class MenuBar(component.Component): ErrorDialog( _('Ownership Change Error'), _('There was an error while trying changing ownership.'), - self.mainwindow.window, details=failure.value.logable(), + self.mainwindow.window, + details=failure.value.logable(), ).run() + client.core.set_torrent_options( - update_torrents, {'owner': username}, + update_torrents, {'owner': username} ).addErrback(failed_change_owner) diff --git a/deluge/ui/gtkui/menubar_osx.py b/deluge/ui/gtkui/menubar_osx.py index 64cfbbad7..511594b04 100644 --- a/deluge/ui/gtkui/menubar_osx.py +++ b/deluge/ui/gtkui/menubar_osx.py @@ -41,7 +41,14 @@ def menubar_osx(gtkui, osxapp): accel_meta(file_items[0], group, 'o') accel_meta(file_items[1], group, 'n') quit_all_item = file_items[3] - accel_swap(quit_all_item, group, 'q', SHIFT_MASK | CONTROL_MASK, 'q', SHIFT_MASK | META_MASK) + accel_swap( + quit_all_item, + group, + 'q', + SHIFT_MASK | CONTROL_MASK, + 'q', + SHIFT_MASK | META_MASK, + ) for item in range(2, len(file_items)): # remove quits file_menu.remove(file_items[item]) diff --git a/deluge/ui/gtkui/new_release_dialog.py b/deluge/ui/gtkui/new_release_dialog.py index c9df8a5bc..246fc0ec8 100644 --- a/deluge/ui/gtkui/new_release_dialog.py +++ b/deluge/ui/gtkui/new_release_dialog.py @@ -16,7 +16,6 @@ from deluge.ui.client import client class NewReleaseDialog(object): - def __init__(self): pass @@ -27,30 +26,35 @@ class NewReleaseDialog(object): # Set the version labels if deluge.common.windows_check() or deluge.common.osx_check(): main_builder.get_object('image_new_release').set_from_file( - deluge.common.get_pixmap('deluge16.png'), + deluge.common.get_pixmap('deluge16.png') ) else: main_builder.get_object('image_new_release').set_from_icon_name('deluge', 4) main_builder.get_object('label_available_version').set_text(available_version) main_builder.get_object('label_client_version').set_text( - deluge.common.get_version(), + deluge.common.get_version() + ) + self.chk_not_show_dialog = main_builder.get_object( + 'chk_do_not_show_new_release' ) - self.chk_not_show_dialog = main_builder.get_object('chk_do_not_show_new_release') main_builder.get_object('button_goto_downloads').connect( - 'clicked', self._on_button_goto_downloads, + 'clicked', self._on_button_goto_downloads ) main_builder.get_object('button_close_new_release').connect( - 'clicked', self._on_button_close_new_release, + 'clicked', self._on_button_close_new_release ) if client.connected(): + def on_info(version): main_builder.get_object('label_server_version').set_text(version) main_builder.get_object('label_server_version').show() main_builder.get_object('label_server_version_text').show() if not client.is_standalone(): - main_builder.get_object('label_client_version_text').set_label(_('<i>Client Version</i>')) + main_builder.get_object('label_client_version_text').set_label( + _('<i>Client Version</i>') + ) client.daemon.info().addCallback(on_info) self.dialog.show() diff --git a/deluge/ui/gtkui/options_tab.py b/deluge/ui/gtkui/options_tab.py index 6fdb13d2e..7e4199ba7 100644 --- a/deluge/ui/gtkui/options_tab.py +++ b/deluge/ui/gtkui/options_tab.py @@ -30,9 +30,15 @@ class OptionsTab(Tab): self.add_tab_widget('spin_max_download', 'value', ['max_download_speed']) self.add_tab_widget('spin_max_upload', 'value', ['max_upload_speed']) self.add_tab_widget('spin_max_connections', 'value_as_int', ['max_connections']) - self.add_tab_widget('spin_max_upload_slots', 'value_as_int', ['max_upload_slots']) - self.add_tab_widget('chk_prioritize_first_last', 'active', ['prioritize_first_last_pieces']) - self.add_tab_widget('chk_sequential_download', 'active', ['sequential_download']) + self.add_tab_widget( + 'spin_max_upload_slots', 'value_as_int', ['max_upload_slots'] + ) + self.add_tab_widget( + 'chk_prioritize_first_last', 'active', ['prioritize_first_last_pieces'] + ) + self.add_tab_widget( + 'chk_sequential_download', 'active', ['sequential_download'] + ) self.add_tab_widget('chk_auto_managed', 'active', ['is_auto_managed']) self.add_tab_widget('chk_stop_at_ratio', 'active', ['stop_at_ratio']) self.add_tab_widget('chk_remove_at_ratio', 'active', ['remove_at_ratio']) @@ -44,20 +50,24 @@ class OptionsTab(Tab): # Connect key press event for spin widgets. for widget_id in self.tab_widgets: if widget_id.startswith('spin_'): - self.tab_widgets[widget_id].obj.connect('key-press-event', self.on_key_press_event) + self.tab_widgets[widget_id].obj.connect( + 'key-press-event', self.on_key_press_event + ) self.button_apply = self.main_builder.get_object('button_apply') self.move_completed_path_chooser = PathChooser('move_completed_paths_list') self.move_completed_path_chooser.set_sensitive( - self.tab_widgets['chk_move_completed'].obj.get_active(), + self.tab_widgets['chk_move_completed'].obj.get_active() ) self.move_completed_path_chooser.connect( - 'text-changed', self.on_path_chooser_text_changed_event, + 'text-changed', self.on_path_chooser_text_changed_event ) self.status_keys.append('move_completed_path') - self.move_completed_hbox = self.main_builder.get_object('hbox_move_completed_path_chooser') + self.move_completed_hbox = self.main_builder.get_object( + 'hbox_move_completed_path_chooser' + ) self.move_completed_hbox.add(self.move_completed_path_chooser) self.move_completed_hbox.show_all() @@ -85,7 +95,7 @@ class OptionsTab(Tab): self.clear() component.get('SessionProxy').get_torrents_status( - {'id': torrent_ids}, self.status_keys, + {'id': torrent_ids}, self.status_keys ).addCallback(self.parse_torrents_statuses) self.prev_torrent_ids = torrent_ids @@ -137,21 +147,32 @@ class OptionsTab(Tab): set_func = 'set_' + widget.func.replace('_as_int', '') getattr(widget.obj, set_func)(status_value) if set_func == 'set_active': - widget.obj.set_inconsistent(status_key in self.inconsistent_keys) + widget.obj.set_inconsistent( + status_key in self.inconsistent_keys + ) - if new_status['move_completed_path'] != self.prev_status['move_completed_path']: + if ( + new_status['move_completed_path'] + != self.prev_status['move_completed_path'] + ): text = new_status['move_completed_path'] - self.move_completed_path_chooser.set_text(text, cursor_end=False, default_text=True) + self.move_completed_path_chooser.set_text( + text, cursor_end=False, default_text=True + ) # Update sensitivity of widgets. - self.tab_widgets['spin_stop_ratio'].obj.set_sensitive(new_status['stop_at_ratio']) - self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(new_status['stop_at_ratio']) + self.tab_widgets['spin_stop_ratio'].obj.set_sensitive( + new_status['stop_at_ratio'] + ) + self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive( + new_status['stop_at_ratio'] + ) # Ensure apply button sensitivity is set False. self.button_apply.set_sensitive(False) self.prev_status = new_status -# === Widget signal handlers === # + # === Widget signal handlers === # def on_button_apply_clicked(self, button): options = {} @@ -161,7 +182,8 @@ class OptionsTab(Tab): continue # A label so read-only widget_value = getattr(widget.obj, 'get_' + widget.func)() if widget_value != self.prev_status[status_key] or ( - status_key in self.inconsistent_keys and not widget.obj.get_inconsistent() + status_key in self.inconsistent_keys + and not widget.obj.get_inconsistent() ): options[status_key] = widget_value diff --git a/deluge/ui/gtkui/path_chooser.py b/deluge/ui/gtkui/path_chooser.py index 249b4c894..7bf081de0 100644 --- a/deluge/ui/gtkui/path_chooser.py +++ b/deluge/ui/gtkui/path_chooser.py @@ -25,12 +25,12 @@ def singleton(cls): if cls not in instances: instances[cls] = cls() return instances[cls] + return getinstance @singleton class PathChoosersHandler(component.Component): - def __init__(self, paths_config_key=None): # self.chooser_name = "PathChooser_%d" % (len(PathChooser.path_choosers) +1) component.Component.__init__(self, 'PathChoosersHandler') @@ -60,6 +60,7 @@ class PathChoosersHandler(component.Component): self.config_properties.update(config) for chooser in self.path_choosers: chooser.set_config(config) + keys = list(self.config_keys_to_funcs_mapping) keys += self.paths_list_keys client.core.get_config_values(keys).addCallback(_on_config_values) @@ -68,8 +69,12 @@ class PathChoosersHandler(component.Component): chooser.config_key_funcs = {} for key in self.config_keys_to_funcs_mapping: chooser.config_key_funcs[key] = [None, None] - chooser.config_key_funcs[key][0] = getattr(chooser, 'get_%s' % self.config_keys_to_funcs_mapping[key]) - chooser.config_key_funcs[key][1] = getattr(chooser, 'set_%s' % self.config_keys_to_funcs_mapping[key]) + chooser.config_key_funcs[key][0] = getattr( + chooser, 'get_%s' % self.config_keys_to_funcs_mapping[key] + ) + chooser.config_key_funcs[key][1] = getattr( + chooser, 'set_%s' % self.config_keys_to_funcs_mapping[key] + ) self.path_choosers.append(chooser) if chooser.paths_config_key not in self.paths_list_keys: @@ -95,7 +100,9 @@ class PathChoosersHandler(component.Component): # The value hasn't been changed in one second, so save to core if self.max_rows_value_set == value_: client.core.set_config({'path_chooser_max_popup_rows': value}) + from twisted.internet import reactor + reactor.callLater(1, update, value) def on_list_values_changed(self, values, key, caller): @@ -115,7 +122,6 @@ class PathChoosersHandler(component.Component): class PathChooser(PathChooserComboBox): - def __init__(self, paths_config_key=None): self.paths_config_key = paths_config_key super(PathChooser, self).__init__() @@ -123,34 +129,52 @@ class PathChooser(PathChooserComboBox): self.chooser_handler.register_chooser(self) self.set_auto_completer_func(self.on_completion) self.connect('list-values-changed', self.on_list_values_changed_event) - self.connect('auto-complete-enabled-toggled', self.on_auto_complete_enabled_toggled) + self.connect( + 'auto-complete-enabled-toggled', self.on_auto_complete_enabled_toggled + ) self.connect('show-filechooser-toggled', self.on_show_filechooser_toggled) - self.connect('show-folder-name-on-button', self.on_show_folder_on_button_toggled) + self.connect( + 'show-folder-name-on-button', self.on_show_folder_on_button_toggled + ) self.connect('show-path-entry-toggled', self.on_show_path_entry_toggled) self.connect('accelerator-set', self.on_accelerator_set) self.connect('max-rows-changed', self.on_max_rows_changed) self.connect('show-hidden-files-toggled', self.on_show_hidden_files_toggled) def on_auto_complete_enabled_toggled(self, widget, value): - self.chooser_handler.set_value_for_path_choosers(value, 'path_chooser_auto_complete_enabled') + self.chooser_handler.set_value_for_path_choosers( + value, 'path_chooser_auto_complete_enabled' + ) def on_show_filechooser_toggled(self, widget, value): - self.chooser_handler.set_value_for_path_choosers(value, 'path_chooser_show_chooser_button_on_localhost') + self.chooser_handler.set_value_for_path_choosers( + value, 'path_chooser_show_chooser_button_on_localhost' + ) def on_show_folder_on_button_toggled(self, widget, value): - self.chooser_handler.set_value_for_path_choosers(value, 'path_chooser_show_folder_name') + self.chooser_handler.set_value_for_path_choosers( + value, 'path_chooser_show_folder_name' + ) def on_show_path_entry_toggled(self, widget, value): - self.chooser_handler.set_value_for_path_choosers(value, 'path_chooser_show_path_entry') + self.chooser_handler.set_value_for_path_choosers( + value, 'path_chooser_show_path_entry' + ) def on_accelerator_set(self, widget, value): - self.chooser_handler.set_value_for_path_choosers(value, 'path_chooser_accelerator_string') + self.chooser_handler.set_value_for_path_choosers( + value, 'path_chooser_accelerator_string' + ) def on_show_hidden_files_toggled(self, widget, value): - self.chooser_handler.set_value_for_path_choosers(value, 'path_chooser_show_hidden_files') + self.chooser_handler.set_value_for_path_choosers( + value, 'path_chooser_show_hidden_files' + ) def on_max_rows_changed(self, widget, value): - self.chooser_handler.set_value_for_path_choosers(value, 'path_chooser_max_popup_rows') + self.chooser_handler.set_value_for_path_choosers( + value, 'path_chooser_max_popup_rows' + ) def on_list_values_changed_event(self, widget, values): self.chooser_handler.on_list_values_changed(values, self.paths_config_key, self) @@ -171,5 +195,6 @@ class PathChooser(PathChooserComboBox): def on_completion(self, args): def on_paths_cb(args): self.complete(args) + d = client.core.get_completion_paths(args) d.addCallback(on_paths_cb) diff --git a/deluge/ui/gtkui/path_combo_chooser.py b/deluge/ui/gtkui/path_combo_chooser.py index f45335623..7d5c46ffd 100755 --- a/deluge/ui/gtkui/path_combo_chooser.py +++ b/deluge/ui/gtkui/path_combo_chooser.py @@ -77,8 +77,7 @@ class ValueList(object): return values def add_values( - self, paths, append=True, scroll_to_row=False, - clear=False, emit_signal=False, + self, paths, append=True, scroll_to_row=False, clear=False, emit_signal=False ): """ Add paths to the liststore @@ -155,7 +154,7 @@ class ValueList(object): if index == len(self.tree_store): index -= 1 if index >= 0: - path = (index, ) + path = (index,) self.treeview.set_cursor(path) self.set_path_selected(path) self.emit('list-value-removed', path_value) @@ -194,11 +193,9 @@ class ValueList(object): keyval = event.keyval state = event.get_state() & gtk.accelerator_get_default_mod_mask() - if keyval == keysyms.Escape or\ - ( - key_is_up(keyval) and - state == gdk.MOD1_MASK - ): # ALT Key + if keyval == keysyms.Escape or ( + key_is_up(keyval) and state == gdk.MOD1_MASK + ): # ALT Key self.popdown() return True # Set entry value to the selected row @@ -219,15 +216,18 @@ class ValueList(object): if event.button != 3: # Double clicked a row, set this as the entry value # and close the popup - if (double_click and event.type == gdk._2BUTTON_PRESS) or\ - (not double_click and event.type == gdk.BUTTON_PRESS): + if (double_click and event.type == gdk._2BUTTON_PRESS) or ( + not double_click and event.type == gdk.BUTTON_PRESS + ): path = self.get_selection_path() if path: self.set_entry_value(path, popdown=True) return True return False - def handle_list_scroll(self, _next=None, path=None, set_entry=False, swap=False, scroll_window=False): + def handle_list_scroll( + self, _next=None, path=None, set_entry=False, swap=False, scroll_window=False + ): """ Handles changes to the row selection. @@ -279,7 +279,7 @@ class ValueList(object): return # This is a regular scroll, not setting value in entry or swapping rows, # so we find a path value anyways - path = (0, ) + path = (0,) cursor = self.treeview.get_cursor() if cursor is not None and cursor[0] is not None: path = cursor[0] @@ -299,13 +299,12 @@ class ValueList(object): index = len(self.tree_store) - 1 # We have the index for the new path - new_path = (index) + new_path = index if swap: p1 = self.tree_store[path][0] p2 = self.tree_store[new_path][0] self.tree_store.swap( - self.tree_store.get_iter(path), - self.tree_store.get_iter(new_path), + self.tree_store.get_iter(path), self.tree_store.get_iter(new_path) ) self.emit('list-values-reordered', [p1, p2]) self.emit('list-values-changed', self.get_values()) @@ -318,7 +317,6 @@ class ValueList(object): class StoredValuesList(ValueList): - def __init__(self): self.tree_store = self.builder.get_object('stored_values_tree_store') self.tree_column = self.builder.get_object('stored_values_treeview_column') @@ -326,15 +324,20 @@ class StoredValuesList(ValueList): self.paths_without_trailing_path_sep = False # Add signal handlers - self.signal_handlers['on_stored_values_treeview_mouse_button_press_event'] = \ - self.on_treeview_mouse_button_press_event + self.signal_handlers[ + 'on_stored_values_treeview_mouse_button_press_event' + ] = self.on_treeview_mouse_button_press_event - self.signal_handlers['on_stored_values_treeview_key_press_event'] = \ - self.on_stored_values_treeview_key_press_event - self.signal_handlers['on_stored_values_treeview_key_release_event'] = \ - self.on_stored_values_treeview_key_release_event + self.signal_handlers[ + 'on_stored_values_treeview_key_press_event' + ] = self.on_stored_values_treeview_key_press_event + self.signal_handlers[ + 'on_stored_values_treeview_key_release_event' + ] = self.on_stored_values_treeview_key_release_event - self.signal_handlers['on_cellrenderertext_edited'] = self.on_cellrenderertext_edited + self.signal_handlers[ + 'on_cellrenderertext_edited' + ] = self.on_cellrenderertext_edited def on_cellrenderertext_edited(self, cellrenderertext, path, new_text): """ @@ -369,7 +372,9 @@ class StoredValuesList(ValueList): """ # This is left click if event.button != 3: - super(StoredValuesList, self).on_treeview_mouse_button_press_event(treeview, event, double_click=True) + super(StoredValuesList, self).on_treeview_mouse_button_press_event( + treeview, event, double_click=True + ) return False # This is right click, create popup menu for this row @@ -405,7 +410,9 @@ class StoredValuesList(ValueList): PathChooserPopup.popup(self) def on_stored_values_treeview_key_press_event(self, widget, event): - super(StoredValuesList, self).on_value_list_treeview_key_press_event(widget, event) + super(StoredValuesList, self).on_value_list_treeview_key_press_event( + widget, event + ) # Prevent the default event handler to move the cursor in the list if key_is_up_or_down(event.keyval): return True @@ -422,17 +429,14 @@ class StoredValuesList(ValueList): ctrl = event.get_state() & gdk.CONTROL_MASK # Edit selected row - if (keyval in [keysyms.Left, keysyms.Right, keysyms.space]): + if keyval in [keysyms.Left, keysyms.Right, keysyms.space]: path = self.get_selection_path() if path: self.on_edit_path(path, self.tree_column) elif key_is_up_or_down(keyval): # Swap the row value if event.get_state() & gdk.CONTROL_MASK: - self.handle_list_scroll( - _next=key_is_down(keyval), - swap=True, - ) + self.handle_list_scroll(_next=key_is_down(keyval), swap=True) else: self.handle_list_scroll(_next=key_is_down(keyval)) elif key_is_pgup_or_pgdown(event.keyval): @@ -447,7 +451,9 @@ class StoredValuesList(ValueList): return True # Add current value to saved list elif is_ascii_value(keyval, 's'): - super(PathChooserComboBox, self).add_current_value_to_saved_list() # pylint: disable=bad-super-call + super( + PathChooserComboBox, self + ).add_current_value_to_saved_list() # pylint: disable=bad-super-call return True # Edit selected value elif is_ascii_value(keyval, 'e'): @@ -456,20 +462,24 @@ class StoredValuesList(ValueList): class CompletionList(ValueList): - def __init__(self): self.tree_store = self.builder.get_object('completion_tree_store') self.tree_column = self.builder.get_object('completion_treeview_column') self.rendererText = self.builder.get_object('completion_cellrenderertext') - self.completion_scrolled_window = self.builder.get_object('completion_scrolled_window') - self.signal_handlers['on_completion_treeview_key_press_event'] = \ - self.on_completion_treeview_key_press_event - self.signal_handlers['on_completion_treeview_motion_notify_event'] = \ - self.on_completion_treeview_motion_notify_event + self.completion_scrolled_window = self.builder.get_object( + 'completion_scrolled_window' + ) + self.signal_handlers[ + 'on_completion_treeview_key_press_event' + ] = self.on_completion_treeview_key_press_event + self.signal_handlers[ + 'on_completion_treeview_motion_notify_event' + ] = self.on_completion_treeview_motion_notify_event # Add super class signal handler - self.signal_handlers['on_completion_treeview_mouse_button_press_event'] = \ - super(CompletionList, self).on_treeview_mouse_button_press_event + self.signal_handlers['on_completion_treeview_mouse_button_press_event'] = super( + CompletionList, self + ).on_treeview_mouse_button_press_event def reduce_values(self, prefix): """ @@ -487,7 +497,9 @@ class CompletionList(ValueList): self.add_values(matching_values, clear=True) def on_completion_treeview_key_press_event(self, widget, event): - ret = super(CompletionList, self).on_value_list_treeview_key_press_event(widget, event) + ret = super(CompletionList, self).on_value_list_treeview_key_press_event( + widget, event + ) if ret: return ret keyval = event.keyval @@ -498,7 +510,9 @@ class CompletionList(ValueList): elif ctrl: # Set show/hide hidden files if is_ascii_value(keyval, 'h'): - self.path_entry.set_show_hidden_files(not self.path_entry.get_show_hidden_files(), do_completion=True) + self.path_entry.set_show_hidden_files( + not self.path_entry.get_show_hidden_files(), do_completion=True + ) return True def on_completion_treeview_motion_notify_event(self, widget, event): @@ -515,13 +529,16 @@ class CompletionList(ValueList): class PathChooserPopup(object): """This creates the popop window for the ComboEntry.""" + def __init__(self, min_visible_rows, max_visible_rows, popup_alignment_widget): self.min_visible_rows = min_visible_rows # Maximum number of rows to display without scrolling self.set_max_popup_rows(max_visible_rows) self.popup_window.realize() self.alignment_widget = popup_alignment_widget - self.popup_buttonbox = None # If set, the height of this widget is the minimum height + self.popup_buttonbox = ( + None + ) # If set, the height of this widget is the minimum height def popup(self): """Make the popup visible.""" @@ -579,8 +596,14 @@ class PathChooserPopup(object): width = self.popup_window.size_request()[0] if self.popup_buttonbox: - buttonbox_height = max(self.popup_buttonbox.size_request()[1], self.popup_buttonbox.get_allocation().height) - buttonbox_width = max(self.popup_buttonbox.size_request()[0], self.popup_buttonbox.get_allocation().width) + buttonbox_height = max( + self.popup_buttonbox.size_request()[1], + self.popup_buttonbox.get_allocation().height, + ) + buttonbox_width = max( + self.popup_buttonbox.size_request()[0], + self.popup_buttonbox.get_allocation().width, + ) treeview_width = self.treeview.size_request()[0] # After removing an element from the tree store, self.treeview.size_request()[0] # returns -1 for some reason, so the requested width cannot be used until the treeview @@ -631,14 +654,17 @@ class PathChooserPopup(object): x = monitor.x + monitor.width - width # Set the position - if y + self.path_entry.get_allocation().height + height <= monitor.y + monitor.height: + if ( + y + self.path_entry.get_allocation().height + height + <= monitor.y + monitor.height + ): y += self.path_entry.get_allocation().height # Not enough space downwards on the screen elif y - height >= monitor.y: y -= height elif ( - monitor.y + monitor.height - (y + self.path_entry.get_allocation().height) > - y - monitor.y + monitor.y + monitor.height - (y + self.path_entry.get_allocation().height) + > y - monitor.y ): y += self.path_entry.get_allocation().height height = monitor.y + monitor.height - y @@ -650,19 +676,30 @@ class PathChooserPopup(object): def popup_grab_window(self): activate_time = 0 - if gdk.pointer_grab( - self.popup_window.get_window(), True, - ( - gdk.BUTTON_PRESS_MASK | - gdk.BUTTON_RELEASE_MASK | - gdk.POINTER_MOTION_MASK - ), - None, None, activate_time, - ) == 0: - if gdk.keyboard_grab(self.popup_window.get_window(), True, activate_time) == 0: + if ( + gdk.pointer_grab( + self.popup_window.get_window(), + True, + ( + gdk.BUTTON_PRESS_MASK + | gdk.BUTTON_RELEASE_MASK + | gdk.POINTER_MOTION_MASK + ), + None, + None, + activate_time, + ) + == 0 + ): + if ( + gdk.keyboard_grab(self.popup_window.get_window(), True, activate_time) + == 0 + ): return True else: - self.popup_window.get_window().get_display().pointer_ungrab(activate_time) + self.popup_window.get_window().get_display().pointer_ungrab( + activate_time + ) return False return False @@ -671,7 +708,9 @@ class PathChooserPopup(object): Sets the text of the entry to the value in path """ - self.path_entry.set_text(self.tree_store[path][0], set_file_chooser_folder=True, trigger_event=True) + self.path_entry.set_text( + self.tree_store[path][0], set_file_chooser_folder=True, trigger_event=True + ) if popdown: self.popdown() @@ -686,21 +725,20 @@ class PathChooserPopup(object): def get_max_popup_rows(self): return self.max_visible_rows -################# -# Callbacks -################# + ################# + # Callbacks + ################# def on_popup_window_button_press_event(self, window, event): # If we're clicking outside of the window close the popup hide = False # Also if the intersection of self and the event is empty, hide # the path_list - if (tuple(self.popup_window.get_allocation().intersect( - gdk.Rectangle( - x=int(event.x), y=int(event.y), - width=1, height=1, - ), - )) == (0, 0, 0, 0)): + if tuple( + self.popup_window.get_allocation().intersect( + gdk.Rectangle(x=int(event.x), y=int(event.y), width=1, height=1) + ) + ) == (0, 0, 0, 0): hide = True # Toplevel is the window that received the event, and parent is the # path_list window. If they are not the same, means the popup should @@ -721,6 +759,7 @@ class StoredValuesPopup(StoredValuesList, PathChooserPopup): The stored values popup """ + def __init__(self, builder, path_entry, max_visible_rows, popup_alignment_widget): self.builder = builder self.treeview = self.builder.get_object('stored_values_treeview') @@ -736,15 +775,23 @@ class StoredValuesPopup(StoredValuesList, PathChooserPopup): self.popup_buttonbox = self.builder.get_object('buttonbox') # Add signal handlers - self.signal_handlers['on_buttonbox_key_press_event'] = self.on_buttonbox_key_press_event - self.signal_handlers['on_stored_values_treeview_scroll_event'] = self.on_scroll_event - self.signal_handlers['on_button_toggle_dropdown_scroll_event'] = self.on_scroll_event + self.signal_handlers[ + 'on_buttonbox_key_press_event' + ] = self.on_buttonbox_key_press_event + self.signal_handlers[ + 'on_stored_values_treeview_scroll_event' + ] = self.on_scroll_event + self.signal_handlers[ + 'on_button_toggle_dropdown_scroll_event' + ] = self.on_scroll_event self.signal_handlers['on_entry_text_scroll_event'] = self.on_scroll_event - self.signal_handlers['on_stored_values_popup_window_focus_out_event'] = \ - self.on_stored_values_popup_window_focus_out_event + self.signal_handlers[ + 'on_stored_values_popup_window_focus_out_event' + ] = self.on_stored_values_popup_window_focus_out_event # For when clicking outside the popup - self.signal_handlers['on_stored_values_popup_window_button_press_event'] = \ - self.on_popup_window_button_press_event + self.signal_handlers[ + 'on_stored_values_popup_window_button_press_event' + ] = self.on_popup_window_button_press_event # Buttons for manipulating the list self.signal_handlers['on_button_add_clicked'] = self.on_button_add_clicked @@ -752,8 +799,12 @@ class StoredValuesPopup(StoredValuesList, PathChooserPopup): self.signal_handlers['on_button_remove_clicked'] = self.on_button_remove_clicked self.signal_handlers['on_button_up_clicked'] = self.on_button_up_clicked self.signal_handlers['on_button_down_clicked'] = self.on_button_down_clicked - self.signal_handlers['on_button_default_clicked'] = self.on_button_default_clicked - self.signal_handlers['on_button_properties_clicked'] = self.path_entry._on_button_properties_clicked + self.signal_handlers[ + 'on_button_default_clicked' + ] = self.on_button_default_clicked + self.signal_handlers[ + 'on_button_properties_clicked' + ] = self.path_entry._on_button_properties_clicked def popup(self): """ @@ -772,11 +823,13 @@ class StoredValuesPopup(StoredValuesList, PathChooserPopup): self.popup_window.grab_add() # Set value selected if it exists - self.set_selected_value(path_without_trailing_path_sep(self.path_entry.get_text())) + self.set_selected_value( + path_without_trailing_path_sep(self.path_entry.get_text()) + ) -################# -# Callbacks -################# + ################# + # Callbacks + ################# def on_stored_values_popup_window_focus_out_event(self, entry, event): """ @@ -795,7 +848,9 @@ class StoredValuesPopup(StoredValuesList, PathChooserPopup): scroll_window = event.get_state() & gdk.SHIFT_MASK self.handle_list_scroll( _next=event.direction == gdk.SCROLL_DOWN, - set_entry=widget != self.treeview, swap=swap, scroll_window=scroll_window, + set_entry=widget != self.treeview, + swap=swap, + scroll_window=scroll_window, ) return True @@ -811,9 +866,9 @@ class StoredValuesPopup(StoredValuesList, PathChooserPopup): return True return False -# -------------------------------------------------- -# Funcs and callbacks on the buttons to manipulate the list -# -------------------------------------------------- + # -------------------------------------------------- + # Funcs and callbacks on the buttons to manipulate the list + # -------------------------------------------------- def add_current_value_to_saved_list(self): text = self.path_entry.get_text() text = path_without_trailing_path_sep(text) @@ -858,6 +913,7 @@ class PathCompletionPopup(CompletionList, PathChooserPopup): The auto completion popup """ + def __init__(self, builder, path_entry, max_visible_rows): self.builder = builder self.treeview = self.builder.get_object('completion_treeview') @@ -871,13 +927,17 @@ class PathCompletionPopup(CompletionList, PathChooserPopup): CompletionList.__init__(self) # Add signal handlers - self.signal_handlers['on_completion_treeview_scroll_event'] = self.on_scroll_event - self.signal_handlers['on_completion_popup_window_focus_out_event'] = \ - self.on_completion_popup_window_focus_out_event + self.signal_handlers[ + 'on_completion_treeview_scroll_event' + ] = self.on_scroll_event + self.signal_handlers[ + 'on_completion_popup_window_focus_out_event' + ] = self.on_completion_popup_window_focus_out_event # For when clicking outside the popup - self.signal_handlers['on_completion_popup_window_button_press_event'] = \ - self.on_popup_window_button_press_event + self.signal_handlers[ + 'on_completion_popup_window_button_press_event' + ] = self.on_popup_window_button_press_event def popup(self): """ @@ -897,11 +957,14 @@ class PathCompletionPopup(CompletionList, PathChooserPopup): self.popup_window.grab_add() self.text_entry.grab_focus() self.text_entry.set_position(len(self.path_entry.text_entry.get_text())) - self.set_selected_value(path_without_trailing_path_sep(self.path_entry.get_text()), select_first=True) + self.set_selected_value( + path_without_trailing_path_sep(self.path_entry.get_text()), + select_first=True, + ) -################# -# Callbacks -################# + ################# + # Callbacks + ################# def on_completion_popup_window_focus_out_event(self, entry, event): """ @@ -919,7 +982,8 @@ class PathCompletionPopup(CompletionList, PathChooserPopup): x, y, state = event.window.get_pointer() self.handle_list_scroll( _next=event.direction == gdk.SCROLL_DOWN, - set_entry=widget != self.treeview, scroll_window=True, + set_entry=widget != self.treeview, + scroll_window=True, ) path = self.treeview.get_path_at_pos(int(x), int(y)) if path: @@ -928,19 +992,25 @@ class PathCompletionPopup(CompletionList, PathChooserPopup): class PathAutoCompleter(object): - def __init__(self, builder, path_entry, max_visible_rows): - self.completion_popup = PathCompletionPopup(builder, path_entry, max_visible_rows) + self.completion_popup = PathCompletionPopup( + builder, path_entry, max_visible_rows + ) self.path_entry = path_entry self.dirs_cache = {} self.use_popup = False self.auto_complete_enabled = True self.signal_handlers = self.completion_popup.signal_handlers - self.signal_handlers['on_completion_popup_window_key_press_event'] = \ - self.on_completion_popup_window_key_press_event - self.signal_handlers['on_entry_text_delete_text'] = self.on_entry_text_delete_text - self.signal_handlers['on_entry_text_insert_text'] = self.on_entry_text_insert_text + self.signal_handlers[ + 'on_completion_popup_window_key_press_event' + ] = self.on_completion_popup_window_key_press_event + self.signal_handlers[ + 'on_entry_text_delete_text' + ] = self.on_entry_text_delete_text + self.signal_handlers[ + 'on_entry_text_insert_text' + ] = self.on_entry_text_insert_text self.accelerator_string = gtk.accelerator_name(keysyms.Tab, 0) def on_entry_text_insert_text(self, entry, new_text, new_text_length, position): @@ -950,7 +1020,9 @@ class PathAutoCompleter(object): new_complete_text = cur_text[:pos] + new_text + cur_text[pos:] # Remove all values from the list that do not start with new_complete_text self.completion_popup.reduce_values(new_complete_text) - self.completion_popup.set_selected_value(new_complete_text, select_first=True) + self.completion_popup.set_selected_value( + new_complete_text, select_first=True + ) if self.completion_popup.is_popped_up(): self.completion_popup.set_window_position_and_size() @@ -977,8 +1049,10 @@ class PathAutoCompleter(object): return ret keyval = event.keyval state = event.get_state() & gtk.accelerator_get_default_mod_mask() - if self.is_auto_completion_accelerator(keyval, state)\ - and self.auto_complete_enabled: + if ( + self.is_auto_completion_accelerator(keyval, state) + and self.auto_complete_enabled + ): values_count = self.completion_popup.get_values_count() if values_count == 1: self.do_completion() @@ -1011,7 +1085,9 @@ class PathAutoCompleter(object): if args['forward_completion']: common_prefix = os.path.commonprefix(paths) if len(common_prefix) > len(value): - self.path_entry.set_text(common_prefix, set_file_chooser_folder=True, trigger_event=True) + self.path_entry.set_text( + common_prefix, set_file_chooser_folder=True, trigger_event=True + ) self.path_entry.text_entry.set_position(len(self.path_entry.get_text())) self.completion_popup.set_values(paths, preserve_selection=True) @@ -1025,21 +1101,23 @@ class PathAutoCompleter(object): class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): __gsignals__ = { - b'list-value-added': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'list-value-removed': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'list-values-reordered': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'list-values-changed': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'auto-complete-enabled-toggled': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'show-filechooser-toggled': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'show-path-entry-toggled': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'show-folder-name-on-button': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'show-hidden-files-toggled': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'accelerator-set': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'max-rows-changed': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), - b'text-changed': (SIGNAL_RUN_FIRST, TYPE_NONE, (object, )), + b'list-value-added': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'list-value-removed': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'list-values-reordered': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'list-values-changed': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'auto-complete-enabled-toggled': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'show-filechooser-toggled': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'show-path-entry-toggled': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'show-folder-name-on-button': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'show-hidden-files-toggled': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'accelerator-set': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'max-rows-changed': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), + b'text-changed': (SIGNAL_RUN_FIRST, TYPE_NONE, (object,)), } - def __init__(self, max_visible_rows=20, auto_complete=True, use_completer_popup=True): + def __init__( + self, max_visible_rows=20, auto_complete=True, use_completer_popup=True + ): gtk.HBox.__init__(self) GObject.__init__(self) self._stored_values_popping_down = False @@ -1051,12 +1129,16 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): self.setting_accelerator_key = False self.builder = gtk.Builder() self.popup_buttonbox = self.builder.get_object('buttonbox') - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'path_combo_chooser.ui'), - )) + self.builder.add_from_file( + resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'path_combo_chooser.ui') + ) + ) self.button_toggle = self.builder.get_object('button_toggle_dropdown') self.text_entry = self.builder.get_object('entry_text') - self.open_filechooser_dialog_button = self.builder.get_object('button_open_dialog') + self.open_filechooser_dialog_button = self.builder.get_object( + 'button_open_dialog' + ) self.filechooser_button = self.open_filechooser_dialog_button self.filechooserdialog = self.builder.get_object('filechooserdialog') self.filechooserdialog.set_transient_for(component.get('MainWindow').window) @@ -1066,7 +1148,9 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): self.combo_hbox = self.builder.get_object('entry_combobox_hbox') # Change the parent of the hbox from the glade Window to this hbox. self.combo_hbox.reparent(self) - StoredValuesPopup.__init__(self, self.builder, self, max_visible_rows, self.combo_hbox) + StoredValuesPopup.__init__( + self, self.builder, self, max_visible_rows, self.combo_hbox + ) self.auto_completer = PathAutoCompleter(self.builder, self, max_visible_rows) self.auto_completer.set_use_popup(use_completer_popup) @@ -1094,16 +1178,27 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): """ return self.text_entry.get_text() - def set_text(self, text, set_file_chooser_folder=True, cursor_end=True, default_text=False, trigger_event=False): + def set_text( + self, + text, + set_file_chooser_folder=True, + cursor_end=True, + default_text=False, + trigger_event=False, + ): """ Set the text for the entry. """ old_text = self.text_entry.get_text() # We must block the "delete-text" signal to avoid the signal handler being called - self.text_entry.handler_block_by_func(self.auto_completer.on_entry_text_delete_text) + self.text_entry.handler_block_by_func( + self.auto_completer.on_entry_text_delete_text + ) self.text_entry.set_text(text) - self.text_entry.handler_unblock_by_func(self.auto_completer.on_entry_text_delete_text) + self.text_entry.handler_unblock_by_func( + self.auto_completer.on_entry_text_delete_text + ) self.text_entry.select_region(0, 0) self.text_entry.set_position(len(text) if cursor_end else 0) @@ -1111,7 +1206,9 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): self.combo_hbox.set_tooltip_text(text) if default_text: self.default_text = text - self.button_default.set_tooltip_text('Restore the default value in the text entry:\n%s' % self.default_text) + self.button_default.set_tooltip_text( + 'Restore the default value in the text entry:\n%s' % self.default_text + ) self.button_default.set_sensitive(True) # Set text for the filechooser dialog button folder_name = '' @@ -1261,9 +1358,9 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): """ self.auto_completer._end_completion(args) -############## -# Callbacks and internal functions -############## + ############## + # Callbacks and internal functions + ############## def on_entry_text_changed(self, entry): self.emit('text-changed', self.get_text()) @@ -1274,15 +1371,21 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): def _set_path_entry_filechooser_widths(self): if self.path_entry_visible: - self.combo_hbox.set_child_packing(self.filechooser_button, 0, 0, 0, gtk.PACK_START) + self.combo_hbox.set_child_packing( + self.filechooser_button, 0, 0, 0, gtk.PACK_START + ) width, height = self.folder_name_label.get_size_request() width = 120 if not self.show_folder_name_on_button: width = 0 self.folder_name_label.set_size_request(width, height) - self.combo_hbox.set_child_packing(self.filechooser_button, 0, 0, 0, gtk.PACK_START) + self.combo_hbox.set_child_packing( + self.filechooser_button, 0, 0, 0, gtk.PACK_START + ) else: - self.combo_hbox.set_child_packing(self.filechooser_button, 1, 1, 0, gtk.PACK_START) + self.combo_hbox.set_child_packing( + self.filechooser_button, 1, 1, 0, gtk.PACK_START + ) self.folder_name_label.set_size_request(-1, -1) # Update text on the button label self.set_text(self.get_text()) @@ -1319,10 +1422,7 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): # Select new row with arrow up/down is pressed if key_is_up_or_down(keyval): - self.handle_list_scroll( - _next=key_is_down(keyval), - set_entry=True, - ) + self.handle_list_scroll(_next=key_is_down(keyval), set_entry=True) return True elif self.auto_completer.is_auto_completion_accelerator(keyval, state): if self.auto_completer.auto_complete_enabled: @@ -1338,7 +1438,9 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): # Swap the show hidden files value on CTRL-h if is_ascii_value(keyval, 'h'): # Set show/hide hidden files - self.set_show_hidden_files(not self.get_show_hidden_files(), emit_event=True) + self.set_show_hidden_files( + not self.get_show_hidden_files(), emit_event=True + ) return True elif is_ascii_value(keyval, 's'): super(PathChooserComboBox, self).add_current_value_to_saved_list() @@ -1363,9 +1465,9 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): self.button_toggle.set_active(False) self._stored_values_popping_down = False -############## -# Config dialog -############## + ############## + # Config dialog + ############## def _on_button_toggle_dropdown_button_press_event(self, widget, event): """Show config when right clicking dropdown toggle button""" @@ -1383,10 +1485,14 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): keyval, mask = gtk.accelerator_parse(self.auto_completer.accelerator_string) self.accelerator_label.set_text(gtk.accelerator_get_label(keyval, mask)) self.visible_rows.set_value(self.get_max_popup_rows()) - self.show_filechooser_checkbutton.set_active(self.get_filechooser_button_visible()) + self.show_filechooser_checkbutton.set_active( + self.get_filechooser_button_visible() + ) self.show_path_entry_checkbutton.set_active(self.path_entry_visible) self.show_hidden_files_checkbutton.set_active(self.get_show_hidden_files()) - self.show_folder_name_on_button_checkbutton.set_active(self.get_show_folder_name_on_button()) + self.show_folder_name_on_button_checkbutton.set_active( + self.get_show_folder_name_on_button() + ) self._set_properties_widgets_sensitive(True) self.config_dialog.show_all() @@ -1398,18 +1504,30 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): def _setup_config_dialog(self): self.config_dialog = self.builder.get_object('completion_config_dialog') - self.enable_completion = self.builder.get_object('enable_auto_completion_checkbutton') - self.show_filechooser_checkbutton = self.builder.get_object('show_filechooser_checkbutton') - self.show_path_entry_checkbutton = self.builder.get_object('show_path_entry_checkbutton') + self.enable_completion = self.builder.get_object( + 'enable_auto_completion_checkbutton' + ) + self.show_filechooser_checkbutton = self.builder.get_object( + 'show_filechooser_checkbutton' + ) + self.show_path_entry_checkbutton = self.builder.get_object( + 'show_path_entry_checkbutton' + ) set_key_button = self.builder.get_object('set_completion_accelerator_button') default_set_accelerator_tooltip = set_key_button.get_tooltip_text() - self.config_short_cuts_frame = self.builder.get_object('config_short_cuts_frame') + self.config_short_cuts_frame = self.builder.get_object( + 'config_short_cuts_frame' + ) self.config_general_frame = self.builder.get_object('config_general_frame') self.accelerator_label = self.builder.get_object('completion_accelerator_label') self.visible_rows = self.builder.get_object('visible_rows_spinbutton') self.visible_rows_label = self.builder.get_object('visible_rows_label') - self.show_hidden_files_checkbutton = self.builder.get_object('show_hidden_files_checkbutton') - self.show_folder_name_on_button_checkbutton = self.builder.get_object('show_folder_name_on_button_checkbutton') + self.show_hidden_files_checkbutton = self.builder.get_object( + 'show_hidden_files_checkbutton' + ) + self.show_folder_name_on_button_checkbutton = self.builder.get_object( + 'show_folder_name_on_button_checkbutton' + ) self.config_dialog.set_transient_for(component.get('MainWindow').window) def on_close(widget, event=None): @@ -1421,33 +1539,53 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): def on_enable_completion_toggled(widget): self.set_auto_complete_enabled(self.enable_completion.get_active()) - self.emit('auto-complete-enabled-toggled', self.enable_completion.get_active()) + self.emit( + 'auto-complete-enabled-toggled', self.enable_completion.get_active() + ) def on_show_filechooser_toggled(widget): - self.set_filechooser_button_visible(self.show_filechooser_checkbutton.get_active()) - self.emit('show-filechooser-toggled', self.show_filechooser_checkbutton.get_active()) - self.show_folder_name_on_button_checkbutton.set_sensitive(self.show_path_entry_checkbutton.get_active() and - self.show_filechooser_checkbutton.get_active()) + self.set_filechooser_button_visible( + self.show_filechooser_checkbutton.get_active() + ) + self.emit( + 'show-filechooser-toggled', + self.show_filechooser_checkbutton.get_active(), + ) + self.show_folder_name_on_button_checkbutton.set_sensitive( + self.show_path_entry_checkbutton.get_active() + and self.show_filechooser_checkbutton.get_active() + ) if not self.filechooser_visible and not self.path_entry_visible: self.show_path_entry_checkbutton.set_active(True) on_show_path_entry_toggled(None) def on_show_path_entry_toggled(widget): self.set_path_entry_visible(self.show_path_entry_checkbutton.get_active()) - self.emit('show-path-entry-toggled', self.show_path_entry_checkbutton.get_active()) - self.show_folder_name_on_button_checkbutton.set_sensitive(self.show_path_entry_checkbutton.get_active() and - self.show_filechooser_checkbutton.get_active()) + self.emit( + 'show-path-entry-toggled', self.show_path_entry_checkbutton.get_active() + ) + self.show_folder_name_on_button_checkbutton.set_sensitive( + self.show_path_entry_checkbutton.get_active() + and self.show_filechooser_checkbutton.get_active() + ) if not self.filechooser_visible and not self.path_entry_visible: self.show_filechooser_checkbutton.set_active(True) on_show_filechooser_toggled(None) def on_show_folder_name_on_button(widget): - self.set_show_folder_name_on_button(self.show_folder_name_on_button_checkbutton.get_active()) + self.set_show_folder_name_on_button( + self.show_folder_name_on_button_checkbutton.get_active() + ) self._set_path_entry_filechooser_widths() - self.emit('show-folder-name-on-button', self.show_folder_name_on_button_checkbutton.get_active()) + self.emit( + 'show-folder-name-on-button', + self.show_folder_name_on_button_checkbutton.get_active(), + ) def on_show_hidden_files_toggled(widget): - self.set_show_hidden_files(self.show_hidden_files_checkbutton.get_active(), emit_event=True) + self.set_show_hidden_files( + self.show_hidden_files_checkbutton.get_active(), emit_event=True + ) def on_max_rows_changed(widget): self.set_max_popup_rows(self.visible_rows.get_value_as_int()) @@ -1455,7 +1593,9 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): def set_accelerator(widget): self.setting_accelerator_key = True - set_key_button.set_tooltip_text('Press the accelerator keys for triggering auto-completion') + set_key_button.set_tooltip_text( + 'Press the accelerator keys for triggering auto-completion' + ) self._set_properties_widgets_sensitive(False) return True @@ -1474,8 +1614,12 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): # If e.g. only CTRL key is pressed. if not gtk.accelerator_valid(event.keyval, accelerator_mask): accelerator_mask = 0 - self.auto_completer.accelerator_string = gtk.accelerator_name(event.keyval, accelerator_mask) - self.accelerator_label.set_text(gtk.accelerator_get_label(event.keyval, accelerator_mask)) + self.auto_completer.accelerator_string = gtk.accelerator_name( + event.keyval, accelerator_mask + ) + self.accelerator_label.set_text( + gtk.accelerator_get_label(event.keyval, accelerator_mask) + ) self.emit('accelerator-set', self.auto_completer.accelerator_string) stop_setting_accelerator() return True @@ -1485,7 +1629,9 @@ class PathChooserComboBox(gtk.HBox, StoredValuesPopup, GObject): if ctrl: # Set show/hide hidden files if is_ascii_value(keyval, 'h'): - self.show_hidden_files_checkbutton.set_active(not self.get_show_hidden_files()) + self.show_hidden_files_checkbutton.set_active( + not self.get_show_hidden_files() + ) return True def on_set_completion_accelerator_button_clicked(widget): @@ -1513,6 +1659,7 @@ type_register(PathChooserComboBox) if __name__ == '__main__': import sys + w = gtk.Window() w.set_position(gtk.WIN_POS_CENTER) w.set_size_request(600, -1) diff --git a/deluge/ui/gtkui/peers_tab.py b/deluge/ui/gtkui/peers_tab.py index ca2fd6366..5966564a6 100644 --- a/deluge/ui/gtkui/peers_tab.py +++ b/deluge/ui/gtkui/peers_tab.py @@ -12,17 +12,33 @@ from __future__ import unicode_literals import logging import os.path -from gtk import (TREE_VIEW_COLUMN_FIXED, Builder, CellRendererPixbuf, CellRendererProgress, CellRendererText, ListStore, - TreeViewColumn) +from gtk import ( + TREE_VIEW_COLUMN_FIXED, + Builder, + CellRendererPixbuf, + CellRendererProgress, + CellRendererText, + ListStore, + TreeViewColumn, +) from gtk.gdk import Pixbuf, pixbuf_new_from_file import deluge.common import deluge.component as component from deluge.ui.client import client from deluge.ui.countries import COUNTRIES -from deluge.ui.gtkui.common import icon_downloading, icon_seeding, load_pickled_state_file, save_pickled_state_file +from deluge.ui.gtkui.common import ( + icon_downloading, + icon_seeding, + load_pickled_state_file, + save_pickled_state_file, +) from deluge.ui.gtkui.torrentdetails import Tab -from deluge.ui.gtkui.torrentview_data_funcs import cell_data_peer_progress, cell_data_speed_down, cell_data_speed_up +from deluge.ui.gtkui.torrentview_data_funcs import ( + cell_data_peer_progress, + cell_data_speed_down, + cell_data_speed_up, +) try: from future_builtins import zip @@ -46,7 +62,9 @@ class PeersTab(Tab): self.listview.connect('query-tooltip', self._on_query_tooltip) # flag, ip, client, downspd, upspd, country code, int_ip, seed/peer icon, progress - self.liststore = ListStore(Pixbuf, str, str, int, int, str, float, Pixbuf, float) + self.liststore = ListStore( + Pixbuf, str, str, int, int, str, float, Pixbuf, float + ) self.cached_flag_pixbufs = {} self.seed_pixbuf = icon_seeding @@ -188,8 +206,13 @@ class PeersTab(Tab): # Column is in wrong position if cstate['position'] == 0: self.listview.move_column_after(column, None) - elif self.listview.get_columns()[cstate['position'] - 1].get_title() != cname: - self.listview.move_column_after(column, self.listview.get_columns()[cstate['position'] - 1]) + elif ( + self.listview.get_columns()[cstate['position'] - 1].get_title() + != cname + ): + self.listview.move_column_after( + column, self.listview.get_columns()[cstate['position'] - 1] + ) def update(self): # Get the first selected torrent @@ -209,7 +232,9 @@ class PeersTab(Tab): self.peers = {} self.torrent_id = torrent_id - component.get('SessionProxy').get_torrent_status(torrent_id, ['peers']).addCallback(self._on_get_torrent_status) + component.get('SessionProxy').get_torrent_status( + torrent_id, ['peers'] + ).addCallback(self._on_get_torrent_status) def get_flag_pixbuf(self, country): if not country.strip(): @@ -221,8 +246,10 @@ class PeersTab(Tab): self.cached_flag_pixbufs[country] = pixbuf_new_from_file( deluge.common.resource_filename( 'deluge', - os.path.join('ui', 'data', 'pixmaps', 'flags', country.lower() + '.png'), - ), + os.path.join( + 'ui', 'data', 'pixmaps', 'flags', country.lower() + '.png' + ), + ) ) except Exception as ex: log.debug('Unable to load flag: %s', ex) @@ -248,7 +275,9 @@ class PeersTab(Tab): self.liststore.set_value(row, 4, peer['up_speed']) if peer['country'] != values[2]: self.liststore.set_value(row, 5, peer['country']) - self.liststore.set_value(row, 0, self.get_flag_pixbuf(peer['country'])) + self.liststore.set_value( + row, 0, self.get_flag_pixbuf(peer['country']) + ) if peer['seed']: icon = self.seed_pixbuf else: @@ -267,16 +296,21 @@ class PeersTab(Tab): # This is an IPv4 address ip_int = sum( int(byte) << shift - for byte, shift in zip(peer['ip'].split(':')[0].split('.'), (24, 16, 8, 0)) + for byte, shift in zip( + peer['ip'].split(':')[0].split('.'), (24, 16, 8, 0) + ) ) peer_ip = peer['ip'] else: # This is an IPv6 address import socket import binascii + # Split out the :port ip = ':'.join(peer['ip'].split(':')[:-1]) - ip_int = int(binascii.hexlify(socket.inet_pton(socket.AF_INET6, ip)), 16) + ip_int = int( + binascii.hexlify(socket.inet_pton(socket.AF_INET6, ip)), 16 + ) peer_ip = '[%s]:%s' % (ip, peer['ip'].split(':')[-1]) if peer['seed']: @@ -284,17 +318,19 @@ class PeersTab(Tab): else: icon = self.peer_pixbuf - row = self.liststore.append([ - self.get_flag_pixbuf(peer['country']), - peer_ip, - peer['client'], - peer['down_speed'], - peer['up_speed'], - peer['country'], - float(ip_int), - icon, - peer['progress'], - ]) + row = self.liststore.append( + [ + self.get_flag_pixbuf(peer['country']), + peer_ip, + peer['client'], + peer['down_speed'], + peer['up_speed'], + peer['country'], + float(ip_int), + icon, + peer['progress'], + ] + ) self.peers[peer['ip']] = row @@ -324,10 +360,7 @@ class PeersTab(Tab): if country_code != ' ' and country_code in COUNTRIES: tooltip.set_text(COUNTRIES[country_code]) # widget here is self.listview - widget.set_tooltip_cell( - tooltip, path, widget.get_column(0), - None, - ) + widget.set_tooltip_cell(tooltip, path, widget.get_column(0), None) return True else: return False @@ -336,9 +369,11 @@ class PeersTab(Tab): """This is a callback for manually adding a peer""" log.debug('on_menuitem_add_peer') builder = Builder() - builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'connect_peer_dialog.ui'), - )) + builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'connect_peer_dialog.ui') + ) + ) peer_dialog = builder.get_object('connect_peer_dialog') txt_ip = builder.get_object('txt_ip') response = peer_dialog.run() diff --git a/deluge/ui/gtkui/piecesbar.py b/deluge/ui/gtkui/piecesbar.py index 2819540a4..8893c6946 100644 --- a/deluge/ui/gtkui/piecesbar.py +++ b/deluge/ui/gtkui/piecesbar.py @@ -82,7 +82,9 @@ class PiecesBar(DrawingArea): self.cr.clip() def roundcorners_border(self): - self.create_roundcorners_subpath(self.cr, 0.5, 0.5, self.width - 1, self.height - 1) + self.create_roundcorners_subpath( + self.cr, 0.5, 0.5, self.width - 1, self.height - 1 + ) self.cr.set_source_rgba(0, 0, 0, 0.9) self.cr.stroke() @@ -94,7 +96,9 @@ class PiecesBar(DrawingArea): degrees = pi / 180 ctx.new_sub_path() ctx.arc(x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees) - ctx.arc(x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees) + ctx.arc( + x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees + ) ctx.arc(x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees) ctx.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees) ctx.close_path() @@ -105,7 +109,11 @@ class PiecesBar(DrawingArea): # Nothing to draw. return - if self.resized() or self.pieces != self.prev_pieces or self.pieces_overlay is None: + if ( + self.resized() + or self.pieces != self.prev_pieces + or self.pieces_overlay is None + ): # Need to recreate the cache drawing self.pieces_overlay = ImageSurface(FORMAT_ARGB32, self.width, self.height) ctx = Context(self.pieces_overlay) @@ -118,7 +126,10 @@ class PiecesBar(DrawingArea): start_pos = 0 piece_width = self.width / len(pieces) pieces_colors = [ - [color / 65535 for color in self.gtkui_config['pieces_color_%s' % state]] + [ + color / 65535 + for color in self.gtkui_config['pieces_color_%s' % state] + ] for state in COLOR_STATES ] for state in pieces: @@ -135,7 +146,11 @@ class PiecesBar(DrawingArea): # Nothing useful to draw, return now! return - if self.resized() or self.fraction != self.prev_fraction or self.progress_overlay is None: + if ( + self.resized() + or self.fraction != self.prev_fraction + or self.progress_overlay is None + ): # Need to recreate the cache drawing self.progress_overlay = ImageSurface(FORMAT_ARGB32, self.width, self.height) ctx = Context(self.progress_overlay) diff --git a/deluge/ui/gtkui/pluginmanager.py b/deluge/ui/gtkui/pluginmanager.py index f1424a693..986a60aa5 100644 --- a/deluge/ui/gtkui/pluginmanager.py +++ b/deluge/ui/gtkui/pluginmanager.py @@ -24,16 +24,17 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, component.Compon component.Component.__init__(self, 'PluginManager') self.config = ConfigManager('gtkui.conf') deluge.pluginmanagerbase.PluginManagerBase.__init__( - self, 'gtkui.conf', 'deluge.plugin.gtkui', + self, 'gtkui.conf', 'deluge.plugin.gtkui' ) - self.hooks = { - 'on_apply_prefs': [], - 'on_show_prefs': [], - } + self.hooks = {'on_apply_prefs': [], 'on_show_prefs': []} - client.register_event_handler('PluginEnabledEvent', self._on_plugin_enabled_event) - client.register_event_handler('PluginDisabledEvent', self._on_plugin_disabled_event) + client.register_event_handler( + 'PluginEnabledEvent', self._on_plugin_enabled_event + ) + client.register_event_handler( + 'PluginDisabledEvent', self._on_plugin_disabled_event + ) def register_hook(self, hook, function): """Register a hook function with the plugin manager""" diff --git a/deluge/ui/gtkui/preferences.py b/deluge/ui/gtkui/preferences.py index f1832f0e6..be011efac 100644 --- a/deluge/ui/gtkui/preferences.py +++ b/deluge/ui/gtkui/preferences.py @@ -23,8 +23,17 @@ from deluge.configmanager import ConfigManager, get_config_dir from deluge.error import AuthManagerError, NotAuthorizedError from deluge.ui.client import client from deluge.ui.common import DISK_CACHE_KEYS, PREFS_CATOG_TRANS -from deluge.ui.gtkui.common import associate_magnet_links, get_clipboard_text, get_deluge_icon -from deluge.ui.gtkui.dialogs import AccountDialog, ErrorDialog, InformationDialog, YesNoDialog +from deluge.ui.gtkui.common import ( + associate_magnet_links, + get_clipboard_text, + get_deluge_icon, +) +from deluge.ui.gtkui.dialogs import ( + AccountDialog, + ErrorDialog, + InformationDialog, + YesNoDialog, +) from deluge.ui.gtkui.path_chooser import PathChooser from deluge.ui.translations_util import get_languages @@ -56,9 +65,11 @@ class Preferences(component.Component): def __init__(self): component.Component.__init__(self, 'Preferences') self.builder = gtk.Builder() - self.builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'preferences_dialog.ui'), - )) + self.builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'preferences_dialog.ui') + ) + ) self.pref_dialog = self.builder.get_object('pref_dialog') self.pref_dialog.set_transient_for(component.get('MainWindow').window) self.pref_dialog.set_icon(get_deluge_icon()) @@ -70,7 +81,7 @@ class Preferences(component.Component): self.load_pref_dialog_state() self.builder.get_object('image_magnet').set_from_file( - deluge.common.get_pixmap('magnet.png'), + deluge.common.get_pixmap('magnet.png') ) # Hide the unused associate magnet button on OSX see: #2420 @@ -86,8 +97,16 @@ class Preferences(component.Component): # Add the default categories prefs_categories = ( - 'interface', 'downloads', 'bandwidth', 'queue', 'network', 'proxy', - 'cache', 'other', 'daemon', 'plugins', + 'interface', + 'downloads', + 'bandwidth', + 'queue', + 'network', + 'proxy', + 'cache', + 'other', + 'daemon', + 'plugins', ) for idx, category in enumerate(prefs_categories): self.liststore.append([idx, category, PREFS_CATOG_TRANS[category]]) @@ -96,6 +115,7 @@ class Preferences(component.Component): def set_separator(model, _iter, data=None): if model.get_value(_iter, 1) == '_separator_': return True + self.treeview.set_row_separator_func(set_separator, None) self.liststore.append([len(self.liststore), '_separator_', '']) # Add a dummy notebook page to keep indexing synced with liststore. @@ -105,20 +125,28 @@ class Preferences(component.Component): 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.SORT_ASCENDING) + self.accounts_liststore.set_sort_column_id( + ACCOUNTS_USERNAME, gtk.SORT_ASCENDING + ) self.accounts_listview = self.builder.get_object('accounts_listview') self.accounts_listview.append_column( - gtk.TreeViewColumn(_('Username'), gtk.CellRendererText(), text=ACCOUNTS_USERNAME), + gtk.TreeViewColumn( + _('Username'), gtk.CellRendererText(), text=ACCOUNTS_USERNAME + ) ) self.accounts_listview.append_column( - gtk.TreeViewColumn(_('Level'), gtk.CellRendererText(), text=ACCOUNTS_LEVEL), + gtk.TreeViewColumn(_('Level'), gtk.CellRendererText(), text=ACCOUNTS_LEVEL) + ) + password_column = gtk.TreeViewColumn( + 'password', gtk.CellRendererText(), text=ACCOUNTS_PASSWORD ) - password_column = gtk.TreeViewColumn('password', gtk.CellRendererText(), text=ACCOUNTS_PASSWORD) self.accounts_listview.append_column(password_column) password_column.set_visible(False) self.accounts_listview.set_model(self.accounts_liststore) - self.accounts_listview.get_selection().connect('changed', self.on_accounts_selection_changed) + self.accounts_listview.get_selection().connect( + 'changed', self.on_accounts_selection_changed + ) self.accounts_frame = self.builder.get_object('AccountsFrame') # Setup plugin tab listview @@ -130,14 +158,20 @@ class Preferences(component.Component): render = gtk.CellRendererToggle() render.connect('toggled', self.on_plugin_toggled) render.set_property('activatable', True) - self.plugin_listview.append_column(gtk.TreeViewColumn(_('Enabled'), render, active=1)) - self.plugin_listview.append_column(gtk.TreeViewColumn(_('Plugin'), gtk.CellRendererText(), text=2)) + self.plugin_listview.append_column( + gtk.TreeViewColumn(_('Enabled'), render, active=1) + ) + self.plugin_listview.append_column( + gtk.TreeViewColumn(_('Plugin'), gtk.CellRendererText(), text=2) + ) # Connect to the 'changed' event of TreeViewSelection to get selection # changes. self.treeview.get_selection().connect('changed', self.on_selection_changed) - self.plugin_listview.get_selection().connect('changed', self.on_plugin_selection_changed) + self.plugin_listview.get_selection().connect( + 'changed', self.on_plugin_selection_changed + ) self.builder.connect_signals(self) @@ -145,6 +179,7 @@ class Preferences(component.Component): self.builder.get_object('alignment_tray_type').set_visible(bool(appindicator)) from deluge.ui.gtkui.gtkui import DEFAULT_PREFS + self.COLOR_DEFAULTS = {} for key in ('missing', 'waiting', 'downloading', 'completed'): self.COLOR_DEFAULTS[key] = DEFAULT_PREFS['pieces_color_%s' % key][:] @@ -158,18 +193,28 @@ class Preferences(component.Component): self.load_languages() def setup_path_choosers(self): - self.download_location_hbox = self.builder.get_object('hbox_download_to_path_chooser') - self.download_location_path_chooser = PathChooser('download_location_paths_list') + self.download_location_hbox = self.builder.get_object( + 'hbox_download_to_path_chooser' + ) + self.download_location_path_chooser = PathChooser( + 'download_location_paths_list' + ) self.download_location_hbox.add(self.download_location_path_chooser) self.download_location_hbox.show_all() - self.move_completed_hbox = self.builder.get_object('hbox_move_completed_to_path_chooser') + self.move_completed_hbox = self.builder.get_object( + 'hbox_move_completed_to_path_chooser' + ) self.move_completed_path_chooser = PathChooser('move_completed_paths_list') self.move_completed_hbox.add(self.move_completed_path_chooser) self.move_completed_hbox.show_all() - self.copy_torrents_to_hbox = self.builder.get_object('hbox_copy_torrent_files_path_chooser') - self.copy_torrent_files_path_chooser = PathChooser('copy_torrent_files_to_paths_list') + self.copy_torrents_to_hbox = self.builder.get_object( + 'hbox_copy_torrent_files_path_chooser' + ) + self.copy_torrent_files_path_chooser = PathChooser( + 'copy_torrent_files_to_paths_list' + ) self.copy_torrents_to_hbox.add(self.copy_torrent_files_path_chooser) self.copy_torrents_to_hbox.show_all() @@ -273,7 +318,9 @@ class Preferences(component.Component): def on_get_config(config): self.core_config = config - client.core.get_available_plugins().addCallback(on_get_available_plugins) + client.core.get_available_plugins().addCallback( + on_get_available_plugins + ) def on_get_available_plugins(plugins): self.all_plugins = plugins @@ -285,7 +332,9 @@ class Preferences(component.Component): def on_get_listen_port(port): self.active_port = port - client.core.get_session_status(DISK_CACHE_KEYS).addCallback(on_get_session_status) + client.core.get_session_status(DISK_CACHE_KEYS).addCallback( + on_get_session_status + ) def on_get_session_status(status): self.cache_status = status @@ -312,14 +361,26 @@ class Preferences(component.Component): 'chk_copy_torrent_file': ('active', 'copy_torrent_file'), 'chk_del_copy_torrent_file': ('active', 'del_copy_torrent_file'), 'chk_pre_allocation': ('active', 'pre_allocate_storage'), - 'chk_prioritize_first_last_pieces': ('active', 'prioritize_first_last_pieces'), + 'chk_prioritize_first_last_pieces': ( + 'active', + 'prioritize_first_last_pieces', + ), 'chk_sequential_download': ('active', 'sequential_download'), 'chk_add_paused': ('active', 'add_paused'), 'active_port_label': ('text', lambda: str(self.active_port)), - 'spin_incoming_port': ('value', lambda: self.core_config['listen_ports'][0]), + 'spin_incoming_port': ( + 'value', + lambda: self.core_config['listen_ports'][0], + ), 'chk_random_incoming_port': ('active', 'random_port'), - 'spin_outgoing_port_min': ('value', lambda: self.core_config['outgoing_ports'][0]), - 'spin_outgoing_port_max': ('value', lambda: self.core_config['outgoing_ports'][1]), + 'spin_outgoing_port_min': ( + 'value', + lambda: self.core_config['outgoing_ports'][0], + ), + 'spin_outgoing_port_max': ( + 'value', + lambda: self.core_config['outgoing_ports'][1], + ), 'chk_random_outgoing_ports': ('active', 'random_outgoing_ports'), 'entry_interface': ('text', 'listen_interface'), 'entry_outgoing_interface': ('text', 'outgoing_interface'), @@ -341,12 +402,23 @@ class Preferences(component.Component): 'spin_max_upload_slots_global': ('value', 'max_upload_slots_global'), 'spin_max_half_open_connections': ('value', 'max_connections_per_second'), 'spin_max_connections_per_second': ('value', 'max_connections_per_second'), - 'chk_ignore_limits_on_local_network': ('active', 'ignore_limits_on_local_network'), + 'chk_ignore_limits_on_local_network': ( + 'active', + 'ignore_limits_on_local_network', + ), 'chk_rate_limit_ip_overhead': ('active', 'rate_limit_ip_overhead'), - - 'spin_max_connections_per_torrent': ('value', 'max_connections_per_torrent'), - 'spin_max_upload_slots_per_torrent': ('value', 'max_upload_slots_per_torrent'), - 'spin_max_download_per_torrent': ('value', 'max_download_speed_per_torrent'), + 'spin_max_connections_per_torrent': ( + 'value', + 'max_connections_per_torrent', + ), + 'spin_max_upload_slots_per_torrent': ( + 'value', + 'max_upload_slots_per_torrent', + ), + 'spin_max_download_per_torrent': ( + 'value', + 'max_download_speed_per_torrent', + ), 'spin_max_upload_per_torrent': ('value', 'max_upload_speed_per_torrent'), 'spin_daemon_port': ('value', 'daemon_port'), 'chk_allow_remote_connections': ('active', 'allow_remote'), @@ -370,11 +442,26 @@ class Preferences(component.Component): 'entry_proxy_pass': ('text', lambda: self.core_config['proxy']['password']), 'entry_proxy_host': ('text', lambda: self.core_config['proxy']['hostname']), 'spin_proxy_port': ('value', lambda: self.core_config['proxy']['port']), - 'chk_proxy_host_resolve': ('active', lambda: self.core_config['proxy']['proxy_hostnames']), - 'chk_proxy_peer_conn': ('active', lambda: self.core_config['proxy']['proxy_peer_connections']), - 'chk_proxy_tracker_conn': ('active', lambda: self.core_config['proxy']['proxy_tracker_connections']), - 'chk_force_proxy': ('active', lambda: self.core_config['proxy']['force_proxy']), - 'chk_anonymous_mode': ('active', lambda: self.core_config['proxy']['anonymous_mode']), + 'chk_proxy_host_resolve': ( + 'active', + lambda: self.core_config['proxy']['proxy_hostnames'], + ), + 'chk_proxy_peer_conn': ( + 'active', + lambda: self.core_config['proxy']['proxy_peer_connections'], + ), + 'chk_proxy_tracker_conn': ( + 'active', + lambda: self.core_config['proxy']['proxy_tracker_connections'], + ), + 'chk_force_proxy': ( + 'active', + lambda: self.core_config['proxy']['force_proxy'], + ), + 'chk_anonymous_mode': ( + 'active', + lambda: self.core_config['proxy']['anonymous_mode'], + ), 'accounts_add': (None, None), 'accounts_listview': (None, None), 'button_cache_refresh': (None, None), @@ -385,9 +472,18 @@ class Preferences(component.Component): 'plugin_listview': (None, None), } - core_widgets[self.download_location_path_chooser] = ('path_chooser', 'download_location') - core_widgets[self.move_completed_path_chooser] = ('path_chooser', 'move_completed_path') - core_widgets[self.copy_torrent_files_path_chooser] = ('path_chooser', 'torrentfiles_location') + core_widgets[self.download_location_path_chooser] = ( + 'path_chooser', + 'download_location', + ) + core_widgets[self.move_completed_path_chooser] = ( + 'path_chooser', + 'move_completed_path', + ) + core_widgets[self.copy_torrent_files_path_chooser] = ( + 'path_chooser', + 'torrentfiles_location', + ) # Update the widgets accordingly for key in core_widgets: @@ -407,7 +503,13 @@ class Preferences(component.Component): if callable(value): value = value() elif modifier: - value = {'active': False, 'not_active': False, 'value': 0, 'text': '', 'path_chooser': ''}[modifier] + value = { + 'active': False, + 'not_active': False, + 'value': 0, + 'text': '', + 'path_chooser': '', + }[modifier] if modifier == 'active': widget.set_active(value) @@ -432,29 +534,53 @@ class Preferences(component.Component): self.on_toggle(widget) # Downloads tab # - self.builder.get_object('chk_show_dialog').set_active(self.gtkui_config['interactive_add']) - self.builder.get_object('chk_focus_dialog').set_active(self.gtkui_config['focus_add_dialog']) + self.builder.get_object('chk_show_dialog').set_active( + self.gtkui_config['interactive_add'] + ) + self.builder.get_object('chk_focus_dialog').set_active( + self.gtkui_config['focus_add_dialog'] + ) # Interface tab # - self.builder.get_object('chk_use_tray').set_active(self.gtkui_config['enable_system_tray']) - self.builder.get_object('chk_min_on_close').set_active(self.gtkui_config['close_to_tray']) - self.builder.get_object('chk_start_in_tray').set_active(self.gtkui_config['start_in_tray']) - self.builder.get_object('radio_appind').set_active(self.gtkui_config['enable_appindicator']) - self.builder.get_object('chk_lock_tray').set_active(self.gtkui_config['lock_tray']) - self.builder.get_object('radio_standalone').set_active(self.gtkui_config['standalone']) - self.builder.get_object('radio_thinclient').set_active(not self.gtkui_config['standalone']) - self.builder.get_object('chk_show_rate_in_title').set_active(self.gtkui_config['show_rate_in_title']) + self.builder.get_object('chk_use_tray').set_active( + self.gtkui_config['enable_system_tray'] + ) + self.builder.get_object('chk_min_on_close').set_active( + self.gtkui_config['close_to_tray'] + ) + self.builder.get_object('chk_start_in_tray').set_active( + self.gtkui_config['start_in_tray'] + ) + self.builder.get_object('radio_appind').set_active( + self.gtkui_config['enable_appindicator'] + ) + self.builder.get_object('chk_lock_tray').set_active( + self.gtkui_config['lock_tray'] + ) + self.builder.get_object('radio_standalone').set_active( + self.gtkui_config['standalone'] + ) + self.builder.get_object('radio_thinclient').set_active( + not self.gtkui_config['standalone'] + ) + self.builder.get_object('chk_show_rate_in_title').set_active( + self.gtkui_config['show_rate_in_title'] + ) self.builder.get_object('chk_focus_main_window_on_add').set_active( - self.gtkui_config['focus_main_window_on_add'], + self.gtkui_config['focus_main_window_on_add'] + ) + self.builder.get_object('piecesbar_toggle').set_active( + self.gtkui_config['show_piecesbar'] ) - self.builder.get_object('piecesbar_toggle').set_active(self.gtkui_config['show_piecesbar']) self.__set_color('completed', from_config=True) self.__set_color('downloading', from_config=True) self.__set_color('waiting', from_config=True) self.__set_color('missing', from_config=True) # Other tab # - self.builder.get_object('chk_show_new_releases').set_active(self.gtkui_config['show_new_releases']) + self.builder.get_object('chk_show_new_releases').set_active( + self.gtkui_config['show_new_releases'] + ) # Cache tab # if client.connected(): @@ -488,107 +614,152 @@ class Preferences(component.Component): new_gtkui_config = {} # Downloads tab # - new_gtkui_config['interactive_add'] = self.builder.get_object('chk_show_dialog').get_active() - new_gtkui_config['focus_add_dialog'] = self.builder.get_object('chk_focus_dialog').get_active() + new_gtkui_config['interactive_add'] = self.builder.get_object( + 'chk_show_dialog' + ).get_active() + new_gtkui_config['focus_add_dialog'] = self.builder.get_object( + 'chk_focus_dialog' + ).get_active() for state in ('missing', 'waiting', 'downloading', 'completed'): color = self.builder.get_object('%s_color' % state).get_color() new_gtkui_config['pieces_color_%s' % state] = [ - color.red, color.green, color.blue, + color.red, + color.green, + color.blue, ] new_core_config['copy_torrent_file'] = self.builder.get_object( - 'chk_copy_torrent_file', + 'chk_copy_torrent_file' ).get_active() new_core_config['del_copy_torrent_file'] = self.builder.get_object( - 'chk_del_copy_torrent_file', + 'chk_del_copy_torrent_file' + ).get_active() + new_core_config['move_completed'] = self.builder.get_object( + 'chk_move_completed' ).get_active() - new_core_config['move_completed'] = self.builder.get_object('chk_move_completed').get_active() - new_core_config['download_location'] = self.download_location_path_chooser.get_text() - new_core_config['move_completed_path'] = self.move_completed_path_chooser.get_text() - new_core_config['torrentfiles_location'] = self.copy_torrent_files_path_chooser.get_text() + new_core_config[ + 'download_location' + ] = self.download_location_path_chooser.get_text() + new_core_config[ + 'move_completed_path' + ] = self.move_completed_path_chooser.get_text() + new_core_config[ + 'torrentfiles_location' + ] = self.copy_torrent_files_path_chooser.get_text() new_core_config['prioritize_first_last_pieces'] = self.builder.get_object( - 'chk_prioritize_first_last_pieces', + 'chk_prioritize_first_last_pieces' ).get_active() new_core_config['sequential_download'] = self.builder.get_object( - 'chk_sequential_download', + 'chk_sequential_download' + ).get_active() + new_core_config['add_paused'] = self.builder.get_object( + 'chk_add_paused' ).get_active() - new_core_config['add_paused'] = self.builder.get_object('chk_add_paused').get_active() new_core_config['pre_allocate_storage'] = self.builder.get_object( - 'chk_pre_allocation', + 'chk_pre_allocation' ).get_active() # Network tab # - listen_ports = [self.builder.get_object('spin_incoming_port').get_value_as_int()] * 2 + listen_ports = [ + self.builder.get_object('spin_incoming_port').get_value_as_int() + ] * 2 new_core_config['listen_ports'] = listen_ports - new_core_config['random_port'] = self.builder.get_object('chk_random_incoming_port').get_active() + new_core_config['random_port'] = self.builder.get_object( + 'chk_random_incoming_port' + ).get_active() outgoing_ports = ( self.builder.get_object('spin_outgoing_port_min').get_value_as_int(), self.builder.get_object('spin_outgoing_port_max').get_value_as_int(), ) new_core_config['outgoing_ports'] = outgoing_ports new_core_config['random_outgoing_ports'] = self.builder.get_object( - 'chk_random_outgoing_ports', + 'chk_random_outgoing_ports' ).get_active() incoming_address = self.builder.get_object('entry_interface').get_text().strip() if deluge.common.is_ip(incoming_address) or not incoming_address: new_core_config['listen_interface'] = incoming_address - outgoing_interface = self.builder.get_object( - 'entry_outgoing_interface').get_text().strip() + outgoing_interface = ( + self.builder.get_object('entry_outgoing_interface').get_text().strip() + ) if not deluge.common.is_ip(outgoing_interface) or not outgoing_interface: new_core_config['outgoing_interface'] = outgoing_interface - new_core_config['peer_tos'] = self.builder.get_object('entry_peer_tos').get_text() + new_core_config['peer_tos'] = self.builder.get_object( + 'entry_peer_tos' + ).get_text() new_core_config['dht'] = self.builder.get_object('chk_dht').get_active() new_core_config['upnp'] = self.builder.get_object('chk_upnp').get_active() new_core_config['natpmp'] = self.builder.get_object('chk_natpmp').get_active() new_core_config['utpex'] = self.builder.get_object('chk_utpex').get_active() new_core_config['lsd'] = self.builder.get_object('chk_lsd').get_active() - new_core_config['enc_in_policy'] = self.builder.get_object('combo_encin').get_active() - new_core_config['enc_out_policy'] = self.builder.get_object('combo_encout').get_active() - new_core_config['enc_level'] = self.builder.get_object('combo_enclevel').get_active() + new_core_config['enc_in_policy'] = self.builder.get_object( + 'combo_encin' + ).get_active() + new_core_config['enc_out_policy'] = self.builder.get_object( + 'combo_encout' + ).get_active() + new_core_config['enc_level'] = self.builder.get_object( + 'combo_enclevel' + ).get_active() # Bandwidth tab # new_core_config['max_connections_global'] = self.builder.get_object( - 'spin_max_connections_global', + 'spin_max_connections_global' ).get_value_as_int() - new_core_config['max_download_speed'] = self.builder.get_object('spin_max_download').get_value() - new_core_config['max_upload_speed'] = self.builder.get_object('spin_max_upload').get_value() + new_core_config['max_download_speed'] = self.builder.get_object( + 'spin_max_download' + ).get_value() + new_core_config['max_upload_speed'] = self.builder.get_object( + 'spin_max_upload' + ).get_value() new_core_config['max_upload_slots_global'] = self.builder.get_object( - 'spin_max_upload_slots_global', + 'spin_max_upload_slots_global' ).get_value_as_int() new_core_config['max_half_open_connections'] = self.builder.get_object( - 'spin_max_half_open_connections', + 'spin_max_half_open_connections' ).get_value_as_int() new_core_config['max_connections_per_second'] = self.builder.get_object( - 'spin_max_connections_per_second', + 'spin_max_connections_per_second' ).get_value_as_int() new_core_config['max_connections_per_torrent'] = self.builder.get_object( - 'spin_max_connections_per_torrent', + 'spin_max_connections_per_torrent' ).get_value_as_int() new_core_config['max_upload_slots_per_torrent'] = self.builder.get_object( - 'spin_max_upload_slots_per_torrent', + 'spin_max_upload_slots_per_torrent' ).get_value_as_int() new_core_config['max_upload_speed_per_torrent'] = self.builder.get_object( - 'spin_max_upload_per_torrent', + 'spin_max_upload_per_torrent' ).get_value() new_core_config['max_download_speed_per_torrent'] = self.builder.get_object( - 'spin_max_download_per_torrent', + 'spin_max_download_per_torrent' ).get_value() new_core_config['ignore_limits_on_local_network'] = self.builder.get_object( - 'chk_ignore_limits_on_local_network', + 'chk_ignore_limits_on_local_network' ).get_active() new_core_config['rate_limit_ip_overhead'] = self.builder.get_object( - 'chk_rate_limit_ip_overhead', + 'chk_rate_limit_ip_overhead' ).get_active() # Interface tab # - new_gtkui_config['enable_system_tray'] = self.builder.get_object('chk_use_tray').get_active() - new_gtkui_config['close_to_tray'] = self.builder.get_object('chk_min_on_close').get_active() - new_gtkui_config['start_in_tray'] = self.builder.get_object('chk_start_in_tray').get_active() - new_gtkui_config['enable_appindicator'] = self.builder.get_object('radio_appind').get_active() - new_gtkui_config['lock_tray'] = self.builder.get_object('chk_lock_tray').get_active() - passhex = sha(self.builder.get_object('txt_tray_password').get_text()).hexdigest() + new_gtkui_config['enable_system_tray'] = self.builder.get_object( + 'chk_use_tray' + ).get_active() + new_gtkui_config['close_to_tray'] = self.builder.get_object( + 'chk_min_on_close' + ).get_active() + new_gtkui_config['start_in_tray'] = self.builder.get_object( + 'chk_start_in_tray' + ).get_active() + new_gtkui_config['enable_appindicator'] = self.builder.get_object( + 'radio_appind' + ).get_active() + new_gtkui_config['lock_tray'] = self.builder.get_object( + 'chk_lock_tray' + ).get_active() + passhex = sha( + self.builder.get_object('txt_tray_password').get_text() + ).hexdigest() if passhex != 'c07eb5a8c0dc7bb81c217b67f11c3b7a5e95ffd7': new_gtkui_config['tray_password'] = passhex @@ -597,25 +768,33 @@ class Preferences(component.Component): new_gtkui_config['standalone'] = new_gtkui_standalone new_gtkui_config['show_rate_in_title'] = self.builder.get_object( - 'chk_show_rate_in_title', + 'chk_show_rate_in_title' ).get_active() new_gtkui_config['focus_main_window_on_add'] = self.builder.get_object( - 'chk_focus_main_window_on_add', + 'chk_focus_main_window_on_add' ).get_active() # Other tab # new_gtkui_config['show_new_releases'] = self.builder.get_object( - 'chk_show_new_releases', + 'chk_show_new_releases' ).get_active() - new_core_config['send_info'] = self.builder.get_object('chk_send_info').get_active() - new_core_config['geoip_db_location'] = self.builder.get_object('entry_geoip').get_text() + new_core_config['send_info'] = self.builder.get_object( + 'chk_send_info' + ).get_active() + new_core_config['geoip_db_location'] = self.builder.get_object( + 'entry_geoip' + ).get_text() # Daemon tab # - new_core_config['daemon_port'] = self.builder.get_object('spin_daemon_port').get_value_as_int() + new_core_config['daemon_port'] = self.builder.get_object( + 'spin_daemon_port' + ).get_value_as_int() new_core_config['allow_remote'] = self.builder.get_object( - 'chk_allow_remote_connections', + 'chk_allow_remote_connections' + ).get_active() + new_core_config['new_release_check'] = self.builder.get_object( + 'chk_new_releases' ).get_active() - new_core_config['new_release_check'] = self.builder.get_object('chk_new_releases').get_active() # Proxy tab # new_core_config['proxy'] = { @@ -624,44 +803,66 @@ class Preferences(component.Component): 'password': self.builder.get_object('entry_proxy_pass').get_text(), 'hostname': self.builder.get_object('entry_proxy_host').get_text(), 'port': self.builder.get_object('spin_proxy_port').get_value_as_int(), - 'proxy_hostnames': self.builder.get_object('chk_proxy_host_resolve').get_active(), - 'proxy_peer_connections': self.builder.get_object('chk_proxy_peer_conn').get_active(), - 'proxy_tracker_connections': self.builder.get_object('chk_proxy_tracker_conn').get_active(), + 'proxy_hostnames': self.builder.get_object( + 'chk_proxy_host_resolve' + ).get_active(), + 'proxy_peer_connections': self.builder.get_object( + 'chk_proxy_peer_conn' + ).get_active(), + 'proxy_tracker_connections': self.builder.get_object( + 'chk_proxy_tracker_conn' + ).get_active(), 'force_proxy': self.builder.get_object('chk_force_proxy').get_active(), - 'anonymous_mode': self.builder.get_object('chk_anonymous_mode').get_active(), + 'anonymous_mode': self.builder.get_object( + 'chk_anonymous_mode' + ).get_active(), } # Queue tab # - new_core_config['queue_new_to_top'] = self.builder.get_object('chk_queue_new_top').get_active() + new_core_config['queue_new_to_top'] = self.builder.get_object( + 'chk_queue_new_top' + ).get_active() new_core_config['max_active_seeding'] = self.builder.get_object( - 'spin_seeding', + 'spin_seeding' ).get_value_as_int() new_core_config['max_active_downloading'] = self.builder.get_object( - 'spin_downloading', + 'spin_downloading' + ).get_value_as_int() + new_core_config['max_active_limit'] = self.builder.get_object( + 'spin_active' ).get_value_as_int() - new_core_config['max_active_limit'] = self.builder.get_object('spin_active').get_value_as_int() new_core_config['dont_count_slow_torrents'] = self.builder.get_object( - 'chk_dont_count_slow_torrents', + 'chk_dont_count_slow_torrents' ).get_active() new_core_config['auto_manage_prefer_seeds'] = self.builder.get_object( - 'chk_auto_manage_prefer_seeds', + 'chk_auto_manage_prefer_seeds' + ).get_active() + new_core_config['stop_seed_at_ratio'] = self.builder.get_object( + 'chk_share_ratio' ).get_active() - new_core_config['stop_seed_at_ratio'] = self.builder.get_object('chk_share_ratio').get_active() new_core_config['remove_seed_at_ratio'] = self.builder.get_object( - 'radio_remove_ratio', + 'radio_remove_ratio' ).get_active() - new_core_config['stop_seed_ratio'] = self.builder.get_object('spin_share_ratio').get_value() + new_core_config['stop_seed_ratio'] = self.builder.get_object( + 'spin_share_ratio' + ).get_value() new_core_config['share_ratio_limit'] = self.builder.get_object( - 'spin_share_ratio_limit', + 'spin_share_ratio_limit' ).get_value() new_core_config['seed_time_ratio_limit'] = self.builder.get_object( - 'spin_seed_time_ratio_limit', + 'spin_seed_time_ratio_limit' + ).get_value() + new_core_config['seed_time_limit'] = self.builder.get_object( + 'spin_seed_time_limit' ).get_value() - new_core_config['seed_time_limit'] = self.builder.get_object('spin_seed_time_limit').get_value() # Cache tab # - new_core_config['cache_size'] = self.builder.get_object('spin_cache_size').get_value_as_int() - new_core_config['cache_expiry'] = self.builder.get_object('spin_cache_expiry').get_value_as_int() + new_core_config['cache_size'] = self.builder.get_object( + 'spin_cache_size' + ).get_value_as_int() + new_core_config['cache_expiry'] = self.builder.get_object( + 'spin_cache_expiry' + ).get_value_as_int() # Run plugin hook to apply preferences component.get('PluginManager').run_on_apply_prefs() @@ -673,8 +874,7 @@ class Preferences(component.Component): active = self.language_combo.get_active() if active == -1: dialog = InformationDialog( - _('Attention'), - _('You must choose a language'), + _('Attention'), _('You must choose a language') ) dialog.run() return @@ -718,20 +918,26 @@ class Preferences(component.Component): self.show() if was_standalone != new_gtkui_standalone: + def on_response(response): if response == gtk.RESPONSE_YES: - shutdown_daemon = (not client.is_standalone() and - client.connected() and - client.is_localhost()) - component.get('MainWindow').quit(shutdown=shutdown_daemon, restart=True) + shutdown_daemon = ( + not client.is_standalone() + and client.connected() + and client.is_localhost() + ) + component.get('MainWindow').quit( + shutdown=shutdown_daemon, restart=True + ) else: self.gtkui_config['standalone'] = not new_gtkui_standalone self.builder.get_object('radio_standalone').set_active( - self.gtkui_config['standalone'], + self.gtkui_config['standalone'] ) self.builder.get_object('radio_thinclient').set_active( - not self.gtkui_config['standalone'], + not self.gtkui_config['standalone'] ) + mode = 'Thinclient' if was_standalone else 'Standalone' dialog = YesNoDialog( _('Switching Deluge Client Mode...'), @@ -747,16 +953,20 @@ class Preferences(component.Component): def __update_cache_status(self): # Updates the cache status labels with the info in the dict cache_labels = ( - 'label_cache_read_ops', 'label_cache_write_ops', - 'label_cache_num_blocks_read', 'label_cache_num_blocks_written', - 'label_cache_read_hit_ratio', 'label_cache_write_hit_ratio', - 'label_cache_num_blocks_cache_hits', 'label_cache_disk_blocks_in_use', + 'label_cache_read_ops', + 'label_cache_write_ops', + 'label_cache_num_blocks_read', + 'label_cache_num_blocks_written', + 'label_cache_read_hit_ratio', + 'label_cache_write_hit_ratio', + 'label_cache_num_blocks_cache_hits', + 'label_cache_disk_blocks_in_use', 'label_cache_read_cache_blocks', ) for widget_name in cache_labels: widget = self.builder.get_object(widget_name) - key = widget_name[len('label_cache_'):] + key = widget_name[len('label_cache_') :] if not widget_name.endswith('ratio'): key = 'disk.' + key value = self.cache_status.get(key, 0) @@ -772,7 +982,9 @@ class Preferences(component.Component): self.cache_status = status self.__update_cache_status() - client.core.get_session_status(DISK_CACHE_KEYS).addCallback(on_get_session_status) + client.core.get_session_status(DISK_CACHE_KEYS).addCallback( + on_get_session_status + ) def on_pref_dialog_delete_event(self, widget, event): self.hide() @@ -816,10 +1028,7 @@ class Preferences(component.Component): 'alignment_tray_type': True, 'chk_lock_tray': True, }, - 'chk_lock_tray': { - 'txt_tray_password': True, - 'password_label': True, - }, + 'chk_lock_tray': {'txt_tray_password': True, 'password_label': True}, 'radio_open_folder_custom': { 'combo_file_manager': False, 'txt_open_folder_location': True, @@ -887,12 +1096,17 @@ class Preferences(component.Component): self.builder.get_object('port_img').set_from_stock(gtk.STOCK_YES, 4) self.builder.get_object('port_img').show() else: - self.builder.get_object('port_img').set_from_stock(gtk.STOCK_DIALOG_WARNING, 4) + self.builder.get_object('port_img').set_from_stock( + gtk.STOCK_DIALOG_WARNING, 4 + ) self.builder.get_object('port_img').show() + client.core.test_listen_port().addCallback(on_get_test) # XXX: Consider using gtk.Spinner() instead of the loading gif # It requires gtk.ver > 2.12 - self.builder.get_object('port_img').set_from_file(deluge.common.get_pixmap('loading.gif')) + self.builder.get_object('port_img').set_from_file( + deluge.common.get_pixmap('loading.gif') + ) self.builder.get_object('port_img').show() client.force_call() @@ -923,9 +1137,15 @@ class Preferences(component.Component): plugin_info = component.get('PluginManager').get_plugin_info(name) self.builder.get_object('label_plugin_author').set_text(plugin_info['Author']) self.builder.get_object('label_plugin_version').set_text(plugin_info['Version']) - self.builder.get_object('label_plugin_email').set_text(plugin_info['Author-email']) - self.builder.get_object('label_plugin_homepage').set_text(plugin_info['Home-page']) - self.builder.get_object('label_plugin_details').set_text(plugin_info['Description']) + self.builder.get_object('label_plugin_email').set_text( + plugin_info['Author-email'] + ) + self.builder.get_object('label_plugin_homepage').set_text( + plugin_info['Home-page'] + ) + self.builder.get_object('label_plugin_details').set_text( + plugin_info['Description'] + ) def on_button_plugin_install_clicked(self, widget): log.debug('on_button_plugin_install_clicked') @@ -933,7 +1153,12 @@ class Preferences(component.Component): _('Select the Plugin'), self.pref_dialog, gtk.FILE_CHOOSER_ACTION_OPEN, - buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK), + buttons=( + gtk.STOCK_CANCEL, + gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, + gtk.RESPONSE_OK, + ), ) chooser.set_transient_for(self.pref_dialog) @@ -956,11 +1181,9 @@ class Preferences(component.Component): from base64 import b64encode import shutil + filename = os.path.split(filepath)[1] - shutil.copyfile( - filepath, - os.path.join(get_config_dir(), 'plugins', filename), - ) + shutil.copyfile(filepath, os.path.join(get_config_dir(), 'plugins', filename)) component.get('PluginManager').scan_for_plugins() @@ -998,22 +1221,41 @@ class Preferences(component.Component): def on_combo_proxy_type_changed(self, widget): proxy_type = self.builder.get_object('combo_proxy_type').get_active() proxy_entries = [ - 'label_proxy_host', 'entry_proxy_host', 'label_proxy_port', 'spin_proxy_port', - 'label_proxy_pass', 'entry_proxy_pass', 'label_proxy_user', 'entry_proxy_user', - 'chk_proxy_host_resolve', 'chk_proxy_peer_conn', 'chk_proxy_tracker_conn', + 'label_proxy_host', + 'entry_proxy_host', + 'label_proxy_port', + 'spin_proxy_port', + 'label_proxy_pass', + 'entry_proxy_pass', + 'label_proxy_user', + 'entry_proxy_user', + 'chk_proxy_host_resolve', + 'chk_proxy_peer_conn', + 'chk_proxy_tracker_conn', ] # 0: None, 1: Socks4, 2: Socks5, 3: Socks5 Auth, 4: HTTP, 5: HTTP Auth, 6: I2P show_entries = [] if proxy_type > 0: - show_entries.extend([ - 'label_proxy_host', 'entry_proxy_host', 'label_proxy_port', 'spin_proxy_port', - 'chk_proxy_peer_conn', 'chk_proxy_tracker_conn', - ]) + show_entries.extend( + [ + 'label_proxy_host', + 'entry_proxy_host', + 'label_proxy_port', + 'spin_proxy_port', + 'chk_proxy_peer_conn', + 'chk_proxy_tracker_conn', + ] + ) if proxy_type in (3, 5): - show_entries.extend([ - 'label_proxy_pass', 'entry_proxy_pass', 'label_proxy_user', 'entry_proxy_user', - ]) + show_entries.extend( + [ + 'label_proxy_pass', + 'entry_proxy_pass', + 'label_proxy_user', + 'entry_proxy_user', + ] + ) if proxy_type in (2, 3, 4, 5): show_entries.extend(['chk_proxy_host_resolve']) @@ -1053,8 +1295,10 @@ class Preferences(component.Component): ErrorDialog( _('Server Side Error'), _('An error occurred on the server'), - parent=self.pref_dialog, details=failure.getErrorMessage(), + parent=self.pref_dialog, + details=failure.getErrorMessage(), ).run() + client.core.get_known_accounts().addCallback(on_ok).addErrback(on_fail) def on_get_known_accounts(self, known_accounts): @@ -1072,9 +1316,15 @@ class Preferences(component.Component): for account in known_accounts: accounts_iter = self.accounts_liststore.append() - self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_USERNAME, account['username']) - self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_LEVEL, account['authlevel']) - self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_PASSWORD, account['password']) + self.accounts_liststore.set_value( + accounts_iter, ACCOUNTS_USERNAME, account['username'] + ) + self.accounts_liststore.set_value( + accounts_iter, ACCOUNTS_LEVEL, account['authlevel'] + ) + self.accounts_liststore.set_value( + accounts_iter, ACCOUNTS_PASSWORD, account['password'] + ) def on_accounts_selection_changed(self, treeselection): log.debug('on_accounts_selection_changed') @@ -1090,7 +1340,9 @@ class Preferences(component.Component): self.builder.get_object('accounts_delete').set_sensitive(False) def on_accounts_add_clicked(self, widget): - dialog = AccountDialog(levels_mapping=client.auth_levels_mapping, parent=self.pref_dialog) + dialog = AccountDialog( + levels_mapping=client.auth_levels_mapping, parent=self.pref_dialog + ) def dialog_finished(response_id): username = dialog.get_username() @@ -1099,28 +1351,36 @@ class Preferences(component.Component): def add_ok(rv): accounts_iter = self.accounts_liststore.append() - self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_USERNAME, username) - self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_LEVEL, authlevel) - self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_PASSWORD, password) + self.accounts_liststore.set_value( + accounts_iter, ACCOUNTS_USERNAME, username + ) + self.accounts_liststore.set_value( + accounts_iter, ACCOUNTS_LEVEL, authlevel + ) + self.accounts_liststore.set_value( + accounts_iter, ACCOUNTS_PASSWORD, password + ) def add_fail(failure): if failure.type == AuthManagerError: ErrorDialog( _('Error Adding Account'), _('Authentication failed'), - parent=self.pref_dialog, details=failure.getErrorMessage(), + parent=self.pref_dialog, + details=failure.getErrorMessage(), ).run() else: ErrorDialog( _('Error Adding Account'), _('An error occurred while adding account'), - parent=self.pref_dialog, details=failure.getErrorMessage(), + parent=self.pref_dialog, + details=failure.getErrorMessage(), ).run() if response_id == gtk.RESPONSE_OK: - client.core.create_account( - username, password, authlevel, - ).addCallback(add_ok).addErrback(add_fail) + client.core.create_account(username, password, authlevel).addCallback( + add_ok + ).addErrback(add_fail) dialog.run().addCallback(dialog_finished) @@ -1138,7 +1398,6 @@ class Preferences(component.Component): ) def dialog_finished(response_id): - def update_ok(rc): model.set_value(itr, ACCOUNTS_PASSWORD, dialog.get_username()) model.set_value(itr, ACCOUNTS_LEVEL, dialog.get_authlevel()) @@ -1147,14 +1406,13 @@ class Preferences(component.Component): ErrorDialog( _('Error Updating Account'), _('An error occurred while updating account'), - parent=self.pref_dialog, details=failure.getErrorMessage(), + parent=self.pref_dialog, + details=failure.getErrorMessage(), ).run() if response_id == gtk.RESPONSE_OK: client.core.update_account( - dialog.get_username(), - dialog.get_password(), - dialog.get_authlevel(), + dialog.get_username(), dialog.get_password(), dialog.get_authlevel() ).addCallback(update_ok).addErrback(update_fail) dialog.run().addCallback(dialog_finished) @@ -1166,8 +1424,10 @@ class Preferences(component.Component): username = model[itr][0] header = _('Remove Account') - text = _('Are you sure you want to remove the account with the ' - 'username "%(username)s"?' % {'username': username}) + text = _( + 'Are you sure you want to remove the account with the ' + 'username "%(username)s"?' % {'username': username} + ) dialog = YesNoDialog(header, text, parent=self.pref_dialog) def dialog_finished(response_id): @@ -1179,18 +1439,22 @@ class Preferences(component.Component): ErrorDialog( _('Error Removing Account'), _('Auhentication failed'), - parent=self.pref_dialog, details=failure.getErrorMessage(), + parent=self.pref_dialog, + details=failure.getErrorMessage(), ).run() else: ErrorDialog( _('Error Removing Account'), _('An error occurred while removing account'), - parent=self.pref_dialog, details=failure.getErrorMessage(), + parent=self.pref_dialog, + details=failure.getErrorMessage(), ).run() + if response_id == gtk.RESPONSE_YES: - client.core.remove_account( - username, - ).addCallback(remove_ok).addErrback(remove_fail) + client.core.remove_account(username).addCallback(remove_ok).addErrback( + remove_fail + ) + dialog.run().addCallback(dialog_finished) def on_piecesbar_toggle_toggled(self, widget): @@ -1228,21 +1492,35 @@ class Preferences(component.Component): def __set_color(self, state, from_config=False): if from_config: color = Color(*self.gtkui_config['pieces_color_%s' % state]) - log.debug('Setting %r color state from config to %s', state, (color.red, color.green, color.blue)) + log.debug( + 'Setting %r color state from config to %s', + state, + (color.red, color.green, color.blue), + ) self.builder.get_object('%s_color' % state).set_color(color) else: color = self.builder.get_object('%s_color' % state).get_color() - log.debug('Setting %r color state to %s', state, (color.red, color.green, color.blue)) - self.gtkui_config['pieces_color_%s' % state] = [color.red, color.green, color.blue] + log.debug( + 'Setting %r color state to %s', + state, + (color.red, color.green, color.blue), + ) + self.gtkui_config['pieces_color_%s' % state] = [ + color.red, + color.green, + color.blue, + ] self.gtkui_config.save() self.gtkui_config.apply_set_functions('pieces_colors') self.builder.get_object('revert_color_%s' % state).set_sensitive( - [color.red, color.green, color.blue] != self.COLOR_DEFAULTS[state], + [color.red, color.green, color.blue] != self.COLOR_DEFAULTS[state] ) def __revert_color(self, state, from_config=False): log.debug('Reverting %r color state', state) - self.builder.get_object('%s_color' % state).set_color(Color(*self.COLOR_DEFAULTS[state])) + self.builder.get_object('%s_color' % state).set_color( + Color(*self.COLOR_DEFAULTS[state]) + ) self.builder.get_object('revert_color_%s' % state).set_sensitive(False) self.gtkui_config.apply_set_functions('pieces_colors') diff --git a/deluge/ui/gtkui/queuedtorrents.py b/deluge/ui/gtkui/queuedtorrents.py index 9312d1da1..9693dca58 100644 --- a/deluge/ui/gtkui/queuedtorrents.py +++ b/deluge/ui/gtkui/queuedtorrents.py @@ -13,7 +13,13 @@ import logging import os.path from gobject import timeout_add -from gtk import STOCK_SORT_DESCENDING, Builder, CellRendererText, ListStore, TreeViewColumn +from gtk import ( + STOCK_SORT_DESCENDING, + Builder, + CellRendererText, + ListStore, + TreeViewColumn, +) import deluge.common import deluge.component as component @@ -26,15 +32,19 @@ log = logging.getLogger(__name__) class QueuedTorrents(component.Component): def __init__(self): - component.Component.__init__(self, 'QueuedTorrents', depend=['StatusBar', 'AddTorrentDialog']) + component.Component.__init__( + self, 'QueuedTorrents', depend=['StatusBar', 'AddTorrentDialog'] + ) self.queue = [] self.status_item = None self.config = ConfigManager('gtkui.conf') self.builder = Builder() - self.builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'queuedtorrents.ui'), - )) + self.builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'queuedtorrents.ui') + ) + ) self.builder.get_object('chk_autoadd').set_active(self.config['autoadd_queued']) self.dialog = self.builder.get_object('queued_torrents_dialog') self.dialog.set_icon(get_logo(32)) @@ -42,7 +52,9 @@ class QueuedTorrents(component.Component): self.builder.connect_signals(self) self.treeview = self.builder.get_object('treeview') - self.treeview.append_column(TreeViewColumn(_('Torrent'), CellRendererText(), text=0)) + self.treeview.append_column( + TreeViewColumn(_('Torrent'), CellRendererText(), text=0) + ) self.liststore = ListStore(str, str) self.treeview.set_model(self.liststore) diff --git a/deluge/ui/gtkui/removetorrentdialog.py b/deluge/ui/gtkui/removetorrentdialog.py index c53e20f48..92bbe1e8c 100644 --- a/deluge/ui/gtkui/removetorrentdialog.py +++ b/deluge/ui/gtkui/removetorrentdialog.py @@ -32,6 +32,7 @@ class RemoveTorrentDialog(object): :raises ValueError: if `torrent_id` contains no torrent_ids or is None """ + def __init__(self, torrent_ids, delete_files=False): if not isinstance(torrent_ids, list) and not isinstance(torrent_ids, tuple): raise TypeError('requires a list of torrent_ids') @@ -42,9 +43,11 @@ class RemoveTorrentDialog(object): self.__torrent_ids = torrent_ids self.builder = gtk.Builder() - self.builder.add_from_file(deluge.common.resource_filename( - 'deluge.ui.gtkui', os.path.join('glade', 'remove_torrent_dialog.ui'), - )) + self.builder.add_from_file( + deluge.common.resource_filename( + 'deluge.ui.gtkui', os.path.join('glade', 'remove_torrent_dialog.ui') + ) + ) self.__dialog = self.builder.get_object('remove_torrent_dialog') self.__dialog.set_transient_for(component.get('MainWindow').window) @@ -55,7 +58,11 @@ class RemoveTorrentDialog(object): label_torrents = self.builder.get_object('label_torrents') num_torrents = len(self.__torrent_ids) if num_torrents == 1: - label_torrents.set_markup(component.get('TorrentView').get_torrent_status(self.__torrent_ids[0])['name']) + label_torrents.set_markup( + component.get('TorrentView').get_torrent_status(self.__torrent_ids[0])[ + 'name' + ] + ) else: label_title.set_markup(_('Remove the selected torrents?')) label_torrents.set_markup(_('Total of %s torrents selected') % num_torrents) diff --git a/deluge/ui/gtkui/sidebar.py b/deluge/ui/gtkui/sidebar.py index 103f2ca5a..f4eacfc17 100644 --- a/deluge/ui/gtkui/sidebar.py +++ b/deluge/ui/gtkui/sidebar.py @@ -25,6 +25,7 @@ class SideBar(component.Component): manages the sidebar-tabs. purpose : plugins """ + def __init__(self): component.Component.__init__(self, 'SideBar') main_builder = component.get('MainWindow').get_builder() diff --git a/deluge/ui/gtkui/status_tab.py b/deluge/ui/gtkui/status_tab.py index 740a7b0d0..ae2c454c7 100644 --- a/deluge/ui/gtkui/status_tab.py +++ b/deluge/ui/gtkui/status_tab.py @@ -15,8 +15,15 @@ import deluge.component as component from deluge.common import fpeer from deluge.configmanager import ConfigManager from deluge.ui.gtkui.piecesbar import PiecesBar -from deluge.ui.gtkui.tab_data_funcs import (fdate_or_never, fpcnt, fratio, fseed_rank_or_dash, fspeed_max, - ftime_or_dash, ftotal_sized) +from deluge.ui.gtkui.tab_data_funcs import ( + fdate_or_never, + fpcnt, + fratio, + fseed_rank_or_dash, + fspeed_max, + ftime_or_dash, + ftotal_sized, +) from deluge.ui.gtkui.torrentdetails import Tab, TabWidget log = logging.getLogger(__name__) @@ -33,19 +40,23 @@ class StatusTab(Tab): self.add_tab_widget('summary_availability', fratio, ('distributed_copies',)) self.add_tab_widget( - 'summary_total_downloaded', ftotal_sized, + 'summary_total_downloaded', + ftotal_sized, ('all_time_download', 'total_payload_download'), ) self.add_tab_widget( - 'summary_total_uploaded', ftotal_sized, + 'summary_total_uploaded', + ftotal_sized, ('total_uploaded', 'total_payload_upload'), ) self.add_tab_widget( - 'summary_download_speed', fspeed_max, + 'summary_download_speed', + fspeed_max, ('download_payload_rate', 'max_download_speed'), ) self.add_tab_widget( - 'summary_upload_speed', fspeed_max, + 'summary_upload_speed', + fspeed_max, ('upload_payload_rate', 'max_upload_speed'), ) self.add_tab_widget('summary_seeds', fpeer, ('num_seeds', 'total_seeds')) @@ -54,12 +65,20 @@ class StatusTab(Tab): self.add_tab_widget('summary_share_ratio', fratio, ('ratio',)) self.add_tab_widget('summary_active_time', ftime_or_dash, ('active_time',)) self.add_tab_widget('summary_seed_time', ftime_or_dash, ('seeding_time',)) - self.add_tab_widget('summary_seed_rank', fseed_rank_or_dash, ('seed_rank', 'seeding_time')) + self.add_tab_widget( + 'summary_seed_rank', fseed_rank_or_dash, ('seed_rank', 'seeding_time') + ) self.add_tab_widget('progressbar', fpcnt, ('progress', 'state', 'message')) - self.add_tab_widget('summary_last_seen_complete', fdate_or_never, ('last_seen_complete',)) - self.add_tab_widget('summary_last_transfer', ftime_or_dash, ('time_since_transfer',)) + self.add_tab_widget( + 'summary_last_seen_complete', fdate_or_never, ('last_seen_complete',) + ) + self.add_tab_widget( + 'summary_last_transfer', ftime_or_dash, ('time_since_transfer',) + ) - self.config.register_set_function('show_piecesbar', self.on_show_piecesbar_config_changed, apply_now=True) + self.config.register_set_function( + 'show_piecesbar', self.on_show_piecesbar_config_changed, apply_now=True + ) def update(self): # Get the first selected torrent @@ -76,7 +95,7 @@ class StatusTab(Tab): status_keys.extend(['pieces', 'num_pieces']) component.get('SessionProxy').get_torrent_status( - selected, status_keys, + selected, status_keys ).addCallback(self._on_get_torrent_status) def _on_get_torrent_status(self, status): @@ -96,7 +115,10 @@ class StatusTab(Tab): if self.config['show_piecesbar']: if self.piecesbar.get_fraction() != fraction: self.piecesbar.set_fraction(fraction) - if status['state'] != 'Checking' and self.piecesbar.get_pieces() != status['pieces']: + if ( + status['state'] != 'Checking' + and self.piecesbar.get_pieces() != status['pieces'] + ): # Skip pieces assignment if checking torrent. self.piecesbar.set_pieces(status['pieces'], status['num_pieces']) self.piecesbar.update() @@ -113,10 +135,12 @@ class StatusTab(Tab): def show_piecesbar(self): if self.piecesbar is None: self.piecesbar = PiecesBar() - self.main_builder.get_object( - 'status_progress_vbox', - ).pack_start(self.piecesbar, False, False, 0) - self.tab_widgets['piecesbar'] = TabWidget(self.piecesbar, fpcnt, ('progress', 'state', 'message')) + self.main_builder.get_object('status_progress_vbox').pack_start( + self.piecesbar, False, False, 0 + ) + self.tab_widgets['piecesbar'] = TabWidget( + self.piecesbar, fpcnt, ('progress', 'state', 'message') + ) self.piecesbar.show() self.progressbar.hide() diff --git a/deluge/ui/gtkui/statusbar.py b/deluge/ui/gtkui/statusbar.py index d9edc9d81..0d50f7571 100644 --- a/deluge/ui/gtkui/statusbar.py +++ b/deluge/ui/gtkui/statusbar.py @@ -24,7 +24,15 @@ log = logging.getLogger(__name__) class StatusBarItem(object): - def __init__(self, image=None, stock=None, text=None, markup=False, callback=None, tooltip=None): + def __init__( + self, + image=None, + stock=None, + text=None, + markup=False, + callback=None, + tooltip=None, + ): self._widgets = [] self._ebox = gtk.EventBox() self._hbox = gtk.HBox() @@ -137,7 +145,8 @@ class StatusBar(component.Component): self.statusbar.show_all() # Create the not connected item self.not_connected_item = StatusBarItem( - stock=gtk.STOCK_STOP, text=_('Not Connected'), + stock=gtk.STOCK_STOP, + text=_('Not Connected'), callback=self._on_notconnected_item_clicked, ) # Show the not connected status bar @@ -146,7 +155,9 @@ class StatusBar(component.Component): # Hide if necessary self.visible(self.config['show_statusbar']) - client.register_event_handler('ConfigValueChangedEvent', self.on_configvaluechanged_event) + client.register_event_handler( + 'ConfigValueChangedEvent', self.on_configvaluechanged_event + ) def start(self): # Add in images and labels @@ -155,35 +166,40 @@ class StatusBar(component.Component): self.connections_item = self.add_item( stock=gtk.STOCK_NETWORK, callback=self._on_connection_item_clicked, - tooltip=_('Connections (Limit)'), pack_start=True, + tooltip=_('Connections (Limit)'), + pack_start=True, ) self.download_item = self.add_item( image=get_pixmap('downloading16.png'), callback=self._on_download_item_clicked, - tooltip=_('Download Speed (Limit)'), pack_start=True, + tooltip=_('Download Speed (Limit)'), + pack_start=True, ) self.upload_item = self.add_item( image=get_pixmap('seeding16.png'), callback=self._on_upload_item_clicked, - tooltip=_('Upload Speed (Limit)'), pack_start=True, + tooltip=_('Upload Speed (Limit)'), + pack_start=True, ) self.traffic_item = self.add_item( image=get_pixmap('traffic16.png'), callback=self._on_traffic_item_clicked, - tooltip=_('Protocol Traffic (Down:Up)'), pack_start=True, + tooltip=_('Protocol Traffic (Down:Up)'), + pack_start=True, ) self.dht_item = StatusBarItem( - image=get_pixmap('dht16.png'), tooltip=_('DHT Nodes'), + image=get_pixmap('dht16.png'), tooltip=_('DHT Nodes') ) self.diskspace_item = self.add_item( stock=gtk.STOCK_HARDDISK, callback=self._on_diskspace_item_clicked, - tooltip=_('Free Disk Space'), pack_start=True, + tooltip=_('Free Disk Space'), + pack_start=True, ) self.health_item = self.add_item( @@ -195,7 +211,7 @@ class StatusBar(component.Component): ) self.external_ip_item = self.add_item( - tooltip=_('External IP Address'), pack_start=True, + tooltip=_('External IP Address'), pack_start=True ) self.health = False @@ -205,11 +221,11 @@ class StatusBar(component.Component): self._on_max_download_speed(configs['max_download_speed']) self._on_max_upload_speed(configs['max_upload_speed']) self._on_dht(configs['dht']) + # Get some config values - client.core.get_config_values([ - 'max_connections_global', 'max_download_speed', - 'max_upload_speed', 'dht', - ]).addCallback(update_config_values) + client.core.get_config_values( + ['max_connections_global', 'max_download_speed', 'max_upload_speed', 'dht'] + ).addCallback(update_config_values) def stop(self): # When stopped, we just show the not connected thingy @@ -238,7 +254,16 @@ class StatusBar(component.Component): def show_not_connected(self): self.hbox.pack_start(self.not_connected_item.get_eventbox(), False, False, 0) - def add_item(self, image=None, stock=None, text=None, markup=False, callback=None, tooltip=None, pack_start=False): + def add_item( + self, + image=None, + stock=None, + text=None, + markup=False, + callback=None, + tooltip=None, + pack_start=False, + ): """Adds an item to the status bar""" # The return tuple.. we return whatever widgets we add item = StatusBarItem(image, stock, text, markup, callback, tooltip) @@ -256,7 +281,9 @@ class StatusBar(component.Component): except Exception as ex: log.debug('Unable to remove widget: %s', ex) - def add_timeout_item(self, seconds=3, image=None, stock=None, text=None, callback=None): + def add_timeout_item( + self, seconds=3, image=None, stock=None, text=None, callback=None + ): """Adds an item to the StatusBar for seconds""" item = self.add_item(image, stock, text, callback) # Start a timer to remove this item in seconds @@ -266,7 +293,7 @@ class StatusBar(component.Component): """Displays a warning to the user in the status bar""" if text not in self.current_warnings: item = self.add_item( - stock=gtk.STOCK_DIALOG_WARNING, text=text, callback=callback, + stock=gtk.STOCK_DIALOG_WARNING, text=text, callback=callback ) self.current_warnings.append(text) timeout_add(3000, self.remove_warning, item) @@ -278,11 +305,18 @@ class StatusBar(component.Component): def clear_statusbar(self): def remove(child): self.hbox.remove(child) + self.hbox.foreach(remove) def send_status_request(self): # Sends an async request for data from the core - keys = ['num_peers', 'upload_rate', 'download_rate', 'payload_upload_rate', 'payload_download_rate'] + keys = [ + 'num_peers', + 'upload_rate', + 'download_rate', + 'payload_upload_rate', + 'payload_download_rate', + ] if self.dht_status: keys.append('dht_nodes') @@ -315,10 +349,18 @@ class StatusBar(component.Component): self.remove_item(self.dht_item) def _on_get_session_status(self, status): - self.download_rate = fspeed(status['payload_download_rate'], precision=0, shortform=True) - self.upload_rate = fspeed(status['payload_upload_rate'], precision=0, shortform=True) - self.download_protocol_rate = (status['download_rate'] - status['payload_download_rate']) // 1024 - self.upload_protocol_rate = (status['upload_rate'] - status['payload_upload_rate']) // 1024 + self.download_rate = fspeed( + status['payload_download_rate'], precision=0, shortform=True + ) + self.upload_rate = fspeed( + status['payload_upload_rate'], precision=0, shortform=True + ) + self.download_protocol_rate = ( + status['download_rate'] - status['payload_download_rate'] + ) // 1024 + self.upload_protocol_rate = ( + status['upload_rate'] - status['payload_upload_rate'] + ) // 1024 self.num_connections = status['num_peers'] self.update_download_label() self.update_upload_label() @@ -336,9 +378,13 @@ class StatusBar(component.Component): def _on_get_free_space(self, space): if space >= 0: - self.diskspace_item.set_markup('<small>%s</small>' % fsize(space, shortform=True)) + self.diskspace_item.set_markup( + '<small>%s</small>' % fsize(space, shortform=True) + ) else: - self.diskspace_item.set_markup('<span foreground="red">' + _('Error') + '</span>') + self.diskspace_item.set_markup( + '<span foreground="red">' + _('Error') + '</span>' + ) def _on_max_download_speed(self, max_download_speed): self.max_download_speed = max_download_speed @@ -357,7 +403,10 @@ class StatusBar(component.Component): if self.max_connections_global < 0: label_string = '%s' % self.num_connections else: - label_string = '%s <small>(%s)</small>' % (self.num_connections, self.max_connections_global) + label_string = '%s <small>(%s)</small>' % ( + self.num_connections, + self.max_connections_global, + ) self.connections_item.set_markup(label_string) @@ -371,7 +420,9 @@ class StatusBar(component.Component): label_string = self.download_rate else: label_string = '%s <small>(%i %s)</small>' % ( - self.download_rate, self.max_download_speed, _('K/s'), + self.download_rate, + self.max_download_speed, + _('K/s'), ) self.download_item.set_markup(label_string) @@ -382,13 +433,19 @@ class StatusBar(component.Component): label_string = self.upload_rate else: label_string = '%s <small>(%i %s)</small>' % ( - self.upload_rate, self.max_upload_speed, _('K/s'), + self.upload_rate, + self.max_upload_speed, + _('K/s'), ) self.upload_item.set_markup(label_string) def update_traffic_label(self): - label_string = '<small>%i:%i %s</small>' % (self.download_protocol_rate, self.upload_protocol_rate, _('K/s')) + label_string = '<small>%i:%i %s</small>' % ( + self.download_protocol_rate, + self.upload_protocol_rate, + _('K/s'), + ) self.traffic_item.set_markup(label_string) def update(self): @@ -398,16 +455,25 @@ class StatusBar(component.Component): log.debug('_on_set_unlimit_other %s', core_key) other_dialog_info = { 'max_download_speed': ( - _('Download Speed Limit'), _('Set the maximum download speed'), - _('K/s'), 'downloading.svg', self.max_download_speed, + _('Download Speed Limit'), + _('Set the maximum download speed'), + _('K/s'), + 'downloading.svg', + self.max_download_speed, ), 'max_upload_speed': ( - _('Upload Speed Limit'), _('Set the maximum upload speed'), - _('K/s'), 'seeding.svg', self.max_upload_speed, + _('Upload Speed Limit'), + _('Set the maximum upload speed'), + _('K/s'), + 'seeding.svg', + self.max_upload_speed, ), 'max_connections_global': ( - _('Incoming Connections'), _('Set the maximum incoming connections'), - '', gtk.STOCK_NETWORK, self.max_connections_global, + _('Incoming Connections'), + _('Set the maximum incoming connections'), + '', + gtk.STOCK_NETWORK, + self.max_connections_global, ), } @@ -424,9 +490,11 @@ class StatusBar(component.Component): if widget.get_name() == 'unlimited': set_value(-1) elif widget.get_name() == 'other': + def dialog_finished(response_id): if response_id == gtk.RESPONSE_OK: set_value(dialog.get_value()) + dialog = dialogs.OtherDialog(*other_dialog_info[core_key]) dialog.run().addCallback(set_value) else: @@ -438,7 +506,9 @@ class StatusBar(component.Component): self.config['tray_download_speed_list'], self._on_set_download_speed, self.max_download_speed, - _('K/s'), show_notset=True, show_other=True, + _('K/s'), + show_notset=True, + show_other=True, ) menu.show_all() menu.popup(None, None, None, event.button, event.time) @@ -452,7 +522,9 @@ class StatusBar(component.Component): self.config['tray_upload_speed_list'], self._on_set_upload_speed, self.max_upload_speed, - _('K/s'), show_notset=True, show_other=True, + _('K/s'), + show_notset=True, + show_other=True, ) menu.show_all() menu.popup(None, None, None, event.button, event.time) @@ -465,7 +537,9 @@ class StatusBar(component.Component): menu = common.build_menu_radio_list( self.config['connection_limit_list'], self._on_set_connection_limit, - self.max_connections_global, show_notset=True, show_other=True, + self.max_connections_global, + show_notset=True, + show_other=True, ) menu.show_all() menu.popup(None, None, None, event.button, event.time) diff --git a/deluge/ui/gtkui/systemtray.py b/deluge/ui/gtkui/systemtray.py index 878aeb584..f54300304 100644 --- a/deluge/ui/gtkui/systemtray.py +++ b/deluge/ui/gtkui/systemtray.py @@ -12,11 +12,22 @@ from __future__ import unicode_literals import logging import os -from gtk import (Builder, RadioMenuItem, status_icon_new_from_icon_name, status_icon_new_from_pixbuf, - status_icon_position_menu) +from gtk import ( + Builder, + RadioMenuItem, + status_icon_new_from_icon_name, + status_icon_new_from_pixbuf, + status_icon_position_menu, +) import deluge.component as component -from deluge.common import fspeed, get_pixmap, osx_check, resource_filename, windows_check +from deluge.common import ( + fspeed, + get_pixmap, + osx_check, + resource_filename, + windows_check, +) from deluge.configmanager import ConfigManager from deluge.ui.client import client from deluge.ui.gtkui import dialogs @@ -48,10 +59,14 @@ class SystemTray(component.Component): 'separatormenuitem3', 'separatormenuitem4', ] - self.config.register_set_function('enable_system_tray', self.on_enable_system_tray_set) + self.config.register_set_function( + 'enable_system_tray', self.on_enable_system_tray_set + ) # bit of a hack to prevent function from doing something on startup self.__enabled_set_once = False - self.config.register_set_function('enable_appindicator', self.on_enable_appindicator_set) + self.config.register_set_function( + 'enable_appindicator', self.on_enable_appindicator_set + ) self.max_download_speed = -1.0 self.download_rate = 0.0 @@ -66,10 +81,9 @@ class SystemTray(component.Component): def enable(self): """Enables the system tray icon.""" self.builder = Builder() - self.builder.add_from_file(resource_filename( - 'deluge.ui.gtkui', - os.path.join('glade', 'tray_menu.ui'), - )) + self.builder.add_from_file( + resource_filename('deluge.ui.gtkui', os.path.join('glade', 'tray_menu.ui')) + ) self.builder.connect_signals(self) @@ -78,9 +92,7 @@ class SystemTray(component.Component): if appindicator and self.config['enable_appindicator']: log.debug('Enabling the Application Indicator...') self.indicator = appindicator.Indicator( - 'deluge', - 'deluge', - appindicator.CATEGORY_APPLICATION_STATUS, + 'deluge', 'deluge', appindicator.CATEGORY_APPLICATION_STATUS ) try: self.indicator.set_property('title', _('Deluge')) @@ -91,8 +103,12 @@ class SystemTray(component.Component): self.indicator.set_menu(self.tray_menu) # Make sure the status of the Show Window MenuItem is correct - self._sig_win_hide = self.mainwindow.window.connect('hide', self._on_window_hide) - self._sig_win_show = self.mainwindow.window.connect('show', self._on_window_show) + self._sig_win_hide = self.mainwindow.window.connect( + 'hide', self._on_window_hide + ) + self._sig_win_show = self.mainwindow.window.connect( + 'show', self._on_window_show + ) if self.mainwindow.visible(): self.builder.get_object('menuitem_show_deluge').set_active(True) else: @@ -111,10 +127,16 @@ class SystemTray(component.Component): self.tray.connect('activate', self.on_tray_clicked) self.tray.connect('popup-menu', self.on_tray_popup) - self.builder.get_object('download-limit-image').set_from_file(get_pixmap('downloading16.png')) - self.builder.get_object('upload-limit-image').set_from_file(get_pixmap('seeding16.png')) + self.builder.get_object('download-limit-image').set_from_file( + get_pixmap('downloading16.png') + ) + self.builder.get_object('upload-limit-image').set_from_file( + get_pixmap('seeding16.png') + ) - client.register_event_handler('ConfigValueChangedEvent', self.config_value_changed) + client.register_event_handler( + 'ConfigValueChangedEvent', self.config_value_changed + ) if client.connected(): # We're connected so we need to get some values from the core self.__start() @@ -146,7 +168,10 @@ class SystemTray(component.Component): def update_config_values(configs): self._on_max_download_speed(configs['max_download_speed']) self._on_max_upload_speed(configs['max_upload_speed']) - client.core.get_config_values(['max_download_speed', 'max_upload_speed']).addCallback(update_config_values) + + client.core.get_config_values( + ['max_download_speed', 'max_upload_speed'] + ).addCallback(update_config_values) def start(self): self.__start() @@ -170,10 +195,9 @@ class SystemTray(component.Component): self.tray.set_visible(False) def send_status_request(self): - client.core.get_session_status([ - 'payload_upload_rate', - 'payload_download_rate', - ]).addCallback(self._on_get_session_status) + client.core.get_session_status( + ['payload_upload_rate', 'payload_download_rate'] + ).addCallback(self._on_get_session_status) def config_value_changed(self, key, value): """This is called when we received a config_value_changed signal from @@ -221,8 +245,13 @@ class SystemTray(component.Component): max_upload_speed = '%s %s' % (max_upload_speed, _('K/s')) msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % ( - _('Deluge'), _('Down'), self.download_rate, - max_download_speed, _('Up'), self.upload_rate, max_upload_speed, + _('Deluge'), + _('Down'), + self.download_rate, + max_download_speed, + _('Up'), + self.upload_rate, + max_upload_speed, ) # Set the tooltip @@ -233,24 +262,28 @@ class SystemTray(component.Component): def build_tray_bwsetsubmenu(self): # Create the Download speed list sub-menu submenu_bwdownset = build_menu_radio_list( - self.config['tray_download_speed_list'], self.on_tray_setbwdown, + self.config['tray_download_speed_list'], + self.on_tray_setbwdown, self.max_download_speed, - _('K/s'), show_notset=True, show_other=True, + _('K/s'), + show_notset=True, + show_other=True, ) # Create the Upload speed list sub-menu submenu_bwupset = build_menu_radio_list( - self.config['tray_upload_speed_list'], self.on_tray_setbwup, + self.config['tray_upload_speed_list'], + self.on_tray_setbwup, self.max_upload_speed, - _('K/s'), show_notset=True, show_other=True, + _('K/s'), + show_notset=True, + show_other=True, ) # Add the sub-menus to the tray menu self.builder.get_object('menuitem_download_limit').set_submenu( - submenu_bwdownset, - ) - self.builder.get_object('menuitem_upload_limit').set_submenu( - submenu_bwupset, + submenu_bwdownset ) + self.builder.get_object('menuitem_upload_limit').set_submenu(submenu_bwupset) # Show the sub-menus for all to see submenu_bwdownset.show_all() @@ -324,7 +357,9 @@ class SystemTray(component.Component): if windows_check() or osx_check(): popup_function = None button = 0 - self.tray_menu.popup(None, None, popup_function, button, activate_time, status_icon) + self.tray_menu.popup( + None, None, popup_function, button, activate_time, status_icon + ) def on_menuitem_show_deluge_activate(self, menuitem): log.debug('on_menuitem_show_deluge_activate') @@ -359,8 +394,12 @@ class SystemTray(component.Component): if not widget.get_active(): return self.setbwlimit( - widget, _('Download Speed Limit'), _('Set the maximum download speed'), - 'max_download_speed', 'tray_download_speed_list', self.max_download_speed, + widget, + _('Download Speed Limit'), + _('Set the maximum download speed'), + 'max_download_speed', + 'tray_download_speed_list', + self.max_download_speed, 'downloading.svg', ) @@ -370,8 +409,12 @@ class SystemTray(component.Component): if not widget.get_active(): return self.setbwlimit( - widget, _('Upload Speed Limit'), _('Set the maximum upload speed'), - 'max_upload_speed', 'tray_upload_speed_list', self.max_upload_speed, + widget, + _('Upload Speed Limit'), + _('Set the maximum upload speed'), + 'max_upload_speed', + 'tray_upload_speed_list', + self.max_upload_speed, 'seeding.svg', ) @@ -387,6 +430,7 @@ class SystemTray(component.Component): def setbwlimit(self, widget, header, text, core_key, ui_key, default, image): """Sets the bandwidth limit based on the user selection.""" + def set_value(value): log.debug('setbwlimit: %s', value) if value is None: diff --git a/deluge/ui/gtkui/toolbar.py b/deluge/ui/gtkui/toolbar.py index c266806c4..b478e0277 100644 --- a/deluge/ui/gtkui/toolbar.py +++ b/deluge/ui/gtkui/toolbar.py @@ -45,7 +45,7 @@ class ToolBar(component.Component): def start(self): self.main_builder.get_object('toolbutton_connectionmanager').set_visible( - not self.config['standalone'], + not self.config['standalone'] ) for widget in self.change_sensitivity: @@ -63,7 +63,9 @@ class ToolBar(component.Component): self.config['show_toolbar'] = visible - def add_toolbutton(self, callback, label=None, image=None, stock=None, tooltip=None): + def add_toolbutton( + self, callback, label=None, image=None, stock=None, tooltip=None + ): """Adds a toolbutton to the toolbar""" toolbutton = ToolButton() if stock is not None: diff --git a/deluge/ui/gtkui/torrentdetails.py b/deluge/ui/gtkui/torrentdetails.py index 95544f532..66498c16f 100644 --- a/deluge/ui/gtkui/torrentdetails.py +++ b/deluge/ui/gtkui/torrentdetails.py @@ -33,7 +33,9 @@ class Tab(object): self.weight = -1 self.main_builder = component.get('MainWindow').get_builder() - self._child_widget = self.main_builder.get_object(child_widget)if child_widget else None + self._child_widget = ( + self.main_builder.get_object(child_widget) if child_widget else None + ) self._tab_label = self.main_builder.get_object(tab_label) if tab_label else None self.tab_widgets = {} @@ -161,7 +163,10 @@ class TorrentDetails(component.Component): state = default_order # We need to rename the tab in the state for backwards compat - self.state = [(tab_name.replace('Statistics', 'Status'), visible) for tab_name, visible in state] + self.state = [ + (tab_name.replace('Statistics', 'Status'), visible) + for tab_name, visible in state + ] for tab in default_tabs.values(): self.add_tab(tab(), generate_menu=False) @@ -175,8 +180,7 @@ class TorrentDetails(component.Component): # weights is a list of visible tab names in weight order weights = sorted( - (tab.weight, name) - for name, tab in self.tabs.items() if tab.is_visible + (tab.weight, name) for name, tab in self.tabs.items() if tab.is_visible ) log.debug('weights: %s', weights) @@ -216,9 +220,7 @@ class TorrentDetails(component.Component): insert_pos = self.tab_insert_position(weight) log.debug('Trying to insert tab at %d', insert_pos) pos = self.notebook.insert_page( - tab.get_child_widget(), - tab.get_tab_label(), - insert_pos, + tab.get_child_widget(), tab.get_tab_label(), insert_pos ) log.debug('Tab inserted at %d', pos) tab.position = pos @@ -288,8 +290,10 @@ class TorrentDetails(component.Component): def show_tab(self, tab_name, generate_menu=True): log.debug( - '%s\n%s\n%s', self.tabs[tab_name].get_child_widget(), - self.tabs[tab_name].get_tab_label(), self.tabs[tab_name].position, + '%s\n%s\n%s', + self.tabs[tab_name].get_child_widget(), + self.tabs[tab_name].get_tab_label(), + self.tabs[tab_name].position, ) position = self.tab_insert_position(self.tabs[tab_name].weight) @@ -395,7 +399,10 @@ class TorrentDetails(component.Component): # Get the tab name name = None for tab in self.tabs: - if self.tabs[tab].position == page_num and self.tabs[tab].is_visible: + if ( + self.tabs[tab].position == page_num + and self.tabs[tab].is_visible + ): name = tab except IndexError: return diff --git a/deluge/ui/gtkui/torrentview.py b/deluge/ui/gtkui/torrentview.py index bc06cd95d..05f246a77 100644 --- a/deluge/ui/gtkui/torrentview.py +++ b/deluge/ui/gtkui/torrentview.py @@ -83,11 +83,11 @@ def eta_column_sort(model, iter1, iter2, data): def seed_peer_column_sort(model, iter1, iter2, data): - v1 = model[iter1][data] # num seeds/peers - v3 = model[iter2][data] # num seeds/peers + v1 = model[iter1][data] # num seeds/peers + v3 = model[iter2][data] # num seeds/peers if v1 == v3: - v2 = model[iter1][data + 1] # total seeds/peers - v4 = model[iter2][data + 1] # total seeds/peers + v2 = model[iter1][data + 1] # total seeds/peers + v4 = model[iter2][data + 1] # total seeds/peers return queue_peer_seed_sort_function(v2, v4) return queue_peer_seed_sort_function(v1, v3) @@ -234,11 +234,16 @@ class SearchBox(object): class TorrentView(ListView, component.Component): """TorrentView handles the listing of torrents.""" + def __init__(self): - component.Component.__init__(self, 'TorrentView', interval=2, depend=['SessionProxy']) + component.Component.__init__( + self, 'TorrentView', interval=2, depend=['SessionProxy'] + ) main_builder = component.get('MainWindow').get_builder() # Call the ListView constructor - ListView.__init__(self, main_builder.get_object('torrent_view'), 'torrentview.state') + ListView.__init__( + self, main_builder.get_object('torrent_view'), 'torrentview.state' + ) log.debug('TorrentView Init..') # If we have gotten the state yet @@ -257,7 +262,9 @@ class TorrentView(ListView, component.Component): self.add_text_column('torrent_id', hidden=True, unique=True) self.add_bool_column('dirty', hidden=True) self.add_func_column( - '#', funcs.cell_data_queue, [int], + '#', + funcs.cell_data_queue, + [int], status_field=['queue'], sort_func=queue_column_sort, ) @@ -269,23 +276,31 @@ class TorrentView(ListView, component.Component): default_sort=True, ) self.add_func_column( - _('Size'), funcs.cell_data_size, + _('Size'), + funcs.cell_data_size, [TYPE_UINT64], status_field=['total_wanted'], ) self.add_func_column( - _('Downloaded'), funcs.cell_data_size, + _('Downloaded'), + funcs.cell_data_size, [TYPE_UINT64], - status_field=['all_time_download'], default=False, + status_field=['all_time_download'], + default=False, ) self.add_func_column( - _('Uploaded'), funcs.cell_data_size, + _('Uploaded'), + funcs.cell_data_size, [TYPE_UINT64], - status_field=['total_uploaded'], default=False, + status_field=['total_uploaded'], + default=False, ) self.add_func_column( - _('Remaining'), funcs.cell_data_size, [TYPE_UINT64], - status_field=['total_remaining'], default=False, + _('Remaining'), + funcs.cell_data_size, + [TYPE_UINT64], + status_field=['total_remaining'], + default=False, ) self.add_progress_column( _('Progress'), @@ -295,67 +310,110 @@ class TorrentView(ListView, component.Component): sort_func=progress_sort, ) self.add_func_column( - _('Seeds'), funcs.cell_data_peer, [int, int], + _('Seeds'), + funcs.cell_data_peer, + [int, int], status_field=['num_seeds', 'total_seeds'], - sort_func=seed_peer_column_sort, default=False, + sort_func=seed_peer_column_sort, + default=False, ) self.add_func_column( - _('Peers'), funcs.cell_data_peer, [int, int], + _('Peers'), + funcs.cell_data_peer, + [int, int], status_field=['num_peers', 'total_peers'], - sort_func=seed_peer_column_sort, default=False, + sort_func=seed_peer_column_sort, + default=False, ) self.add_func_column( - _('Seeds:Peers'), funcs.cell_data_ratio_seeds_peers, [float], - status_field=['seeds_peers_ratio'], default=False, + _('Seeds:Peers'), + funcs.cell_data_ratio_seeds_peers, + [float], + status_field=['seeds_peers_ratio'], + default=False, ) self.add_func_column( - _('Down Speed'), funcs.cell_data_speed_down, [int], + _('Down Speed'), + funcs.cell_data_speed_down, + [int], status_field=['download_payload_rate'], ) self.add_func_column( - _('Up Speed'), funcs.cell_data_speed_up, [int], + _('Up Speed'), + funcs.cell_data_speed_up, + [int], status_field=['upload_payload_rate'], ) self.add_func_column( - _('Down Limit'), funcs.cell_data_speed_limit_down, [float], - status_field=['max_download_speed'], default=False, + _('Down Limit'), + funcs.cell_data_speed_limit_down, + [float], + status_field=['max_download_speed'], + default=False, ) self.add_func_column( - _('Up Limit'), funcs.cell_data_speed_limit_up, [float], - status_field=['max_upload_speed'], default=False, + _('Up Limit'), + funcs.cell_data_speed_limit_up, + [float], + status_field=['max_upload_speed'], + default=False, ) self.add_func_column( - _('ETA'), funcs.cell_data_time, [int], - status_field=['eta'], sort_func=eta_column_sort, + _('ETA'), + funcs.cell_data_time, + [int], + status_field=['eta'], + sort_func=eta_column_sort, ) self.add_func_column( - _('Ratio'), funcs.cell_data_ratio_ratio, [float], - status_field=['ratio'], default=False, + _('Ratio'), + funcs.cell_data_ratio_ratio, + [float], + status_field=['ratio'], + default=False, ) self.add_func_column( - _('Avail'), funcs.cell_data_ratio_avail, [float], - status_field=['distributed_copies'], default=False, + _('Avail'), + funcs.cell_data_ratio_avail, + [float], + status_field=['distributed_copies'], + default=False, ) self.add_func_column( - _('Added'), funcs.cell_data_date_added, [int], - status_field=['time_added'], default=False, + _('Added'), + funcs.cell_data_date_added, + [int], + status_field=['time_added'], + default=False, ) self.add_func_column( - _('Completed'), funcs.cell_data_date_completed, [int], - status_field=['completed_time'], default=False, + _('Completed'), + funcs.cell_data_date_completed, + [int], + status_field=['completed_time'], + default=False, ) self.add_func_column( - _('Complete Seen'), funcs.cell_data_date_or_never, [int], - status_field=['last_seen_complete'], default=False, + _('Complete Seen'), + funcs.cell_data_date_or_never, + [int], + status_field=['last_seen_complete'], + default=False, ) self.add_texticon_column( - _('Tracker'), function=funcs.cell_data_trackericon, - status_field=['tracker_host', 'tracker_host'], default=False, + _('Tracker'), + function=funcs.cell_data_trackericon, + status_field=['tracker_host', 'tracker_host'], + default=False, + ) + self.add_text_column( + _('Download Folder'), status_field=['download_location'], default=False ) - self.add_text_column(_('Download Folder'), status_field=['download_location'], default=False) self.add_text_column(_('Owner'), status_field=['owner'], default=False) self.add_bool_column( - _('Shared'), status_field=['shared'], default=False, + _('Shared'), + status_field=['shared'], + default=False, tooltip=_('Torrent is shared between other Deluge users or not.'), ) self.restore_columns_order_from_state() @@ -395,15 +453,23 @@ class TorrentView(ListView, component.Component): continue status_fields.extend(listview_column.status_field) component.get('SessionProxy').get_torrents_status( - {}, status_fields, + {}, status_fields ).addCallback(self._on_session_state) - client.register_event_handler('TorrentStateChangedEvent', self.on_torrentstatechanged_event) + client.register_event_handler( + 'TorrentStateChangedEvent', self.on_torrentstatechanged_event + ) client.register_event_handler('TorrentAddedEvent', self.on_torrentadded_event) - client.register_event_handler('TorrentRemovedEvent', self.on_torrentremoved_event) + client.register_event_handler( + 'TorrentRemovedEvent', self.on_torrentremoved_event + ) client.register_event_handler('SessionPausedEvent', self.on_sessionpaused_event) - client.register_event_handler('SessionResumedEvent', self.on_sessionresumed_event) - client.register_event_handler('TorrentQueueChangedEvent', self.on_torrentqueuechanged_event) + client.register_event_handler( + 'SessionResumedEvent', self.on_sessionresumed_event + ) + client.register_event_handler( + 'TorrentQueueChangedEvent', self.on_torrentqueuechanged_event + ) def _on_session_state(self, state): self.add_rows(state) @@ -416,12 +482,22 @@ class TorrentView(ListView, component.Component): def stop(self): """Stops the torrentview""" - client.deregister_event_handler('TorrentStateChangedEvent', self.on_torrentstatechanged_event) + client.deregister_event_handler( + 'TorrentStateChangedEvent', self.on_torrentstatechanged_event + ) client.deregister_event_handler('TorrentAddedEvent', self.on_torrentadded_event) - client.deregister_event_handler('TorrentRemovedEvent', self.on_torrentremoved_event) - client.deregister_event_handler('SessionPausedEvent', self.on_sessionpaused_event) - client.deregister_event_handler('SessionResumedEvent', self.on_sessionresumed_event) - client.deregister_event_handler('TorrentQueueChangedEvent', self.on_torrentqueuechanged_event) + client.deregister_event_handler( + 'TorrentRemovedEvent', self.on_torrentremoved_event + ) + client.deregister_event_handler( + 'SessionPausedEvent', self.on_sessionpaused_event + ) + client.deregister_event_handler( + 'SessionResumedEvent', self.on_sessionresumed_event + ) + client.deregister_event_handler( + 'TorrentQueueChangedEvent', self.on_torrentqueuechanged_event + ) if self.treeview.get_selection(): self.treeview.get_selection().unselect_all() @@ -501,9 +577,11 @@ class TorrentView(ListView, component.Component): # Request the statuses for all these torrent_ids, this is async so we # will deal with the return in a signal callback. - d = component.get('SessionProxy').get_torrents_status( - self.filter, status_keys, - ).addCallback(self._on_get_torrents_status) + d = ( + component.get('SessionProxy') + .get_torrents_status(self.filter, status_keys) + .addCallback(self._on_get_torrents_status) + ) if select_row: d.addCallback(self.select_first_row) @@ -527,7 +605,10 @@ class TorrentView(ListView, component.Component): """ if self.got_state: - if self.search_box.search_pending is not None and self.search_box.search_pending.active(): + if ( + self.search_box.search_pending is not None + and self.search_box.search_pending.active() + ): # An update request is scheduled, let's wait for that one return # Send a status request @@ -616,7 +697,15 @@ class TorrentView(ListView, component.Component): for torrent_id in torrent_ids: # Insert a new row to the liststore row = self.liststore.append() - self.liststore.set(row, torrent_id_column, torrent_id, dirty_column, True, filter_column, True) + self.liststore.set( + row, + torrent_id_column, + torrent_id, + dirty_column, + True, + filter_column, + True, + ) def remove_row(self, torrent_id): """Removes a row with torrent_id""" @@ -629,7 +718,10 @@ class TorrentView(ListView, component.Component): def mark_dirty(self, torrent_id=None): for row in self.liststore: - if not torrent_id or row[self.columns['torrent_id'].column_indices[0]] == torrent_id: + if ( + not torrent_id + or row[self.columns['torrent_id'].column_indices[0]] == torrent_id + ): # log.debug('marking %s dirty', torrent_id) row[self.columns['dirty'].column_indices[0]] = True if torrent_id: @@ -660,11 +752,19 @@ class TorrentView(ListView, component.Component): log.debug('Unable to get iter from path: %s', ex) continue - child_row = self.treeview.get_model().convert_iter_to_child_iter(None, row) - child_row = self.treeview.get_model().get_model().convert_iter_to_child_iter(child_row) + child_row = self.treeview.get_model().convert_iter_to_child_iter( + None, row + ) + child_row = ( + self.treeview.get_model() + .get_model() + .convert_iter_to_child_iter(child_row) + ) if self.liststore.iter_is_valid(child_row): try: - value = self.liststore.get_value(child_row, self.columns['torrent_id'].column_indices[0]) + value = self.liststore.get_value( + child_row, self.columns['torrent_id'].column_indices[0] + ) except Exception as ex: log.debug('Unable to get value from row: %s', ex) else: @@ -699,8 +799,12 @@ class TorrentView(ListView, component.Component): row = self.model_filter.get_iter(path[0]) if self.get_selected_torrents(): - if (self.model_filter.get_value(row, self.columns['torrent_id'].column_indices[0]) - not in self.get_selected_torrents()): + if ( + self.model_filter.get_value( + row, self.columns['torrent_id'].column_indices[0] + ) + not in self.get_selected_torrents() + ): self.treeview.get_selection().unselect_all() self.treeview.get_selection().select_iter(row) else: @@ -718,7 +822,9 @@ class TorrentView(ListView, component.Component): def on_drag_drop(self, widget, drag_context, x, y, timestamp): widget.stop_emission('drag-drop') - def on_drag_data_received(self, widget, drag_context, x, y, selection_data, info, timestamp): + def on_drag_data_received( + self, widget, drag_context, x, y, selection_data, info, timestamp + ): widget.stop_emission('drag_data_received') def on_columns_changed_event(self, treeview): @@ -750,7 +856,10 @@ class TorrentView(ListView, component.Component): if self.filter.get('state', None) is not None: # We have a filter set, let's see if theres anything to hide # and remove from status - if torrent_id in self.status and self.status[torrent_id]['state'] != state: + if ( + torrent_id in self.status + and self.status[torrent_id]['state'] != state + ): row[self.columns['filter'].column_indices[0]] = False del self.status[torrent_id] diff --git a/deluge/ui/gtkui/torrentview_data_funcs.py b/deluge/ui/gtkui/torrentview_data_funcs.py index 4e3260c0d..db57355be 100644 --- a/deluge/ui/gtkui/torrentview_data_funcs.py +++ b/deluge/ui/gtkui/torrentview_data_funcs.py @@ -14,8 +14,16 @@ from functools import partial import deluge.common as common import deluge.component as component -from deluge.ui.gtkui.common import (create_blank_pixbuf, get_pixbuf_at_size, icon_alert, icon_checking, - icon_downloading, icon_inactive, icon_queued, icon_seeding) +from deluge.ui.gtkui.common import ( + create_blank_pixbuf, + get_pixbuf_at_size, + icon_alert, + icon_checking, + icon_downloading, + icon_inactive, + icon_queued, + icon_seeding, +) # Holds the info for which status icon to display based on TORRENT_STATE ICON_STATE = { @@ -155,6 +163,7 @@ def cell_data_speed(cell, model, row, data, cache_key): except AttributeError: print('AttributeError') import traceback + traceback.print_exc() if func_last_value[cache_key] == speed: return @@ -162,7 +171,9 @@ def cell_data_speed(cell, model, row, data, cache_key): if speed > 0: speed_str = common.fspeed(speed, shortform=True) - cell.set_property('markup', '{0} <small>{1}</small>'.format(*tuple(speed_str.split()))) + cell.set_property( + 'markup', '{0} <small>{1}</small>'.format(*tuple(speed_str.split())) + ) else: cell.set_property('text', '') @@ -187,7 +198,9 @@ def cell_data_speed_limit(cell, model, row, data, cache_key): if speed > 0: speed_str = common.fspeed(speed * 1024, shortform=True) - cell.set_property('markup', '{0} <small>{1}</small>'.format(*tuple(speed_str.split()))) + cell.set_property( + 'markup', '{0} <small>{1}</small>'.format(*tuple(speed_str.split())) + ) else: cell.set_property('text', '') @@ -237,7 +250,9 @@ def cell_data_ratio(cell, model, row, data, cache_key): if func_last_value[cache_key] == ratio: return func_last_value[cache_key] = ratio - cell.set_property('text', '∞' if ratio < 0 else ('%.1f' % ratio).rstrip('0').rstrip('.')) + cell.set_property( + 'text', '∞' if ratio < 0 else ('%.1f' % ratio).rstrip('0').rstrip('.') + ) def cell_data_ratio_seeds_peers(column, cell, model, row, data): diff --git a/deluge/ui/gtkui/trackers_tab.py b/deluge/ui/gtkui/trackers_tab.py index 48677b536..55999761b 100644 --- a/deluge/ui/gtkui/trackers_tab.py +++ b/deluge/ui/gtkui/trackers_tab.py @@ -21,7 +21,9 @@ log = logging.getLogger(__name__) class TrackersTab(Tab): def __init__(self): - super(TrackersTab, self).__init__('Trackers', 'trackers_tab', 'trackers_tab_label') + super(TrackersTab, self).__init__( + 'Trackers', 'trackers_tab', 'trackers_tab_label' + ) self.add_tab_widget('summary_next_announce', ftime, ('next_announce',)) self.add_tab_widget('summary_tracker', None, ('tracker_host',)) @@ -43,7 +45,9 @@ class TrackersTab(Tab): return session = component.get('SessionProxy') - session.get_torrent_status(selected, self.status_keys).addCallback(self._on_get_torrent_status) + session.get_torrent_status(selected, self.status_keys).addCallback( + self._on_get_torrent_status + ) def _on_get_torrent_status(self, status): # Check to see if we got valid data from the core @@ -64,5 +68,6 @@ class TrackersTab(Tab): torrent_id = component.get('TorrentView').get_selected_torrent() if torrent_id: from deluge.ui.gtkui.edittrackersdialog import EditTrackersDialog + dialog = EditTrackersDialog(torrent_id, component.get('MainWindow').window) dialog.run() diff --git a/deluge/ui/hostlist.py b/deluge/ui/hostlist.py index 10d7063b5..ee4c7df7c 100644 --- a/deluge/ui/hostlist.py +++ b/deluge/ui/hostlist.py @@ -89,9 +89,15 @@ def migrate_config_2_to_3(config): class HostList(object): """This class contains methods for adding, removing and looking up hosts in hostlist.conf.""" + def __init__(self): migrate_hostlist('hostlist.conf.1.2', 'hostlist.conf') - self.config = Config('hostlist.conf', default_hostlist(), config_dir=get_config_dir(), file_version=3) + self.config = Config( + 'hostlist.conf', + default_hostlist(), + config_dir=get_config_dir(), + file_version=3, + ) self.config.run_converter((1, 2), 3, migrate_config_2_to_3) self.config.save() @@ -109,7 +115,11 @@ class HostList(object): """ for host_entry in self.config['hosts']: - if (hostname, port, username) == (host_entry[1], host_entry[2], host_entry[3]): + if (hostname, port, username) == ( + host_entry[1], + host_entry[2], + host_entry[3], + ): if skip_host_id is not None and skip_host_id == host_entry[0]: continue raise ValueError('Host details already in hostlist') @@ -126,8 +136,10 @@ class HostList(object): Returns: str: The new host id. """ - if (not password and not username or username == 'localclient') and hostname in LOCALHOST: - username, password = get_localhost_auth() + if ( + not password and not username or username == 'localclient' + ) and hostname in LOCALHOST: + username, password = get_localhost_auth() validate_host_info(hostname, port) self.check_info_exists(hostname, port, username) @@ -175,6 +187,7 @@ class HostList(object): def on_connect(result, c, host_id): """Successfully connected to a daemon""" + def on_info(info, c): c.disconnect() return host_id, 'Online', info @@ -202,7 +215,11 @@ class HostList(object): log.error('Error resolving host %s to ip: %s', host, ex.args[1]) return status_offline - host_conn_info = (ip, port, 'localclient' if not user and host in LOCALHOST else user) + host_conn_info = ( + ip, + port, + 'localclient' if not user and host in LOCALHOST else user, + ) if client.connected() and host_conn_info == client.connection_info(): # Currently connected to host_id daemon. def on_info(info, host_id): @@ -232,7 +249,9 @@ class HostList(object): validate_host_info(hostname, port) self.check_info_exists(hostname, port, username, skip_host_id=host_id) - if (not password and not username or username == 'localclient') and hostname in LOCALHOST: + if ( + not password and not username or username == 'localclient' + ) and hostname in LOCALHOST: username, password = get_localhost_auth() for idx, host_entry in enumerate(self.config['hosts']): diff --git a/deluge/ui/sessionproxy.py b/deluge/ui/sessionproxy.py index 8c363b478..5af8e79cd 100644 --- a/deluge/ui/sessionproxy.py +++ b/deluge/ui/sessionproxy.py @@ -28,6 +28,7 @@ class SessionProxy(component.Component): and will try to satisfy client requests from the cache. """ + def __init__(self): log.debug('SessionProxy init..') component.Component.__init__(self, 'SessionProxy', interval=5) @@ -43,7 +44,9 @@ class SessionProxy(component.Component): self.cache_times = {} def start(self): - client.register_event_handler('TorrentStateChangedEvent', self.on_torrent_state_changed) + client.register_event_handler( + 'TorrentStateChangedEvent', self.on_torrent_state_changed + ) client.register_event_handler('TorrentRemovedEvent', self.on_torrent_removed) client.register_event_handler('TorrentAddedEvent', self.on_torrent_added) @@ -54,10 +57,13 @@ class SessionProxy(component.Component): self.torrents.setdefault(torrent_id, [time(), {}]) self.cache_times.setdefault(torrent_id, {}) return torrent_ids + return client.core.get_session_state().addCallback(on_get_session_state) def stop(self): - client.deregister_event_handler('TorrentStateChangedEvent', self.on_torrent_state_changed) + client.deregister_event_handler( + 'TorrentStateChangedEvent', self.on_torrent_state_changed + ) client.deregister_event_handler('TorrentRemovedEvent', self.on_torrent_removed) client.deregister_event_handler('TorrentAddedEvent', self.on_torrent_added) self.torrents = {} @@ -77,7 +83,9 @@ class SessionProxy(component.Component): """ sd = {} keys = set(keys) - keys_len = -1 # The number of keys for the current cache (not the len of keys_diff_cached) + keys_len = ( + -1 + ) # The number of keys for the current cache (not the len of keys_diff_cached) keys_diff_cached = [] for torrent_id in torrent_ids: @@ -128,12 +136,13 @@ class SessionProxy(component.Component): keys = list(self.torrents[torrent_id][1]) for key in keys: - if time() - self.cache_times[torrent_id].get(key, 0.0) > self.cache_time: + if ( + time() - self.cache_times[torrent_id].get(key, 0.0) + > self.cache_time + ): keys_to_get.append(key) if not keys_to_get: - return succeed( - self.create_status_dict([torrent_id], keys)[torrent_id], - ) + return succeed(self.create_status_dict([torrent_id], keys)[torrent_id]) else: d = client.core.get_torrent_status(torrent_id, keys_to_get, True) @@ -144,6 +153,7 @@ class SessionProxy(component.Component): for key in keys_to_get: self.cache_times[torrent_id][key] = t return self.create_status_dict([torrent_id], keys)[torrent_id] + return d.addCallback(on_status, torrent_id) else: d = client.core.get_torrent_status(torrent_id, keys, True) @@ -157,6 +167,7 @@ class SessionProxy(component.Component): self.cache_times[torrent_id][key] = t return result + return d.addCallback(on_status) def get_torrents_status(self, filter_dict, keys): @@ -206,11 +217,15 @@ class SessionProxy(component.Component): else: # We need to check if a key is expired for key in keys: - if t - self.cache_times[torrent_id].get(key, 0.0) > self.cache_time: + if ( + t - self.cache_times[torrent_id].get(key, 0.0) + > self.cache_time + ): to_fetch.append(torrent_id) break return to_fetch + # ----------------------------------------------------------------------- if not filter_dict: @@ -254,6 +269,7 @@ class SessionProxy(component.Component): t = time() for key in status: self.cache_times[torrent_id][key] = t + client.core.get_torrent_status(torrent_id, []).addCallback(on_status) def on_torrent_removed(self, torrent_id): diff --git a/deluge/ui/tracker_icons.py b/deluge/ui/tracker_icons.py index 492d96d23..48c742f77 100644 --- a/deluge/ui/tracker_icons.py +++ b/deluge/ui/tracker_icons.py @@ -36,6 +36,7 @@ except ImportError: PIL_INSTALLED = False else: import deluge.ui.Win32IconImagePlugin # NOQA pylint: disable=unused-import, ungrouped-imports + PIL_INSTALLED = True log = logging.getLogger(__name__) @@ -45,6 +46,7 @@ class TrackerIcon(object): """ Represents a tracker's icon """ + def __init__(self, filename): """ Initialises a new TrackerIcon object @@ -66,9 +68,11 @@ class TrackerIcon(object): :returns: whether or not they're equal :rtype: boolean """ - return (os.path.samefile(self.filename, other.filename) or - self.get_mimetype() == other.get_mimetype() and - self.get_data() == other.get_data()) + return ( + os.path.samefile(self.filename, other.filename) + or self.get_mimetype() == other.get_mimetype() + and self.get_data() == other.get_data() + ) def get_mimetype(self): """ @@ -122,6 +126,7 @@ class TrackerIcons(Component): """ A TrackerIcon factory class """ + def __init__(self, icon_dir=None, no_icon=None): """ Initialises a new TrackerIcons object @@ -209,18 +214,20 @@ class TrackerIcons(Component): # Start callback chain d = self.download_page(host) d.addCallbacks( - self.on_download_page_complete, self.on_download_page_fail, + self.on_download_page_complete, + self.on_download_page_fail, errbackArgs=(host,), ) d.addCallback(self.parse_html_page) d.addCallbacks( - self.on_parse_complete, self.on_parse_fail, - callbackArgs=(host,), + self.on_parse_complete, self.on_parse_fail, callbackArgs=(host,) ) d.addCallback(self.download_icon, host) d.addCallbacks( - self.on_download_icon_complete, self.on_download_icon_fail, - callbackArgs=(host,), errbackArgs=(host,), + self.on_download_icon_complete, + self.on_download_icon_fail, + callbackArgs=(host,), + errbackArgs=(host,), ) if PIL_INSTALLED: d.addCallback(self.resize_icon) @@ -279,7 +286,8 @@ class TrackerIcons(Component): self.redirects[host] = url_to_host(location) d = self.download_page(host, url=location) d.addCallbacks( - self.on_download_page_complete, self.on_download_page_fail, + self.on_download_page_complete, + self.on_download_page_fail, errbackArgs=(host,), ) @@ -354,7 +362,8 @@ class TrackerIcons(Component): raise NoIconsError('empty icons list') (url, mimetype) = icons.pop(0) d = download_file( - url, os.path.join(self.dir, host_to_icon_name(host, mimetype)), + url, + os.path.join(self.dir, host_to_icon_name(host, mimetype)), force_filename=True, ) d.addCallback(self.check_icon_is_valid) @@ -421,25 +430,36 @@ class TrackerIcons(Component): if f.check(PageRedirect): # Handle redirect errors location = urljoin(self.host_to_url(host), error_msg.split(' to ')[1]) - d = self.download_icon([(location, extension_to_mimetype(location.rpartition('.')[2]))] + icons, host) + d = self.download_icon( + [(location, extension_to_mimetype(location.rpartition('.')[2]))] + + icons, + host, + ) if not icons: d.addCallbacks( - self.on_download_icon_complete, self.on_download_icon_fail, - callbackArgs=(host,), errbackArgs=(host,), + self.on_download_icon_complete, + self.on_download_icon_fail, + callbackArgs=(host,), + errbackArgs=(host,), ) elif f.check(NoResource, ForbiddenResource) and icons: d = self.download_icon(icons, host) elif f.check(NoIconsError): # No icons, try favicon.ico as an act of desperation d = self.download_icon( - [( - urljoin(self.host_to_url(host), 'favicon.ico'), - extension_to_mimetype('ico'), - )], host, + [ + ( + urljoin(self.host_to_url(host), 'favicon.ico'), + extension_to_mimetype('ico'), + ) + ], + host, ) d.addCallbacks( - self.on_download_icon_complete, self.on_download_icon_fail, - callbackArgs=(host,), errbackArgs=(host,), + self.on_download_icon_complete, + self.on_download_icon_fail, + callbackArgs=(host,), + errbackArgs=(host,), ) else: # No icons :( @@ -501,6 +521,7 @@ class TrackerIcons(Component): host = self.redirects[host] return 'http://%s/' % host + # ------- HELPER CLASSES ------ @@ -508,13 +529,18 @@ class FaviconParser(HTMLParser): """ A HTMLParser which extracts favicons from a HTML page """ + def __init__(self): self.icons = [] self.left_head = False HTMLParser.__init__(self) def handle_starttag(self, tag, attrs): - if tag == 'link' and ('rel', 'icon') in attrs or ('rel', 'shortcut icon') in attrs: + if ( + tag == 'link' + and ('rel', 'icon') in attrs + or ('rel', 'shortcut icon') in attrs + ): href = None icon_type = None for attr, value in attrs: @@ -548,6 +574,7 @@ class FaviconParser(HTMLParser): # ------ HELPER FUNCTIONS ------ + def url_to_host(url): """ Given a URL, returns the host it belongs to @@ -626,6 +653,7 @@ def extension_to_mimetype(extension): """ return MIME_MAP[extension.lower()] + # ------ EXCEPTIONS ------ diff --git a/deluge/ui/translations_util.py b/deluge/ui/translations_util.py index cdffc3230..cd0aac88d 100644 --- a/deluge/ui/translations_util.py +++ b/deluge/ui/translations_util.py @@ -20,15 +20,20 @@ from six.moves import builtins import deluge.common log = logging.getLogger(__name__) -log.addHandler(logging.NullHandler()) # Silence: No handlers could be found for logger "deluge.util.lang" +log.addHandler( + logging.NullHandler() +) # Silence: No handlers could be found for logger "deluge.util.lang" def set_dummy_trans(warn_msg=None): - def _func(*txt): if warn_msg: - log.warning('"%s" has been marked for translation, but translation is unavailable.', txt[0]) + log.warning( + '"%s" has been marked for translation, but translation is unavailable.', + txt[0], + ) return txt[0] + builtins.__dict__['_'] = _func builtins.__dict__['ngettext'] = builtins.__dict__['_n'] = _func @@ -77,7 +82,9 @@ def set_language(lang): translations_path = get_translations_path() try: - ro = gettext.translation('deluge', localedir=translations_path, languages=[lang]) + ro = gettext.translation( + 'deluge', localedir=translations_path, languages=[lang] + ) ro.install() except IOError as ex: log.warning('IOError when loading translations: %s', ex) @@ -95,13 +102,16 @@ def setup_translations(setup_gettext=True, setup_pygtk=False): if deluge.common.windows_check(): import ctypes + try: libintl = ctypes.cdll.intl except WindowsError: # Fallback to named dll. libintl = ctypes.cdll.LoadLibrary('libintl-8.dll') - libintl.bindtextdomain(domain, translations_path.encode(sys.getfilesystemencoding())) + libintl.bindtextdomain( + domain, translations_path.encode(sys.getfilesystemencoding()) + ) libintl.textdomain(domain) libintl.bind_textdomain_codeset(domain, 'UTF-8') libintl.gettext.restype = ctypes.c_char_p @@ -109,6 +119,7 @@ def setup_translations(setup_gettext=True, setup_pygtk=False): # Use glade for plugins that still uses it import gtk import gtk.glade + gtk.glade.bindtextdomain(domain, translations_path) gtk.glade.textdomain(domain) except Exception as ex: diff --git a/deluge/ui/ui.py b/deluge/ui/ui.py index c1bd16483..6b149f72d 100644 --- a/deluge/ui/ui.py +++ b/deluge/ui/ui.py @@ -22,6 +22,7 @@ log = logging.getLogger(__name__) try: from setproctitle import setproctitle except ImportError: + def setproctitle(title): return @@ -31,6 +32,7 @@ class UI(object): Base class for UI implementations. """ + cmd_description = """Override with command description""" def __init__(self, name, **kwargs): diff --git a/deluge/ui/ui_entry.py b/deluge/ui/ui_entry.py index 8ce79e14d..b20be980b 100644 --- a/deluge/ui/ui_entry.py +++ b/deluge/ui/ui_entry.py @@ -26,9 +26,7 @@ import deluge.configmanager from deluge.ui.baseargparser import BaseArgParser from deluge.ui.translations_util import setup_translations -DEFAULT_PREFS = { - 'default_ui': 'gtk', -} +DEFAULT_PREFS = {'default_ui': 'gtk'} AMBIGUOUS_CMD_ARGS = ('-h', '--help', '-v', '-V', '--version') @@ -52,7 +50,10 @@ def start_ui(): """Function to enable reuse of UI Options group""" group = _parser.add_argument_group(_('UI Options')) group.add_argument( - '-s', '--set-default-ui', dest='default_ui', choices=ui_titles, + '-s', + '--set-default-ui', + dest='default_ui', + choices=ui_titles, help=_('Set the default UI to be run, when no UI is specified'), ) return _parser @@ -84,12 +85,15 @@ def start_ui(): # Create subparser for each registered UI. Empty title is used to remove unwanted positional text. subparsers = parser.add_subparsers( - dest='selected_ui', metavar='{%s} [UI args]' % ','.join(ui_titles), title=None, + dest='selected_ui', + metavar='{%s} [UI args]' % ','.join(ui_titles), + title=None, help=_('Alternative UI to launch, with optional ui args \n (default UI: *)'), ) for ui in ui_titles: parser_ui = subparsers.add_parser( - ui, common_help=False, + ui, + common_help=False, help=getattr(ui_entrypoints[ui], 'cmd_description', ''), ) parser_ui.add_argument('ui_args', nargs=argparse.REMAINDER) @@ -110,21 +114,26 @@ def start_ui(): sys.argv.remove(selected_ui) try: - ui = ui_entrypoints[selected_ui](prog='%s %s' % (os.path.basename(sys.argv[0]), selected_ui), ui_args=ui_args) + ui = ui_entrypoints[selected_ui]( + prog='%s %s' % (os.path.basename(sys.argv[0]), selected_ui), ui_args=ui_args + ) except KeyError as ex: log.error( 'Unable to find chosen UI: "%s". Please choose a different UI ' - 'or use "--set-default-ui" to change default UI.', selected_ui, + 'or use "--set-default-ui" to change default UI.', + selected_ui, ) except ImportError as ex: import traceback + error_type, error_value, tb = sys.exc_info() stack = traceback.extract_tb(tb) last_frame = stack[-1] if last_frame[0] == __file__: log.error( 'Unable to find chosen UI: "%s". Please choose a different UI ' - 'or use "--set-default-ui" to change default UI.', selected_ui, + 'or use "--set-default-ui" to change default UI.', + selected_ui, ) else: log.exception(ex) diff --git a/deluge/ui/web/auth.py b/deluge/ui/web/auth.py index 4ee526cbe..aefd879e6 100644 --- a/deluge/ui/web/auth.py +++ b/deluge/ui/web/auth.py @@ -103,8 +103,10 @@ class Auth(JSONComponent): checksum = str(make_checksum(session_id)) request.addCookie( - b'_session_id', session_id + checksum, - path=request.base + 'json', expires=expires_str, + b'_session_id', + session_id + checksum, + path=request.base + 'json', + expires=expires_str, ) log.debug('Creating session for %s', login) @@ -144,6 +146,7 @@ class Auth(JSONComponent): # We are using the 1.1 webui auth method log.debug('Received a password via the 1.1 auth method') from base64 import b64decode + m = hashlib.md5() m.update(b64decode(config['old_pwd_salt'])) m.update(password.encode('utf8')) @@ -200,8 +203,10 @@ class Auth(JSONComponent): _session_id = request.getCookie('_session_id') request.addCookie( - b'_session_id', _session_id, - path=request.base + b'json', expires=expires_str, + b'_session_id', + _session_id, + path=request.base + b'json', + expires=expires_str, ) if method: diff --git a/deluge/ui/web/common.py b/deluge/ui/web/common.py index ee4f3cde5..0935e2f57 100644 --- a/deluge/ui/web/common.py +++ b/deluge/ui/web/common.py @@ -33,7 +33,9 @@ def escape(text): def compress(contents, request): request.setHeader(b'content-encoding', b'gzip') - compress_zlib = zlib.compressobj(6, zlib.DEFLATED, zlib.MAX_WBITS + 16, zlib.DEF_MEM_LEVEL, 0) + compress_zlib = zlib.compressobj( + 6, zlib.DEFLATED, zlib.MAX_WBITS + 16, zlib.DEF_MEM_LEVEL, 0 + ) contents = compress_zlib.compress(contents) contents += compress_zlib.flush() return contents @@ -49,25 +51,19 @@ try: A template that adds some built-ins to the rendering """ - builtins = { - '_': _, - 'escape': escape, - 'version': common.get_version(), - } + builtins = {'_': _, 'escape': escape, 'version': common.get_version()} def render(self, *args, **data): data.update(self.builtins) rendered = MakoTemplate.render_unicode(self, *args, **data) return rendered.encode('utf-8') + + except ImportError: import warnings - warnings.warn( - 'The Mako library is required to run deluge.ui.web', - RuntimeWarning, - ) + + warnings.warn('The Mako library is required to run deluge.ui.web', RuntimeWarning) class Template(object): def __new__(cls, *args, **kwargs): - raise RuntimeError( - 'The Mako library is required to run deluge.ui.web', - ) + raise RuntimeError('The Mako library is required to run deluge.ui.web') diff --git a/deluge/ui/web/json_api.py b/deluge/ui/web/json_api.py index 28ce22e0c..4ae84bb4c 100644 --- a/deluge/ui/web/json_api.py +++ b/deluge/ui/web/json_api.py @@ -55,6 +55,7 @@ def export(auth_level=AUTH_LEVEL_DEFAULT): :type auth_level: int """ + def wrap(func, *args, **kwargs): func._json_export = True func._json_auth_level = auth_level @@ -95,9 +96,11 @@ class JSON(resource.Resource, component.Component): Returns: t.i.d.Deferred: A deferred returning the available remote methods """ + def on_get_methods(methods): self._remote_methods = methods return methods + return client.daemon.get_method_list().addCallback(on_get_methods) def _exec_local(self, method, params, request): @@ -144,7 +147,9 @@ class JSON(resource.Resource, component.Component): request_id = request_data['id'] except KeyError as ex: message = 'Invalid JSON request, missing param %s in %s' % ( - ex, request_data) + ex, + request_data, + ) raise JSONException(message) result = None @@ -178,7 +183,10 @@ class JSON(resource.Resource, component.Component): Handles any failures that occurred while making an rpc call. """ log.error(reason) - response['error'] = {'message': '%s: %s' % (reason.__class__.__name__, str(reason)), 'code': 4} + response['error'] = { + 'message': '%s: %s' % (reason.__class__.__name__, str(reason)), + 'code': 4, + } return self._send_response(request, response) def _on_json_request(self, request): @@ -187,7 +195,9 @@ class JSON(resource.Resource, component.Component): _handle_request method for further processing. """ if request.getHeader(b'content-type') != b'application/json': - message = 'Invalid JSON request content-type: %s' % request.getHeader('content-type') + message = 'Invalid JSON request content-type: %s' % request.getHeader( + 'content-type' + ) raise JSONException(message) log.debug('json-request: %s', request.json) @@ -208,7 +218,8 @@ class JSON(resource.Resource, component.Component): """ log.error(reason) response = { - 'result': None, 'id': None, + 'result': None, + 'id': None, 'error': { 'code': 5, 'message': '%s: %s' % (reason.__class__.__name__, str(reason)), @@ -355,13 +366,8 @@ class WebApi(JSONComponent): the web interface. The complete web json interface also exposes all the methods available from the core RPC. """ - XSS_VULN_KEYS = [ - 'name', - 'message', - 'comment', - 'tracker_status', - 'peers', - ] + + XSS_VULN_KEYS = ['name', 'message', 'comment', 'tracker_status', 'peers'] def __init__(self): super(WebApi, self).__init__('Web', depend=['SessionProxy']) @@ -374,8 +380,12 @@ class WebApi(JSONComponent): self.sessionproxy = SessionProxy() def disable(self): - client.deregister_event_handler('PluginEnabledEvent', self._json.get_remote_methods) - client.deregister_event_handler('PluginDisabledEvent', self._json.get_remote_methods) + client.deregister_event_handler( + 'PluginEnabledEvent', self._json.get_remote_methods + ) + client.deregister_event_handler( + 'PluginDisabledEvent', self._json.get_remote_methods + ) if client.is_standalone(): component.get('Web.PluginManager').stop() @@ -384,8 +394,12 @@ class WebApi(JSONComponent): client.set_disconnect_callback(None) def enable(self): - client.register_event_handler('PluginEnabledEvent', self._json.get_remote_methods) - client.register_event_handler('PluginDisabledEvent', self._json.get_remote_methods) + client.register_event_handler( + 'PluginEnabledEvent', self._json.get_remote_methods + ) + client.register_event_handler( + 'PluginDisabledEvent', self._json.get_remote_methods + ) if client.is_standalone(): component.get('Web.PluginManager').start() @@ -452,6 +466,7 @@ class WebApi(JSONComponent): def on_disconnect(reason): return str(reason) + d.addCallback(on_disconnect) return d @@ -487,10 +502,16 @@ class WebApi(JSONComponent): ui_info['stats']['num_connections'] = stats['num_peers'] ui_info['stats']['upload_rate'] = stats['payload_upload_rate'] ui_info['stats']['download_rate'] = stats['payload_download_rate'] - ui_info['stats']['download_protocol_rate'] = stats['download_rate'] - stats['payload_download_rate'] - ui_info['stats']['upload_protocol_rate'] = stats['upload_rate'] - stats['payload_upload_rate'] + ui_info['stats']['download_protocol_rate'] = ( + stats['download_rate'] - stats['payload_download_rate'] + ) + ui_info['stats']['upload_protocol_rate'] = ( + stats['upload_rate'] - stats['payload_upload_rate'] + ) ui_info['stats']['dht_nodes'] = stats['dht_nodes'] - ui_info['stats']['has_incoming_connections'] = stats['has_incoming_connections'] + ui_info['stats']['has_incoming_connections'] = stats[ + 'has_incoming_connections' + ] def got_filters(filters): ui_info['filters'] = filters @@ -513,15 +534,17 @@ class WebApi(JSONComponent): d2 = client.core.get_filter_tree() d2.addCallback(got_filters) - d3 = client.core.get_session_status([ - 'num_peers', - 'payload_download_rate', - 'payload_upload_rate', - 'download_rate', - 'upload_rate', - 'dht_nodes', - 'has_incoming_connections', - ]) + d3 = client.core.get_session_status( + [ + 'num_peers', + 'payload_download_rate', + 'payload_upload_rate', + 'download_rate', + 'upload_rate', + 'dht_nodes', + 'has_incoming_connections', + ] + ) d3.addCallback(got_stats) d4 = client.core.get_free_space(self.core_config.get('download_location')) @@ -697,7 +720,8 @@ class WebApi(JSONComponent): if is_magnet(torrent['path']): log.info( 'Adding torrent from magnet uri `%s` with options `%r`', - torrent['path'], torrent['options'], + torrent['path'], + torrent['options'], ) d = client.core.add_torrent_magnet(torrent['path'], torrent['options']) deferreds.append(d) @@ -707,9 +731,12 @@ class WebApi(JSONComponent): fdump = b64encode(_file.read()) log.info( 'Adding torrent from file `%s` with options `%r`', - filename, torrent['options'], + filename, + torrent['options'], + ) + d = client.core.add_torrent_file_async( + filename, fdump, torrent['options'] ) - d = client.core.add_torrent_file_async(filename, fdump, torrent['options']) deferreds.append(d) return DeferredList(deferreds, consumeErrors=False) @@ -742,6 +769,7 @@ class WebApi(JSONComponent): :type host_id: string """ + def response(result): return result @@ -820,12 +848,13 @@ class WebApi(JSONComponent): return main_deferred try: + def on_connect(connected, c): if not connected: main_deferred.callback((False, _('Daemon not running'))) return c.daemon.shutdown() - main_deferred.callback((True, )) + main_deferred.callback((True,)) def on_connect_failed(reason): main_deferred.callback((False, reason)) @@ -951,6 +980,7 @@ class WebUtils(JSONComponent): """ Utility functions for the webui that do not fit in the WebApi. """ + def __init__(self): super(WebUtils, self).__init__('WebUtils') diff --git a/deluge/ui/web/pluginmanager.py b/deluge/ui/web/pluginmanager.py index fe5f0bf25..24f20ce94 100644 --- a/deluge/ui/web/pluginmanager.py +++ b/deluge/ui/web/pluginmanager.py @@ -43,8 +43,12 @@ class PluginManager(PluginManagerBase, component.Component): self.config = ConfigManager('web.conf') PluginManagerBase.__init__(self, 'web.conf', 'deluge.plugin.web') - client.register_event_handler('PluginEnabledEvent', self._on_plugin_enabled_event) - client.register_event_handler('PluginDisabledEvent', self._on_plugin_disabled_event) + client.register_event_handler( + 'PluginEnabledEvent', self._on_plugin_enabled_event + ) + client.register_event_handler( + 'PluginDisabledEvent', self._on_plugin_disabled_event + ) def _on_get_enabled_plugins(self, plugins): for plugin in plugins: @@ -61,18 +65,26 @@ class PluginManager(PluginManagerBase, component.Component): try: plugin = component.get('WebPlugin.' + name) except KeyError: - log.debug('%s plugin contains no WebUI code, ignoring WebUI disable call.', name) + log.debug( + '%s plugin contains no WebUI code, ignoring WebUI disable call.', name + ) return info = gather_info(plugin) scripts = component.get('Scripts') for script in info['scripts']: - scripts.remove_script('%s/%s' % (name.lower(), os.path.basename(script).lower())) + scripts.remove_script( + '%s/%s' % (name.lower(), os.path.basename(script).lower()) + ) for script in info['debug_scripts']: - scripts.remove_script('%s/%s' % (name.lower(), os.path.basename(script).lower()), 'debug') - scripts.remove_script('%s/%s' % (name.lower(), os.path.basename(script).lower()), 'dev') + scripts.remove_script( + '%s/%s' % (name.lower(), os.path.basename(script).lower()), 'debug' + ) + scripts.remove_script( + '%s/%s' % (name.lower(), os.path.basename(script).lower()), 'dev' + ) super(PluginManager, self).disable_plugin(name) @@ -83,7 +95,9 @@ class PluginManager(PluginManagerBase, component.Component): try: plugin = component.get('WebPlugin.' + name) except KeyError: - log.info('%s plugin contains no WebUI code, ignoring WebUI enable call.', name) + log.info( + '%s plugin contains no WebUI code, ignoring WebUI enable call.', name + ) return info = gather_info(plugin) @@ -91,12 +105,18 @@ class PluginManager(PluginManagerBase, component.Component): scripts = component.get('Scripts') for script in info['scripts']: log.debug('adding script %s for %s', name, os.path.basename(script)) - scripts.add_script('%s/%s' % (name.lower(), os.path.basename(script)), script) + scripts.add_script( + '%s/%s' % (name.lower(), os.path.basename(script)), script + ) for script in info['debug_scripts']: log.debug('adding debug script %s for %s', name, os.path.basename(script)) - scripts.add_script('%s/%s' % (name.lower(), os.path.basename(script)), script, 'debug') - scripts.add_script('%s/%s' % (name.lower(), os.path.basename(script)), script, 'dev') + scripts.add_script( + '%s/%s' % (name.lower(), os.path.basename(script)), script, 'debug' + ) + scripts.add_script( + '%s/%s' % (name.lower(), os.path.basename(script)), script, 'dev' + ) def start(self): """ @@ -111,8 +131,12 @@ class PluginManager(PluginManagerBase, component.Component): Stop the plugin manager """ self.disable_plugins() - client.deregister_event_handler('PluginEnabledEvent', self._on_plugin_enabled_event) - client.deregister_event_handler('PluginDisabledEvent', self._on_plugin_disabled_event) + client.deregister_event_handler( + 'PluginEnabledEvent', self._on_plugin_enabled_event + ) + client.deregister_event_handler( + 'PluginDisabledEvent', self._on_plugin_disabled_event + ) def update(self): pass @@ -126,7 +150,12 @@ class PluginManager(PluginManagerBase, component.Component): return info = gather_info(plugin) info['name'] = name - info['scripts'] = ['js/%s/%s' % (name.lower(), os.path.basename(s)) for s in info['scripts']] - info['debug_scripts'] = ['js/%s/%s' % (name.lower(), os.path.basename(s)) for s in info['debug_scripts']] + info['scripts'] = [ + 'js/%s/%s' % (name.lower(), os.path.basename(s)) for s in info['scripts'] + ] + info['debug_scripts'] = [ + 'js/%s/%s' % (name.lower(), os.path.basename(s)) + for s in info['debug_scripts'] + ] del info['script_directories'] return info diff --git a/deluge/ui/web/server.py b/deluge/ui/web/server.py index 62691e335..cfcf92b2e 100644 --- a/deluge/ui/web/server.py +++ b/deluge/ui/web/server.py @@ -37,13 +37,11 @@ CONFIG_DEFAULTS = { # Misc Settings 'enabled_plugins': [], 'default_daemon': '', - # Auth Settings 'pwd_salt': 'c26ab3bbd8b137f99cd83c2c1c0963bcc1a35cad', 'pwd_sha1': '2ce1a410bcdcc53064129b6d950f2e9fee4edc1e', 'session_timeout': 3600, 'sessions': {}, - # UI Settings 'sidebar_show_zero': False, 'sidebar_multiple_filters': True, @@ -52,7 +50,6 @@ CONFIG_DEFAULTS = { 'theme': 'gray', 'first_login': True, 'language': '', - # Server Settings 'base': '/', 'interface': '0.0.0.0', @@ -63,8 +60,12 @@ CONFIG_DEFAULTS = { } UI_CONFIG_KEYS = ( - 'theme', 'sidebar_show_zero', 'sidebar_multiple_filters', - 'show_session_speed', 'base', 'first_login', + 'theme', + 'sidebar_show_zero', + 'sidebar_multiple_filters', + 'show_session_speed', + 'base', + 'first_login', ) @@ -89,6 +90,7 @@ class MockGetText(resource.Resource): It will be used to define the `_` (underscore) function for translations, and will return the string to translate, as is. """ + def render(self, request): request.setHeader(b'content-type', b'text/javascript; encoding=utf-8') data = b'function _(string) { return string; }' @@ -114,10 +116,7 @@ class Upload(resource.Resource): print(request.args) if b'file' not in request.args: request.setResponseCode(http.OK) - return json.dumps({ - 'success': True, - 'files': [], - }) + return json.dumps({'success': True, 'files': []}) tempdir = tempfile.mkdtemp(prefix='delugeweb-') log.debug('uploading files to %s', tempdir) @@ -132,12 +131,7 @@ class Upload(resource.Resource): request.setHeader(b'content-type', b'text/html') request.setResponseCode(http.OK) - return compress( - json.dumps({ - 'success': True, - 'files': filenames, - }), request, - ) + return compress(json.dumps({'success': True, 'files': filenames}), request) class Render(resource.Resource): @@ -170,7 +164,6 @@ class Render(resource.Resource): class Tracker(resource.Resource): - def __init__(self): resource.Resource.__init__(self) try: @@ -185,8 +178,7 @@ class Tracker(resource.Resource): def on_got_icon(self, icon, request): if icon: request.setHeader( - b'cache-control', - b'public, must-revalidate, max-age=86400', + b'cache-control', b'public, must-revalidate, max-age=86400' ) request.setHeader(b'content-type', icon.get_mimetype().encode('utf8')) request.setResponseCode(http.OK) @@ -212,8 +204,7 @@ class Flag(resource.Resource): filename = common.resource_filename('deluge', os.path.join(*path)) if os.path.exists(filename): request.setHeader( - b'cache-control', - b'public, must-revalidate, max-age=86400', + b'cache-control', b'public, must-revalidate, max-age=86400' ) request.setHeader(b'content-type', b'image/png') with open(filename, 'rb') as _file: @@ -226,7 +217,6 @@ class Flag(resource.Resource): class LookupResource(resource.Resource, component.Component): - def __init__(self, name, *directories): resource.Resource.__init__(self) component.Component.__init__(self, name) @@ -274,13 +264,16 @@ class LookupResource(resource.Resource, component.Component): class ScriptResource(resource.Resource, component.Component): - def __init__(self): resource.Resource.__init__(self) component.Component.__init__(self, 'Scripts') self.__scripts = {} for script_type in ['normal', 'debug', 'dev']: - self.__scripts[script_type] = {'scripts': {}, 'order': [], 'files_exist': True} + self.__scripts[script_type] = { + 'scripts': {}, + 'order': [], + 'files_exist': True, + } def has_script_type_files(self, script_type): """Returns whether all the script files exist for this script type. @@ -385,9 +378,15 @@ class ScriptResource(resource.Resource, component.Component): # Ensure sub-directory scripts are top of list with root directory scripts bottom. if dirnames: - scripts.extend(['js/' + os.path.basename(root) + '/' + f for f in files]) + scripts.extend( + ['js/' + os.path.basename(root) + '/' + f for f in files] + ) else: - dirpath = os.path.basename(os.path.dirname(root)) + '/' + os.path.basename(root) + dirpath = ( + os.path.basename(os.path.dirname(root)) + + '/' + + os.path.basename(root) + ) for filename in reversed(files): scripts.insert(script_idx, 'js/' + dirpath + '/' + filename) @@ -417,7 +416,7 @@ class ScriptResource(resource.Resource, component.Component): if isinstance(filepath, tuple): filepath = filepath[0] - path = filepath + lookup_path[len(pattern):] + path = filepath + lookup_path[len(pattern) :] if not os.path.isfile(path): continue @@ -453,7 +452,8 @@ class TopLevel(resource.Resource): else: log.warning( 'Cannot find "gettext.js" translation file!' - ' Text will only be available in English.') + ' Text will only be available in English.' + ) self.putChild(b'gettext.js', MockGetText()) self.putChild(b'flag', Flag()) self.putChild(b'icons', LookupResource('Icons', rpath('icons'))) @@ -462,16 +462,32 @@ class TopLevel(resource.Resource): js = ScriptResource() # configure the dev scripts - js.add_script('ext-base-debug.js', rpath('js', 'extjs', 'ext-base-debug.js'), 'dev') - js.add_script('ext-all-debug.js', rpath('js', 'extjs', 'ext-all-debug.js'), 'dev') - js.add_script_folder('ext-extensions', rpath('js', 'extjs', 'ext-extensions'), 'dev') + js.add_script( + 'ext-base-debug.js', rpath('js', 'extjs', 'ext-base-debug.js'), 'dev' + ) + js.add_script( + 'ext-all-debug.js', rpath('js', 'extjs', 'ext-all-debug.js'), 'dev' + ) + js.add_script_folder( + 'ext-extensions', rpath('js', 'extjs', 'ext-extensions'), 'dev' + ) js.add_script_folder('deluge-all', rpath('js', 'deluge-all'), 'dev') # configure the debug scripts - js.add_script('ext-base-debug.js', rpath('js', 'extjs', 'ext-base-debug.js'), 'debug') - js.add_script('ext-all-debug.js', rpath('js', 'extjs', 'ext-all-debug.js'), 'debug') - js.add_script('ext-extensions-debug.js', rpath('js', 'extjs', 'ext-extensions-debug.js'), 'debug') - js.add_script('deluge-all-debug.js', rpath('js', 'deluge-all-debug.js'), 'debug') + js.add_script( + 'ext-base-debug.js', rpath('js', 'extjs', 'ext-base-debug.js'), 'debug' + ) + js.add_script( + 'ext-all-debug.js', rpath('js', 'extjs', 'ext-all-debug.js'), 'debug' + ) + js.add_script( + 'ext-extensions-debug.js', + rpath('js', 'extjs', 'ext-extensions-debug.js'), + 'debug', + ) + js.add_script( + 'deluge-all-debug.js', rpath('js', 'deluge-all-debug.js'), 'debug' + ) # configure the normal scripts js.add_script('ext-base.js', rpath('js', 'extjs', 'ext-base.js')) @@ -558,9 +574,14 @@ class TopLevel(resource.Resource): if not self.js.has_script_type_files(script_type): if not dev_ver: - log.warning('Failed to enable WebUI "%s" mode, script files are missing!', script_type) + log.warning( + 'Failed to enable WebUI "%s" mode, script files are missing!', + script_type, + ) # Fallback to checking other types in order and selecting first with files available. - for alt_script_type in [x for x in ['normal', 'debug', 'dev'] if x != script_type]: + for alt_script_type in [ + x for x in ['normal', 'debug', 'dev'] if x != script_type + ]: if self.js.has_script_type_files(alt_script_type): script_type = alt_script_type if not dev_ver: @@ -588,7 +609,6 @@ class TopLevel(resource.Resource): class DelugeWeb(component.Component): - def __init__(self, options=None, daemon=True): """ Setup the DelugeWeb server. @@ -600,7 +620,9 @@ class DelugeWeb(component.Component): """ component.Component.__init__(self, 'DelugeWeb', depend=['Web']) - self.config = configmanager.ConfigManager('web.conf', defaults=CONFIG_DEFAULTS, file_version=2) + self.config = configmanager.ConfigManager( + 'web.conf', defaults=CONFIG_DEFAULTS, file_version=2 + ) self.config.run_converter((0, 1), 2, self._migrate_config_1_to_2) self.config.register_set_function('language', self._on_language_changed) self.socket = None @@ -614,7 +636,9 @@ class DelugeWeb(component.Component): self.base = self.config['base'] if options: - self.interface = options.interface if options.interface is not None else self.interface + self.interface = ( + options.interface if options.interface is not None else self.interface + ) self.port = options.port if options.port else self.port self.base = options.base if options.base else self.base if options.ssl: @@ -624,8 +648,7 @@ class DelugeWeb(component.Component): if self.base != '/': # Strip away slashes and serve on the base path as well as root path - self.top_level.putChild( - self.base.strip('/'), self.top_level) + self.top_level.putChild(self.base.strip('/'), self.top_level) setup_translations(setup_gettext=True, setup_pygtk=False) @@ -657,10 +680,10 @@ class DelugeWeb(component.Component): def win_handler(ctrl_type): log.debug('ctrl type: %s', ctrl_type) - if ctrl_type == CTRL_CLOSE_EVENT or \ - ctrl_type == CTRL_SHUTDOWN_EVENT: + if ctrl_type == CTRL_CLOSE_EVENT or ctrl_type == CTRL_SHUTDOWN_EVENT: self.shutdown() return 1 + SetConsoleCtrlHandler(win_handler) def start(self): @@ -699,7 +722,7 @@ class DelugeWeb(component.Component): self.port, self.site, get_context_factory(cert, pkey), - interface=self.interface + interface=self.interface, ) ip = self.socket.getHost().host ip = '[%s]' % ip if is_ipv6(ip) else ip diff --git a/deluge/ui/web/web.py b/deluge/ui/web/web.py index 8d1e12794..4d0624791 100644 --- a/deluge/ui/web/web.py +++ b/deluge/ui/web/web.py @@ -24,24 +24,42 @@ class Web(UI): cmd_description = """Web-based user interface (http://localhost:8112)""" def __init__(self, *args, **kwargs): - super(Web, self).__init__('web', *args, description='Starts the Deluge Web interface', **kwargs) + super(Web, self).__init__( + 'web', *args, description='Starts the Deluge Web interface', **kwargs + ) self.__server = None group = self.parser.add_argument_group(_('Web Server Options')) group.add_argument( - '-i', '--interface', metavar='<ip_address>', action='store', + '-i', + '--interface', + metavar='<ip_address>', + action='store', help=_('IP address for web server to listen on'), ) group.add_argument( - '-p', '--port', metavar='<port>', type=int, action='store', + '-p', + '--port', + metavar='<port>', + type=int, + action='store', help=_('Port for web server to listen on'), ) group.add_argument( - '-b', '--base', metavar='<path>', action='store', + '-b', + '--base', + metavar='<path>', + action='store', help=_('Set the base path that the ui is running on'), ) - group.add_argument('--ssl', action='store_true', help=_('Force the web server to use SSL')) - group.add_argument('--no-ssl', action='store_true', help=_('Force the web server to disable SSL')) + group.add_argument( + '--ssl', action='store_true', help=_('Force the web server to use SSL') + ) + group.add_argument( + '--no-ssl', + action='store_true', + help=_('Force the web server to disable SSL'), + ) self.parser.add_process_arg_group() @property @@ -52,6 +70,7 @@ class Web(UI): super(Web, self).start() from deluge.ui.web import server + self.__server = server.DelugeWeb(options=self.options) def run(): @@ -59,8 +78,14 @@ class Web(UI): self.server.install_signal_handlers() self.server.start() except CannotListenError as ex: - log.error('%s \nCheck that deluge-web or webui plugin is not already running.', ex) + log.error( + '%s \nCheck that deluge-web or webui plugin is not already running.', + ex, + ) except Exception as ex: log.exception(ex) raise - run_profiled(run, output_file=self.options.profile, do_profile=self.options.profile) + + run_profiled( + run, output_file=self.options.profile, do_profile=self.options.profile + ) |