diff options
Diffstat (limited to 'deluge/core/core.py')
-rw-r--r-- | deluge/core/core.py | 476 |
1 files changed, 231 insertions, 245 deletions
diff --git a/deluge/core/core.py b/deluge/core/core.py index 9a19e30..35cf019 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import glob import logging import os @@ -17,8 +14,9 @@ import shutil import tempfile import threading from base64 import b64decode, b64encode +from typing import Any, Dict, List, Optional, Tuple, Union +from urllib.request import URLError, urlopen -from six import string_types from twisted.internet import defer, reactor, task from twisted.web.client import Agent, readBody @@ -41,7 +39,7 @@ from deluge.core.pluginmanager import PluginManager 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.decorators import deprecated, maybe_coroutine from deluge.error import ( AddTorrentError, DelugeError, @@ -56,12 +54,6 @@ from deluge.event import ( ) from deluge.httpdownloader import download_file -try: - from urllib.request import urlopen, URLError -except ImportError: - # PY2 fallback - from urllib2 import urlopen, URLError - log = logging.getLogger(__name__) DEPR_SESSION_STATUS_KEYS = { @@ -120,7 +112,7 @@ class Core(component.Component): component.Component.__init__(self, 'Core') # Start the libtorrent session. - user_agent = 'Deluge/{} libtorrent/{}'.format(DELUGE_VER, LT_VERSION) + user_agent = f'Deluge/{DELUGE_VER} libtorrent/{LT_VERSION}' peer_id = self._create_peer_id(DELUGE_VER) log.debug('Starting session (peer_id: %s, user_agent: %s)', peer_id, user_agent) settings_pack = { @@ -173,19 +165,25 @@ class Core(component.Component): # store the one in the config so we can restore it on shutdown self._old_listen_interface = None if listen_interface: - if deluge.common.is_ip(listen_interface): + if deluge.common.is_interface(listen_interface): 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', + 'Invalid listen interface (must be IP Address or Interface Name): %s', listen_interface, ) self._old_outgoing_interface = None if outgoing_interface: - self._old_outgoing_interface = self.config['outgoing_interface'] - self.config['outgoing_interface'] = outgoing_interface + if deluge.common.is_interface(outgoing_interface): + self._old_outgoing_interface = self.config['outgoing_interface'] + self.config['outgoing_interface'] = outgoing_interface + else: + log.error( + 'Invalid outgoing interface (must be IP Address or Interface Name): %s', + outgoing_interface, + ) # New release check information self.__new_release = None @@ -243,13 +241,12 @@ class Core(component.Component): """Apply libtorrent session settings. Args: - settings (dict): A dict of lt session settings to apply. - + settings: A dict of lt session settings to apply. """ self.session.apply_settings(settings) @staticmethod - def _create_peer_id(version): + def _create_peer_id(version: str) -> str: """Create a peer_id fingerprint. This creates the peer_id and modifies the release char to identify @@ -264,11 +261,10 @@ class Core(component.Component): ``--DE201b--`` (beta pre-release of v2.0.1) Args: - version (str): The version string in PEP440 dotted notation. + version: The version string in PEP440 dotted notation. Returns: - str: The formattted peer_id with Deluge prefix e.g. '--DE200s--' - + The formatted peer_id with Deluge prefix e.g. '--DE200s--' """ split = deluge.common.VersionSplit(version) # Fill list with zeros to length of 4 and use lt to create fingerprint. @@ -301,7 +297,7 @@ class Core(component.Component): if os.path.isfile(filepath): log.debug('Creating backup of %s at: %s', filename, filepath_bak) shutil.copy2(filepath, filepath_bak) - except IOError as ex: + except OSError as ex: log.error('Unable to backup %s to %s: %s', filepath, filepath_bak, ex) else: log.info('Saving the %s at: %s', filename, filepath) @@ -311,18 +307,17 @@ class Core(component.Component): _file.flush() os.fsync(_file.fileno()) shutil.move(filepath_tmp, filepath) - except (IOError, EOFError) as ex: + except (OSError, EOFError) as ex: log.error('Unable to save %s: %s', filename, ex) if os.path.isfile(filepath_bak): log.info('Restoring backup of %s from: %s', filename, filepath_bak) shutil.move(filepath_bak, filepath) - def _load_session_state(self): + def _load_session_state(self) -> dict: """Loads the libtorrent session state Returns: - dict: A libtorrent sesion state, empty dict if unable to load it. - + A libtorrent sesion state, empty dict if unable to load it. """ filename = 'session.state' filepath = get_config_dir(filename) @@ -333,7 +328,7 @@ class Core(component.Component): try: with open(_filepath, 'rb') as _file: state = lt.bdecode(_file.read()) - except (IOError, EOFError, RuntimeError) as ex: + except (OSError, EOFError, RuntimeError) as ex: log.warning('Unable to load %s: %s', _filepath, ex) else: log.info('Successfully loaded %s: %s', filename, _filepath) @@ -358,8 +353,8 @@ class Core(component.Component): if blocks_read: self.session_status['read_hit_ratio'] = ( - self.session_status['disk.num_blocks_cache_hits'] / blocks_read - ) + blocks_read - self.session_status['disk.num_read_ops'] + ) / blocks_read else: self.session_status['read_hit_ratio'] = 0.0 @@ -404,18 +399,19 @@ class Core(component.Component): # Exported Methods @export - def add_torrent_file_async(self, filename, filedump, options, save_state=True): - """Adds a torrent file to the session asynchonously. + def add_torrent_file_async( + self, filename: str, filedump: str, options: dict, save_state: bool = True + ) -> 'defer.Deferred[Optional[str]]': + """Adds a torrent file to the session asynchronously. Args: - filename (str): The filename of the torrent. - filedump (str): A base64 encoded string of torrent file contents. - options (dict): The options to apply to the torrent upon adding. - save_state (bool): If the state should be saved after adding the file. + filename: The filename of the torrent. + filedump: A base64 encoded string of torrent file contents. + options: The options to apply to the torrent upon adding. + save_state: If the state should be saved after adding the file. Returns: - Deferred: The torrent ID or None. - + The torrent ID or None. """ try: filedump = b64decode(filedump) @@ -436,42 +432,39 @@ class Core(component.Component): return d @export - def prefetch_magnet_metadata(self, magnet, timeout=30): + @maybe_coroutine + async def prefetch_magnet_metadata( + self, magnet: str, timeout: int = 30 + ) -> Tuple[str, bytes]: """Download magnet metadata without adding to Deluge session. Used by UIs to get magnet files for selection before adding to session. + The metadata is bencoded and for transfer base64 encoded. + Args: - magnet (str): The magnet uri. - timeout (int): Number of seconds to wait before cancelling request. + magnet: The magnet URI. + timeout: Number of seconds to wait before canceling request. Returns: - Deferred: A tuple of (torrent_id (str), metadata (dict)) for the magnet. + A tuple of (torrent_id, metadata) for the magnet. """ - - def on_metadata(result, result_d): - """Return result of torrent_id and metadata""" - result_d.callback(result) - return result - - d = self.torrentmanager.prefetch_metadata(magnet, timeout) - # Use a seperate callback chain to handle existing prefetching magnet. - result_d = defer.Deferred() - d.addBoth(on_metadata, result_d) - return result_d + return await self.torrentmanager.prefetch_metadata(magnet, timeout) @export - def add_torrent_file(self, filename, filedump, options): + def add_torrent_file( + self, filename: str, filedump: Union[str, bytes], options: dict + ) -> Optional[str]: """Adds a torrent file to the session. Args: - filename (str): The filename of the torrent. - filedump (str): A base64 encoded string of the torrent file contents. - options (dict): The options to apply to the torrent upon adding. + filename: The filename of the torrent. + filedump: A base64 encoded string of the torrent file contents. + options: The options to apply to the torrent upon adding. Returns: - str: The torrent_id or None. + The torrent_id or None. """ try: filedump = b64decode(filedump) @@ -487,24 +480,26 @@ class Core(component.Component): raise @export - def add_torrent_files(self, torrent_files): - """Adds multiple torrent files to the session asynchonously. + def add_torrent_files( + self, torrent_files: List[Tuple[str, Union[str, bytes], dict]] + ) -> 'defer.Deferred[List[AddTorrentError]]': + """Adds multiple torrent files to the session asynchronously. Args: - torrent_files (list of tuples): Torrent files as tuple of (filename, filedump, options). + torrent_files: Torrent files as tuple of + ``(filename, filedump, options)``. Returns: - Deferred - + A list of errors (if there were any) """ - @defer.inlineCallbacks - def add_torrents(): + @maybe_coroutine + async def add_torrents(): errors = [] last_index = len(torrent_files) - 1 for idx, torrent in enumerate(torrent_files): try: - yield self.add_torrent_file_async( + await self.add_torrent_file_async( torrent[0], torrent[1], torrent[2], save_state=idx == last_index ) except AddTorrentError as ex: @@ -515,93 +510,89 @@ class Core(component.Component): return task.deferLater(reactor, 0, add_torrents) @export - def add_torrent_url(self, url, options, headers=None): - """ - Adds a torrent from a url. Deluge will attempt to fetch the torrent - from url prior to adding it to the session. + @maybe_coroutine + async def add_torrent_url( + self, url: str, options: dict, headers: dict = None + ) -> 'defer.Deferred[Optional[str]]': + """Adds a torrent from a URL. Deluge will attempt to fetch the torrent + from the URL prior to adding it to the session. - :param url: the url pointing to the torrent file - :type url: string - :param options: the options to apply to the torrent on add - :type options: dict - :param headers: any optional headers to send - :type headers: dict + Args: + url: the URL pointing to the torrent file + options: the options to apply to the torrent on add + headers: any optional headers to send - :returns: a Deferred which returns the torrent_id as a str or None + Returns: + a Deferred which returns the torrent_id as a str or None """ - log.info('Attempting to add url %s', url) + log.info('Attempting to add URL %s', url) - def on_download_success(filename): - # We got the file, so add it to the session + tmp_fd, tmp_file = tempfile.mkstemp(prefix='deluge_url.', suffix='.torrent') + try: + filename = await download_file( + url, tmp_file, headers=headers, force_filename=True + ) + except Exception: + log.error('Failed to add torrent from URL %s', url) + raise + else: with open(filename, 'rb') as _file: data = _file.read() + return self.add_torrent_file(filename, b64encode(data), options) + finally: try: - os.remove(filename) + os.close(tmp_fd) + os.remove(tmp_file) except OSError as ex: - log.warning('Could not remove temp file: %s', ex) - return self.add_torrent_file(filename, b64encode(data), options) - - def on_download_fail(failure): - # Log the error and pass the failure onto the client - log.error('Failed to add torrent from url %s', url) - return failure - - tmp_fd, tmp_file = tempfile.mkstemp(prefix='deluge_url.', suffix='.torrent') - os.close(tmp_fd) - d = download_file(url, tmp_file, headers=headers, force_filename=True) - d.addCallbacks(on_download_success, on_download_fail) - return d + log.warning(f'Unable to delete temp file {tmp_file}: , {ex}') @export - def add_torrent_magnet(self, uri, options): - """ - Adds a torrent from a magnet link. - - :param uri: the magnet link - :type uri: string - :param options: the options to apply to the torrent on add - :type options: dict + def add_torrent_magnet(self, uri: str, options: dict) -> str: + """Adds a torrent from a magnet link. - :returns: the torrent_id - :rtype: string + Args: + uri: the magnet link + options: the options to apply to the torrent on add + Returns: + the torrent_id """ - log.debug('Attempting to add by magnet uri: %s', uri) + log.debug('Attempting to add by magnet URI: %s', uri) return self.torrentmanager.add(magnet=uri, options=options) @export - def remove_torrent(self, torrent_id, remove_data): + def remove_torrent(self, torrent_id: str, remove_data: bool) -> bool: """Removes a single torrent from the session. Args: - torrent_id (str): The torrent ID to remove. - remove_data (bool): If True, also remove the downloaded data. + torrent_id: The torrent ID to remove. + remove_data: If True, also remove the downloaded data. Returns: - bool: True if removed successfully. + True if removed successfully. Raises: InvalidTorrentError: If the torrent ID does not exist in the session. - """ log.debug('Removing torrent %s from the core.', torrent_id) return self.torrentmanager.remove(torrent_id, remove_data) @export - def remove_torrents(self, torrent_ids, remove_data): + def remove_torrents( + self, torrent_ids: List[str], remove_data: bool + ) -> 'defer.Deferred[List[Tuple[str, str]]]': """Remove multiple torrents from the session. Args: - torrent_ids (list): The torrent IDs to remove. - remove_data (bool): If True, also remove the downloaded data. + torrent_ids: The torrent IDs to remove. + remove_data: If True, also remove the downloaded data. Returns: - list: An empty list if no errors occurred otherwise the list contains - tuples of strings, a torrent ID and an error message. For example: - - [('<torrent_id>', 'Error removing torrent')] + An empty list if no errors occurred otherwise the list contains + tuples of strings, a torrent ID and an error message. For example: + [('<torrent_id>', 'Error removing torrent')] """ log.info('Removing %d torrents from core.', len(torrent_ids)) @@ -625,17 +616,17 @@ class Core(component.Component): return task.deferLater(reactor, 0, do_remove_torrents) @export - def get_session_status(self, keys): + def get_session_status(self, keys: List[str]) -> Dict[str, Union[int, float]]: """Gets the session status values for 'keys', these keys are taking from libtorrent's session status. See: http://www.rasterbar.com/products/libtorrent/manual.html#status - :param keys: the keys for which we want values - :type keys: list - :returns: a dictionary of {key: value, ...} - :rtype: dict + Args: + keys: the keys for which we want values + Returns: + a dictionary of {key: value, ...} """ if not keys: return self.session_status @@ -652,26 +643,26 @@ class Core(component.Component): ) status[key] = self.session_status[new_key] else: - log.warning('Session status key not valid: %s', key) + log.debug('Session status key not valid: %s', key) return status @export - def force_reannounce(self, torrent_ids): + def force_reannounce(self, torrent_ids: List[str]) -> None: log.debug('Forcing reannouncment to: %s', torrent_ids) for torrent_id in torrent_ids: self.torrentmanager[torrent_id].force_reannounce() @export - def pause_torrent(self, torrent_id): + def pause_torrent(self, torrent_id: str) -> None: """Pauses a torrent""" log.debug('Pausing: %s', torrent_id) - if not isinstance(torrent_id, string_types): + if not isinstance(torrent_id, str): self.pause_torrents(torrent_id) else: self.torrentmanager[torrent_id].pause() @export - def pause_torrents(self, torrent_ids=None): + def pause_torrents(self, torrent_ids: List[str] = None) -> None: """Pauses a list of torrents""" if not torrent_ids: torrent_ids = self.torrentmanager.get_torrent_list() @@ -679,27 +670,27 @@ class Core(component.Component): self.pause_torrent(torrent_id) @export - def connect_peer(self, torrent_id, ip, port): + def connect_peer(self, torrent_id: str, ip: str, port: int): log.debug('adding peer %s to %s', ip, torrent_id) if not self.torrentmanager[torrent_id].connect_peer(ip, port): log.warning('Error adding peer %s:%s to %s', ip, port, torrent_id) @export - def move_storage(self, torrent_ids, dest): + def move_storage(self, torrent_ids: List[str], dest: str): log.debug('Moving storage %s to %s', torrent_ids, dest) for torrent_id in torrent_ids: if not self.torrentmanager[torrent_id].move_storage(dest): log.warning('Error moving torrent %s to %s', torrent_id, dest) @export - def pause_session(self): + def pause_session(self) -> None: """Pause the entire session""" if not self.session.is_paused(): self.session.pause() component.get('EventManager').emit(SessionPausedEvent()) @export - def resume_session(self): + def resume_session(self) -> None: """Resume the entire session""" if self.session.is_paused(): self.session.resume() @@ -708,21 +699,21 @@ class Core(component.Component): component.get('EventManager').emit(SessionResumedEvent()) @export - def is_session_paused(self): + def is_session_paused(self) -> bool: """Returns the activity of the session""" return self.session.is_paused() @export - def resume_torrent(self, torrent_id): + def resume_torrent(self, torrent_id: str) -> None: """Resumes a torrent""" log.debug('Resuming: %s', torrent_id) - if not isinstance(torrent_id, string_types): + if not isinstance(torrent_id, str): self.resume_torrents(torrent_id) else: self.torrentmanager[torrent_id].resume() @export - def resume_torrents(self, torrent_ids=None): + def resume_torrents(self, torrent_ids: List[str] = None) -> None: """Resumes a list of torrents""" if not torrent_ids: torrent_ids = self.torrentmanager.get_torrent_list() @@ -746,7 +737,7 @@ class Core(component.Component): import traceback traceback.print_exc() - # Torrent was probaly removed meanwhile + # Torrent was probably removed meanwhile return {} # Ask the plugin manager to fill in the plugin keys @@ -755,7 +746,9 @@ class Core(component.Component): return status @export - def get_torrent_status(self, torrent_id, keys, diff=False): + def get_torrent_status( + self, torrent_id: str, keys: List[str], diff: bool = False + ) -> dict: torrent_keys, plugin_keys = self.torrentmanager.separate_keys( keys, [torrent_id] ) @@ -769,57 +762,54 @@ class Core(component.Component): ) @export - def get_torrents_status(self, filter_dict, keys, diff=False): - """ - returns all torrents , optionally filtered by filter_dict. - """ + @maybe_coroutine + async def get_torrents_status( + self, filter_dict: dict, keys: List[str], diff: bool = False + ) -> dict: + """returns all torrents , optionally filtered by filter_dict.""" + all_keys = not keys torrent_ids = self.filtermanager.filter_torrent_ids(filter_dict) - d = self.torrentmanager.torrents_status_update(torrent_ids, keys, diff=diff) - - def add_plugin_fields(args): - status_dict, plugin_keys = args - # 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) - ) - return status_dict - - d.addCallback(add_plugin_fields) - return d + status_dict, plugin_keys = await self.torrentmanager.torrents_status_update( + torrent_ids, keys, diff=diff + ) + # Ask the plugin manager to fill in the plugin keys + if len(plugin_keys) > 0 or all_keys: + for key in status_dict: + status_dict[key].update(self.pluginmanager.get_status(key, plugin_keys)) + return status_dict @export - def get_filter_tree(self, show_zero_hits=True, hide_cat=None): - """ - returns {field: [(value,count)] } + def get_filter_tree( + self, show_zero_hits: bool = True, hide_cat: List[str] = None + ) -> Dict: + """returns {field: [(value,count)] } for use in sidebar(s) """ return self.filtermanager.get_filter_tree(show_zero_hits, hide_cat) @export - def get_session_state(self): + def get_session_state(self) -> List[str]: """Returns a list of torrent_ids in the session.""" # Get the torrent list from the TorrentManager return self.torrentmanager.get_torrent_list() @export - def get_config(self): + def get_config(self) -> dict: """Get all the preferences as a dictionary""" return self.config.config @export - def get_config_value(self, key): + def get_config_value(self, key: str) -> Any: """Get the config value for key""" return self.config.get(key) @export - def get_config_values(self, keys): + def get_config_values(self, keys: List[str]) -> Dict[str, Any]: """Get the config values for the entered keys""" return {key: self.config.get(key) for key in keys} @export - def set_config(self, config): + def set_config(self, config: Dict[str, Any]): """Set the config with values from dictionary""" # Load all the values into the configuration for key in config: @@ -828,21 +818,20 @@ class Core(component.Component): self.config[key] = config[key] @export - def get_listen_port(self): + def get_listen_port(self) -> int: """Returns the active listen port""" return self.session.listen_port() @export - def get_proxy(self): + def get_proxy(self) -> Dict[str, Any]: """Returns the proxy settings Returns: - dict: Contains proxy settings. + Proxy settings. Notes: Proxy type names: 0: None, 1: Socks4, 2: Socks5, 3: Socks5 w Auth, 4: HTTP, 5: HTTP w Auth, 6: I2P - """ settings = self.session.get_settings() @@ -865,51 +854,60 @@ class Core(component.Component): return proxy_dict @export - def get_available_plugins(self): + def get_available_plugins(self) -> List[str]: """Returns a list of plugins available in the core""" return self.pluginmanager.get_available_plugins() @export - def get_enabled_plugins(self): + def get_enabled_plugins(self) -> List[str]: """Returns a list of enabled plugins in the core""" return self.pluginmanager.get_enabled_plugins() @export - def enable_plugin(self, plugin): + def enable_plugin(self, plugin: str) -> 'defer.Deferred[bool]': return self.pluginmanager.enable_plugin(plugin) @export - def disable_plugin(self, plugin): + def disable_plugin(self, plugin: str) -> 'defer.Deferred[bool]': return self.pluginmanager.disable_plugin(plugin) @export - def force_recheck(self, torrent_ids): + def force_recheck(self, torrent_ids: List[str]) -> None: """Forces a data recheck on torrent_ids""" for torrent_id in torrent_ids: self.torrentmanager[torrent_id].force_recheck() @export - def set_torrent_options(self, torrent_ids, options): + def set_torrent_options( + self, torrent_ids: List[str], options: Dict[str, Any] + ) -> None: """Sets the torrent options for torrent_ids Args: - torrent_ids (list): A list of torrent_ids to set the options for. - options (dict): A dict of torrent options to set. See torrent.TorrentOptions class for valid keys. + torrent_ids: A list of torrent_ids to set the options for. + options: A dict of torrent options to set. See + ``torrent.TorrentOptions`` class for valid keys. """ if 'owner' in options and not self.authmanager.has_account(options['owner']): raise DelugeError('Username "%s" is not known.' % options['owner']) - if isinstance(torrent_ids, string_types): + if isinstance(torrent_ids, str): torrent_ids = [torrent_ids] for torrent_id in torrent_ids: self.torrentmanager[torrent_id].set_options(options) @export - def set_torrent_trackers(self, torrent_id, trackers): - """Sets a torrents tracker list. trackers will be [{"url", "tier"}]""" + def set_torrent_trackers( + self, torrent_id: str, trackers: List[Dict[str, Any]] + ) -> None: + """Sets a torrents tracker list. trackers will be ``[{"url", "tier"}]``""" return self.torrentmanager[torrent_id].set_trackers(trackers) + @export + def get_magnet_uri(self, torrent_id: str) -> str: + return self.torrentmanager[torrent_id].get_magnet_uri() + @deprecated @export def set_torrent_max_connections(self, torrent_id, value): @@ -985,7 +983,7 @@ class Core(component.Component): @export def get_path_size(self, path): """Returns the size of the file or folder 'path' and -1 if the path is - unaccessible (non-existent or insufficient privs)""" + inaccessible (non-existent or insufficient privileges)""" return deluge.common.get_path_size(path) @export @@ -1055,11 +1053,11 @@ class Core(component.Component): self.add_torrent_file(os.path.split(target)[1], filedump, options) @export - def upload_plugin(self, filename, filedump): + def upload_plugin(self, filename: str, filedump: Union[str, bytes]) -> None: """This method is used to upload new plugins to the daemon. It is used when connecting to the daemon remotely and installing a new plugin on - the client side. 'plugin_data' is a xmlrpc.Binary object of the file data, - ie, plugin_file.read()""" + the client side. ``plugin_data`` is a ``xmlrpc.Binary`` object of the file data, + i.e. ``plugin_file.read()``""" try: filedump = b64decode(filedump) @@ -1073,26 +1071,24 @@ class Core(component.Component): component.get('CorePluginManager').scan_for_plugins() @export - def rescan_plugins(self): - """ - Rescans the plugin folders for new plugins - """ + def rescan_plugins(self) -> None: + """Re-scans the plugin folders for new plugins""" component.get('CorePluginManager').scan_for_plugins() @export - def rename_files(self, torrent_id, filenames): - """ - Rename files in torrent_id. Since this is an asynchronous operation by + def rename_files( + self, torrent_id: str, filenames: List[Tuple[int, str]] + ) -> defer.Deferred: + """Rename files in ``torrent_id``. Since this is an asynchronous operation by libtorrent, watch for the TorrentFileRenamedEvent to know when the files have been renamed. - :param torrent_id: the torrent_id to rename files - :type torrent_id: string - :param filenames: a list of index, filename pairs - :type filenames: ((index, filename), ...) - - :raises InvalidTorrentError: if torrent_id is invalid + Args: + torrent_id: the torrent_id to rename files + filenames: a list of index, filename pairs + Raises: + InvalidTorrentError: if torrent_id is invalid """ if torrent_id not in self.torrentmanager.torrents: raise InvalidTorrentError('torrent_id is not in session') @@ -1103,21 +1099,20 @@ class Core(component.Component): return task.deferLater(reactor, 0, rename) @export - def rename_folder(self, torrent_id, folder, new_folder): - """ - Renames the 'folder' to 'new_folder' in 'torrent_id'. Watch for the + def rename_folder( + self, torrent_id: str, folder: str, new_folder: str + ) -> defer.Deferred: + """Renames the 'folder' to 'new_folder' in 'torrent_id'. Watch for the TorrentFolderRenamedEvent which is emitted when the folder has been renamed successfully. - :param torrent_id: the torrent to rename folder in - :type torrent_id: string - :param folder: the folder to rename - :type folder: string - :param new_folder: the new folder name - :type new_folder: string - - :raises InvalidTorrentError: if the torrent_id is invalid + Args: + torrent_id: the torrent to rename folder in + folder: the folder to rename + new_folder: the new folder name + Raises: + InvalidTorrentError: if the torrent_id is invalid """ if torrent_id not in self.torrentmanager.torrents: raise InvalidTorrentError('torrent_id is not in session') @@ -1125,7 +1120,7 @@ class Core(component.Component): return self.torrentmanager[torrent_id].rename_folder(folder, new_folder) @export - def queue_top(self, torrent_ids): + def queue_top(self, torrent_ids: List[str]) -> None: 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( @@ -1139,7 +1134,7 @@ class Core(component.Component): log.warning('torrent_id: %s does not exist in the queue', torrent_id) @export - def queue_up(self, torrent_ids): + def queue_up(self, torrent_ids: List[str]) -> None: log.debug('Attempting to queue %s to up', torrent_ids) torrents = ( (self.torrentmanager.get_queue_position(torrent_id), torrent_id) @@ -1164,7 +1159,7 @@ class Core(component.Component): prev_queue_position = queue_position @export - def queue_down(self, torrent_ids): + def queue_down(self, torrent_ids: List[str]) -> None: log.debug('Attempting to queue %s to down', torrent_ids) torrents = ( (self.torrentmanager.get_queue_position(torrent_id), torrent_id) @@ -1189,7 +1184,7 @@ class Core(component.Component): prev_queue_position = queue_position @export - def queue_bottom(self, torrent_ids): + def queue_bottom(self, torrent_ids: List[str]) -> None: 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( @@ -1203,17 +1198,15 @@ class Core(component.Component): log.warning('torrent_id: %s does not exist in the queue', torrent_id) @export - def glob(self, path): + def glob(self, path: str) -> List[str]: return glob.glob(path) @export - def test_listen_port(self): - """ - Checks if the active port is open - - :returns: True if the port is open, False if not - :rtype: bool + def test_listen_port(self) -> 'defer.Deferred[Optional[bool]]': + """Checks if the active port is open + Returns: + True if the port is open, False if not """ port = self.get_listen_port() url = 'https://deluge-torrent.org/test_port.php?port=%s' % port @@ -1232,18 +1225,17 @@ class Core(component.Component): return d @export - def get_free_space(self, path=None): - """ - Returns the number of free bytes at path + def get_free_space(self, path: str = None) -> int: + """Returns the number of free bytes at path - :param path: the path to check free space at, if None, use the default download location - :type path: string - - :returns: the number of free bytes at path - :rtype: int + Args: + path: the path to check free space at, if None, use the default download location - :raises InvalidPathError: if the path is invalid + Returns: + the number of free bytes at path + Raises: + InvalidPathError: if the path is invalid """ if not path: path = self.config['download_location'] @@ -1256,46 +1248,40 @@ class Core(component.Component): self.external_ip = external_ip @export - def get_external_ip(self): - """ - Returns the external ip address recieved from libtorrent. - """ + def get_external_ip(self) -> str: + """Returns the external IP address received from libtorrent.""" return self.external_ip @export - def get_libtorrent_version(self): - """ - Returns the libtorrent version. - - :returns: the version - :rtype: string + def get_libtorrent_version(self) -> str: + """Returns the libtorrent version. + Returns: + the version """ return LT_VERSION @export - def get_completion_paths(self, args): - """ - Returns the available path completions for the input value. - """ + def get_completion_paths(self, args: Dict[str, Any]) -> Dict[str, Any]: + """Returns the available path completions for the input value.""" return path_chooser_common.get_completion_paths(args) @export(AUTH_LEVEL_ADMIN) - def get_known_accounts(self): + def get_known_accounts(self) -> List[Dict[str, Any]]: return self.authmanager.get_known_accounts() @export(AUTH_LEVEL_NONE) - def get_auth_levels_mappings(self): + def get_auth_levels_mappings(self) -> Tuple[Dict[str, int], Dict[int, str]]: return (AUTH_LEVELS_MAPPING, AUTH_LEVELS_MAPPING_REVERSE) @export(AUTH_LEVEL_ADMIN) - def create_account(self, username, password, authlevel): + def create_account(self, username: str, password: str, authlevel: str) -> bool: return self.authmanager.create_account(username, password, authlevel) @export(AUTH_LEVEL_ADMIN) - def update_account(self, username, password, authlevel): + def update_account(self, username: str, password: str, authlevel: str) -> bool: return self.authmanager.update_account(username, password, authlevel) @export(AUTH_LEVEL_ADMIN) - def remove_account(self, username): + def remove_account(self, username: str) -> bool: return self.authmanager.remove_account(username) |