Fix files to pass new Flake8 checkers

Some new flake8 checkers were added so fix these new warnings and
any issues uncovered.

Use add-trailing-comma to fix missing trailing commas. It does not
format it as well as I would like however it was fast to change and
helps with git changes in future.

Removed pylint from tox due to large number of warnings.
This commit is contained in:
Calum Lind 2018-06-01 15:35:19 +01:00
commit d642fa3989
166 changed files with 3294 additions and 1907 deletions

View file

@ -19,7 +19,7 @@ class RpcApi(object):
def scan_for_methods(obj): def scan_for_methods(obj):
methods = { methods = {
'__doc__': 'Methods available in %s' % obj.__name__.lower() '__doc__': 'Methods available in %s' % obj.__name__.lower(),
} }
for d in dir(obj): for d in dir(obj):
if not hasattr(getattr(obj, d), '_rpcserver_export'): if not hasattr(getattr(obj, d), '_rpcserver_export'):

View file

@ -63,7 +63,7 @@ TORRENT_STATE = [
'Paused', 'Paused',
'Error', 'Error',
'Queued', 'Queued',
'Moving' 'Moving',
] ]
# The output formatting for json.dump # The output formatting for json.dump
@ -103,7 +103,8 @@ def get_default_config_dir(filename=None):
import _winreg as winreg # For Python 2. import _winreg as winreg # For Python 2.
hkey = winreg.OpenKey( hkey = winreg.OpenKey(
winreg.HKEY_CURRENT_USER, winreg.HKEY_CURRENT_USER,
'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders') 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders',
)
app_data_reg = winreg.QueryValueEx(hkey, 'AppData') app_data_reg = winreg.QueryValueEx(hkey, 'AppData')
app_data_path = app_data_reg[0] app_data_path = app_data_reg[0]
winreg.CloseKey(hkey) winreg.CloseKey(hkey)
@ -273,7 +274,7 @@ def resource_filename(module, path):
This is a work-around that. This is a work-around that.
""" """
return pkg_resources.require('Deluge>=%s' % get_version())[0].get_resource_filename( 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])),
) )
@ -554,18 +555,20 @@ def tokenize(text):
return tokenized_input return tokenized_input
size_units = (dict(prefix='b', divider=1, singular='byte', plural='bytes'), size_units = [
dict(prefix='KiB', divider=1024**1), {'prefix': 'b', 'divider': 1, 'singular': 'byte', 'plural': 'bytes'},
dict(prefix='MiB', divider=1024**2), {'prefix': 'KiB', 'divider': 1024**1},
dict(prefix='GiB', divider=1024**3), {'prefix': 'MiB', 'divider': 1024**2},
dict(prefix='TiB', divider=1024**4), {'prefix': 'GiB', 'divider': 1024**3},
dict(prefix='PiB', divider=1024**5), {'prefix': 'TiB', 'divider': 1024**4},
dict(prefix='KB', divider=1000**1), {'prefix': 'PiB', 'divider': 1024**5},
dict(prefix='MB', divider=1000**2), {'prefix': 'KB', 'divider': 1000**1},
dict(prefix='GB', divider=1000**3), {'prefix': 'MB', 'divider': 1000**2},
dict(prefix='TB', divider=1000**4), {'prefix': 'GB', 'divider': 1000**3},
dict(prefix='PB', divider=1000**5), {'prefix': 'TB', 'divider': 1000**4},
dict(prefix='m', divider=1000**2)) {'prefix': 'PB', 'divider': 1000**5},
{'prefix': 'm', 'divider': 1000**2},
]
class InvalidSize(Exception): class InvalidSize(Exception):
@ -906,10 +909,12 @@ def decode_bytes(byte_str, encoding='utf8'):
elif not isinstance(byte_str, bytes): elif not isinstance(byte_str, bytes):
return byte_str return byte_str
encodings = [lambda: ('utf8', 'strict'), encodings = [
lambda: ('utf8', 'strict'),
lambda: ('iso-8859-1', 'strict'), lambda: ('iso-8859-1', 'strict'),
lambda: (chardet.detect(byte_str)['encoding'], 'strict'), lambda: (chardet.detect(byte_str)['encoding'], 'strict'),
lambda: (encoding, 'ignore')] lambda: (encoding, 'ignore'),
]
if encoding is not 'utf8': if encoding is not 'utf8':
encodings.insert(0, lambda: (encoding, 'strict')) encodings.insert(0, lambda: (encoding, 'strict'))
@ -949,7 +954,10 @@ def utf8_encode_structure(data):
if isinstance(data, (list, tuple)): if isinstance(data, (list, tuple)):
return type(data)([utf8_encode_structure(d) for d in data]) return type(data)([utf8_encode_structure(d) for d in data])
elif isinstance(data, dict): elif isinstance(data, dict):
return dict([utf8_encode_structure(d) for d in data.items()]) return {
utf8_encode_structure(k): utf8_encode_structure(v)
for k, v in data.items()
}
elif not isinstance(data, bytes): elif not isinstance(data, bytes):
try: try:
return data.encode('utf8') return data.encode('utf8')
@ -968,7 +976,8 @@ class VersionSplit(object):
""" """
def __init__(self, ver): def __init__(self, ver):
version_re = re.compile(r""" version_re = re.compile(
r"""
^ ^
(?P<version>\d+\.\d+) # minimum 'N.N' (?P<version>\d+\.\d+) # minimum 'N.N'
(?P<extraversion>(?:\.\d+)*) # any number of extra '.N' segments (?P<extraversion>(?:\.\d+)*) # any number of extra '.N' segments
@ -978,7 +987,8 @@ class VersionSplit(object):
(?P<prerelversion>\d+(?:\.\d+)*) (?P<prerelversion>\d+(?:\.\d+)*)
)? )?
(?P<postdev>(\.post(?P<post>\d+))?(\.dev(?P<dev>\d+))?)? (?P<postdev>(\.post(?P<post>\d+))?(\.dev(?P<dev>\d+))?)?
$""", re.VERBOSE) $""", re.VERBOSE,
)
# Check for PEP 386 compliant version # Check for PEP 386 compliant version
match = re.search(version_re, ver) match = re.search(version_re, ver)
@ -1061,11 +1071,13 @@ def create_localclient_account(append=False):
create_auth_file() create_auth_file()
with open(auth_file, 'a' if append else 'w') as _file: with open(auth_file, 'a' if append else 'w') as _file:
_file.write(':'.join([ _file.write(
':'.join([
'localclient', 'localclient',
sha(str(random.random()).encode('utf8')).hexdigest(), sha(str(random.random()).encode('utf8')).hexdigest(),
str(AUTH_LEVEL_ADMIN) str(AUTH_LEVEL_ADMIN),
]) + '\n') ]) + '\n',
)
_file.flush() _file.flush()
os.fsync(_file.fileno()) os.fsync(_file.fileno())

View file

@ -146,10 +146,12 @@ class Component(object):
elif self._component_state == 'Started': elif self._component_state == 'Started':
d = succeed(True) d = succeed(True)
else: else:
d = fail(ComponentException('Trying to start component "%s" but it is ' d = fail(ComponentException(
'Trying to start component "%s" but it is '
'not in a stopped state. Current state: %s' % 'not in a stopped state. Current state: %s' %
(self._component_name, self._component_state), (self._component_name, self._component_state),
traceback.format_stack(limit=4))) traceback.format_stack(limit=4),
))
return d return d
def _component_stop(self): def _component_stop(self):
@ -193,10 +195,12 @@ class Component(object):
elif self._component_state == 'Paused': elif self._component_state == 'Paused':
d = succeed(None) d = succeed(None)
else: else:
d = fail(ComponentException('Trying to pause component "%s" but it is ' d = fail(ComponentException(
'Trying to pause component "%s" but it is '
'not in a started state. Current state: %s' % 'not in a started state. Current state: %s' %
(self._component_name, self._component_state), (self._component_name, self._component_state),
traceback.format_stack(limit=4))) traceback.format_stack(limit=4),
))
return d return d
def _component_resume(self): def _component_resume(self):
@ -207,10 +211,12 @@ class Component(object):
d = maybeDeferred(self._component_start_timer) d = maybeDeferred(self._component_start_timer)
d.addCallback(on_resume) d.addCallback(on_resume)
else: else:
d = fail(ComponentException('Trying to resume component "%s" but it is ' d = fail(ComponentException(
'Trying to resume component "%s" but it is '
'not in a paused state. Current state: %s' % 'not in a paused state. Current state: %s' %
(self._component_name, self._component_state), (self._component_name, self._component_state),
traceback.format_stack(limit=4))) traceback.format_stack(limit=4),
))
return d return d
def _component_shutdown(self): def _component_shutdown(self):

View file

@ -128,7 +128,7 @@ class Config(object):
# These hold the version numbers and they will be set when loaded # These hold the version numbers and they will be set when loaded
self.__version = { self.__version = {
'format': 1, 'format': 1,
'file': file_version 'file': file_version,
} }
# This will get set with a reactor.callLater whenever a config option # This will get set with a reactor.callLater whenever a config option
@ -189,7 +189,8 @@ class Config(object):
# Do not allow the type to change unless it is None # Do not allow the type to change unless it is None
if value is not None and not isinstance( if value is not None and not isinstance(
self.__config[key], type(None)) and not isinstance(self.__config[key], type(value)): self.__config[key], type(None),
) and not isinstance(self.__config[key], type(value)):
try: try:
oldtype = type(self.__config[key]) oldtype = type(self.__config[key])
value = oldtype(value) value = oldtype(value)
@ -427,8 +428,10 @@ class Config(object):
log.exception(ex) log.exception(ex)
log.warning('Unable to load config file: %s', filename) log.warning('Unable to load config file: %s', filename)
log.debug('Config %s version: %s.%s loaded: %s', filename, log.debug(
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): def save(self, filename=None):
"""Save configuration to disk. """Save configuration to disk.
@ -510,16 +513,20 @@ class Config(object):
raise ValueError('output_version needs to be greater than input_range') raise ValueError('output_version needs to be greater than input_range')
if self.__version['file'] not in input_range: if self.__version['file'] not in input_range:
log.debug('File version %s is not in input_range %s, ignoring converter function..', log.debug(
self.__version['file'], input_range) 'File version %s is not in input_range %s, ignoring converter function..',
self.__version['file'], input_range,
)
return return
try: try:
self.__config = func(self.__config) self.__config = func(self.__config)
except Exception as ex: except Exception as ex:
log.exception(ex) log.exception(ex)
log.error('There was an exception try to convert config file %s %s to %s', log.error(
self.__config_file, self.__version['file'], output_version) 'There was an exception try to convert config file %s %s to %s',
self.__config_file, self.__version['file'], output_version,
)
raise ex raise ex
else: else:
self.__version['file'] = output_version self.__version['file'] = output_version

View file

@ -94,9 +94,11 @@ class _ConfigManager(object):
log.debug('Getting config: %s', config_file) log.debug('Getting config: %s', config_file)
# Create the config object if not already created # Create the config object if not already created
if config_file not in self.config_files: if config_file not in self.config_files:
self.config_files[config_file] = Config(config_file, defaults, self.config_files[config_file] = Config(
config_file, defaults,
config_dir=self.config_directory, config_dir=self.config_directory,
file_version=file_version) file_version=file_version,
)
return self.config_files[config_file] return self.config_files[config_file]

View file

@ -39,13 +39,15 @@ class AlertManager(component.Component):
self.alert_queue_size = 10000 self.alert_queue_size = 10000
self.set_alert_queue_size(self.alert_queue_size) self.set_alert_queue_size(self.alert_queue_size)
alert_mask = (lt.alert.category_t.error_notification | alert_mask = (
lt.alert.category_t.error_notification |
lt.alert.category_t.port_mapping_notification | lt.alert.category_t.port_mapping_notification |
lt.alert.category_t.storage_notification | lt.alert.category_t.storage_notification |
lt.alert.category_t.tracker_notification | lt.alert.category_t.tracker_notification |
lt.alert.category_t.status_notification | lt.alert.category_t.status_notification |
lt.alert.category_t.ip_block_notification | lt.alert.category_t.ip_block_notification |
lt.alert.category_t.performance_warning) lt.alert.category_t.performance_warning
)
self.session.apply_settings({'alert_mask': alert_mask}) self.session.apply_settings({'alert_mask': alert_mask})

View file

@ -28,7 +28,8 @@ AUTH_LEVELS_MAPPING = {
'READONLY': AUTH_LEVEL_READONLY, 'READONLY': AUTH_LEVEL_READONLY,
'DEFAULT': AUTH_LEVEL_NORMAL, 'DEFAULT': AUTH_LEVEL_NORMAL,
'NORMAL': AUTH_LEVEL_DEFAULT, 'NORMAL': AUTH_LEVEL_DEFAULT,
'ADMIN': AUTH_LEVEL_ADMIN} 'ADMIN': AUTH_LEVEL_ADMIN,
}
AUTH_LEVELS_MAPPING_REVERSE = {v: k for k, v in AUTH_LEVELS_MAPPING.items()} AUTH_LEVELS_MAPPING_REVERSE = {v: k for k, v in AUTH_LEVELS_MAPPING.items()}
@ -45,7 +46,7 @@ class Account(object):
'username': self.username, 'username': self.username,
'password': self.password, 'password': self.password,
'authlevel': AUTH_LEVELS_MAPPING_REVERSE[self.authlevel], 'authlevel': AUTH_LEVELS_MAPPING_REVERSE[self.authlevel],
'authlevel_int': self.authlevel 'authlevel_int': self.authlevel,
} }
def __repr__(self): def __repr__(self):
@ -98,7 +99,7 @@ class AuthManager(component.Component):
""" """
if not username: if not username:
raise AuthenticationRequired( raise AuthenticationRequired(
'Username and Password are required.', username 'Username and Password are required.', username,
) )
if username not in self.__auth: if username not in self.__auth:
@ -129,8 +130,10 @@ class AuthManager(component.Component):
if authlevel not in AUTH_LEVELS_MAPPING: if authlevel not in AUTH_LEVELS_MAPPING:
raise AuthManagerError('Invalid auth level: %s' % authlevel) raise AuthManagerError('Invalid auth level: %s' % authlevel)
try: try:
self.__auth[username] = Account(username, password, self.__auth[username] = Account(
AUTH_LEVELS_MAPPING[authlevel]) username, password,
AUTH_LEVELS_MAPPING[authlevel],
)
self.write_auth_file() self.write_auth_file()
return True return True
except Exception as ex: except Exception as ex:
@ -157,7 +160,7 @@ class AuthManager(component.Component):
raise AuthManagerError('Username not known', username) raise AuthManagerError('Username not known', username)
elif username == component.get('RPCServer').get_session_user(): elif username == component.get('RPCServer').get_session_user():
raise AuthManagerError( 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] del self.__auth[username]
@ -232,8 +235,10 @@ class AuthManager(component.Component):
lsplit = line.split(':') lsplit = line.split(':')
if len(lsplit) == 2: if len(lsplit) == 2:
username, password = lsplit username, password = lsplit
log.warning('Your auth entry for %s contains no auth level, ' log.warning(
'using AUTH_LEVEL_DEFAULT(%s)..', username, AUTH_LEVEL_DEFAULT) 'Your auth entry for %s contains no auth level, '
'using AUTH_LEVEL_DEFAULT(%s)..', username, AUTH_LEVEL_DEFAULT,
)
if username == 'localclient': if username == 'localclient':
authlevel = AUTH_LEVEL_ADMIN authlevel = AUTH_LEVEL_ADMIN
else: else:

View file

@ -109,7 +109,8 @@ class Core(component.Component):
peer_id = self._create_peer_id(DELUGE_VER) peer_id = self._create_peer_id(DELUGE_VER)
log.debug( log.debug(
'Starting session (peer_id: %s, user_agent: %s)', 'Starting session (peer_id: %s, user_agent: %s)',
peer_id, user_agent) peer_id, user_agent,
)
settings_pack = { settings_pack = {
'peer_fingerprint': peer_id, 'peer_fingerprint': peer_id,
'user_agent': user_agent, 'user_agent': user_agent,
@ -390,7 +391,7 @@ class Core(component.Component):
try: try:
d = self.torrentmanager.add_async( 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: except RuntimeError as ex:
log.error('There was an error adding the torrent file %s: %s', filename, ex) log.error('There was an error adding the torrent file %s: %s', filename, ex)
@ -418,7 +419,8 @@ class Core(component.Component):
try: try:
return self.torrentmanager.add( return self.torrentmanager.add(
filedump=filedump, options=options, filename=filename) filedump=filedump, options=options, filename=filename,
)
except RuntimeError as ex: except RuntimeError as ex:
log.error('There was an error adding the torrent file %s: %s', filename, ex) log.error('There was an error adding the torrent file %s: %s', filename, ex)
raise raise
@ -441,7 +443,8 @@ class Core(component.Component):
for idx, torrent in enumerate(torrent_files): for idx, torrent in enumerate(torrent_files):
try: try:
yield self.add_torrent_file_async( 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: except AddTorrentError as ex:
log.warn('Error when adding torrent: %s', ex) log.warn('Error when adding torrent: %s', ex)
errors.append(ex) errors.append(ex)
@ -651,8 +654,10 @@ class Core(component.Component):
@export @export
def get_torrent_status(self, torrent_id, keys, diff=False): 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, return self.create_torrent_status(
all_keys=not keys) torrent_id, torrent_keys, plugin_keys, diff=diff, update=True,
all_keys=not keys,
)
@export @export
def get_torrents_status(self, filter_dict, keys, diff=False): def get_torrents_status(self, filter_dict, keys, diff=False):
@ -699,7 +704,7 @@ class Core(component.Component):
@export @export
def get_config_values(self, keys): def get_config_values(self, keys):
"""Get the config values for the entered keys""" """Get the config values for the entered keys"""
return dict((key, self.config.get(key)) for key in keys) return {key: self.config.get(key) for key in keys}
@export @export
def set_config(self, config): def set_config(self, config):
@ -740,7 +745,7 @@ class Core(component.Component):
'port': proxy_port, 'port': proxy_port,
'proxy_hostnames': settings['proxy_hostnames'], 'proxy_hostnames': settings['proxy_hostnames'],
'proxy_peer_connections': settings['proxy_peer_connections'], 'proxy_peer_connections': settings['proxy_peer_connections'],
'proxy_tracker_connections': settings['proxy_tracker_connections'] 'proxy_tracker_connections': settings['proxy_tracker_connections'],
} }
return proxy_dict return proxy_dict
@ -870,11 +875,14 @@ class Core(component.Component):
return deluge.common.get_path_size(path) return deluge.common.get_path_size(path)
@export @export
def create_torrent(self, path, tracker, piece_length, comment, target, def create_torrent(
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..') log.debug('creating torrent..')
threading.Thread(target=self._create_torrent_thread, threading.Thread(
target=self._create_torrent_thread,
args=( args=(
path, path,
tracker, tracker,
@ -885,10 +893,14 @@ class Core(component.Component):
private, private,
created_by, created_by,
trackers, trackers,
add_to_session)).start() add_to_session,
),
).start()
def _create_torrent_thread(self, path, tracker, piece_length, comment, target, def _create_torrent_thread(
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 from deluge import metafile
metafile.make_meta_file( metafile.make_meta_file(
path, path,
@ -899,7 +911,8 @@ class Core(component.Component):
webseeds=webseeds, webseeds=webseeds,
private=private, private=private,
created_by=created_by, created_by=created_by,
trackers=trackers) trackers=trackers,
)
log.debug('torrent created!') log.debug('torrent created!')
if add_to_session: if add_to_session:
options = {} options = {}
@ -1055,8 +1068,10 @@ class Core(component.Component):
:rtype: bool :rtype: bool
""" """
d = getPage(b'http://deluge-torrent.org/test_port.php?port=%s' % d = getPage(
self.get_listen_port(), timeout=30) b'http://deluge-torrent.org/test_port.php?port=%s' %
self.get_listen_port(), timeout=30,
)
def on_get_page(result): def on_get_page(result):
return bool(int(result)) return bool(int(result))

View file

@ -65,8 +65,10 @@ def is_daemon_running(pid_file):
class Daemon(object): class Daemon(object):
"""The Deluge Daemon class""" """The Deluge Daemon class"""
def __init__(self, listen_interface=None, outgoing_interface=None, interface=None, port=None, standalone=False, def __init__(
read_only_config_keys=None): self, listen_interface=None, outgoing_interface=None, interface=None, port=None, standalone=False,
read_only_config_keys=None,
):
""" """
Args: Args:
listen_interface (str, optional): The IP address to listen to bittorrent connections on. listen_interface (str, optional): The IP address to listen to bittorrent connections on.
@ -98,9 +100,11 @@ class Daemon(object):
SetConsoleCtrlHandler(win_handler) SetConsoleCtrlHandler(win_handler)
# Start the core as a thread and join it until it's done # Start the core as a thread and join it until it's done
self.core = Core(listen_interface=listen_interface, self.core = Core(
listen_interface=listen_interface,
outgoing_interface=outgoing_interface, outgoing_interface=outgoing_interface,
read_only_config_keys=read_only_config_keys) read_only_config_keys=read_only_config_keys,
)
if port is None: if port is None:
port = self.core.config['daemon_port'] port = self.core.config['daemon_port']
@ -114,11 +118,13 @@ class Daemon(object):
port=port, port=port,
allow_remote=self.core.config['allow_remote'], allow_remote=self.core.config['allow_remote'],
listen=not standalone, listen=not standalone,
interface=interface interface=interface,
) )
log.debug('Listening to UI on: %s:%s and bittorrent on: %s Making connections out on: %s', log.debug(
interface, port, listen_interface, outgoing_interface) 'Listening to UI on: %s:%s and bittorrent on: %s Making connections out on: %s',
interface, port, listen_interface, outgoing_interface,
)
def start(self): def start(self):
# Register the daemon and the core RPCs # Register the daemon and the core RPCs

View file

@ -23,16 +23,26 @@ from deluge.ui.translations_util import set_dummy_trans
def add_daemon_options(parser): def add_daemon_options(parser):
group = parser.add_argument_group(_('Daemon Options')) group = parser.add_argument_group(_('Daemon Options'))
group.add_argument('-u', '--ui-interface', metavar='<ip-addr>', action='store', group.add_argument(
help=_('IP address to listen for UI connections')) '-u', '--ui-interface', metavar='<ip-addr>', action='store',
group.add_argument('-p', '--port', metavar='<port>', action='store', type=int, help=_('IP address to listen for UI connections'),
help=_('Port to listen for UI connections on')) )
group.add_argument('-i', '--interface', metavar='<ip-addr>', dest='listen_interface', action='store', group.add_argument(
help=_('IP address to listen for BitTorrent connections')) '-p', '--port', metavar='<port>', action='store', type=int,
group.add_argument('-o', '--outinterface', metavar='<ip-addr>', dest='outgoing_interface', help=_('Port to listen for UI connections on'),
action='store', help=_('The IP address for outgoing BitTorrent connections.')) )
group.add_argument('--read-only-config-keys', metavar='<comma-separated-keys>', action='store', group.add_argument(
help=_('Config keys to be unmodified by `set_config` RPC'), type=str, default='') '-i', '--interface', metavar='<ip-addr>', dest='listen_interface', action='store',
help=_('IP address to listen for BitTorrent connections'),
)
group.add_argument(
'-o', '--outinterface', metavar='<ip-addr>', dest='outgoing_interface',
action='store', help=_('The IP address 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='',
)
parser.add_process_arg_group() parser.add_process_arg_group()
@ -59,8 +69,10 @@ def start_daemon(skip_start=False):
from deluge.core.daemon import is_daemon_running from deluge.core.daemon import is_daemon_running
pid_file = get_config_dir('deluged.pid') pid_file = get_config_dir('deluged.pid')
if is_daemon_running(pid_file): if is_daemon_running(pid_file):
print('Cannot run multiple daemons with same config directory.\n' print(
'If you believe this is an error, force starting by deleting: %s' % pid_file) 'Cannot run multiple daemons with same config directory.\n'
'If you believe this is an error, force starting by deleting: %s' % pid_file,
)
sys.exit(1) sys.exit(1)
log = getLogger(__name__) log = getLogger(__name__)
@ -74,19 +86,23 @@ def start_daemon(skip_start=False):
def run_daemon(options): def run_daemon(options):
try: try:
from deluge.core.daemon import Daemon from deluge.core.daemon import Daemon
daemon = Daemon(listen_interface=options.listen_interface, daemon = Daemon(
listen_interface=options.listen_interface,
outgoing_interface=options.outgoing_interface, outgoing_interface=options.outgoing_interface,
interface=options.ui_interface, interface=options.ui_interface,
port=options.port, port=options.port,
read_only_config_keys=options.read_only_config_keys.split(',')) read_only_config_keys=options.read_only_config_keys.split(','),
)
if skip_start: if skip_start:
return daemon return daemon
else: else:
daemon.start() daemon.start()
except CannotListenError as ex: except CannotListenError as ex:
log.error('Cannot start deluged, listen port in use.\n' log.error(
'Cannot start deluged, listen port in use.\n'
' Check for other running daemons or services using this port: %s:%s', ' Check for other running daemons or services using this port: %s:%s',
ex.interface, ex.port) ex.interface, ex.port,
)
sys.exit(1) sys.exit(1)
except Exception as ex: except Exception as ex:
log.error('Unable to start deluged: %s', ex) log.error('Unable to start deluged: %s', ex)

View file

@ -194,7 +194,7 @@ class FilterManager(component.Component):
tree_keys.remove(cat) tree_keys.remove(cat)
torrent_keys, plugin_keys = self.torrents.separate_keys(tree_keys, torrent_ids) torrent_keys, plugin_keys = self.torrents.separate_keys(tree_keys, torrent_ids)
items = dict((field, self.tree_fields[field]()) for field in tree_keys) items = {field: self.tree_fields[field]() for field in tree_keys}
for torrent_id in list(torrent_ids): 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}

View file

@ -33,7 +33,8 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, component.Compon
# Call the PluginManagerBase constructor # Call the PluginManagerBase constructor
deluge.pluginmanagerbase.PluginManagerBase.__init__( deluge.pluginmanagerbase.PluginManagerBase.__init__(
self, 'core.conf', 'deluge.plugin.core') self, 'core.conf', 'deluge.plugin.core',
)
def start(self): def start(self):
# Enable plugins that are enabled in the config # Enable plugins that are enabled in the config

View file

@ -72,8 +72,10 @@ DEFAULT_PREFS = {
'max_upload_speed': -1.0, 'max_upload_speed': -1.0,
'max_download_speed': -1.0, 'max_download_speed': -1.0,
'max_upload_slots_global': 4, 'max_upload_slots_global': 4,
'max_half_open_connections': (lambda: deluge.common.windows_check() and 'max_half_open_connections': (
(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, 'max_connections_per_second': 20,
'ignore_limits_on_local_network': True, 'ignore_limits_on_local_network': True,
'max_connections_per_torrent': -1, 'max_connections_per_torrent': -1,
@ -123,7 +125,7 @@ DEFAULT_PREFS = {
'cache_expiry': 60, 'cache_expiry': 60,
'auto_manage_prefer_seeds': False, 'auto_manage_prefer_seeds': False,
'shared': False, 'shared': False,
'super_seeding': False 'super_seeding': False,
} }
@ -207,19 +209,28 @@ class PreferencesManager(component.Component):
interface = str(self.config['listen_interface'].strip()) interface = str(self.config['listen_interface'].strip())
interface = interface if interface else '0.0.0.0' interface = interface if interface else '0.0.0.0'
log.debug('Listen Interface: %s, Ports: %s with use_sys_port: %s', log.debug(
interface, listen_ports, self.config['listen_use_sys_port']) 'Listen Interface: %s, Ports: %s with use_sys_port: %s',
interfaces = ['%s:%s' % (interface, port) for port in range(listen_ports[0], listen_ports[1]+1)] interface, listen_ports, self.config['listen_use_sys_port'],
)
interfaces = [
'%s:%s' % (interface, port)
for port in range(listen_ports[0], listen_ports[1] + 1)
]
self.core.apply_session_settings( self.core.apply_session_settings(
{'listen_system_port_fallback': self.config['listen_use_sys_port'], {
'listen_interfaces': ''.join(interfaces)}) 'listen_system_port_fallback': self.config['listen_use_sys_port'],
'listen_interfaces': ''.join(interfaces),
},
)
def __set_outgoing_on(self): def __set_outgoing_on(self):
""" Set the interface address for outgoing BitTorrent connections.""" """ Set the interface address for outgoing BitTorrent connections."""
outinterface = self.config['outgoing_interface'].strip() outinterface = self.config['outgoing_interface'].strip()
outinterface = outinterface if outinterface else '0.0.0.0' outinterface = outinterface if outinterface else '0.0.0.0'
self.core.apply_session_settings( self.core.apply_session_settings(
{'outgoing_interfaces': outinterface}) {'outgoing_interfaces': outinterface},
)
def _on_set_outgoing_ports(self, key, value): def _on_set_outgoing_ports(self, key, value):
self.__set_outgoing_ports() self.__set_outgoing_ports()
@ -254,7 +265,8 @@ class PreferencesManager(component.Component):
'router.bitcomet.com:6881', 'router.bitcomet.com:6881',
'dht.transmissionbt.com:6881', 'dht.transmissionbt.com:6881',
'dht.aelitis.com:6881', 'dht.aelitis.com:6881',
]) ],
)
self.core.apply_session_settings({ self.core.apply_session_settings({
'dht_bootstrap_nodes': ','.join(dht_bootstraps), 'dht_bootstrap_nodes': ','.join(dht_bootstraps),
'enable_dht': value, 'enable_dht': value,
@ -286,10 +298,13 @@ class PreferencesManager(component.Component):
# Convert Deluge enc_level values to libtorrent enc_level values. # 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( self.core.apply_session_settings(
{'out_enc_policy': lt.enc_policy(self.config['enc_out_policy']), {
'out_enc_policy': lt.enc_policy(self.config['enc_out_policy']),
'in_enc_policy': lt.enc_policy(self.config['enc_in_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}) 'prefer_rc4': True,
},
)
def _on_set_max_connections_global(self, key, value): def _on_set_max_connections_global(self, key, value):
self.core.apply_session_setting('connections_limit', value) self.core.apply_session_setting('connections_limit', value)
@ -376,7 +391,8 @@ class PreferencesManager(component.Component):
self.new_release_timer.stop() self.new_release_timer.stop()
# Set a timer to check for a new release every 3 days # Set a timer to check for a new release every 3 days
self.new_release_timer = LoopingCall( 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) self.new_release_timer.start(72 * 60 * 60, False)
else: else:
if self.new_release_timer and self.new_release_timer.running: if self.new_release_timer and self.new_release_timer.running:
@ -392,7 +408,7 @@ class PreferencesManager(component.Component):
'proxy_peer_connections': value['proxy_peer_connections'], 'proxy_peer_connections': value['proxy_peer_connections'],
'proxy_tracker_connections': value['proxy_tracker_connections'], 'proxy_tracker_connections': value['proxy_tracker_connections'],
'force_proxy': value['force_proxy'], 'force_proxy': value['force_proxy'],
'anonymous_mode': value['anonymous_mode'] 'anonymous_mode': value['anonymous_mode'],
} }
if value['type'] == lt.proxy_type.i2p_proxy: if value['type'] == lt.proxy_type.i2p_proxy:

View file

@ -134,8 +134,10 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
for call in request: for call in request:
if len(call) != 4: if len(call) != 4:
log.debug('Received invalid rpc request: number of items ' log.debug(
'in request is %s', len(call)) 'Received invalid rpc request: number of items '
'in request is %s', len(call),
)
continue continue
# log.debug('RPCRequest: %s', format_request(call)) # log.debug('RPCRequest: %s', format_request(call))
reactor.callLater(0, self.dispatch, *call) reactor.callLater(0, self.dispatch, *call)
@ -161,11 +163,14 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
This method is called when a new client connects. This method is called when a new client connects.
""" """
peer = self.transport.getPeer() peer = self.transport.getPeer()
log.info('Deluge Client connection made from: %s:%s', log.info(
peer.host, peer.port) 'Deluge Client connection made from: %s:%s',
peer.host, peer.port,
)
# Set the initial auth level of this session to AUTH_LEVEL_NONE # Set the initial auth level of this session to AUTH_LEVEL_NONE
self.factory.authorized_sessions[ self.factory.authorized_sessions[
self.transport.sessionno] = self.AuthLevel(AUTH_LEVEL_NONE, '') self.transport.sessionno
] = self.AuthLevel(AUTH_LEVEL_NONE, '')
def connectionLost(self, reason=connectionDone): # NOQA: N802 def connectionLost(self, reason=connectionDone): # NOQA: N802
""" """
@ -219,16 +224,19 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
exc_type.__name__, exc_type.__name__,
exc_value._args, exc_value._args,
exc_value._kwargs, exc_value._kwargs,
formated_tb formated_tb,
)) ))
except AttributeError: except AttributeError:
# This is not a deluge exception (object has no attribute '_args), let's wrap it # 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 ' log.warning(
'An exception occurred while sending RPC_ERROR to '
'client. Wrapping it and resending. 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: try:
raise WrappedException( raise WrappedException(
str(exc_value), exc_type.__name__, formated_tb) str(exc_value), exc_type.__name__, formated_tb,
)
except WrappedException: except WrappedException:
send_error() send_error()
except Exception as ex: except Exception as ex:
@ -249,7 +257,8 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
ret = component.get('AuthManager').authorize(*args, **kwargs) ret = component.get('AuthManager').authorize(*args, **kwargs)
if ret: if ret:
self.factory.authorized_sessions[ self.factory.authorized_sessions[
self.transport.sessionno] = self.AuthLevel(ret, args[0]) self.transport.sessionno
] = self.AuthLevel(ret, args[0])
self.factory.session_protocols[self.transport.sessionno] = self self.factory.session_protocols[self.transport.sessionno] = self
except Exception as ex: except Exception as ex:
send_error() send_error()
@ -294,8 +303,10 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
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: if auth_level < method_auth_requirement:
# This session is not allowed to call this method # This session is not allowed to call this method
log.debug('Session %s is attempting an unauthorized method call!', log.debug(
self.transport.sessionno) 'Session %s is attempting an unauthorized method call!',
self.transport.sessionno,
)
raise NotAuthorizedError(auth_level, method_auth_requirement) raise NotAuthorizedError(auth_level, method_auth_requirement)
# Set the session_id in the factory so that methods can know # Set the session_id in the factory so that methods can know
# which session is calling it. # which session is calling it.
@ -514,7 +525,7 @@ class RPCServer(component.Component):
log.debug('Emit Event: %s %s', event.name, event.args) log.debug('Emit Event: %s %s', event.name, event.args)
# This session is interested so send a RPC_EVENT # This session is interested so send a RPC_EVENT
self.factory.session_protocols[session_id].sendData( 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): def emit_event_for_session_id(self, session_id, event):
@ -530,14 +541,18 @@ class RPCServer(component.Component):
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 return
if session_id not in self.factory.interested_events: if session_id not in self.factory.interested_events:
log.debug('Session ID %s is not interested in any events. Not sending event "%s".', log.debug(
session_id, event.name) 'Session ID %s is not interested in any events. Not sending event "%s".',
session_id, event.name,
)
return return
if event.name not in self.factory.interested_events[session_id]: 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 return
log.debug('Sending event "%s" with args "%s" to session id "%s".', log.debug(
event.name, event.args, session_id) 'Sending event "%s" with args "%s" to session id "%s".',
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): def stop(self):

View file

@ -52,7 +52,7 @@ LT_TORRENT_STATE_MAP = {
'finished': 'Seeding', 'finished': 'Seeding',
'seeding': 'Seeding', 'seeding': 'Seeding',
'allocating': 'Allocating', 'allocating': 'Allocating',
'checking_resume_data': 'Checking' 'checking_resume_data': 'Checking',
} }
@ -114,7 +114,7 @@ def convert_lt_files(files):
'index': index, 'index': index,
'path': file_path.replace('\\', '/'), 'path': file_path.replace('\\', '/'),
'size': _file.size, 'size': _file.size,
'offset': _file.offset 'offset': _file.offset,
}) })
return filelist return filelist
@ -172,7 +172,7 @@ class TorrentOptions(dict):
'shared': 'shared', 'shared': 'shared',
'stop_at_ratio': 'stop_seed_at_ratio', 'stop_at_ratio': 'stop_seed_at_ratio',
'stop_ratio': 'stop_seed_ratio', 'stop_ratio': 'stop_seed_ratio',
'super_seeding': 'super_seeding' 'super_seeding': 'super_seeding',
} }
for opt_k, conf_k in options_conf_map.items(): for opt_k, conf_k in options_conf_map.items():
self[opt_k] = config[conf_k] self[opt_k] = config[conf_k]
@ -639,8 +639,10 @@ class Torrent(object):
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): if log.isEnabledFor(logging.DEBUG):
log.debug('State from lt was: %s | Session is paused: %s\nTorrent state set from "%s" to "%s" (%s)', log.debug(
'error' if status_error else status.state, session_paused, old_state, self.state, self.torrent_id) '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,
)
if self.forced_error: 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)
@ -699,8 +701,10 @@ class Torrent(object):
eta = 0 eta = 0
if self.is_finished and self.options['stop_at_ratio'] and status.upload_payload_rate: if self.is_finished and self.options['stop_at_ratio'] and status.upload_payload_rate:
# We're a seed, so calculate the time to the 'stop_share_ratio' # We're a seed, so calculate the time to the 'stop_share_ratio'
eta = ((status.all_time_download * self.options['stop_ratio']) - eta = (
status.all_time_upload) // status.upload_payload_rate (status.all_time_download * self.options['stop_ratio']) -
status.all_time_upload
) // status.upload_payload_rate
elif status.download_payload_rate: elif status.download_payload_rate:
left = status.total_wanted - status.total_wanted_done left = status.total_wanted - status.total_wanted_done
if left > 0: if left > 0:
@ -825,8 +829,10 @@ class Torrent(object):
""" """
if not self.has_metadata: if not self.has_metadata:
return [] return []
return [progress / _file.size if _file.size else 0.0 for progress, _file in return [
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): def get_tracker_host(self):
"""Get the hostname of the currently connected tracker. """Get the hostname of the currently connected tracker.
@ -1019,7 +1025,8 @@ class Torrent(object):
'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'], '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), self.status.num_complete / self.status.num_incomplete
),
'seed_rank': lambda: self.status.seed_rank, 'seed_rank': lambda: self.status.seed_rank,
'state': lambda: self.state, 'state': lambda: self.state,
'stop_at_ratio': lambda: self.options['stop_at_ratio'], 'stop_at_ratio': lambda: self.options['stop_at_ratio'],
@ -1061,7 +1068,7 @@ class Torrent(object):
'super_seeding': lambda: self.status.super_seeding, 'super_seeding': lambda: self.status.super_seeding,
'time_since_download': lambda: self.status.time_since_download, 'time_since_download': lambda: self.status.time_since_download,
'time_since_upload': lambda: self.status.time_since_upload, 'time_since_upload': lambda: self.status.time_since_upload,
'time_since_transfer': self.get_time_since_transfer 'time_since_transfer': self.get_time_since_transfer,
} }
def pause(self): def pause(self):
@ -1147,9 +1154,11 @@ class Torrent(object):
try: try:
os.makedirs(dest) os.makedirs(dest)
except OSError as ex: except OSError as ex:
log.error('Could not move storage for torrent %s since %s does ' log.error(
'Could not move storage for torrent %s since %s does '
'not exist and could not create the directory: %s', 'not exist and could not create the directory: %s',
self.torrent_id, dest, ex) self.torrent_id, dest, ex,
)
return False return False
try: try:
@ -1183,7 +1192,8 @@ class Torrent(object):
# Don't generate fastresume data if torrent is in a Deluge Error state. # Don't generate fastresume data if torrent is in a Deluge Error state.
if self.forced_error: if self.forced_error:
component.get('TorrentManager').waiting_on_resume_data[self.torrent_id].errback( component.get('TorrentManager').waiting_on_resume_data[self.torrent_id].errback(
UserWarning('Skipped creating resume_data while in Error state')) UserWarning('Skipped creating resume_data while in Error state'),
)
else: else:
self.handle.save_resume_data(flags) self.handle.save_resume_data(flags)
@ -1315,7 +1325,7 @@ class Torrent(object):
if _file['path'].startswith(folder): if _file['path'].startswith(folder):
# Keep track of filerenames we're waiting on # Keep track of filerenames we're waiting on
wait_on_folder[_file['index']] = Deferred().addBoth( 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) new_path = _file['path'].replace(folder, new_folder, 1)
try: try:

View file

@ -38,7 +38,8 @@ LT_DEFAULT_ADD_TORRENT_FLAGS = (
lt.add_torrent_params_flags_t.flag_paused | lt.add_torrent_params_flags_t.flag_paused |
lt.add_torrent_params_flags_t.flag_auto_managed | 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_update_subscribe |
lt.add_torrent_params_flags_t.flag_apply_ip_filter) lt.add_torrent_params_flags_t.flag_apply_ip_filter
)
class TorrentState: # pylint: disable=old-style-class class TorrentState: # pylint: disable=old-style-class
@ -48,7 +49,8 @@ class TorrentState: # pylint: disable=old-style-class
This must be old style class to avoid breaking torrent.state file. This must be old style class to avoid breaking torrent.state file.
""" """
def __init__(self, def __init__(
self,
torrent_id=None, torrent_id=None,
filename=None, filename=None,
trackers=None, trackers=None,
@ -74,7 +76,8 @@ class TorrentState: # pylint: disable=old-style-class
owner=None, owner=None,
shared=False, shared=False,
super_seeding=False, super_seeding=False,
name=None): name=None,
):
# Build the class atrribute list from args # Build the class atrribute list from args
for key, value in locals().items(): for key, value in locals().items():
if key == 'self': if key == 'self':
@ -113,8 +116,10 @@ class TorrentManager(component.Component):
""" """
def __init__(self): def __init__(self):
component.Component.__init__(self, 'TorrentManager', interval=5, component.Component.__init__(
depend=['CorePluginManager', 'AlertManager']) self, 'TorrentManager', interval=5,
depend=['CorePluginManager', 'AlertManager'],
)
log.debug('TorrentManager init...') log.debug('TorrentManager init...')
# Set the libtorrent session # Set the libtorrent session
self.session = component.get('Core').session self.session = component.get('Core').session
@ -154,8 +159,10 @@ class TorrentManager(component.Component):
self.prev_saved_state = None self.prev_saved_state = None
# Register set functions # Register set functions
set_config_keys = ['max_connections_per_torrent', 'max_upload_slots_per_torrent', set_config_keys = [
'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: for config_key in set_config_keys:
on_set_func = getattr(self, ''.join(['on_set_', config_key])) on_set_func = getattr(self, ''.join(['on_set_', config_key]))
@ -170,7 +177,7 @@ class TorrentManager(component.Component):
'file_renamed_alert', 'file_error_alert', 'file_completed_alert', 'file_renamed_alert', 'file_error_alert', 'file_completed_alert',
'storage_moved_alert', 'storage_moved_failed_alert', 'state_update_alert', 'storage_moved_alert', 'storage_moved_failed_alert', 'state_update_alert',
'state_changed_alert', 'save_resume_data_alert', 'save_resume_data_failed_alert', 'state_changed_alert', 'save_resume_data_alert', 'save_resume_data_failed_alert',
'fastresume_rejected_alert' 'fastresume_rejected_alert',
] ]
for alert_handle in alert_handles: for alert_handle in alert_handles:
@ -230,7 +237,8 @@ class TorrentManager(component.Component):
for torrent_id, torrent in self.torrents.items(): for torrent_id, torrent in self.torrents.items():
# XXX: Should the state check be those that _can_ be stopped at ratio # XXX: Should the state check be those that _can_ be stopped at ratio
if torrent.options['stop_at_ratio'] and torrent.state not in ( 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... # If the global setting is set, but the per-torrent isn't...
# Just skip to the next torrent. # 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 # This is so that a user can turn-off the stop at ratio option on a per-torrent basis
@ -308,7 +316,7 @@ class TorrentManager(component.Component):
return options return options
def _build_torrent_params( 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.""" """Create the add_torrent_params dict for adding torrent to libtorrent."""
add_torrent_params = {} add_torrent_params = {}
@ -361,10 +369,14 @@ class TorrentManager(component.Component):
add_torrent_params['resume_data'] = resume_data add_torrent_params['resume_data'] = resume_data
# Set flags: enable duplicate_is_error & override_resume_data, disable auto_managed. # Set flags: enable duplicate_is_error & override_resume_data, disable auto_managed.
add_torrent_params['flags'] = ((LT_DEFAULT_ADD_TORRENT_FLAGS | 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_duplicate_is_error |
lt.add_torrent_params_flags_t.flag_override_resume_data) ^ lt.add_torrent_params_flags_t.flag_override_resume_data
lt.add_torrent_params_flags_t.flag_auto_managed) ) ^
lt.add_torrent_params_flags_t.flag_auto_managed
)
if options['seed_mode']: if options['seed_mode']:
add_torrent_params['flags'] |= lt.add_torrent_params_flags_t.flag_seed_mode add_torrent_params['flags'] |= lt.add_torrent_params_flags_t.flag_seed_mode
@ -411,7 +423,8 @@ class TorrentManager(component.Component):
options = self._build_torrent_options(options) options = self._build_torrent_options(options)
__, add_torrent_params = self._build_torrent_params( __, 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 # We need to pause the AlertManager momentarily to prevent alerts
# for this torrent being generated before a Torrent object is created. # for this torrent being generated before a Torrent object is created.
@ -426,7 +439,8 @@ class TorrentManager(component.Component):
raise AddTorrentError('Unable to add torrent to session: %s' % ex) raise AddTorrentError('Unable to add torrent to session: %s' % ex)
torrent = self._add_torrent_obj( 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 return torrent.torrent_id
def add_async( def add_async(
@ -470,7 +484,8 @@ class TorrentManager(component.Component):
options = self._build_torrent_options(options) options = self._build_torrent_options(options)
torrent_id, add_torrent_params = self._build_torrent_params( torrent_id, add_torrent_params = self._build_torrent_params(
torrent_info, magnet, options, resume_data) torrent_info, magnet, options, resume_data,
)
d = Deferred() 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)
@ -509,10 +524,12 @@ class TorrentManager(component.Component):
log.debug('Torrent added: %s', str(handle.info_hash())) log.debug('Torrent added: %s', str(handle.info_hash()))
if log.isEnabledFor(logging.INFO): if log.isEnabledFor(logging.INFO):
name_and_owner = torrent.get_status(['name', 'owner']) name_and_owner = torrent.get_status(['name', 'owner'])
log.info('Torrent %s from user "%s" %s', log.info(
'Torrent %s from user "%s" %s',
name_and_owner['name'], name_and_owner['name'],
name_and_owner['owner'], name_and_owner['owner'],
from_state and 'loaded' or 'added') from_state and 'loaded' or 'added',
)
# Write the .torrent file to the state directory. # Write the .torrent file to the state directory.
if filedump: if filedump:
@ -525,10 +542,11 @@ class TorrentManager(component.Component):
return torrent return torrent
def add_async_callback( 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( 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) d.callback(torrent.torrent_id)
@ -670,7 +688,8 @@ class TorrentManager(component.Component):
magnet = t_state.magnet magnet = t_state.magnet
torrent_info = self.get_torrent_info_from_file( 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'),
)
if torrent_info: if torrent_info:
magnet = None magnet = None
@ -740,7 +759,7 @@ class TorrentManager(component.Component):
torrent.options['owner'], torrent.options['owner'],
torrent.options['shared'], torrent.options['shared'],
torrent.options['super_seeding'], torrent.options['super_seeding'],
torrent.options['name'] torrent.options['name'],
) )
state.torrents.append(torrent_state) state.torrents.append(torrent_state)
return state return state
@ -1058,9 +1077,11 @@ class TorrentManager(component.Component):
if log.isEnabledFor(logging.DEBUG): if log.isEnabledFor(logging.DEBUG):
log.debug('Finished %s ', torrent_id) log.debug('Finished %s ', torrent_id)
log.debug('Torrent settings: is_finished: %s, total_download: %s, move_completed: %s, move_path: %s', 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.options['move_completed_path'],
)
torrent.update_state() torrent.update_state()
if not torrent.is_finished and total_download: if not torrent.is_finished and total_download:

View file

@ -129,8 +129,10 @@ def deprecated(func):
@wraps(func) @wraps(func)
def depr_func(*args, **kwargs): def depr_func(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning) # Turn off filter warnings.simplefilter('always', DeprecationWarning) # Turn off filter
warnings.warn('Call to deprecated function {}.'.format(func.__name__), warnings.warn(
category=DeprecationWarning, stacklevel=2) 'Call to deprecated function {}.'.format(func.__name__),
category=DeprecationWarning, stacklevel=2,
)
warnings.simplefilter('default', DeprecationWarning) # Reset filter warnings.simplefilter('default', DeprecationWarning) # Reset filter
return func(*args, **kwargs) return func(*args, **kwargs)

View file

@ -63,17 +63,22 @@ class IncompatibleClient(_ClientSideRecreateError):
def __init__(self, daemon_version): def __init__(self, daemon_version):
self.daemon_version = daemon_version self.daemon_version = daemon_version
msg = 'Your deluge client is not compatible with the daemon. '\ msg = (
'Please upgrade your client to %(daemon_version)s' % \ 'Your deluge client is not compatible with the daemon. '
dict(daemon_version=self.daemon_version) 'Please upgrade your client to %(daemon_version)s'
) % {'daemon_version': self.daemon_version}
super(IncompatibleClient, self).__init__(message=msg) super(IncompatibleClient, self).__init__(message=msg)
class NotAuthorizedError(_ClientSideRecreateError): class NotAuthorizedError(_ClientSideRecreateError):
def __init__(self, current_level, required_level): def __init__(self, current_level, required_level):
msg = 'Auth level too low: %(current_level)s < %(required_level)s' % \ msg = (
dict(current_level=current_level, required_level=required_level) 'Auth level too low: %(current_level)s < %(required_level)s'
) % {
'current_level': current_level,
'required_level': required_level,
}
super(NotAuthorizedError, self).__init__(message=msg) super(NotAuthorizedError, self).__init__(message=msg)
self.current_level = current_level self.current_level = current_level
self.required_level = required_level self.required_level = required_level

View file

@ -34,8 +34,10 @@ class HTTPDownloader(client.HTTPDownloader):
""" """
Factory class for downloading files and keeping track of progress. Factory class for downloading files and keeping track of progress.
""" """
def __init__(self, url, filename, part_callback=None, headers=None, def __init__(
force_filename=False, allow_compression=True): self, url, filename, part_callback=None, headers=None,
force_filename=False, allow_compression=True,
):
""" """
:param url: the url to download from :param url: the url to download from
:type url: string :type url: string
@ -227,8 +229,10 @@ def _download_file(url, filename, callback=None, headers=None, force_filename=Fa
return factory.deferred return factory.deferred
def download_file(url, filename, callback=None, headers=None, force_filename=False, def download_file(
allow_compression=True, handle_redirects=True): url, filename, callback=None, headers=None, force_filename=False,
allow_compression=True, handle_redirects=True,
):
""" """
Downloads a file from a specific URL and returns a Deferred. A callback Downloads a file from a specific URL and returns a Deferred. A callback
function can be specified to be called as parts are received. function can be specified to be called as parts are received.
@ -259,18 +263,24 @@ def download_file(url, filename, callback=None, headers=None, force_filename=Fal
def on_download_fail(failure): def on_download_fail(failure):
if failure.check(PageRedirect) and handle_redirects: if failure.check(PageRedirect) and handle_redirects:
new_url = urljoin(url, failure.getErrorMessage().split(' to ')[1]) new_url = urljoin(url, failure.getErrorMessage().split(' to ')[1])
result = _download_file(new_url, filename, callback=callback, headers=headers, result = _download_file(
new_url, filename, callback=callback, headers=headers,
force_filename=force_filename, force_filename=force_filename,
allow_compression=allow_compression) allow_compression=allow_compression,
)
result.addCallbacks(on_download_success, on_download_fail) result.addCallbacks(on_download_success, on_download_fail)
else: else:
# Log the failure and pass to the caller # Log the failure and pass to the caller
log.warning('Error occurred downloading file from "%s": %s', log.warning(
url, failure.getErrorMessage()) 'Error occurred downloading file from "%s": %s',
url, failure.getErrorMessage(),
)
result = failure result = failure
return result return result
d = _download_file(url, filename, callback=callback, headers=headers, d = _download_file(
force_filename=force_filename, allow_compression=allow_compression) url, filename, callback=callback, headers=headers,
force_filename=force_filename, allow_compression=allow_compression,
)
d.addCallbacks(on_download_success, on_download_fail) d.addCallbacks(on_download_success, on_download_fail)
return d return d

View file

@ -45,7 +45,7 @@ class Logging(LoggingLoggerClass):
for handler in logging.getLogger().handlers: for handler in logging.getLogger().handlers:
handler.setFormatter(logging.Formatter( handler.setFormatter(logging.Formatter(
DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH, DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH,
datefmt='%H:%M:%S' datefmt='%H:%M:%S',
)) ))
@defer.inlineCallbacks @defer.inlineCallbacks
@ -88,8 +88,10 @@ class Logging(LoggingLoggerClass):
while hasattr(f, 'f_code'): while hasattr(f, 'f_code'):
co = f.f_code co = f.f_code
filename = os.path.normcase(co.co_filename) filename = os.path.normcase(co.co_filename)
if filename in (__file__.replace('.pyc', '.py'), if filename in (
defer.__file__.replace('.pyc', '.py')): __file__.replace('.pyc', '.py'),
defer.__file__.replace('.pyc', '.py'),
):
f = f.f_back f = f.f_back
continue continue
rv = (filename, f.f_lineno, co.co_name) rv = (filename, f.f_lineno, co.co_name)
@ -105,12 +107,14 @@ levels = {
'none': logging.CRITICAL, 'none': logging.CRITICAL,
'debug': logging.DEBUG, 'debug': logging.DEBUG,
'trace': 5, 'trace': 5,
'garbage': 1 'garbage': 1,
} }
def setup_logger(level='error', filename=None, filemode='w', logrotate=None, def setup_logger(
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 Sets up the basic logger and if `:param:filename` is set, then it will log
to that file instead of stdout. to that file instead of stdout.
@ -137,7 +141,7 @@ def setup_logger(level='error', filename=None, filemode='w', logrotate=None,
if filename and logrotate: if filename and logrotate:
handler = logging.handlers.RotatingFileHandler( handler = logging.handlers.RotatingFileHandler(
filename, maxBytes=logrotate, filename, maxBytes=logrotate,
backupCount=5, encoding='utf-8' backupCount=5, encoding='utf-8',
) )
elif filename and filemode == 'w': elif filename and filemode == 'w':
handler_cls = logging.FileHandler handler_cls = logging.FileHandler
@ -151,7 +155,7 @@ def setup_logger(level='error', filename=None, filemode='w', logrotate=None,
formatter = logging.Formatter( formatter = logging.Formatter(
DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH, DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH,
datefmt='%H:%M:%S' datefmt='%H:%M:%S',
) )
handler.setFormatter(formatter) handler.setFormatter(formatter)
@ -214,8 +218,10 @@ def tweak_logging_levels():
if not os.path.isfile(logging_config_file): if not os.path.isfile(logging_config_file):
return return
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.warn('logging.conf found! tweaking logging levels from %s', log.warn(
logging_config_file) 'logging.conf found! tweaking logging levels from %s',
logging_config_file,
)
with open(logging_config_file, 'r') as _file: with open(logging_config_file, 'r') as _file:
for line in _file: for line in _file:
if line.strip().startswith('#'): if line.strip().startswith('#'):
@ -249,9 +255,11 @@ def get_plugin_logger(logger_name):
caller_module = inspect.getmodule(module_stack[0]) caller_module = inspect.getmodule(module_stack[0])
# In some weird cases caller_module might be None, try to continue # In some weird cases caller_module might be None, try to continue
caller_module_name = getattr(caller_module, '__name__', '') caller_module_name = getattr(caller_module, '__name__', '')
warnings.warn_explicit(DEPRECATION_WARNING, DeprecationWarning, warnings.warn_explicit(
DEPRECATION_WARNING, DeprecationWarning,
module_stack[1], module_stack[2], module_stack[1], module_stack[2],
caller_module_name) caller_module_name,
)
if 'deluge.plugins.' in logger_name: if 'deluge.plugins.' in logger_name:
return logging.getLogger(logger_name) return logging.getLogger(logger_name)
@ -291,16 +299,20 @@ class _BackwardsCompatibleLOG(object):
caller_module = inspect.getmodule(module_stack[0]) caller_module = inspect.getmodule(module_stack[0])
# In some weird cases caller_module might be None, try to continue # In some weird cases caller_module might be None, try to continue
caller_module_name = getattr(caller_module, '__name__', '') caller_module_name = getattr(caller_module, '__name__', '')
warnings.warn_explicit(DEPRECATION_WARNING, DeprecationWarning, warnings.warn_explicit(
DEPRECATION_WARNING, DeprecationWarning,
module_stack[1], module_stack[2], module_stack[1], module_stack[2],
caller_module_name) caller_module_name,
)
if caller_module: if caller_module:
for member in stack: for member in stack:
module = inspect.getmodule(member[0]) module = inspect.getmodule(member[0])
if not module: if not module:
continue continue
if module.__name__ in ('deluge.plugins.pluginbase', if module.__name__ in (
'deluge.plugins.init'): 'deluge.plugins.pluginbase',
'deluge.plugins.init',
):
logger_name += '.plugin.%s' % caller_module_name logger_name += '.plugin.%s' % caller_module_name
# Monkey Patch The Plugin Module # Monkey Patch The Plugin Module
caller_module.log = logging.getLogger(logger_name) caller_module.log = logging.getLogger(logger_name)
@ -308,7 +320,7 @@ class _BackwardsCompatibleLOG(object):
else: else:
logging.getLogger(logger_name).warning( logging.getLogger(logger_name).warning(
"Unable to monkey-patch the calling module's `log` attribute! " "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) return getattr(logging.getLogger(logger_name), name)

View file

@ -68,7 +68,7 @@ class TorrentMetadata(object):
raise InvalidPath('Need to set a data_path!') raise InvalidPath('Need to set a data_path!')
torrent = { torrent = {
'info': {} 'info': {},
} }
if self.comment: if self.comment:

View file

@ -50,13 +50,15 @@ class RemoteFileProgress(object):
def __call__(self, piece_count, num_pieces): def __call__(self, piece_count, num_pieces):
component.get('RPCServer').emit_event_for_session_id( 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, 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, safe=None, content_type=None, target=None, webseeds=None, name=None,
private=False, created_by=None, trackers=None): private=False, created_by=None, trackers=None,
):
data = {'creation date': int(gmtime())} data = {'creation date': int(gmtime())}
if url: if url:
data['announce'] = url.strip() data['announce'] = url.strip()
@ -147,8 +149,10 @@ def makeinfo(path, piece_length, progress, name=None, content_type=None, private
size = os.path.getsize(f) size = os.path.getsize(f)
p2 = [n.encode('utf8') for n in p] p2 = [n.encode('utf8') for n in p]
if content_type: if content_type:
fs.append({'length': size, 'path': p2, fs.append({
'content_type': content_type}) # HEREDAVE. bad for batch! 'length': size, 'path': p2,
'content_type': content_type,
}) # HEREDAVE. bad for batch!
else: else:
fs.append({'length': size, 'path': p2}) fs.append({'length': size, 'path': p2})
with open(f, 'rb') as file_: with open(f, 'rb') as file_:
@ -173,11 +177,13 @@ def makeinfo(path, piece_length, progress, name=None, content_type=None, private
if not name: if not name:
name = os.path.split(path)[1] name = os.path.split(path)[1]
return {'pieces': b''.join(pieces), return {
'pieces': b''.join(pieces),
'piece length': piece_length, 'piece length': piece_length,
'files': fs, 'files': fs,
'name': name.encode('utf8'), 'name': name.encode('utf8'),
'private': private} 'private': private,
}
else: else:
size = os.path.getsize(path) size = os.path.getsize(path)
if size >= piece_length: if size >= piece_length:
@ -198,15 +204,19 @@ def makeinfo(path, piece_length, progress, name=None, content_type=None, private
progress(piece_count, num_pieces) progress(piece_count, num_pieces)
name = os.path.split(path)[1].encode('utf8') name = os.path.split(path)[1].encode('utf8')
if content_type is not None: if content_type is not None:
return {'pieces': b''.join(pieces), return {
'pieces': b''.join(pieces),
'piece length': piece_length, 'length': size, 'piece length': piece_length, 'length': size,
'name': name, 'name': name,
'content_type': content_type, 'content_type': content_type,
'private': private} 'private': private,
return {'pieces': b''.join(pieces), }
return {
'pieces': b''.join(pieces),
'piece length': piece_length, 'length': size, 'piece length': piece_length, 'length': size,
'name': name, 'name': name,
'private': private} 'private': private,
}
def subfiles(d): def subfiles(d):

View file

@ -108,10 +108,12 @@ class PluginManagerBase(object):
self.available_plugins = [] self.available_plugins = []
for name in self.pkg_env: for name in self.pkg_env:
log.debug('Found plugin: %s %s at %s', log.debug(
'Found plugin: %s %s at %s',
self.pkg_env[name][0].project_name, self.pkg_env[name][0].project_name,
self.pkg_env[name][0].version, self.pkg_env[name][0].version,
self.pkg_env[name][0].location) self.pkg_env[name][0].location,
)
self.available_plugins.append(self.pkg_env[name][0].project_name) self.available_plugins.append(self.pkg_env[name][0].project_name)
def enable_plugin(self, plugin_name): def enable_plugin(self, plugin_name):
@ -162,7 +164,7 @@ class PluginManagerBase(object):
warnings.warn_explicit( warnings.warn_explicit(
DEPRECATION_WARNING % name, DEPRECATION_WARNING % name,
DeprecationWarning, DeprecationWarning,
instance.__module__, 0 instance.__module__, 0,
) )
if self._component_state == 'Started': if self._component_state == 'Started':
def on_enabled(result, instance): def on_enabled(result, instance):
@ -179,8 +181,10 @@ class PluginManagerBase(object):
return True return True
def on_started_error(result, instance): def on_started_error(result, instance):
log.error('Failed to start plugin: %s\n%s', plugin_name, log.error(
result.getTraceback(elideFrameworkCode=1, detail='brief')) 'Failed to start plugin: %s\n%s', plugin_name,
result.getTraceback(elideFrameworkCode=1, detail='brief'),
)
self.plugins[plugin_name.replace('-', ' ')] = instance self.plugins[plugin_name.replace('-', ' ')] = instance
self.disable_plugin(plugin_name) self.disable_plugin(plugin_name)
return False return False

View file

@ -37,7 +37,7 @@ log = logging.getLogger(__name__)
DEFAULT_PREFS = { DEFAULT_PREFS = {
'watchdirs': {}, 'watchdirs': {},
'next_id': 1 'next_id': 1,
} }
@ -64,7 +64,7 @@ OPTIONS_AVAILABLE = { # option: builtin
'add_paused': True, 'add_paused': True,
'queue_to_top': False, 'queue_to_top': False,
'owner': True, 'owner': True,
'seed_mode': True 'seed_mode': True,
} }
MAX_NUM_ATTEMPTS = 10 MAX_NUM_ATTEMPTS = 10
@ -91,7 +91,7 @@ class Core(CorePluginBase):
self.watchdirs = self.config['watchdirs'] self.watchdirs = self.config['watchdirs']
component.get('EventManager').register_event_handler( component.get('EventManager').register_event_handler(
'PreTorrentRemovedEvent', self.__on_pre_torrent_removed 'PreTorrentRemovedEvent', self.__on_pre_torrent_removed,
) )
# Dict of Filename:Attempts # Dict of Filename:Attempts
@ -109,7 +109,7 @@ class Core(CorePluginBase):
def disable(self): def disable(self):
# disable all running looping calls # disable all running looping calls
component.get('EventManager').deregister_event_handler( 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(): for loopingcall in self.update_timers.values():
loopingcall.stop() loopingcall.stop()
@ -124,12 +124,12 @@ class Core(CorePluginBase):
watchdir_id = str(watchdir_id) watchdir_id = str(watchdir_id)
options = self._make_unicode(options) options = self._make_unicode(options)
check_input( check_input(
watchdir_id in self.watchdirs, _('Watch folder does not exist.') watchdir_id in self.watchdirs, _('Watch folder does not exist.'),
) )
if 'path' in options: if 'path' in options:
options['abspath'] = os.path.abspath(options['path']) options['abspath'] = os.path.abspath(options['path'])
check_input( check_input(
os.path.isdir(options['abspath']), _('Path does not exist.') os.path.isdir(options['abspath']), _('Path does not exist.'),
) )
for w_id, w in self.watchdirs.items(): for w_id, w in self.watchdirs.items():
if options['abspath'] == w['abspath'] and watchdir_id != w_id: if options['abspath'] == w['abspath'] and watchdir_id != w_id:
@ -210,8 +210,10 @@ class Core(CorePluginBase):
watchdir = self.watchdirs[watchdir_id] watchdir = self.watchdirs[watchdir_id]
if not watchdir['enabled']: if not watchdir['enabled']:
# We shouldn't be updating because this watchdir is not enabled # We shouldn't be updating because this watchdir is not enabled
log.debug('Watchdir id %s is not enabled. Disabling it.', log.debug(
watchdir_id) 'Watchdir id %s is not enabled. Disabling it.',
watchdir_id,
)
self.disable_watchdir(watchdir_id) self.disable_watchdir(watchdir_id)
return return
@ -271,8 +273,10 @@ class Core(CorePluginBase):
if filename in self.invalid_torrents: if filename in self.invalid_torrents:
self.invalid_torrents[filename] += 1 self.invalid_torrents[filename] += 1
if self.invalid_torrents[filename] >= MAX_NUM_ATTEMPTS: if self.invalid_torrents[filename] >= MAX_NUM_ATTEMPTS:
log.warning('Maximum attempts reached while trying to add the ' log.warning(
'torrent file with the path %s', filepath) 'Maximum attempts reached while trying to add the '
'torrent file with the path %s', filepath,
)
os.rename(filepath, filepath + '.invalid') os.rename(filepath, filepath + '.invalid')
del self.invalid_torrents[filename] del self.invalid_torrents[filename]
else: else:
@ -305,8 +309,10 @@ class Core(CorePluginBase):
elif watchdir.get('copy_torrent_toggle'): elif watchdir.get('copy_torrent_toggle'):
copy_torrent_path = watchdir['copy_torrent'] copy_torrent_path = watchdir['copy_torrent']
copy_torrent_file = os.path.join(copy_torrent_path, filename) copy_torrent_file = os.path.join(copy_torrent_path, filename)
log.debug('Moving added torrent file "%s" to "%s"', log.debug(
os.path.basename(filepath), copy_torrent_path) 'Moving added torrent file "%s" to "%s"',
os.path.basename(filepath), copy_torrent_path,
)
shutil.move(filepath, copy_torrent_file) shutil.move(filepath, copy_torrent_file)
else: else:
os.remove(filepath) os.remove(filepath)
@ -322,7 +328,8 @@ class Core(CorePluginBase):
d = component.get('Core').add_torrent_magnet(filedump.strip(), options) d = component.get('Core').add_torrent_magnet(filedump.strip(), options)
else: else:
d = component.get('Core').add_torrent_file_async( d = component.get('Core').add_torrent_file_async(
filename, base64.encodestring(filedump), options) filename, base64.encodestring(filedump), options,
)
d.addCallback(on_torrent_added, filename, filepath) d.addCallback(on_torrent_added, filename, filepath)
d.addErrback(fail_torrent_add, filepath, magnet) d.addErrback(fail_torrent_add, filepath, magnet)
except AddTorrentError as ex: except AddTorrentError as ex:
@ -331,8 +338,10 @@ class Core(CorePluginBase):
def on_update_watchdir_error(self, failure, watchdir_id): def on_update_watchdir_error(self, failure, watchdir_id):
"""Disables any watch folders with un-handled exceptions.""" """Disables any watch folders with un-handled exceptions."""
self.disable_watchdir(watchdir_id) self.disable_watchdir(watchdir_id)
log.error('Disabling "%s", error during update: %s', log.error(
self.watchdirs[watchdir_id]['path'], failure) 'Disabling "%s", error during update: %s',
self.watchdirs[watchdir_id]['path'], failure,
)
@export @export
def enable_watchdir(self, watchdir_id): def enable_watchdir(self, watchdir_id):
@ -341,7 +350,7 @@ class Core(CorePluginBase):
if w_id not in self.update_timers or not self.update_timers[w_id].running: 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] = LoopingCall(self.update_watchdir, w_id)
self.update_timers[w_id].start(5).addErrback( 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 # Update the config
if not self.watchdirs[w_id]['enabled']: if not self.watchdirs[w_id]['enabled']:
@ -383,8 +392,10 @@ class Core(CorePluginBase):
session_user = rpcserver.get_session_user() session_user = rpcserver.get_session_user()
session_auth_level = rpcserver.get_session_auth_level() session_auth_level = rpcserver.get_session_auth_level()
if session_auth_level == AUTH_LEVEL_ADMIN: if session_auth_level == AUTH_LEVEL_ADMIN:
log.debug('Current logged in user %s is an ADMIN, send all ' log.debug(
'watchdirs', session_user) 'Current logged in user %s is an ADMIN, send all '
'watchdirs', session_user,
)
return self.watchdirs return self.watchdirs
watchdirs = {} watchdirs = {}
@ -392,8 +403,10 @@ class Core(CorePluginBase):
if watchdir.get('owner', 'localclient') == session_user: if watchdir.get('owner', 'localclient') == session_user:
watchdirs[watchdir_id] = watchdir watchdirs[watchdir_id] = watchdir
log.debug('Current logged in user %s is not an ADMIN, send only ' log.debug(
'their watchdirs: %s', session_user, list(watchdirs)) 'Current logged in user %s is not an ADMIN, send only '
'their watchdirs: %s', session_user, list(watchdirs),
)
return watchdirs return watchdirs
def _make_unicode(self, options): def _make_unicode(self, options):
@ -414,7 +427,7 @@ class Core(CorePluginBase):
check_input(os.path.isdir(abswatchdir), _('Path does not exist.')) check_input(os.path.isdir(abswatchdir), _('Path does not exist.'))
check_input( check_input(
os.access(abswatchdir, os.R_OK | os.W_OK), os.access(abswatchdir, os.R_OK | os.W_OK),
'You must have read and write access to watch folder.' 'You must have read and write access to watch folder.',
) )
if abswatchdir in [wd['abspath'] for wd in self.watchdirs.values()]: if abswatchdir in [wd['abspath'] for wd in self.watchdirs.values()]:
raise Exception('Path is already being watched.') raise Exception('Path is already being watched.')
@ -449,9 +462,11 @@ class Core(CorePluginBase):
try: try:
torrent = component.get('TorrentManager')[torrent_id] torrent = component.get('TorrentManager')[torrent_id]
except KeyError: except KeyError:
log.warning('Unable to remove torrent file for torrent id %s. It' log.warning(
'Unable to remove torrent file for torrent id %s. It'
'was already deleted from the TorrentManager', 'was already deleted from the TorrentManager',
torrent_id) torrent_id,
)
return return
torrent_fname = torrent.filename torrent_fname = torrent.filename
for watchdir in self.watchdirs.values(): for watchdir in self.watchdirs.values():
@ -466,9 +481,13 @@ class Core(CorePluginBase):
if os.path.isfile(torrent_fname_path): if os.path.isfile(torrent_fname_path):
try: try:
os.remove(torrent_fname_path) os.remove(torrent_fname_path)
log.info('Removed torrent file "%s" from "%s"', log.info(
torrent_fname, copy_torrent_path) 'Removed torrent file "%s" from "%s"',
torrent_fname, copy_torrent_path,
)
break break
except OSError as ex: except OSError as ex:
log.info('Failed to removed torrent file "%s" from "%s": %s', log.info(
torrent_fname, copy_torrent_path, ex) 'Failed to removed torrent file "%s" from "%s": %s',
torrent_fname, copy_torrent_path, ex,
)

View file

@ -37,8 +37,10 @@ class IncompatibleOption(Exception):
class OptionsDialog(object): class OptionsDialog(object):
spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio'] spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio']
spin_int_ids = ['max_upload_slots', 'max_connections'] spin_int_ids = ['max_upload_slots', 'max_connections']
chk_ids = ['stop_at_ratio', 'remove_at_ratio', 'move_completed', chk_ids = [
'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): def __init__(self):
self.accounts = gtk.ListStore(str) self.accounts = gtk.ListStore(str)
@ -55,7 +57,7 @@ class OptionsDialog(object):
'on_opts_apply': self.on_apply, 'on_opts_apply': self.on_apply,
'on_opts_cancel': self.on_cancel, 'on_opts_cancel': self.on_cancel,
'on_options_dialog_close': self.on_cancel, 'on_options_dialog_close': self.on_cancel,
'on_toggle_toggled': self.on_toggle_toggled 'on_toggle_toggled': self.on_toggle_toggled,
}) })
self.dialog = self.builder.get_object('options_dialog') self.dialog = self.builder.get_object('options_dialog')
self.dialog.set_transient_for(component.get('Preferences').pref_dialog) self.dialog.set_transient_for(component.get('Preferences').pref_dialog)
@ -77,22 +79,22 @@ class OptionsDialog(object):
def load_options(self, options): def load_options(self, options):
self.builder.get_object('enabled').set_active(options.get('enabled', True)) self.builder.get_object('enabled').set_active(options.get('enabled', True))
self.builder.get_object('append_extension_toggle').set_active( 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( 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( 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( 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( self.builder.get_object('delete_copy_torrent_toggle').set_active(
options.get('delete_copy_torrent_toggle', False) options.get('delete_copy_torrent_toggle', False),
) )
self.builder.get_object('seed_mode').set_active( self.builder.get_object('seed_mode').set_active(
options.get('seed_mode', False) options.get('seed_mode', False),
) )
self.accounts.clear() self.accounts.clear()
self.labels.clear() self.labels.clear()
@ -120,17 +122,19 @@ class OptionsDialog(object):
self.builder.get_object('isnt_queue_to_top').set_active(True) self.builder.get_object('isnt_queue_to_top').set_active(True)
if not options.get('auto_managed', True): if not options.get('auto_managed', True):
self.builder.get_object('isnt_auto_managed').set_active(True) self.builder.get_object('isnt_auto_managed').set_active(True)
for field in ['move_completed_path', 'path', 'download_location', for field in [
'copy_torrent']: 'move_completed_path', 'path', 'download_location',
'copy_torrent',
]:
if client.is_localhost(): if client.is_localhost():
self.builder.get_object(field + '_chooser').set_current_folder( 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 + '_chooser').show()
self.builder.get_object(field + '_entry').hide() self.builder.get_object(field + '_entry').hide()
else: else:
self.builder.get_object(field + '_entry').set_text( 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 + '_entry').show()
self.builder.get_object(field + '_chooser').hide() self.builder.get_object(field + '_chooser').hide()
@ -139,33 +143,33 @@ class OptionsDialog(object):
def on_core_config(config): def on_core_config(config):
if client.is_localhost(): if client.is_localhost():
self.builder.get_object('download_location_chooser').set_current_folder( 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']): 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_toggle').set_active(True)
self.builder.get_object('move_completed_path_chooser').set_current_folder( self.builder.get_object('move_completed_path_chooser').set_current_folder(
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']): 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_toggle').set_active(True)
self.builder.get_object('copy_torrent_chooser').set_current_folder( 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: else:
self.builder.get_object('download_location_entry').set_text( 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']): if options.get('move_completed_toggle', config['move_completed']):
self.builder.get_object('move_completed_toggle').set_active( 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( 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']): 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_toggle').set_active(True)
self.builder.get_object('copy_torrent_entry').set_text( 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']):
@ -180,7 +184,7 @@ class OptionsDialog(object):
for account in accounts: for account in accounts:
acc_iter = self.accounts.append() acc_iter = self.accounts.append()
self.accounts.set_value( self.accounts.set_value(
acc_iter, 0, account['username'] acc_iter, 0, account['username'],
) )
if account['username'] == owner: if account['username'] == owner:
selected_iter = acc_iter selected_iter = acc_iter
@ -215,7 +219,7 @@ class OptionsDialog(object):
client.core.get_enabled_plugins().addCallback(on_get_enabled_plugins) client.core.get_enabled_plugins().addCallback(on_get_enabled_plugins)
if client.get_auth_level() == deluge.common.AUTH_LEVEL_ADMIN: if client.get_auth_level() == deluge.common.AUTH_LEVEL_ADMIN:
client.core.get_known_accounts().addCallback( 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) ).addErrback(on_accounts_failure)
else: else:
acc_iter = self.accounts.append() acc_iter = self.accounts.append()
@ -224,11 +228,13 @@ class OptionsDialog(object):
self.builder.get_object('OwnerCombobox').set_sensitive(False) self.builder.get_object('OwnerCombobox').set_sensitive(False)
def set_sensitive(self): def set_sensitive(self):
maintoggles = ['download_location', 'append_extension', maintoggles = [
'download_location', 'append_extension',
'move_completed', 'label', 'max_download_speed', 'move_completed', 'label', 'max_download_speed',
'max_upload_speed', 'max_connections', 'max_upload_speed', 'max_connections',
'max_upload_slots', 'add_paused', 'auto_managed', 'max_upload_slots', 'add_paused', 'auto_managed',
'stop_at_ratio', 'queue_to_top', 'copy_torrent'] 'stop_at_ratio', 'queue_to_top', 'copy_torrent',
]
for maintoggle in maintoggles: for maintoggle in maintoggles:
self.on_toggle_toggled(self.builder.get_object(maintoggle + '_toggle')) self.on_toggle_toggled(self.builder.get_object(maintoggle + '_toggle'))
@ -278,7 +284,7 @@ class OptionsDialog(object):
try: try:
options = self.generate_opts() options = self.generate_opts()
client.autoadd.set_options( client.autoadd.set_options(
str(self.watchdir_id), options str(self.watchdir_id), options,
).addCallbacks(self.on_added, self.on_error_show) ).addCallbacks(self.on_added, self.on_error_show)
except IncompatibleOption as ex: except IncompatibleOption as ex:
dialogs.ErrorDialog(_('Incompatible Option'), str(ex), self.dialog).run() dialogs.ErrorDialog(_('Incompatible Option'), str(ex), self.dialog).run()
@ -308,28 +314,37 @@ class OptionsDialog(object):
if client.is_localhost(): if client.is_localhost():
options['path'] = self.builder.get_object('path_chooser').get_filename() options['path'] = self.builder.get_object('path_chooser').get_filename()
options['download_location'] = self.builder.get_object( options['download_location'] = self.builder.get_object(
'download_location_chooser').get_filename() 'download_location_chooser',
).get_filename()
options['move_completed_path'] = self.builder.get_object( options['move_completed_path'] = self.builder.get_object(
'move_completed_path_chooser').get_filename() 'move_completed_path_chooser',
).get_filename()
options['copy_torrent'] = self.builder.get_object( options['copy_torrent'] = self.builder.get_object(
'copy_torrent_chooser').get_filename() 'copy_torrent_chooser',
).get_filename()
else: else:
options['path'] = self.builder.get_object('path_entry').get_text() options['path'] = self.builder.get_object('path_entry').get_text()
options['download_location'] = self.builder.get_object( options['download_location'] = self.builder.get_object(
'download_location_entry').get_text() 'download_location_entry',
).get_text()
options['move_completed_path'] = self.builder.get_object( options['move_completed_path'] = self.builder.get_object(
'move_completed_path_entry').get_text() 'move_completed_path_entry',
).get_text()
options['copy_torrent'] = self.builder.get_object( options['copy_torrent'] = self.builder.get_object(
'copy_torrent_entry').get_text() 'copy_torrent_entry',
).get_text()
options['label'] = self.builder.get_object('label').child.get_text().lower() 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[ options['owner'] = self.accounts[
self.builder.get_object('OwnerCombobox').get_active()][0] self.builder.get_object('OwnerCombobox').get_active()
][0]
for key in ['append_extension_toggle', 'download_location_toggle', for key in [
'append_extension_toggle', 'download_location_toggle',
'label_toggle', 'copy_torrent_toggle', 'label_toggle', 'copy_torrent_toggle',
'delete_copy_torrent_toggle', 'seed_mode']: 'delete_copy_torrent_toggle', 'seed_mode',
]:
options[key] = self.builder.get_object(key).get_active() options[key] = self.builder.get_object(key).get_active()
for spin_id in self.spin_ids: for spin_id in self.spin_ids:
@ -343,8 +358,10 @@ class OptionsDialog(object):
options[chk_id + '_toggle'] = self.builder.get_object(chk_id + '_toggle').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']: if options['copy_torrent_toggle'] and options['path'] == options['copy_torrent']:
raise IncompatibleOption(_('"Watch Folder" directory and "Copy of .torrent' raise IncompatibleOption(_(
' files to" directory cannot be the same!')) '"Watch Folder" directory and "Copy of .torrent'
' files to" directory cannot be the same!',
))
return options return options
@ -357,13 +374,13 @@ class GtkUI(GtkPluginBase):
self.opts_dialog = OptionsDialog() self.opts_dialog = OptionsDialog()
component.get('PluginManager').register_hook( component.get('PluginManager').register_hook(
'on_apply_prefs', self.on_apply_prefs 'on_apply_prefs', self.on_apply_prefs,
) )
component.get('PluginManager').register_hook( component.get('PluginManager').register_hook(
'on_show_prefs', self.on_show_prefs 'on_show_prefs', self.on_show_prefs,
) )
client.register_event_handler( client.register_event_handler(
'AutoaddOptionsChangedEvent', self.on_options_changed_event 'AutoaddOptionsChangedEvent', self.on_options_changed_event,
) )
self.watchdirs = {} self.watchdirs = {}
@ -386,16 +403,16 @@ class GtkUI(GtkPluginBase):
sw.add(self.treeView) sw.add(self.treeView)
sw.show_all() sw.show_all()
component.get('Preferences').add_page( component.get('Preferences').add_page(
_('AutoAdd'), self.builder.get_object('prefs_box') _('AutoAdd'), self.builder.get_object('prefs_box'),
) )
def disable(self): def disable(self):
component.get('Preferences').remove_page(_('AutoAdd')) component.get('Preferences').remove_page(_('AutoAdd'))
component.get('PluginManager').deregister_hook( component.get('PluginManager').deregister_hook(
'on_apply_prefs', self.on_apply_prefs 'on_apply_prefs', self.on_apply_prefs,
) )
component.get('PluginManager').deregister_hook( component.get('PluginManager').deregister_hook(
'on_show_prefs', self.on_show_prefs 'on_show_prefs', self.on_show_prefs,
) )
def create_model(self): def create_model(self):
@ -403,14 +420,14 @@ class GtkUI(GtkPluginBase):
for watchdir_id, watchdir in self.watchdirs.items(): for watchdir_id, watchdir in self.watchdirs.items():
store.append([ store.append([
watchdir_id, watchdir['enabled'], watchdir_id, watchdir['enabled'],
watchdir.get('owner', 'localclient'), watchdir['path'] watchdir.get('owner', 'localclient'), watchdir['path'],
]) ])
return store return store
def create_columns(self, treeview): def create_columns(self, treeview):
renderer_toggle = gtk.CellRendererToggle() renderer_toggle = gtk.CellRendererToggle()
column = gtk.TreeViewColumn( column = gtk.TreeViewColumn(
_('Active'), renderer_toggle, activatable=1, active=1 _('Active'), renderer_toggle, activatable=1, active=1,
) )
column.set_sort_column_id(1) column.set_sort_column_id(1)
treeview.append_column(column) treeview.append_column(column)
@ -490,7 +507,7 @@ class GtkUI(GtkPluginBase):
for watchdir_id, watchdir in self.watchdirs.items(): for watchdir_id, watchdir in self.watchdirs.items():
self.store.append([ self.store.append([
watchdir_id, watchdir['enabled'], watchdir_id, watchdir['enabled'],
watchdir.get('owner', 'localclient'), watchdir['path'] watchdir.get('owner', 'localclient'), watchdir['path'],
]) ])
# Workaround for cached glade signal appearing when re-enabling plugin in same session # Workaround for cached glade signal appearing when re-enabling plugin in same session
if self.builder.get_object('edit_button'): if self.builder.get_object('edit_button'):

View file

@ -45,5 +45,5 @@ setup(
%s = deluge.plugins.%s:GtkUIPlugin %s = deluge.plugins.%s:GtkUIPlugin
[deluge.plugin.web] [deluge.plugin.web]
%s = deluge.plugins.%s:WebUIPlugin %s = deluge.plugins.%s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower()) * 3) """ % ((__plugin_name__, __plugin_name__.lower()) * 3),
) )

View file

@ -165,5 +165,5 @@ class IP(object):
def __repr__(self): def __repr__(self):
return '<%s long=%s address="%s">' % ( return '<%s long=%s address="%s">' % (
self.__class__.__name__, self.long, self.address self.__class__.__name__, self.long, self.address,
) )

View file

@ -104,14 +104,14 @@ class Core(CorePluginBase):
self.update_timer = LoopingCall(self.check_import) self.update_timer = LoopingCall(self.check_import)
if self.config['check_after_days'] > 0: if self.config['check_after_days'] > 0:
self.update_timer.start( 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): def disable(self):
self.config.save() self.config.save()
log.debug('Reset IP filter') log.debug('Reset IP filter')
self.core.session.get_ip_filter().add_rule( 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') log.debug('Blocklist: Plugin disabled')
@ -189,7 +189,7 @@ class Core(CorePluginBase):
try: try:
ip = IP.parse(ip) ip = IP.parse(ip)
self.blocklist.add_rule( self.blocklist.add_rule(
ip.address, ip.address, ALLOW_RANGE ip.address, ip.address, ALLOW_RANGE,
) )
saved.add(ip.address) saved.add(ip.address)
log.debug('Added %s to whitelisted', ip) log.debug('Added %s to whitelisted', ip)
@ -223,15 +223,17 @@ class Core(CorePluginBase):
self.update_timer.stop() self.update_timer.stop()
if self.config['check_after_days'] > 0: if self.config['check_after_days'] > 0:
self.update_timer.start( 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 continue
self.config[key] = config[key] self.config[key] = config[key]
if needs_blocklist_import: if needs_blocklist_import:
log.debug('IP addresses were removed from the whitelist. Since we ' log.debug(
'IP addresses were removed from the whitelist. Since we '
'do not know if they were blocked before. Re-import ' '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 self.has_imported = False
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) d.addCallbacks(self.on_import_complete, self.on_import_error)
@ -318,7 +320,7 @@ class Core(CorePluginBase):
self.is_downloading = True self.is_downloading = True
return download_file( return download_file(
url, deluge.configmanager.get_config_dir('blocklist.download'), url, deluge.configmanager.get_config_dir('blocklist.download'),
on_retrieve_data, headers on_retrieve_data, headers,
) )
def on_download_complete(self, blocklist): def on_download_complete(self, blocklist):
@ -365,8 +367,10 @@ class Core(CorePluginBase):
else: else:
log.warning('Blocklist download failed: %s', error_msg) log.warning('Blocklist download failed: %s', error_msg)
if self.failed_attempts < self.config['try_times']: if self.failed_attempts < self.config['try_times']:
log.debug('Try downloading blocklist again... (%s/%s)', log.debug(
self.failed_attempts, self.config['try_times']) 'Try downloading blocklist again... (%s/%s)',
self.failed_attempts, self.config['try_times'],
)
self.failed_attempts += 1 self.failed_attempts += 1
d = self.download_list() d = self.download_list()
d.addCallbacks(self.on_download_complete, self.on_download_error) d.addCallbacks(self.on_download_complete, self.on_download_error)

View file

@ -17,25 +17,25 @@ import zipfile
def Zipped(reader): # NOQA: N802 def Zipped(reader): # NOQA: N802
"""Blocklist reader for zipped blocklists""" """Blocklist reader for zipped blocklists"""
def open(self): def _open(self):
z = zipfile.ZipFile(self.file) z = zipfile.ZipFile(self.file)
f = z.open(z.namelist()[0]) f = z.open(z.namelist()[0])
return f return f
reader.open = open reader.open = _open
return reader return reader
def GZipped(reader): # NOQA: N802 def GZipped(reader): # NOQA: N802
"""Blocklist reader for gzipped blocklists""" """Blocklist reader for gzipped blocklists"""
def open(self): def _open(self):
return gzip.open(self.file) return gzip.open(self.file)
reader.open = open reader.open = _open
return reader return reader
def BZipped2(reader): # NOQA: N802 def BZipped2(reader): # NOQA: N802
"""Blocklist reader for bzipped2 blocklists""" """Blocklist reader for bzipped2 blocklists"""
def open(self): def _open(self):
return bz2.BZ2File(self.file) return bz2.BZ2File(self.file)
reader.open = open reader.open = _open
return reader return reader

View file

@ -15,19 +15,19 @@ from .readers import EmuleReader, PeerGuardianReader, SafePeerReader
COMPRESSION_TYPES = { COMPRESSION_TYPES = {
'PK': 'Zip', 'PK': 'Zip',
'\x1f\x8b': 'GZip', '\x1f\x8b': 'GZip',
'BZ': 'BZip2' 'BZ': 'BZip2',
} }
DECOMPRESSERS = { DECOMPRESSERS = {
'Zip': Zipped, 'Zip': Zipped,
'GZip': GZipped, 'GZip': GZipped,
'BZip2': BZipped2 'BZip2': BZipped2,
} }
READERS = { READERS = {
'Emule': EmuleReader, 'Emule': EmuleReader,
'SafePeer': SafePeerReader, 'SafePeer': SafePeerReader,
'PeerGuardian': PeerGuardianReader 'PeerGuardian': PeerGuardianReader,
} }

View file

@ -35,7 +35,7 @@ class GtkUI(GtkPluginBase):
image=common.get_resource('blocklist16.png'), image=common.get_resource('blocklist16.png'),
text='', text='',
callback=self._on_status_item_clicked, callback=self._on_status_item_clicked,
tooltip=_('Blocked IP Ranges /Whitelisted IP Ranges') tooltip=_('Blocked IP Ranges /Whitelisted IP Ranges'),
) )
# Register some hooks # Register some hooks
@ -67,7 +67,8 @@ class GtkUI(GtkPluginBase):
self.builder.get_object('image_up_to_date').hide() self.builder.get_object('image_up_to_date').hide()
self.status_item.set_text( 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.set_fraction(status['file_progress'])
self.progress_bar.show() self.progress_bar.show()
@ -79,7 +80,8 @@ class GtkUI(GtkPluginBase):
self.builder.get_object('image_up_to_date').hide() self.builder.get_object('image_up_to_date').hide()
self.status_item.set_text( self.status_item.set_text(
'Importing ' + str(status['num_blocked'])) 'Importing ' + str(status['num_blocked']),
)
self.progress_bar.set_text('Importing %s' % (status['num_blocked'])) self.progress_bar.set_text('Importing %s' % (status['num_blocked']))
self.progress_bar.pulse() self.progress_bar.pulse()
self.progress_bar.show() self.progress_bar.show()
@ -97,12 +99,15 @@ class GtkUI(GtkPluginBase):
self.status_item.set_text('%(num_blocked)s/%(num_whited)s' % status) self.status_item.set_text('%(num_blocked)s/%(num_whited)s' % status)
self.builder.get_object('label_filesize').set_text( 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( 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_type').set_text(status['file_type'])
self.builder.get_object('label_url').set_text( self.builder.get_object('label_url').set_text(
status['file_url']) status['file_url'],
)
client.blocklist.get_status().addCallback(_on_get_status) client.blocklist.get_status().addCallback(_on_get_status)
@ -155,18 +160,24 @@ class GtkUI(GtkPluginBase):
self.builder.connect_signals({ self.builder.connect_signals({
'on_button_check_download_clicked': self._on_button_check_download_clicked, 'on_button_check_download_clicked': self._on_button_check_download_clicked,
'on_button_force_download_clicked': self._on_button_force_download_clicked, 'on_button_force_download_clicked': self._on_button_force_download_clicked,
'on_whitelist_add_clicked': (self.on_add_button_clicked, 'on_whitelist_add_clicked': (
self.whitelist_treeview), self.on_add_button_clicked,
'on_whitelist_remove_clicked': (self.on_delete_button_clicked, self.whitelist_treeview,
self.whitelist_treeview), ),
'on_whitelist_remove_clicked': (
self.on_delete_button_clicked,
self.whitelist_treeview,
),
}) })
# Set button icons # Set button icons
self.builder.get_object('image_download').set_from_file( 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( 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 # Update the preferences page with config values from the core
self._on_show_prefs() self._on_show_prefs()
@ -174,13 +185,14 @@ class GtkUI(GtkPluginBase):
# Add the page to the preferences dialog # Add the page to the preferences dialog
self.plugin.add_preferences_page( self.plugin.add_preferences_page(
_('Blocklist'), _('Blocklist'),
self.builder.get_object('blocklist_prefs_box')) self.builder.get_object('blocklist_prefs_box'),
)
def build_whitelist_model_treeview(self): def build_whitelist_model_treeview(self):
self.whitelist_treeview = self.builder.get_object('whitelist_treeview') self.whitelist_treeview = self.builder.get_object('whitelist_treeview')
treeview_selection = self.whitelist_treeview.get_selection() treeview_selection = self.whitelist_treeview.get_selection()
treeview_selection.connect( treeview_selection.connect(
'changed', self.on_whitelist_treeview_selection_changed 'changed', self.on_whitelist_treeview_selection_changed,
) )
self.whitelist_model = gtk.ListStore(str, bool) self.whitelist_model = gtk.ListStore(str, bool)
renderer = gtk.CellRendererText() renderer = gtk.CellRendererText()
@ -207,11 +219,15 @@ class GtkUI(GtkPluginBase):
def on_whitelist_treeview_selection_changed(self, selection): def on_whitelist_treeview_selection_changed(self, selection):
model, selected_connection_iter = selection.get_selected() model, selected_connection_iter = selection.get_selected()
if selected_connection_iter: if selected_connection_iter:
self.builder.get_object('whitelist_delete').set_property('sensitive', self.builder.get_object('whitelist_delete').set_property(
True) 'sensitive',
True,
)
else: else:
self.builder.get_object('whitelist_delete').set_property('sensitive', self.builder.get_object('whitelist_delete').set_property(
False) 'sensitive',
False,
)
def on_add_button_clicked(self, widget, treeview): def on_add_button_clicked(self, widget, treeview):
model = treeview.get_model() model = treeview.get_model()
@ -228,5 +244,5 @@ class GtkUI(GtkPluginBase):
self.whitelist_model.clear() self.whitelist_model.clear()
for ip in whitelist: for ip in whitelist:
self.whitelist_model.set( self.whitelist_model.set(
self.whitelist_model.append(), 0, ip, 1, True self.whitelist_model.append(), 0, ip, 1, True,
) )

View file

@ -21,7 +21,7 @@ FORMAT_LIST = [
('gzmule', _('Emule IP list (GZip)')), ('gzmule', _('Emule IP list (GZip)')),
('spzip', _('SafePeer Text (Zipped)')), ('spzip', _('SafePeer Text (Zipped)')),
('pgtext', _('PeerGuardian Text (Uncompressed)')), ('pgtext', _('PeerGuardian Text (Uncompressed)')),
('p2bgz', _('PeerGuardian P2B (GZip)')) ('p2bgz', _('PeerGuardian P2B (GZip)')),
] ]

View file

@ -39,5 +39,5 @@ setup(
%s = deluge.plugins.%s:GtkUIPlugin %s = deluge.plugins.%s:GtkUIPlugin
[deluge.plugin.web] [deluge.plugin.web]
%s = deluge.plugins.%s:WebUIPlugin %s = deluge.plugins.%s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower()) * 3) """ % ((__plugin_name__, __plugin_name__.lower()) * 3),
) )

View file

@ -26,7 +26,7 @@ from deluge.plugins.pluginbase import CorePluginBase
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
'commands': [] 'commands': [],
} }
EXECUTE_ID = 0 EXECUTE_ID = 0
@ -36,7 +36,7 @@ EXECUTE_COMMAND = 2
EVENT_MAP = { EVENT_MAP = {
'complete': 'TorrentFinishedEvent', 'complete': 'TorrentFinishedEvent',
'added': 'TorrentAddedEvent', 'added': 'TorrentAddedEvent',
'removed': 'TorrentRemovedEvent' 'removed': 'TorrentRemovedEvent',
} }
@ -118,8 +118,10 @@ class Core(CorePluginBase):
command = os.path.expandvars(command[EXECUTE_COMMAND]) command = os.path.expandvars(command[EXECUTE_COMMAND])
command = os.path.expanduser(command) command = os.path.expanduser(command)
cmd_args = [torrent_id.encode('utf8'), torrent_name.encode('utf8'), cmd_args = [
download_location.encode('utf8')] torrent_id.encode('utf8'), torrent_name.encode('utf8'),
download_location.encode('utf8'),
]
if windows_check(): if windows_check():
# Escape ampersand on windows (see #2784) # Escape ampersand on windows (see #2784)
cmd_args = [cmd_arg.replace('&', '^^^&') for cmd_arg in cmd_args] cmd_args = [cmd_arg.replace('&', '^^^&') for cmd_arg in cmd_args]

View file

@ -30,7 +30,7 @@ EXECUTE_COMMAND = 2
EVENT_MAP = { EVENT_MAP = {
'complete': _('Torrent Complete'), 'complete': _('Torrent Complete'),
'added': _('Torrent Added'), 'added': _('Torrent Added'),
'removed': _('Torrent Removed') 'removed': _('Torrent Removed'),
} }
EVENTS = ['complete', 'added', 'removed'] EVENTS = ['complete', 'added', 'removed']

View file

@ -40,5 +40,5 @@ setup(
%s = deluge.plugins.%s:GtkUIPlugin %s = deluge.plugins.%s:GtkUIPlugin
[deluge.plugin.web] [deluge.plugin.web]
%s = deluge.plugins.%s:WebUIPlugin %s = deluge.plugins.%s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower()) * 3) """ % ((__plugin_name__, __plugin_name__.lower()) * 3),
) )

View file

@ -30,7 +30,7 @@ log = logging.getLogger(__name__)
DEFAULT_PREFS = { DEFAULT_PREFS = {
'extract_path': '', 'extract_path': '',
'use_name_folder': True 'use_name_folder': True,
} }
if windows_check(): if windows_check():

View file

@ -51,7 +51,7 @@ class GtkUI(GtkPluginBase):
config = { config = {
'extract_path': path, 'extract_path': path,
'use_name_folder': self.builder.get_object('chk_use_name').get_active() 'use_name_folder': self.builder.get_object('chk_use_name').get_active(),
} }
client.extractor.set_config(config) client.extractor.set_config(config)

View file

@ -53,5 +53,5 @@ setup(
%s = deluge.plugins.%s:GtkUIPlugin %s = deluge.plugins.%s:GtkUIPlugin
[deluge.plugin.web] [deluge.plugin.web]
%s = deluge.plugins.%s:WebUIPlugin %s = deluge.plugins.%s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower()) * 3) """ % ((__plugin_name__, __plugin_name__.lower()) * 3),
) )

View file

@ -57,7 +57,7 @@ OPTIONS_DEFAULTS = {
'move_completed': False, 'move_completed': False,
'move_completed_path': '', 'move_completed_path': '',
'auto_add': False, 'auto_add': False,
'auto_add_trackers': [] 'auto_add_trackers': [],
} }
NO_LABEL = 'No Label' NO_LABEL = 'No Label'
@ -108,7 +108,7 @@ class Core(CorePluginBase):
pass pass
def init_filter_dict(self): def init_filter_dict(self):
filter_dict = dict([(label, 0) for label in self.labels]) filter_dict = {label: 0 for label in self.labels}
filter_dict['All'] = len(self.torrents) filter_dict['All'] = len(self.torrents)
return filter_dict return filter_dict
@ -208,8 +208,8 @@ class Core(CorePluginBase):
torrent.set_options( torrent.set_options(
{ {
'move_completed': options['move_completed'], 'move_completed': options['move_completed'],
'move_completed_path': options['move_completed_path'] 'move_completed_path': options['move_completed_path'],
} },
) )
def _unset_torrent_options(self, torrent_id, label_id): def _unset_torrent_options(self, torrent_id, label_id):
@ -233,8 +233,8 @@ class Core(CorePluginBase):
torrent.set_options( torrent.set_options(
{ {
'move_completed': self.core_cfg.config['move_completed'], 'move_completed': self.core_cfg.config['move_completed'],
'move_completed_path': self.core_cfg.config['move_completed_path'] 'move_completed_path': self.core_cfg.config['move_completed_path'],
} },
) )
def _has_auto_match(self, torrent, label_options): def _has_auto_match(self, torrent, label_options):
@ -310,7 +310,10 @@ class Core(CorePluginBase):
@export @export
def get_config(self): def get_config(self):
"""see : label_set_config""" """see : label_set_config"""
return dict((key, self.config[key]) for key in CORE_OPTIONS if key in self.config.config) return {
key: self.config[key]
for key in CORE_OPTIONS if key in self.config.config
}
@export @export
def set_config(self, options): def set_config(self, options):

View file

@ -126,8 +126,10 @@ class AddDialog(object):
class OptionsDialog(object): class OptionsDialog(object):
spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio'] spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio']
spin_int_ids = ['max_upload_slots', 'max_connections'] spin_int_ids = ['max_upload_slots', 'max_connections']
chk_ids = ['apply_max', 'apply_queue', 'stop_at_ratio', 'apply_queue', 'remove_at_ratio', chk_ids = [
'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. # list of tuples, because order matters when nesting.
sensitive_groups = [ sensitive_groups = [
@ -136,7 +138,7 @@ class OptionsDialog(object):
('stop_at_ratio', ['remove_at_ratio', 'stop_ratio']), # nested ('stop_at_ratio', ['remove_at_ratio', 'stop_ratio']), # nested
('apply_move_completed', ['move_completed']), ('apply_move_completed', ['move_completed']),
('move_completed', ['move_completed_path']), # nested ('move_completed', ['move_completed_path']), # nested
('auto_add', ['auto_add_trackers']) ('auto_add', ['auto_add_trackers']),
] ]
def __init__(self): def __init__(self):

View file

@ -44,5 +44,5 @@ setup(
%s = deluge.plugins.%s:GtkUIPlugin %s = deluge.plugins.%s:GtkUIPlugin
[deluge.plugin.web] [deluge.plugin.web]
%s = deluge.plugins.%s:WebUIPlugin %s = deluge.plugins.%s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower()) * 3) """ % ((__plugin_name__, __plugin_name__.lower()) * 3),
) )

View file

@ -37,7 +37,7 @@ class CustomNotifications(object):
'email': {}, 'email': {},
'popup': {}, 'popup': {},
'blink': {}, 'blink': {},
'sound': {} 'sound': {},
} }
def enable(self): def enable(self):
@ -72,7 +72,7 @@ class CustomNotifications(object):
wrapper, handler = self.custom_notifications[kind][eventtype] wrapper, handler = self.custom_notifications[kind][eventtype]
try: try:
component.get('EventManager').register_event_handler( component.get('EventManager').register_event_handler(
eventtype, wrapper eventtype, wrapper,
) )
except KeyError: except KeyError:
from deluge.ui.client import client from deluge.ui.client import client
@ -83,7 +83,7 @@ class CustomNotifications(object):
wrapper, handler = self.custom_notifications[kind][eventtype] wrapper, handler = self.custom_notifications[kind][eventtype]
try: try:
component.get('EventManager').deregister_event_handler( component.get('EventManager').deregister_event_handler(
eventtype, wrapper eventtype, wrapper,
) )
except KeyError: except KeyError:
from deluge.ui.client import client from deluge.ui.client import client
@ -99,8 +99,10 @@ class CustomNotifications(object):
if known_events[eventtype].__module__.startswith('deluge.event'): if known_events[eventtype].__module__.startswith('deluge.event'):
if handler.__self__ is self: if handler.__self__ is self:
return True return True
log.error('You cannot register custom notification providers ' log.error(
'for built-in event types.') 'You cannot register custom notification providers '
'for built-in event types.',
)
return False return False
return True return True

View file

@ -41,8 +41,8 @@ DEFAULT_PREFS = {
'smtp_recipients': [], 'smtp_recipients': [],
# Subscriptions # Subscriptions
'subscriptions': { 'subscriptions': {
'email': [] 'email': [],
} },
} }
@ -53,8 +53,10 @@ class CoreNotifications(CustomNotifications):
def enable(self): def enable(self):
CustomNotifications.enable(self) CustomNotifications.enable(self)
self.register_custom_email_notification('TorrentFinishedEvent', self.register_custom_email_notification(
self._on_torrent_finished_event) 'TorrentFinishedEvent',
self._on_torrent_finished_event,
)
def disable(self): def disable(self):
self.deregister_custom_email_notification('TorrentFinishedEvent') self.deregister_custom_email_notification('TorrentFinishedEvent')
@ -77,8 +79,10 @@ class CoreNotifications(CustomNotifications):
if not self.config['smtp_enabled']: if not self.config['smtp_enabled']:
return defer.succeed('SMTP notification not enabled.') return defer.succeed('SMTP notification not enabled.')
subject, message = result subject, message = result
log.debug('Spawning new thread to send email with subject: %s: %s', log.debug(
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 # Spawn thread because we don't want Deluge to lock up while we send the
# email. # email.
return threads.deferToThread(self._notify_email, subject, message) return threads.deferToThread(self._notify_email, subject, message)
@ -103,7 +107,8 @@ class CoreNotifications(CustomNotifications):
'smtp_from': self.config['smtp_from'], 'smtp_from': self.config['smtp_from'],
'subject': subject, 'subject': subject,
'smtp_recipients': to_addrs_str, 'smtp_recipients': to_addrs_str,
'date': formatdate()} 'date': formatdate(),
}
headers = """\ headers = """\
From: %(smtp_from)s From: %(smtp_from)s
To: %(smtp_recipients)s To: %(smtp_recipients)s
@ -176,7 +181,7 @@ Date: %(date)s
'downloading "%(name)s", which includes %(num_files)i files.' 'downloading "%(name)s", which includes %(num_files)i files.'
'\nTo stop receiving these alerts, simply turn off email ' '\nTo stop receiving these alerts, simply turn off email '
"notification in Deluge's preferences.\n\n" "notification in Deluge's preferences.\n\n"
'Thank you,\nDeluge.' 'Thank you,\nDeluge.',
) % torrent_status ) % torrent_status
return subject, message return subject, message
@ -196,7 +201,8 @@ class Core(CorePluginBase, CoreNotifications):
def enable(self): def enable(self):
CoreNotifications.enable(self) CoreNotifications.enable(self)
self.config = deluge.configmanager.ConfigManager( self.config = deluge.configmanager.ConfigManager(
'notifications-core.conf', DEFAULT_PREFS) 'notifications-core.conf', DEFAULT_PREFS,
)
log.debug('ENABLING CORE NOTIFICATIONS') log.debug('ENABLING CORE NOTIFICATIONS')
def disable(self): def disable(self):

View file

@ -67,8 +67,10 @@ DEFAULT_PREFS = {
} }
RECIPIENT_FIELD, RECIPIENT_EDIT = list(range(2)) RECIPIENT_FIELD, RECIPIENT_EDIT = list(range(2))
(SUB_EVENT, SUB_EVENT_DOC, SUB_NOT_EMAIL, SUB_NOT_POPUP, SUB_NOT_BLINK, (
SUB_NOT_SOUND) = list(range(6)) 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)) SND_EVENT, SND_EVENT_DOC, SND_NAME, SND_PATH = list(range(4))
@ -80,13 +82,13 @@ class GtkUiNotifications(CustomNotifications):
def enable(self): def enable(self):
CustomNotifications.enable(self) CustomNotifications.enable(self)
self.register_custom_blink_notification( self.register_custom_blink_notification(
'TorrentFinishedEvent', self._on_torrent_finished_event_blink 'TorrentFinishedEvent', self._on_torrent_finished_event_blink,
) )
self.register_custom_sound_notification( self.register_custom_sound_notification(
'TorrentFinishedEvent', self._on_torrent_finished_event_sound 'TorrentFinishedEvent', self._on_torrent_finished_event_sound,
) )
self.register_custom_popup_notification( self.register_custom_popup_notification(
'TorrentFinishedEvent', self._on_torrent_finished_event_popup 'TorrentFinishedEvent', self._on_torrent_finished_event_popup,
) )
def disable(self): def disable(self):
@ -146,17 +148,22 @@ class GtkUiNotifications(CustomNotifications):
def handle_custom_blink_notification(self, result, eventtype): def handle_custom_blink_notification(self, result, eventtype):
if result: if result:
return defer.maybeDeferred(self.__blink) return defer.maybeDeferred(self.__blink)
return defer.succeed('Will not blink. The returned value from the custom ' return defer.succeed(
'handler was: %s' % result) 'Will not blink. The returned value from the custom '
'handler was: %s' % result,
)
def handle_custom_sound_notification(self, result, eventtype): def handle_custom_sound_notification(self, result, eventtype):
if isinstance(result, ''.__class__): if isinstance(result, ''.__class__):
if not result and eventtype in self.config['custom_sounds']: if not result and eventtype in self.config['custom_sounds']:
return defer.maybeDeferred( 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.maybeDeferred(self.__play_sound, result)
return defer.succeed('Will not play sound. The returned value from the ' return defer.succeed(
'custom handler was: %s' % result) 'Will not play sound. The returned value from the '
'custom handler was: %s' % result,
)
def __blink(self): def __blink(self):
self.systray.blink(True) self.systray.blink(True)
@ -219,12 +226,16 @@ class GtkUiNotifications(CustomNotifications):
log.debug('Failed to get torrent status to be able to show the popup') log.debug('Failed to get torrent status to be able to show the popup')
def _on_torrent_finished_event_got_torrent_status(self, torrent_status): def _on_torrent_finished_event_got_torrent_status(self, torrent_status):
log.debug('Handler for TorrentFinishedEvent GTKUI called. ' log.debug(
'Got Torrent Status') 'Handler for TorrentFinishedEvent GTKUI called. '
'Got Torrent Status',
)
title = _('Finished Torrent') title = _('Finished Torrent')
torrent_status['num_files'] = torrent_status['file_progress'].count(1.0) torrent_status['num_files'] = torrent_status['file_progress'].count(1.0)
message = _('The torrent "%(name)s" including %(num_files)i file(s) ' message = _(
'has finished downloading.') % torrent_status 'The torrent "%(name)s" including %(num_files)i file(s) '
'has finished downloading.',
) % torrent_status
return title, message return title, message
@ -235,7 +246,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
def enable(self): def enable(self):
self.config = deluge.configmanager.ConfigManager( self.config = deluge.configmanager.ConfigManager(
'notifications-gtk.conf', DEFAULT_PREFS 'notifications-gtk.conf', DEFAULT_PREFS,
) )
self.builder = gtk.Builder() self.builder = gtk.Builder()
self.builder.add_from_file(get_resource('config.ui')) self.builder.add_from_file(get_resource('config.ui'))
@ -248,73 +259,97 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
self.build_notifications_model_populate_treeview() self.build_notifications_model_populate_treeview()
client.notifications.get_handled_events().addCallback( client.notifications.get_handled_events().addCallback(
self.popuplate_what_needs_handled_events self.popuplate_what_needs_handled_events,
) )
self.builder.connect_signals({ self.builder.connect_signals({
'on_add_button_clicked': (self.on_add_button_clicked, 'on_add_button_clicked': (
self.recipients_treeview), self.on_add_button_clicked,
'on_delete_button_clicked': (self.on_delete_button_clicked, self.recipients_treeview,
self.recipients_treeview), ),
'on_delete_button_clicked': (
self.on_delete_button_clicked,
self.recipients_treeview,
),
'on_enabled_toggled': self.on_enabled_toggled, 'on_enabled_toggled': self.on_enabled_toggled,
'on_sound_enabled_toggled': self.on_sound_enabled_toggled, 'on_sound_enabled_toggled': self.on_sound_enabled_toggled,
'on_sounds_edit_button_clicked': self.on_sounds_edit_button_clicked, 'on_sounds_edit_button_clicked': self.on_sounds_edit_button_clicked,
'on_sounds_revert_button_clicked': self.on_sounds_revert_button_clicked, 'on_sounds_revert_button_clicked': self.on_sounds_revert_button_clicked,
'on_sound_path_update_preview': self.on_sound_path_update_preview 'on_sound_path_update_preview': self.on_sound_path_update_preview,
}) })
component.get('Preferences').add_page(_('Notifications'), self.prefs) component.get('Preferences').add_page(_('Notifications'), self.prefs)
component.get('PluginManager').register_hook('on_apply_prefs', component.get('PluginManager').register_hook(
self.on_apply_prefs) 'on_apply_prefs',
component.get('PluginManager').register_hook('on_show_prefs', self.on_apply_prefs,
self.on_show_prefs) )
component.get('PluginManager').register_hook(
'on_show_prefs',
self.on_show_prefs,
)
if not POPUP_AVAILABLE: if not POPUP_AVAILABLE:
self.builder.get_object('popup_enabled').set_property('sensitive', self.builder.get_object('popup_enabled').set_property(
False) 'sensitive',
False,
)
if not SOUND_AVAILABLE: if not SOUND_AVAILABLE:
# for widget_name in ('sound_enabled', 'sound_path', 'sounds_page', 'sounds_page_label'): # 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(widget_name).set_property('sensitive', False)
self.builder.get_object('sound_enabled').set_property('sensitive', self.builder.get_object('sound_enabled').set_property(
False) 'sensitive',
False,
)
self.builder.get_object('sound_path').set_property('sensitive', False) self.builder.get_object('sound_path').set_property('sensitive', False)
self.builder.get_object('sounds_page').set_property('sensitive', self.builder.get_object('sounds_page').set_property(
False) 'sensitive',
self.builder.get_object('sounds_page_label').set_property('sensitive', False,
False) )
self.builder.get_object('sounds_page_label').set_property(
'sensitive',
False,
)
self.systray = component.get('SystemTray') self.systray = component.get('SystemTray')
if not hasattr(self.systray, 'tray'): if not hasattr(self.systray, 'tray'):
# Tray is not beeing used # Tray is not beeing used
self.builder.get_object('blink_enabled').set_property('sensitive', self.builder.get_object('blink_enabled').set_property(
False) 'sensitive',
False,
)
GtkUiNotifications.enable(self) GtkUiNotifications.enable(self)
def disable(self): def disable(self):
GtkUiNotifications.disable(self) GtkUiNotifications.disable(self)
component.get('Preferences').remove_page(_('Notifications')) component.get('Preferences').remove_page(_('Notifications'))
component.get('PluginManager').deregister_hook('on_apply_prefs', component.get('PluginManager').deregister_hook(
self.on_apply_prefs) 'on_apply_prefs',
component.get('PluginManager').deregister_hook('on_show_prefs', self.on_apply_prefs,
self.on_show_prefs) )
component.get('PluginManager').deregister_hook(
'on_show_prefs',
self.on_show_prefs,
)
def build_recipients_model_populate_treeview(self): def build_recipients_model_populate_treeview(self):
# SMTP Recipients treeview/model # SMTP Recipients treeview/model
self.recipients_treeview = self.builder.get_object('smtp_recipients') self.recipients_treeview = self.builder.get_object('smtp_recipients')
treeview_selection = self.recipients_treeview.get_selection() treeview_selection = self.recipients_treeview.get_selection()
treeview_selection.connect( treeview_selection.connect(
'changed', self.on_recipients_treeview_selection_changed 'changed', self.on_recipients_treeview_selection_changed,
) )
self.recipients_model = gtk.ListStore(str, bool) self.recipients_model = gtk.ListStore(str, bool)
renderer = gtk.CellRendererText() renderer = gtk.CellRendererText()
renderer.connect('edited', self.on_cell_edited, self.recipients_model) renderer.connect('edited', self.on_cell_edited, self.recipients_model)
renderer.set_data('recipient', RECIPIENT_FIELD) renderer.set_data('recipient', RECIPIENT_FIELD)
column = gtk.TreeViewColumn('Recipients', renderer, column = gtk.TreeViewColumn(
'Recipients', renderer,
text=RECIPIENT_FIELD, text=RECIPIENT_FIELD,
editable=RECIPIENT_EDIT) editable=RECIPIENT_EDIT,
)
column.set_expand(True) column.set_expand(True)
self.recipients_treeview.append_column(column) self.recipients_treeview.append_column(column)
self.recipients_treeview.set_model(self.recipients_model) self.recipients_treeview.set_model(self.recipients_model)
@ -324,7 +359,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
self.sounds_treeview = self.builder.get_object('sounds_treeview') self.sounds_treeview = self.builder.get_object('sounds_treeview')
sounds_selection = self.sounds_treeview.get_selection() sounds_selection = self.sounds_treeview.get_selection()
sounds_selection.connect( sounds_selection.connect(
'changed', self.on_sounds_treeview_selection_changed 'changed', self.on_sounds_treeview_selection_changed,
) )
self.sounds_treeview.set_tooltip_column(SND_EVENT_DOC) self.sounds_treeview.set_tooltip_column(SND_EVENT_DOC)
@ -360,7 +395,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
self.subscriptions_treeview = self.builder.get_object('subscriptions_treeview') self.subscriptions_treeview = self.builder.get_object('subscriptions_treeview')
subscriptions_selection = self.subscriptions_treeview.get_selection() subscriptions_selection = self.subscriptions_treeview.get_selection()
subscriptions_selection.connect( 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_treeview.set_tooltip_column(SUB_EVENT_DOC)
self.subscriptions_model = gtk.ListStore(str, str, bool, bool, bool, bool) self.subscriptions_model = gtk.ListStore(str, str, bool, bool, bool, bool)
@ -426,7 +461,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
SND_EVENT, event_name, SND_EVENT, event_name,
SND_EVENT_DOC, event_doc, SND_EVENT_DOC, event_doc,
SND_NAME, basename(snd_path), SND_NAME, basename(snd_path),
SND_PATH, snd_path SND_PATH, snd_path,
) )
def populate_subscriptions(self, handled_events, email_subscriptions=None): def populate_subscriptions(self, handled_events, email_subscriptions=None):
@ -443,7 +478,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
SUB_NOT_EMAIL, event_name in email_subscriptions, SUB_NOT_EMAIL, event_name in email_subscriptions,
SUB_NOT_POPUP, event_name in subscriptions_dict['popup'], SUB_NOT_POPUP, event_name in subscriptions_dict['popup'],
SUB_NOT_BLINK, event_name in subscriptions_dict['blink'], SUB_NOT_BLINK, event_name in subscriptions_dict['blink'],
SUB_NOT_SOUND, event_name in subscriptions_dict['sound'] SUB_NOT_SOUND, event_name in subscriptions_dict['sound'],
) )
def on_apply_prefs(self): def on_apply_prefs(self):
@ -465,8 +500,10 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
old_sound_file = self.config['sound_path'] old_sound_file = self.config['sound_path']
new_sound_file = self.builder.get_object('sound_path').get_filename() new_sound_file = self.builder.get_object('sound_path').get_filename()
log.debug('Old Default sound file: %s New one: %s', log.debug(
old_sound_file, new_sound_file) 'Old Default sound file: %s New one: %s',
old_sound_file, new_sound_file,
)
custom_sounds = {} custom_sounds = {}
for event_name, event_doc, filename, filepath in self.sounds_model: for event_name, event_doc, filename, filepath in self.sounds_model:
log.debug('Custom sound for event "%s": %s', event_name, filename) log.debug('Custom sound for event "%s": %s', event_name, filename)
@ -482,9 +519,9 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
'subscriptions': { 'subscriptions': {
'popup': current_popup_subscriptions, 'popup': current_popup_subscriptions,
'blink': current_blink_subscriptions, 'blink': current_blink_subscriptions,
'sound': current_sound_subscriptions 'sound': current_sound_subscriptions,
}, },
'custom_sounds': custom_sounds 'custom_sounds': custom_sounds,
}) })
self.config.save() self.config.save()
@ -496,9 +533,11 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
'smtp_pass': self.builder.get_object('smtp_pass').get_text(), 'smtp_pass': self.builder.get_object('smtp_pass').get_text(),
'smtp_from': self.builder.get_object('smtp_from').get_text(), 'smtp_from': self.builder.get_object('smtp_from').get_text(),
'smtp_tls': self.builder.get_object('smtp_tls').get_active(), 'smtp_tls': self.builder.get_object('smtp_tls').get_active(),
'smtp_recipients': [dest[0] for dest in self.recipients_model if 'smtp_recipients': [
dest[0] != 'USER@HOST'], dest[0] for dest in self.recipients_model if
'subscriptions': {'email': current_email_subscriptions} dest[0] != 'USER@HOST'
],
'subscriptions': {'email': current_email_subscriptions},
} }
client.notifications.set_config(core_config) client.notifications.set_config(core_config)
@ -517,20 +556,22 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
self.builder.get_object('smtp_tls').set_active(core_config['smtp_tls']) self.builder.get_object('smtp_tls').set_active(core_config['smtp_tls'])
self.recipients_model.clear() self.recipients_model.clear()
for recipient in core_config['smtp_recipients']: for recipient in core_config['smtp_recipients']:
self.recipients_model.set(self.recipients_model.append(), self.recipients_model.set(
self.recipients_model.append(),
RECIPIENT_FIELD, recipient, RECIPIENT_FIELD, recipient,
RECIPIENT_EDIT, False) RECIPIENT_EDIT, False,
)
self.builder.get_object('smtp_enabled').set_active( self.builder.get_object('smtp_enabled').set_active(
core_config['smtp_enabled'] core_config['smtp_enabled'],
) )
self.builder.get_object('sound_enabled').set_active( 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.builder.get_object('popup_enabled').set_active(
self.config['popup_enabled'] self.config['popup_enabled'],
) )
self.builder.get_object('blink_enabled').set_active( self.builder.get_object('blink_enabled').set_active(
self.config['blink_enabled'] self.config['blink_enabled'],
) )
if self.config['sound_path']: if self.config['sound_path']:
sound_path = self.config['sound_path'] sound_path = self.config['sound_path']
@ -543,19 +584,21 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
client.notifications.get_handled_events().addCallback( client.notifications.get_handled_events().addCallback(
self.popuplate_what_needs_handled_events, self.popuplate_what_needs_handled_events,
core_config['subscriptions']['email'] core_config['subscriptions']['email'],
) )
def on_sound_path_update_preview(self, filechooser): def on_sound_path_update_preview(self, filechooser):
client.notifications.get_handled_events().addCallback( client.notifications.get_handled_events().addCallback(
self.populate_sounds self.populate_sounds,
) )
def on_add_button_clicked(self, widget, treeview): def on_add_button_clicked(self, widget, treeview):
model = treeview.get_model() model = treeview.get_model()
model.set(model.append(), model.set(
model.append(),
RECIPIENT_FIELD, 'USER@HOST', RECIPIENT_FIELD, 'USER@HOST',
RECIPIENT_EDIT, True) RECIPIENT_EDIT, True,
)
def on_delete_button_clicked(self, widget, treeview): def on_delete_button_clicked(self, widget, treeview):
selection = treeview.get_selection() selection = treeview.get_selection()
@ -570,20 +613,28 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
def on_recipients_treeview_selection_changed(self, selection): def on_recipients_treeview_selection_changed(self, selection):
model, selected_connection_iter = selection.get_selected() model, selected_connection_iter = selection.get_selected()
if selected_connection_iter: if selected_connection_iter:
self.builder.get_object('delete_button').set_property('sensitive', self.builder.get_object('delete_button').set_property(
True) 'sensitive',
True,
)
else: else:
self.builder.get_object('delete_button').set_property('sensitive', self.builder.get_object('delete_button').set_property(
False) 'sensitive',
False,
)
def on_subscriptions_treeview_selection_changed(self, selection): def on_subscriptions_treeview_selection_changed(self, selection):
model, selected_connection_iter = selection.get_selected() model, selected_connection_iter = selection.get_selected()
if selected_connection_iter: if selected_connection_iter:
self.builder.get_object('delete_button').set_property('sensitive', self.builder.get_object('delete_button').set_property(
True) 'sensitive',
True,
)
else: else:
self.builder.get_object('delete_button').set_property('sensitive', self.builder.get_object('delete_button').set_property(
False) 'sensitive',
False,
)
def on_sounds_treeview_selection_changed(self, selection): def on_sounds_treeview_selection_changed(self, selection):
model, selected_iter = selection.get_selected() model, selected_iter = selection.get_selected()
@ -605,9 +656,11 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
model, selected_iter = selection.get_selected() model, selected_iter = selection.get_selected()
if selected_iter: if selected_iter:
log.debug('on_sounds_revert_button_clicked: got iter') log.debug('on_sounds_revert_button_clicked: got iter')
model.set(selected_iter, model.set(
selected_iter,
SND_PATH, self.config['sound_path'], SND_PATH, self.config['sound_path'],
SND_NAME, basename(self.config['sound_path'])) SND_NAME, basename(self.config['sound_path']),
)
def on_sounds_edit_button_clicked(self, widget): def on_sounds_edit_button_clicked(self, widget):
log.debug('on_sounds_edit_button_clicked') log.debug('on_sounds_edit_button_clicked')
@ -617,10 +670,12 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
path = model.get(selected_iter, SND_PATH)[0] path = model.get(selected_iter, SND_PATH)[0]
dialog = gtk.FileChooserDialog( dialog = gtk.FileChooserDialog(
title=_('Choose Sound File'), title=_('Choose Sound File'),
buttons=(gtk.STOCK_CANCEL, buttons=(
gtk.STOCK_CANCEL,
gtk.RESPONSE_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN, gtk.STOCK_OPEN,
gtk.RESPONSE_OK) gtk.RESPONSE_OK,
),
) )
dialog.set_filename(path) dialog.set_filename(path)
@ -629,34 +684,48 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
new_filename = dialog.get_filename() new_filename = dialog.get_filename()
dialog.destroy() dialog.destroy()
log.debug(new_filename) log.debug(new_filename)
model.set(selected_iter, model.set(
selected_iter,
SND_PATH, new_filename, SND_PATH, new_filename,
SND_NAME, basename(new_filename)) SND_NAME, basename(new_filename),
)
d = defer.maybeDeferred(dialog.run) d = defer.maybeDeferred(dialog.run)
d.addCallback(update_model) d.addCallback(update_model)
log.debug('dialog should have been shown') log.debug('dialog should have been shown')
def on_enabled_toggled(self, widget): def on_enabled_toggled(self, widget):
for widget_name in ('smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass', for widget_name in (
'smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass',
'smtp_pass', 'smtp_tls', 'smtp_from', 'smtp_pass', 'smtp_tls', 'smtp_from',
'smtp_recipients'): 'smtp_recipients',
self.builder.get_object(widget_name).set_property('sensitive', ):
widget.get_active()) self.builder.get_object(widget_name).set_property(
'sensitive',
widget.get_active(),
)
def on_sound_enabled_toggled(self, widget): def on_sound_enabled_toggled(self, widget):
if widget.get_active(): if widget.get_active():
self.builder.get_object('sound_path').set_property('sensitive', True) self.builder.get_object('sound_path').set_property('sensitive', True)
self.builder.get_object('sounds_page').set_property('sensitive', self.builder.get_object('sounds_page').set_property(
True) 'sensitive',
self.builder.get_object('sounds_page_label').set_property('sensitive', True,
True) )
self.builder.get_object('sounds_page_label').set_property(
'sensitive',
True,
)
else: else:
self.builder.get_object('sound_path').set_property('sensitive', False) self.builder.get_object('sound_path').set_property('sensitive', False)
self.builder.get_object('sounds_page').set_property('sensitive', self.builder.get_object('sounds_page').set_property(
False) 'sensitive',
self.builder.get_object('sounds_page_label').set_property('sensitive', False,
False) )
self.builder.get_object('sounds_page_label').set_property(
'sensitive',
False,
)
# for widget_name in ('sounds_path', 'sounds_page', 'sounds_page_label'): # for widget_name in ('sounds_path', 'sounds_page', 'sounds_page_label'):
# self.builder.get_object(widget_name).set_property('sensitive', # self.builder.get_object(widget_name).set_property('sensitive',

View file

@ -34,7 +34,7 @@ class TestEmailNotifications(component.Component):
self.n = 1 self.n = 1
self.events = [ self.events = [
FooEvent(), FooEvent(),
CustomEvent() CustomEvent(),
] ]
self.events_classes = [] self.events_classes = []
@ -45,21 +45,21 @@ class TestEmailNotifications(component.Component):
# component.get('CorePlugin.Notifications').register_custom_email_notification( # component.get('CorePlugin.Notifications').register_custom_email_notification(
component.get('Notifications').register_custom_email_notification( component.get('Notifications').register_custom_email_notification(
event.__class__.__name__, event.__class__.__name__,
self.custom_email_message_provider self.custom_email_message_provider,
) )
elif self.__imp == 'gtk': elif self.__imp == 'gtk':
notifications_component = component.get('Notifications') notifications_component = component.get('Notifications')
notifications_component.register_custom_popup_notification( notifications_component.register_custom_popup_notification(
event.__class__.__name__, event.__class__.__name__,
self.custom_popup_message_provider self.custom_popup_message_provider,
) )
notifications_component.register_custom_blink_notification( notifications_component.register_custom_blink_notification(
event.__class__.__name__, event.__class__.__name__,
self.custom_blink_message_provider self.custom_blink_message_provider,
) )
notifications_component.register_custom_sound_notification( notifications_component.register_custom_sound_notification(
event.__class__.__name__, event.__class__.__name__,
self.custom_sound_message_provider self.custom_sound_message_provider,
) )
self.lc.start(60, False) self.lc.start(60, False)

View file

@ -52,5 +52,5 @@ setup(
%s = deluge.plugins.%s:GtkUIPlugin %s = deluge.plugins.%s:GtkUIPlugin
[deluge.plugin.web] [deluge.plugin.web]
%s = deluge.plugins.%s:WebUIPlugin %s = deluge.plugins.%s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower()) * 3) """ % ((__plugin_name__, __plugin_name__.lower()) * 3),
) )

View file

@ -32,13 +32,13 @@ DEFAULT_PREFS = {
'low_active': -1, 'low_active': -1,
'low_active_down': -1, 'low_active_down': -1,
'low_active_up': -1, 'low_active_up': -1,
'button_state': [[0] * 7 for dummy in range(24)] 'button_state': [[0] * 7 for dummy in range(24)],
} }
STATES = { STATES = {
0: 'Green', 0: 'Green',
1: 'Yellow', 1: 'Yellow',
2: 'Red' 2: 'Red',
} }
CONTROLLED_SETTINGS = [ CONTROLLED_SETTINGS = [
@ -46,7 +46,7 @@ CONTROLLED_SETTINGS = [
'max_upload_speed', 'max_upload_speed',
'max_active_limit', 'max_active_limit',
'max_active_downloading', 'max_active_downloading',
'max_active_seeding' 'max_active_seeding',
] ]
@ -127,7 +127,7 @@ class Core(CorePluginBase):
'active_downloads': self.config['low_active_down'], 'active_downloads': self.config['low_active_down'],
'active_seeds': self.config['low_active_up'], 'active_seeds': self.config['low_active_up'],
'download_rate_limit': int(self.config['low_down'] * 1024), 'download_rate_limit': int(self.config['low_down'] * 1024),
'upload_rate_limit': int(self.config['low_up'] * 1024) 'upload_rate_limit': int(self.config['low_up'] * 1024),
} }
component.get('Core').apply_session_settings(settings) component.get('Core').apply_session_settings(settings)
# Resume the session if necessary # Resume the session if necessary

View file

@ -31,8 +31,10 @@ DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
class SchedulerSelectWidget(gtk.DrawingArea): class SchedulerSelectWidget(gtk.DrawingArea):
def __init__(self, hover): def __init__(self, hover):
super(SchedulerSelectWidget, self).__init__() super(SchedulerSelectWidget, self).__init__()
self.set_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | self.set_events(
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) self.connect('expose_event', self.expose)
self.connect('button_press_event', self.mouse_down) self.connect('button_press_event', self.mouse_down)
@ -40,9 +42,11 @@ class SchedulerSelectWidget(gtk.DrawingArea):
self.connect('motion_notify_event', self.mouse_hover) self.connect('motion_notify_event', self.mouse_hover)
self.connect('leave_notify_event', self.mouse_leave) self.connect('leave_notify_event', self.mouse_leave)
self.colors = [[115 / 255, 210 / 255, 22 / 255], self.colors = [
[115 / 255, 210 / 255, 22 / 255],
[237 / 255, 212 / 255, 0 / 255], [237 / 255, 212 / 255, 0 / 255],
[204 / 255, 0 / 255, 0 / 255]] [204 / 255, 0 / 255, 0 / 255],
]
self.button_state = [[0] * 7 for dummy in range(24)] self.button_state = [[0] * 7 for dummy in range(24)]
self.start_point = [0, 0] self.start_point = [0, 0]
@ -69,11 +73,15 @@ class SchedulerSelectWidget(gtk.DrawingArea):
for y in range(7): for y in range(7):
for x in range(24): for x in range(24):
context.set_source_rgba(self.colors[self.button_state[x][y]][0], 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]][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) context.rectangle(
width * (6 * x / 145 + 1 / 145), height * (6 * y / 43 + 1 / 43),
5 * width / 145, 5 * height / 43,
)
context.fill_preserve() context.fill_preserve()
context.set_source_rgba(0.5, 0.5, 0.5, 0.5) context.set_source_rgba(0.5, 0.5, 0.5, 0.5)
context.stroke() context.stroke()
@ -123,9 +131,11 @@ class SchedulerSelectWidget(gtk.DrawingArea):
if self.get_point(event) != self.hover_point: if self.get_point(event) != self.hover_point:
self.hover_point = self.get_point(event) self.hover_point = self.get_point(event)
self.hover_label.set_text(self.hover_days[self.hover_point[1]] + self.hover_label.set_text(
self.hover_days[self.hover_point[1]] +
' ' + str(self.hover_point[0]) + ' ' + str(self.hover_point[0]) +
':00 - ' + str(self.hover_point[0]) + ':59') ':00 - ' + str(self.hover_point[0]) + ':59',
)
if self.mouse_press: 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]]]
@ -153,7 +163,8 @@ class GtkUI(GtkPluginBase):
image=get_resource('green.png'), image=get_resource('green.png'),
text='', text='',
callback=self.on_status_item_clicked, callback=self.on_status_item_clicked,
tooltip='Scheduler') tooltip='Scheduler',
)
def on_state_deferred(state): def on_state_deferred(state):
self.state = state self.state = state

View file

@ -44,5 +44,5 @@ setup(
%s = deluge.plugins.%s:GtkUIPlugin %s = deluge.plugins.%s:GtkUIPlugin
[deluge.plugin.web] [deluge.plugin.web]
%s = deluge.plugins.%s:WebUIPlugin %s = deluge.plugins.%s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower()) * 3) """ % ((__plugin_name__, __plugin_name__.lower()) * 3),
) )

View file

@ -32,7 +32,7 @@ DEFAULT_TOTALS = {
'total_download': 0, 'total_download': 0,
'total_payload_upload': 0, 'total_payload_upload': 0,
'total_payload_download': 0, 'total_payload_download': 0,
'stats': {} 'stats': {},
} }
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -85,7 +85,7 @@ class Core(CorePluginBase):
'dht_cache_nodes', 'dht_cache_nodes',
'dht_torrents', 'dht_torrents',
'num_peers', 'num_peers',
'num_connections' 'num_connections',
) )
self.update_stats() self.update_stats()
@ -121,9 +121,11 @@ class Core(CorePluginBase):
pass 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['dht_cache_nodes'] = stats['dht.dht_node_cache']
stats.update(self.core.get_config_values(['max_download', stats.update(self.core.get_config_values([
'max_download',
'max_upload', 'max_upload',
'max_num_connections'])) 'max_num_connections',
]))
# status = self.core.session.status() # status = self.core.session.status()
# for stat in dir(status): # for stat in dir(status):
# if not stat.startswith('_') and stat not in stats: # if not stat.startswith('_') and stat not in stats:
@ -193,7 +195,8 @@ class Core(CorePluginBase):
@export @export
def get_session_totals(self): def get_session_totals(self):
return self.core.get_session_status( 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 @export
def set_config(self, config): def set_config(self, config):

View file

@ -69,7 +69,7 @@ class Graph(object):
self.mean_selected = True self.mean_selected = True
self.legend_selected = True self.legend_selected = True
self.max_selected = True self.max_selected = True
self.black = (0, 0, 0,) self.black = (0, 0, 0)
self.interval = 2 # 2 secs self.interval = 2 # 2 secs
self.text_bg = (255, 255, 255, 128) # prototyping self.text_bg = (255, 255, 255, 128) # prototyping
self.set_left_axis() self.set_left_axis()
@ -83,7 +83,7 @@ class Graph(object):
'label': label, 'label': label,
'line': line, 'line': line,
'fill': fill, 'fill': fill,
'color': color 'color': color,
} }
def set_stats(self, stats): def set_stats(self, stats):
@ -273,7 +273,8 @@ class Graph(object):
self.ctx.line_to( self.ctx.line_to(
int(right - (len(values) - 1) * step), int(right - (len(values) - 1) * step),
bottom) bottom,
)
self.ctx.close_path() self.ctx.close_path()
def draw_value_poly(self, values, color, max_value, bounds, fill=False): def draw_value_poly(self, values, color, max_value, bounds, fill=False):

View file

@ -45,7 +45,9 @@ DEFAULT_CONF = {
}, },
'seeds_graph': { 'seeds_graph': {
'num_peers': str(gtk.gdk.Color('blue')), 'num_peers': str(gtk.gdk.Color('blue')),
}}} },
},
}
def neat_time(column, cell, model, data): def neat_time(column, cell, model, data):
@ -126,9 +128,11 @@ class GraphsTab(Tab):
# set a clip region # 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() context.clip()
self.graph.draw_to_context(context, self.graph.draw_to_context(
context,
self.graph_widget.allocation.width, self.graph_widget.allocation.width,
self.graph_widget.allocation.height) self.graph_widget.allocation.height,
)
# Do not propagate the event # Do not propagate the event
return False return False
@ -153,12 +157,18 @@ class GraphsTab(Tab):
self.graph_widget = self.bandwidth_graph self.graph_widget = self.bandwidth_graph
self.graph = Graph() self.graph = Graph()
colors = self.colors['bandwidth_graph'] colors = self.colors['bandwidth_graph']
self.graph.add_stat('download_rate', label='Download Rate', self.graph.add_stat(
color=gtk_to_graph_color(colors['download_rate'])) 'download_rate', label='Download Rate',
self.graph.add_stat('upload_rate', label='Upload Rate', color=gtk_to_graph_color(colors['download_rate']),
color=gtk_to_graph_color(colors['upload_rate'])) )
self.graph.set_left_axis(formatter=fspeed_shortform, min=10240, self.graph.add_stat(
formatter_scale=size_formatter_scale) '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,
)
def select_connections_graph(self): def select_connections_graph(self):
log.debug('Selecting connections graph') log.debug('Selecting connections graph')
@ -183,9 +193,11 @@ class GraphsTab(Tab):
def set_colors(self, colors): def set_colors(self, colors):
self.colors = colors self.colors = colors
# Fake switch page to update the graph colors (HACKY) # Fake switch page to update the graph colors (HACKY)
self._on_notebook_switch_page(self.notebook, self._on_notebook_switch_page(
self.notebook,
None, # This is unused None, # This is unused
self.notebook.get_current_page()) self.notebook.get_current_page(),
)
def _on_intervals_changed(self, intervals): def _on_intervals_changed(self, intervals):
liststore = gtk.ListStore(int) liststore = gtk.ListStore(int)

View file

@ -46,10 +46,10 @@ class StatsTestCase(BaseTestCase):
raise unittest.SkipTest('WebUi plugin not available for testing') raise unittest.SkipTest('WebUi plugin not available for testing')
totals = yield client.stats.get_totals() totals = yield client.stats.get_totals()
self.assertEquals(totals['total_upload'], 0) self.assertEqual(totals['total_upload'], 0)
self.assertEquals(totals['total_payload_upload'], 0) self.assertEqual(totals['total_payload_upload'], 0)
self.assertEquals(totals['total_payload_download'], 0) self.assertEqual(totals['total_payload_download'], 0)
self.assertEquals(totals['total_download'], 0) self.assertEqual(totals['total_download'], 0)
# print_totals(totals) # print_totals(totals)
@defer.inlineCallbacks @defer.inlineCallbacks
@ -59,10 +59,10 @@ class StatsTestCase(BaseTestCase):
raise unittest.SkipTest('WebUi plugin not available for testing') raise unittest.SkipTest('WebUi plugin not available for testing')
totals = yield client.stats.get_session_totals() totals = yield client.stats.get_session_totals()
self.assertEquals(totals['total_upload'], 0) self.assertEqual(totals['total_upload'], 0)
self.assertEquals(totals['total_payload_upload'], 0) self.assertEqual(totals['total_payload_upload'], 0)
self.assertEquals(totals['total_payload_download'], 0) self.assertEqual(totals['total_payload_download'], 0)
self.assertEquals(totals['total_download'], 0) self.assertEqual(totals['total_download'], 0)
# print_totals(totals) # print_totals(totals)
@pytest.mark.gtkui @pytest.mark.gtkui

View file

@ -48,5 +48,5 @@ setup(
%s = deluge.plugins.%s:GtkUIPlugin %s = deluge.plugins.%s:GtkUIPlugin
[deluge.plugin.web] [deluge.plugin.web]
%s = deluge.plugins.%s:WebUIPlugin %s = deluge.plugins.%s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower()) * 3) """ % ((__plugin_name__, __plugin_name__.lower()) * 3),
) )

View file

@ -28,8 +28,10 @@ class GtkUI(GtkPluginBase):
self.core = client.toggle self.core = client.toggle
self.plugin = component.get('PluginManager') self.plugin = component.get('PluginManager')
self.separator = self.plugin.add_toolbar_separator() self.separator = self.plugin.add_toolbar_separator()
self.button = self.plugin.add_toolbar_button(self._on_button_clicked, label='Pause Session', self.button = self.plugin.add_toolbar_button(
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): def disable(self):
component.get('PluginManager').remove_toolbar_button(self.button) component.get('PluginManager').remove_toolbar_button(self.button)

View file

@ -45,5 +45,5 @@ setup(
%s = deluge.plugins.%s:GtkUIPlugin %s = deluge.plugins.%s:GtkUIPlugin
[deluge.plugin.web] [deluge.plugin.web]
%s = deluge.plugins.%s:WebUIPlugin %s = deluge.plugins.%s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower()) * 3) """ % ((__plugin_name__, __plugin_name__.lower()) * 3),
) )

View file

@ -33,7 +33,7 @@ log = logging.getLogger(__name__)
DEFAULT_PREFS = { DEFAULT_PREFS = {
'enabled': False, 'enabled': False,
'ssl': False, 'ssl': False,
'port': 8112 'port': 8112,
} }

View file

@ -49,7 +49,7 @@ class GtkUI(GtkPluginBase):
config = { config = {
'enabled': self.builder.get_object('enabled_checkbutton').get_active(), 'enabled': self.builder.get_object('enabled_checkbutton').get_active(),
'ssl': self.builder.get_object('ssl_checkbutton').get_active(), 'ssl': self.builder.get_object('ssl_checkbutton').get_active(),
'port': self.builder.get_object('port_spinbutton').get_value_as_int() 'port': self.builder.get_object('port_spinbutton').get_value_as_int(),
} }
client.webui.set_config(config) client.webui.set_config(config)
@ -75,8 +75,10 @@ class GtkUI(GtkPluginBase):
icon.set_padding(5, 5) icon.set_padding(5, 5)
hbox.pack_start(icon, False, False) hbox.pack_start(icon, False, False)
label = gtk.Label(_('The Deluge web interface is not installed, ' label = gtk.Label(_(
'please install the\ninterface and try again')) 'The Deluge web interface is not installed, '
'please install the\ninterface and try again',
))
label.set_alignment(0, 0.5) label.set_alignment(0, 0.5)
label.set_padding(5, 5) label.set_padding(5, 5)
hbox.pack_start(label) hbox.pack_start(label)

View file

@ -42,5 +42,5 @@ setup(
%s = deluge.plugins.%s:CorePlugin %s = deluge.plugins.%s:CorePlugin
[deluge.plugin.gtkui] [deluge.plugin.gtkui]
%s = deluge.plugins.%s:GtkUIPlugin %s = deluge.plugins.%s:GtkUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower()) * 2) """ % ((__plugin_name__, __plugin_name__.lower()) * 2),
) )

View file

@ -73,8 +73,8 @@ __all__ = ['dumps', 'loads']
py3 = sys.version_info[0] >= 3 py3 = sys.version_info[0] >= 3
if py3: if py3:
long = int # pylint: disable=redefined-builtin long = int # noqa: A001, pylint: disable=redefined-builtin
unicode = str # pylint: disable=redefined-builtin unicode = str # noqa: A001, pylint: disable=redefined-builtin
def int2byte(c): def int2byte(c):
return bytes([c]) return bytes([c])
@ -434,9 +434,13 @@ def test():
f1 = struct.unpack('!f', struct.pack('!f', 25.5))[0] f1 = struct.unpack('!f', struct.pack('!f', 25.5))[0]
f2 = struct.unpack('!f', struct.pack('!f', 29.3))[0] f2 = struct.unpack('!f', struct.pack('!f', 29.3))[0]
f3 = struct.unpack('!f', struct.pack('!f', -0.6))[0] 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), 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, 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),) 2**64, 2**30, 2**33, 2**62, 2**64, False, False, True, -1, 2, 0,
),
)
assert loads(dumps(ld)) == ld assert loads(dumps(ld)) == ld
d = dict(zip(range(-100000, 100000), range(-100000, 100000))) 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})
@ -444,15 +448,15 @@ def test():
assert loads(dumps(ld)) == ld 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 assert loads(dumps(ld)) == ld
ld = tuple([dict(zip(range(n), range(n))) for n in range(100)]) + (b'b',) ld = tuple(dict(zip(range(n), range(n))) for n in range(100)) + (b'b',)
assert loads(dumps(ld)) == ld assert loads(dumps(ld)) == ld
ld = tuple([dict(zip(range(n), range(-n, 0))) for n in range(100)]) + (b'b',) ld = tuple(dict(zip(range(n), range(-n, 0))) for n in range(100)) + (b'b',)
assert loads(dumps(ld)) == ld assert loads(dumps(ld)) == ld
ld = tuple([tuple(range(n)) for n in range(100)]) + (b'b',) ld = tuple(tuple(range(n)) for n in range(100)) + (b'b',)
assert loads(dumps(ld)) == ld assert loads(dumps(ld)) == ld
ld = tuple([b'a' * n for n in range(1000)]) + (b'b',) ld = tuple(b'a' * n for n in range(1000)) + (b'b',)
assert loads(dumps(ld)) == ld assert loads(dumps(ld)) == ld
ld = tuple([b'a' * n for n in range(1000)]) + (None, True, None) ld = tuple(b'a' * n for n in range(1000)) + (None, True, None)
assert loads(dumps(ld)) == ld assert loads(dumps(ld)) == ld
assert loads(dumps(None)) is None assert loads(dumps(None)) is None
assert loads(dumps({None: None})) == {None: None} assert loads(dumps({None: None})) == {None: None}

View file

@ -20,10 +20,14 @@ 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('-m', '--module-name', metavar='<module name>', help='Module name')
parser.add_argument('-p', '--basepath', metavar='<path>', required=True, help='Base path') parser.add_argument('-p', '--basepath', metavar='<path>', required=True, help='Base path')
parser.add_argument('-a', '--author-name', metavar='<author name>', required=True, parser.add_argument(
help='Author name,for the GPL header') '-a', '--author-name', metavar='<author name>', required=True,
parser.add_argument('-e', '--author-email', metavar='<author email>', required=True, help='Author name,for the GPL header',
help='Author email,for the GPL header') )
parser.add_argument(
'-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('-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')
@ -71,7 +75,7 @@ def create_plugin():
'python_path': python_path, 'python_path': python_path,
'url': options.url, 'url': options.url,
'configdir': options.configdir, 'configdir': options.configdir,
'current_year': datetime.utcnow().year 'current_year': datetime.utcnow().year,
} }
filename = os.path.join(path, filename) filename = os.path.join(path, filename)

View file

@ -33,20 +33,34 @@ def is_float_digit(string):
# set up command-line options # set up command-line options
parser = OptionParser() parser = OptionParser()
parser.add_option('--port', help='port for deluge backend host (default: 58846)', default='58846', dest='port') 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)', parser.add_option(
default='localhost', dest='host') '--host', help='hostname of deluge backend to connect to (default: localhost)',
parser.add_option('--max_active_limit', dest='max_active_limit', default='localhost', dest='host',
help='sets the absolute maximum number of active torrents on the deluge backend') )
parser.add_option('--max_active_downloading', dest='max_active_downloading', parser.add_option(
help='sets the maximum number of active downloading torrents on the deluge backend') '--max_active_limit', dest='max_active_limit',
parser.add_option('--max_active_seeding', dest='max_active_seeding', help='sets the absolute maximum number of active torrents on the deluge backend',
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', parser.add_option(
dest='max_download_speed') '--max_active_downloading', dest='max_active_downloading',
parser.add_option('--max_upload_speed', help='sets the maximum global upload speed on the deluge backend', help='sets the maximum number of active downloading torrents on the deluge backend',
dest='max_upload_speed') )
parser.add_option('--debug', help='outputs debug information to the console', default=False, action='store_true', parser.add_option(
dest='debug') '--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',
dest='max_download_speed',
)
parser.add_option(
'--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',
dest='debug',
)
# grab command-line options # grab command-line options
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
@ -80,7 +94,8 @@ if options.max_active_seeding:
if options.max_download_speed: if options.max_download_speed:
if is_float_digit(options.max_download_speed) and ( 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) settings['max_download_speed'] = float(options.max_download_speed)
else: else:
sys.stderr.write('ERROR: Invalid max_download_speed parameter!\n') sys.stderr.write('ERROR: Invalid max_download_speed parameter!\n')
@ -88,7 +103,8 @@ if options.max_download_speed:
if options.max_upload_speed: if options.max_upload_speed:
if is_float_digit(options.max_upload_speed) and ( 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) settings['max_upload_speed'] = float(options.max_upload_speed)
else: else:
sys.stderr.write('ERROR: Invalid max_upload_speed parameter!\n') sys.stderr.write('ERROR: Invalid max_upload_speed parameter!\n')

View file

@ -25,9 +25,11 @@ class BaseTestCase(unittest.TestCase):
def setUp(self): # NOQA: N803 def setUp(self): # NOQA: N803
if len(component._ComponentRegistry.components) != 0: if len(component._ComponentRegistry.components) != 0:
warnings.warn('The component._ComponentRegistry.components is not empty on test setup.\n' 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' % 'This is probably caused by another test that did not clean up after finishing!: %s' %
component._ComponentRegistry.components) component._ComponentRegistry.components,
)
d = maybeDeferred(self.set_up) d = maybeDeferred(self.set_up)
def on_setup_error(error): def on_setup_error(error):

View file

@ -209,8 +209,10 @@ class ProcessOutputHandler(protocol.ProcessProtocol):
print('\n%s' % prefixed) print('\n%s' % prefixed)
def start_core(listen_port=58846, logfile=None, timeout=10, timeout_msg=None, def start_core(
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. """Start the deluge core as a daemon.
Args: Args:
@ -242,7 +244,7 @@ try:
daemon = deluge.core.daemon_entry.start_daemon(skip_start=True) daemon = deluge.core.daemon_entry.start_daemon(skip_start=True)
%s %s
daemon.start() daemon.start()
except: except Exception:
import traceback import traceback
sys.stderr.write('Exception raised:\\n %%s' %% traceback.format_exc()) sys.stderr.write('Exception raised:\\n %%s' %% traceback.format_exc())
""" % (config_directory, listen_port, custom_script) """ % (config_directory, listen_port, custom_script)
@ -254,11 +256,17 @@ except:
# Specify the triggers for daemon log output # Specify the triggers for daemon log output
default_core_cb['triggers'] = [ default_core_cb['triggers'] = [
{'expr': 'Finished loading ', 'value': lambda reader, data, data_all: reader}, {'expr': 'Finished loading ', 'value': lambda reader, data, data_all: reader},
{'expr': 'Could not listen on localhost:%d' % (listen_port), 'type': 'errback', # Error from libtorrent {
'value': lambda reader, data, data_all: CannotListenError('localhost', listen_port, 'expr': 'Could not listen on localhost:%d' % (listen_port), 'type': 'errback', # Error from libtorrent
'Could not start deluge test client!\n%s' % data)}, 'value': lambda reader, data, data_all: CannotListenError(
{'expr': 'Traceback', 'type': 'errback', 'localhost', listen_port,
'value': lambda reader, data, data_all: DelugeError('Traceback found when starting daemon:\n%s' % data)} '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),
},
] ]
callbacks.append(default_core_cb) callbacks.append(default_core_cb)

View file

@ -36,8 +36,10 @@ class DaemonBase(object):
return d return d
@defer.inlineCallbacks @defer.inlineCallbacks
def start_core(self, arg, custom_script='', logfile='', print_stdout=True, print_stderr=True, timeout=5, def start_core(
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 == '': if logfile == '':
logfile = 'daemon_%s.log' % self.id() logfile = 'daemon_%s.log' % self.id()
@ -52,12 +54,14 @@ class DaemonBase(object):
for dummy in range(port_range): for dummy in range(port_range):
try: try:
d, self.core = common.start_core(listen_port=self.listen_port, logfile=logfile, d, self.core = common.start_core(
listen_port=self.listen_port, logfile=logfile,
timeout=timeout, timeout_msg='Timeout!', timeout=timeout, timeout_msg='Timeout!',
custom_script=custom_script, custom_script=custom_script,
print_stdout=print_stdout, print_stdout=print_stdout,
print_stderr=print_stderr, print_stderr=print_stderr,
extra_callbacks=extra_callbacks) extra_callbacks=extra_callbacks,
)
yield d yield d
except CannotListenError as ex: except CannotListenError as ex:
exception_error = ex exception_error = ex

View file

@ -26,5 +26,5 @@ class AuthManagerTestCase(BaseTestCase):
def test_authorize(self): def test_authorize(self):
self.assertEqual( self.assertEqual(
self.auth.authorize(*get_localhost_auth()), self.auth.authorize(*get_localhost_auth()),
AUTH_LEVEL_ADMIN AUTH_LEVEL_ADMIN,
) )

View file

@ -36,8 +36,10 @@ class NoVersionSendingDaemonSSLProxy(DaemonSSLProxy):
class NoVersionSendingClient(Client): class NoVersionSendingClient(Client):
def connect(self, host='127.0.0.1', port=58846, username='', password='', def connect(
skip_authentication=False): self, host='127.0.0.1', port=58846, username='', password='',
skip_authentication=False,
):
self._daemon_proxy = NoVersionSendingDaemonSSLProxy() self._daemon_proxy = NoVersionSendingDaemonSSLProxy()
self._daemon_proxy.set_disconnect_callback(self.__on_disconnect) self._daemon_proxy.set_disconnect_callback(self.__on_disconnect)
@ -116,7 +118,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
def on_failure(failure): def on_failure(failure):
self.assertEqual( self.assertEqual(
failure.trap(error.BadLoginError), failure.trap(error.BadLoginError),
error.BadLoginError error.BadLoginError,
) )
self.assertEqual(failure.value.message, 'Password does not match') self.assertEqual(failure.value.message, 'Password does not match')
self.addCleanup(client.disconnect) self.addCleanup(client.disconnect)
@ -131,7 +133,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
def on_failure(failure): def on_failure(failure):
self.assertEqual( self.assertEqual(
failure.trap(error.BadLoginError), failure.trap(error.BadLoginError),
error.BadLoginError error.BadLoginError,
) )
self.assertEqual(failure.value.message, 'Username does not exist') self.assertEqual(failure.value.message, 'Username does not exist')
self.addCleanup(client.disconnect) self.addCleanup(client.disconnect)
@ -146,7 +148,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
def on_failure(failure): def on_failure(failure):
self.assertEqual( self.assertEqual(
failure.trap(error.AuthenticationRequired), failure.trap(error.AuthenticationRequired),
error.AuthenticationRequired error.AuthenticationRequired,
) )
self.assertEqual(failure.value.username, username) self.assertEqual(failure.value.username, username)
self.addCleanup(client.disconnect) self.addCleanup(client.disconnect)
@ -179,13 +181,13 @@ class ClientTestCase(BaseTestCase, DaemonBase):
username, password = get_localhost_auth() username, password = get_localhost_auth()
no_version_sending_client = NoVersionSendingClient() no_version_sending_client = NoVersionSendingClient()
d = no_version_sending_client.connect( 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): def on_failure(failure):
self.assertEqual( self.assertEqual(
failure.trap(error.IncompatibleClient), failure.trap(error.IncompatibleClient),
error.IncompatibleClient error.IncompatibleClient,
) )
self.addCleanup(no_version_sending_client.disconnect) self.addCleanup(no_version_sending_client.disconnect)

View file

@ -116,7 +116,8 @@ class CommonTestCase(unittest.TestCase):
def test_parse_human_size(self): def test_parse_human_size(self):
from deluge.common import parse_human_size from deluge.common import parse_human_size
sizes = [('1', 1), sizes = [
('1', 1),
('10 bytes', 10), ('10 bytes', 10),
('2048 bytes', 2048), ('2048 bytes', 2048),
('1MiB', 2**(10 * 2)), ('1MiB', 2**(10 * 2)),
@ -126,7 +127,8 @@ class CommonTestCase(unittest.TestCase):
('1M', 10**6), ('1M', 10**6),
('1MB', 10**6), ('1MB', 10**6),
('1 GB', 10**9), ('1 GB', 10**9),
('1 TB', 10**12)] ('1 TB', 10**12),
]
for human_size, byte_size in sizes: for human_size, byte_size in sizes:
parsed = parse_human_size(human_size) parsed = parse_human_size(human_size)
@ -135,7 +137,8 @@ class CommonTestCase(unittest.TestCase):
def test_archive_files(self): def test_archive_files(self):
arc_filelist = [ arc_filelist = [
get_test_data_file('test.torrent'), get_test_data_file('test.torrent'),
get_test_data_file('deluge.png')] get_test_data_file('deluge.png'),
]
arc_filepath = archive_files('test-arc', arc_filelist) arc_filepath = archive_files('test-arc', arc_filelist)
with tarfile.open(arc_filepath, 'r') as tar: with tarfile.open(arc_filepath, 'r') as tar:

View file

@ -137,7 +137,8 @@ class ComponentTestClass(BaseTestCase):
self.assertRaises( self.assertRaises(
component.ComponentAlreadyRegistered, component.ComponentAlreadyRegistered,
ComponentTester, ComponentTester,
'test_register_exception_c1') 'test_register_exception_c1',
)
def test_stop_component(self): def test_stop_component(self):
def on_stop(result, c): def on_stop(result, c):
@ -230,11 +231,15 @@ class ComponentTestClass(BaseTestCase):
result = yield component.start() result = yield component.start()
self.assertEqual( self.assertEqual(
[(result[0][0], result[0][1].value)], [(result[0][0], result[0][1].value)],
[(defer.FAILURE, [(
defer.FAILURE,
component.ComponentException( component.ComponentException(
'Trying to start component "%s" but it is ' 'Trying to start component "%s" but it is '
'not in a stopped state. Current state: %s' % 'not in a stopped state. Current state: %s' %
('test_pause_c1', 'Paused'), ''))]) ('test_pause_c1', 'Paused'), '',
),
)],
)
def test_shutdown(self): def test_shutdown(self):
def on_shutdown(result, c1): def on_shutdown(result, c1):

View file

@ -75,8 +75,10 @@ class TopLevelResource(Resource):
self.putChild('cookie', CookieResource()) self.putChild('cookie', CookieResource())
self.putChild('partial', PartialDownload()) self.putChild('partial', PartialDownload())
self.putChild('redirect', RedirectResource()) self.putChild('redirect', RedirectResource())
self.putChild('ubuntu-9.04-desktop-i386.iso.torrent', self.putChild(
File(common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent'))) 'ubuntu-9.04-desktop-i386.iso.torrent',
File(common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent')),
)
class CoreTestCase(BaseTestCase): class CoreTestCase(BaseTestCase):
@ -152,7 +154,7 @@ class CoreTestCase(BaseTestCase):
from deluge.bencode import bdecode, bencode from deluge.bencode import bdecode, bencode
with open(filename, 'rb') as _file: with open(filename, 'rb') as _file:
info_hash = sha(bencode(bdecode(_file.read())[b'info'])).hexdigest() info_hash = sha(bencode(bdecode(_file.read())[b'info'])).hexdigest()
self.assertEquals(torrent_id, info_hash) self.assertEqual(torrent_id, info_hash)
def test_add_torrent_file_invalid_filedump(self): def test_add_torrent_file_invalid_filedump(self):
options = {} options = {}

View file

@ -31,8 +31,10 @@ class ErrorTestCase(unittest.TestCase):
def test_incompatible_client(self): def test_incompatible_client(self):
version = '1.3.6' version = '1.3.6'
e = deluge.error.IncompatibleClient(version) e = deluge.error.IncompatibleClient(version)
self.assertEqual(str(e), 'Your deluge client is not compatible with the daemon. \ self.assertEqual(
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): def test_not_authorized_error(self):
current_level = 5 current_level = 5

View file

@ -85,8 +85,10 @@ class FilesTabTestCase(BaseTestCase):
return _verify_treestore(treestore.get_iter_root(), tree) return _verify_treestore(treestore.get_iter_root(), tree)
def test_files_tab(self): def test_files_tab(self):
self.filestab.files_list[self.t_id] = ({'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13}, self.filestab.files_list[self.t_id] = (
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14}) {'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14},
)
self.filestab.update_files() 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')
@ -96,8 +98,10 @@ class FilesTabTestCase(BaseTestCase):
self.assertTrue(ret) self.assertTrue(ret)
def test_files_tab2(self): def test_files_tab2(self):
self.filestab.files_list[self.t_id] = ({'index': 0, 'path': '1/1/test_10.txt', 'offset': 0, 'size': 13}, self.filestab.files_list[self.t_id] = (
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14}) {'index': 0, 'path': '1/1/test_10.txt', 'offset': 0, 'size': 13},
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14},
)
self.filestab.update_files() 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')
@ -107,8 +111,10 @@ class FilesTabTestCase(BaseTestCase):
self.assertTrue(ret) self.assertTrue(ret)
def test_files_tab3(self): def test_files_tab3(self):
self.filestab.files_list[self.t_id] = ({'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13}, self.filestab.files_list[self.t_id] = (
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14}) {'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14},
)
self.filestab.update_files() 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')
@ -118,20 +124,30 @@ class FilesTabTestCase(BaseTestCase):
self.assertTrue(ret) self.assertTrue(ret)
def test_files_tab4(self): def test_files_tab4(self):
self.filestab.files_list[self.t_id] = ({'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13}, self.filestab.files_list[self.t_id] = (
{'index': 1, 'path': '1/test_100.txt', 'offset': 13, 'size': 14}) {'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
{'index': 1, 'path': '1/test_100.txt', 'offset': 13, 'size': 14},
)
self.filestab.update_files() 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']]], ret = self.verify_treestore(
['test_10.txt']]]]) self.filestab.treestore, [[
'1/', [
['2/', [['test_100.txt']]],
['test_10.txt'],
],
]],
)
if not ret: if not ret:
self.print_treestore('Treestore not expected:', self.filestab.treestore) self.print_treestore('Treestore not expected:', self.filestab.treestore)
self.assertTrue(ret) self.assertTrue(ret)
def test_files_tab5(self): def test_files_tab5(self):
self.filestab.files_list[self.t_id] = ({'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13}, self.filestab.files_list[self.t_id] = (
{'index': 1, 'path': '2/test_100.txt', 'offset': 13, 'size': 14}) {'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
{'index': 1, 'path': '2/test_100.txt', 'offset': 13, 'size': 14},
)
self.filestab.update_files() 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')

View file

@ -43,8 +43,10 @@ class RenameResource(Resource):
def render(self, request): def render(self, request):
filename = request.args.get('filename', ['renamed_file'])[0] filename = request.args.get('filename', ['renamed_file'])[0]
request.setHeader(b'Content-Type', b'text/plain') request.setHeader(b'Content-Type', b'text/plain')
request.setHeader(b'Content-Disposition', b'attachment; filename=' + request.setHeader(
filename) b'Content-Disposition', b'attachment; filename=' +
filename,
)
return b'This file should be called ' + filename return b'This file should be called ' + filename

View file

@ -38,7 +38,7 @@ class JSONBase(BaseTestCase, DaemonBase):
def connect_client(self, *args, **kwargs): def connect_client(self, *args, **kwargs):
return client.connect( return client.connect(
'localhost', self.listen_port, username=kwargs.get('user', ''), 'localhost', self.listen_port, username=kwargs.get('user', ''),
password=kwargs.get('password', '') password=kwargs.get('password', ''),
) )
def disconnect_client(self, *args): def disconnect_client(self, *args):
@ -219,18 +219,24 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase):
daemon.rpcserver.register_object(test) daemon.rpcserver.register_object(test)
""" """
from twisted.internet.defer import Deferred from twisted.internet.defer import Deferred
extra_callback = {'deferred': Deferred(), 'types': ['stderr'], extra_callback = {
'deferred': Deferred(), 'types': ['stderr'],
'timeout': 10, 'timeout': 10,
'triggers': [{'expr': 'in test_raise_error', 'triggers': [{
'value': lambda reader, data, data_all: 'Test'}]} 'expr': 'in test_raise_error',
'value': lambda reader, data, data_all: 'Test',
}],
}
def on_test_raise(*args): def on_test_raise(*args):
self.assertTrue('Unhandled error in Deferred:' in self.core.stderr_out) self.assertTrue('Unhandled error in Deferred:' in self.core.stderr_out)
self.assertTrue('in test_raise_error' in self.core.stderr_out) self.assertTrue('in test_raise_error' in self.core.stderr_out)
extra_callback['deferred'].addCallback(on_test_raise) extra_callback['deferred'].addCallback(on_test_raise)
d.addCallback(self.start_core, custom_script=custom_script, print_stdout=False, print_stderr=False, d.addCallback(
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) d.addCallbacks(self.connect_client, self.terminate_core)
return d return d
@ -257,7 +263,8 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase):
self.assertEqual( self.assertEqual(
response['error']['message'], response['error']['message'],
'Failure: [Failure instance: Traceback (failure with no frames):' 'Failure: [Failure instance: Traceback (failure with no frames):'
" <class 'deluge.error.DelugeError'>: DelugeERROR\n]") " <class 'deluge.error.DelugeError'>: DelugeERROR\n]",
)
self.assertEqual(response['error']['code'], 4) self.assertEqual(response['error']['code'], 4)
request.write = write request.write = write

View file

@ -31,8 +31,10 @@ class TorrentTestCase(BaseTestCase):
def setup_config(self): def setup_config(self):
config_dir = common.set_tmp_config_dir() config_dir = common.set_tmp_config_dir()
core_config = deluge.config.Config('core.conf', defaults=deluge.core.preferencesmanager.DEFAULT_PREFS, core_config = deluge.config.Config(
config_dir=config_dir) 'core.conf', defaults=deluge.core.preferencesmanager.DEFAULT_PREFS,
config_dir=config_dir,
)
core_config.save() core_config.save()
def set_up(self): def set_up(self):
@ -77,8 +79,10 @@ class TorrentTestCase(BaseTestCase):
return atp return atp
def test_set_prioritize_first_last_pieces(self): def test_set_prioritize_first_last_pieces(self):
piece_indexes = [0, 1, 50, 51, 52, 110, 111, 112, 113, 200, 201, 202, 212, piece_indexes = [
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):
@ -169,7 +173,7 @@ class TorrentTestCase(BaseTestCase):
'num_seeds': 16777215, 'sequential_download': 0, 'announce_to_trackers': 1, '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, 'peers': '\n\x00\x02\x0f=\xc6SC\x17]\xd8}\x7f\x00\x00\x01=\xc6', 'finished_time': 13399,
'last_upload': 13399, 'trackers': [[]], 'super_seeding': 0, 'last_upload': 13399, 'trackers': [[]], 'super_seeding': 0,
'file sizes': [[512000, 1411826586]], 'last_download': 13399 'file sizes': [[512000, 1411826586]], 'last_download': 13399,
} }
torrent_state = TorrentState( torrent_state = TorrentState(
torrent_id='2dc5d0e71a66fe69649a640d39cb00a259704973', torrent_id='2dc5d0e71a66fe69649a640d39cb00a259704973',
@ -185,7 +189,8 @@ class TorrentTestCase(BaseTestCase):
filedump = _file.read() filedump = _file.read()
resume_data = utf8_encode_structure(resume_data) resume_data = utf8_encode_structure(resume_data)
torrent_id = self.core.torrentmanager.add( 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] torrent = self.core.torrentmanager.torrents[torrent_id]
def assert_resume_data(): def assert_resume_data():

View file

@ -42,7 +42,8 @@ setup_translations()
@pytest.mark.gtkui @pytest.mark.gtkui
class TorrentviewTestCase(BaseTestCase): class TorrentviewTestCase(BaseTestCase):
default_column_index = ['filter', 'torrent_id', 'dirty', '#', default_column_index = [
'filter', 'torrent_id', 'dirty', '#',
'Name', 'Name',
'Size', 'Downloaded', 'Uploaded', 'Remaining', 'Size', 'Downloaded', 'Uploaded', 'Remaining',
'Progress', 'Progress',
@ -50,8 +51,10 @@ class TorrentviewTestCase(BaseTestCase):
'Down Speed', 'Up Speed', 'Down Limit', 'Up Limit', 'Down Speed', 'Up Speed', 'Down Limit', 'Up Limit',
'ETA', 'Ratio', 'Avail', 'ETA', 'Ratio', 'Avail',
'Added', 'Completed', 'Complete Seen', 'Added', 'Completed', 'Complete Seen',
'Tracker', 'Download Folder', 'Owner', 'Shared'] 'Tracker', 'Download Folder', 'Owner', 'Shared',
default_liststore_columns = [bool, str, bool, int, ]
default_liststore_columns = [
bool, str, bool, int,
str, str, # Name str, str, # Name
TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64,
float, str, # Progress float, str, # Progress
@ -61,7 +64,8 @@ class TorrentviewTestCase(BaseTestCase):
int, int, int, int, int, int,
str, str, # Tracker str, str, # Tracker
str, str, str, str,
bool] # shared bool,
] # shared
def set_up(self): def set_up(self):
if libs_available is False: if libs_available is False:
@ -89,10 +93,14 @@ class TorrentviewTestCase(BaseTestCase):
# Add a text column # Add a text column
test_col = 'Test column' test_col = 'Test column'
self.torrentview.add_text_column(test_col, status_field=['label']) self.torrentview.add_text_column(test_col, status_field=['label'])
self.assertEqual(len(self.torrentview.liststore_columns), self.assertEqual(
len(TorrentviewTestCase.default_liststore_columns) + 1) len(self.torrentview.liststore_columns),
self.assertEqual(len(self.torrentview.column_index), len(TorrentviewTestCase.default_liststore_columns) + 1,
len(TorrentviewTestCase.default_column_index) + 1) )
self.assertEqual(
len(self.torrentview.column_index),
len(TorrentviewTestCase.default_column_index) + 1,
)
self.assertEqual(self.torrentview.column_index[-1], test_col) self.assertEqual(self.torrentview.column_index[-1], test_col)
self.assertEqual(self.torrentview.columns[test_col].column_indices, [32]) self.assertEqual(self.torrentview.columns[test_col].column_indices, [32])
@ -106,10 +114,14 @@ class TorrentviewTestCase(BaseTestCase):
test_col2 = 'Test column2' test_col2 = 'Test column2'
self.torrentview.add_text_column(test_col2, status_field=['label2']) self.torrentview.add_text_column(test_col2, status_field=['label2'])
self.assertEqual(len(self.torrentview.liststore_columns), self.assertEqual(
len(TorrentviewTestCase.default_liststore_columns) + 2) len(self.torrentview.liststore_columns),
self.assertEqual(len(self.torrentview.column_index), len(TorrentviewTestCase.default_liststore_columns) + 2,
len(TorrentviewTestCase.default_column_index) + 2) )
self.assertEqual(
len(self.torrentview.column_index),
len(TorrentviewTestCase.default_column_index) + 2,
)
# test_col # test_col
self.assertEqual(self.torrentview.column_index[-2], test_col) self.assertEqual(self.torrentview.column_index[-2], test_col)
self.assertEqual(self.torrentview.columns[test_col].column_indices, [32]) self.assertEqual(self.torrentview.columns[test_col].column_indices, [32])
@ -140,10 +152,14 @@ class TorrentviewTestCase(BaseTestCase):
# Remove test_col # Remove test_col
self.torrentview.remove_column(test_col) self.torrentview.remove_column(test_col)
self.assertEqual(len(self.torrentview.liststore_columns), self.assertEqual(
len(TorrentviewTestCase.default_liststore_columns) + 1) len(self.torrentview.liststore_columns),
self.assertEqual(len(self.torrentview.column_index), len(TorrentviewTestCase.default_liststore_columns) + 1,
len(TorrentviewTestCase.default_column_index) + 1) )
self.assertEqual(
len(self.torrentview.column_index),
len(TorrentviewTestCase.default_column_index) + 1,
)
self.assertEqual(self.torrentview.column_index[-1], test_col2) self.assertEqual(self.torrentview.column_index[-1], test_col2)
self.assertEqual(self.torrentview.columns[test_col2].column_indices, [32]) self.assertEqual(self.torrentview.columns[test_col2].column_indices, [32])
@ -159,10 +175,14 @@ class TorrentviewTestCase(BaseTestCase):
# Add a column with multiple column types # Add a column with multiple column types
test_col3 = 'Test column3' 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), self.assertEqual(
len(TorrentviewTestCase.default_liststore_columns) + 2) len(self.torrentview.liststore_columns),
self.assertEqual(len(self.torrentview.column_index), len(TorrentviewTestCase.default_liststore_columns) + 2,
len(TorrentviewTestCase.default_column_index) + 1) )
self.assertEqual(
len(self.torrentview.column_index),
len(TorrentviewTestCase.default_column_index) + 1,
)
self.assertEqual(self.torrentview.column_index[-1], test_col3) self.assertEqual(self.torrentview.column_index[-1], test_col3)
self.assertEqual(self.torrentview.columns[test_col3].column_indices, [32, 33]) self.assertEqual(self.torrentview.columns[test_col3].column_indices, [32, 33])

View file

@ -118,10 +118,12 @@ class DelugeTransferProtocolTestCase(unittest.TestCase):
""" """
self.transfer = TransferTestClass() 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}, self.msg2 = (
2, 3, {'key_float': 12424.29423},
{'key_unicode': 'some string'}, {'key_unicode': 'some string'},
{'key_dict_with_tuple': {'key_tuple': (1, 2, 3)}}, {'key_dict_with_tuple': {'key_tuple': (1, 2, 3)}},
{'keylist': [4, '5', 6.7]}) {'keylist': [4, '5', 6.7]},
)
self.msg1_expected_compressed_base64 = 'RAAAADF4nDvKwJjenp1aGZ+ZV+Lgxfv9PYRXXFLU'\ self.msg1_expected_compressed_base64 = 'RAAAADF4nDvKwJjenp1aGZ+ZV+Lgxfv9PYRXXFLU'\
'XZyfm6oAZGTmpad3gAST8vNznAEAJhSQ' 'XZyfm6oAZGTmpad3gAST8vNznAEAJhSQ'

View file

@ -33,11 +33,23 @@ class UICommonTestCase(unittest.TestCase):
ti = TorrentInfo(filename) ti = TorrentInfo(filename)
files = ti.files_tree['unicode_filenames'] files = ti.files_tree['unicode_filenames']
self.assertTrue((b'\xe3\x83\x86\xe3\x82\xaf\xe3\x82\xb9\xe3\x83\xbb\xe3\x83' self.assertTrue(
b'\x86\xe3\x82\xaf\xe3\x82\xb5\xe3\x83\xb3.mkv').decode('utf8') in files) (
self.assertTrue((b'\xd0\x9c\xd0\xb8\xd1\x85\xd0\xb0\xd0\xb8\xd0\xbb \xd0\x93' b'\xe3\x83\x86\xe3\x82\xaf\xe3\x82\xb9\xe3\x83\xbb\xe3\x83'
b'\xd0\xbe\xd1\x80\xd0\xb1\xd0\xb0\xd1\x87\xd1\x91\xd0\xb2.mkv').decode('utf8') in files) b'\x86\xe3\x82\xaf\xe3\x82\xb5\xe3\x83\xb3.mkv'
).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,
)
self.assertTrue(b"Alisher ibn G'iyosiddin Navoiy.mkv".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) self.assertTrue(b'Ascii title.mkv'.decode('utf8') in files)
self.assertTrue((b'\xe0\xa6\xb8\xe0\xa7\x81\xe0\xa6\x95\xe0\xa7\x81\xe0\xa6\xae\xe0\xa6\xbe' self.assertTrue(
b'\xe0\xa6\xb0 \xe0\xa6\xb0\xe0\xa6\xbe\xe0\xa7\x9f.mkv').decode('utf8') in files) (
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,
)

View file

@ -60,7 +60,7 @@ class StringFileDescriptor(object):
class UIBaseTestCase(object): class UIBaseTestCase(object):
def __init__(self): def __init__(self):
self.var = dict() self.var = {}
def set_up(self): def set_up(self):
common.set_tmp_config_dir() common.set_tmp_config_dir()
@ -339,8 +339,10 @@ class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase):
@defer.inlineCallbacks @defer.inlineCallbacks
def test_console_command_status(self): def test_console_command_status(self):
username, password = get_localhost_auth() username, password = get_localhost_auth()
self.patch(sys, 'argv', self.var['sys_arg_cmd'] + ['--port'] + ['58900'] + ['--username'] + self.patch(
[username] + ['--password'] + [password] + ['status']) sys, 'argv', self.var['sys_arg_cmd'] + ['--port'] + ['58900'] + ['--username'] +
[username] + ['--password'] + [password] + ['status'],
)
fd = StringFileDescriptor(sys.stdout) fd = StringFileDescriptor(sys.stdout)
self.patch(sys, 'stdout', fd) self.patch(sys, 'stdout', fd)

View file

@ -72,8 +72,8 @@ class WebAPITestCase(WebServerTestBase):
'233f23632af0a74748bc5dd1d8717564748877baa16420e6898e17e8aa365e6e': { '233f23632af0a74748bc5dd1d8717564748877baa16420e6898e17e8aa365e6e': {
'login': 'skrot', 'login': 'skrot',
'expires': 1460030877.0, 'expires': 1460030877.0,
'level': 10 'level': 10,
} },
} }
self.deluge_web.web_api.set_config(config) self.deluge_web.web_api.set_config(config)
web_config = component.get('DelugeWeb').config.config web_config = component.get('DelugeWeb').config.config
@ -147,7 +147,10 @@ class WebAPITestCase(WebServerTestBase):
ret['contents'], { ret['contents'], {
'azcvsupdater_2.6.2.jar': { 'azcvsupdater_2.6.2.jar': {
'priority': 4, 'index': 0, 'offset': 0, 'progress': 0.0, 'path': 'priority': 4, 'index': 0, 'offset': 0, 'progress': 0.0, 'path':
'azcvsupdater_2.6.2.jar', 'type': 'file', 'size': 307949}}) 'azcvsupdater_2.6.2.jar', 'type': 'file', 'size': 307949,
},
},
)
@defer.inlineCallbacks @defer.inlineCallbacks
def test_download_torrent_from_url(self): def test_download_torrent_from_url(self):
@ -170,7 +173,10 @@ class WebAPITestCase(WebServerTestBase):
d = yield agent.request( d = yield agent.request(
b'POST', b'POST',
b'http://127.0.0.1:%s/json' % self.webserver_listen_port, b'http://127.0.0.1:%s/json' % self.webserver_listen_port,
Headers({b'User-Agent': [b'Twisted Web Client Example'], Headers({
b'Content-Type': [b'application/json']}), b'User-Agent': [b'Twisted Web Client Example'],
FileBodyProducer(BytesIO(bad_body))) b'Content-Type': [b'application/json'],
}),
FileBodyProducer(BytesIO(bad_body)),
)
yield d yield d

View file

@ -42,12 +42,16 @@ class WebServerTestCase(WebServerTestBase, WebServerMockBase):
# UnicodeDecodeError: 'utf8' codec can't decode byte 0xe5 in position 0: invalid continuation byte # UnicodeDecodeError: 'utf8' codec can't decode byte 0xe5 in position 0: invalid continuation byte
filename = get_test_data_file('filehash_field.torrent') 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 = {'User-Agent': ['Twisted Web Client Example'], headers = {
'Content-Type': ['application/json']} 'User-Agent': ['Twisted Web Client Example'],
'Content-Type': ['application/json'],
}
url = 'http://127.0.0.1:%s/json' % self.webserver_listen_port url = 'http://127.0.0.1:%s/json' % self.webserver_listen_port
d = yield agent.request(b'POST', url.encode('utf-8'), Headers(utf8_encode_structure(headers)), d = yield agent.request(
FileBodyProducer(BytesIO(input_file.encode('utf-8')))) b'POST', url.encode('utf-8'), Headers(utf8_encode_structure(headers)),
FileBodyProducer(BytesIO(input_file.encode('utf-8'))),
)
try: try:
body = yield twisted.web.client.readBody(d) body = yield twisted.web.client.readBody(d)
except AttributeError: except AttributeError:

View file

@ -28,12 +28,14 @@ class _Reporter(object):
self.klass = klass self.klass = klass
deluge = _Reporter('Deluge reporter that suppresses Stacktrace from TODO tests', deluge = _Reporter(
'Deluge reporter that suppresses Stacktrace from TODO tests',
'twisted.plugins.delugereporter', 'twisted.plugins.delugereporter',
description='Deluge Reporter', description='Deluge Reporter',
longOpt='deluge-reporter', longOpt='deluge-reporter',
shortOpt=None, shortOpt=None,
klass='DelugeReporter') klass='DelugeReporter',
)
class DelugeReporter(TreeReporter): class DelugeReporter(TreeReporter):

View file

@ -100,7 +100,7 @@ class Win32IcoFile(object):
def sizes(self): def sizes(self):
"""Get a list of all available icon sizes and color depths.""" """Get a list of all available icon sizes and color depths."""
return set((h['width'], h['height']) for h in self.entry) return {(h['width'], h['height']) for h in self.entry}
def get_image(self, size, bpp=False): def get_image(self, size, bpp=False):
"""Get an image from the icon """Get an image from the icon
@ -171,7 +171,7 @@ class Win32IcoFile(object):
im.size, # (w, h) im.size, # (w, h)
alpha_bytes, # source chars alpha_bytes, # source chars
'raw', # raw decoder 'raw', # raw decoder
('L', 0, -1) # 8bpp inverted, unpadded, reversed ('L', 0, -1), # 8bpp inverted, unpadded, reversed
) )
# apply mask image as alpha channel # apply mask image as alpha channel
@ -198,7 +198,7 @@ class Win32IcoFile(object):
im.size, # (w, h) im.size, # (w, h)
mask_data, # source chars mask_data, # source chars
'raw', # raw decoder 'raw', # raw decoder
('1;I', w // 8, -1) # 1bpp inverted, padded, reversed ('1;I', w // 8, -1), # 1bpp inverted, padded, reversed
) )
# now we have two images, im is XOR image and mask is AND image # now we have two images, im is XOR image and mask is AND image
@ -215,7 +215,8 @@ class Win32IcoFile(object):
def __repr__(self): def __repr__(self):
s = 'Microsoft Icon: %d images (max %dx%d %dbpp)' % ( 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']) self.entry[0]['bpp'],
)
return s return s
# end Win32IcoFile # end Win32IcoFile

View file

@ -170,26 +170,48 @@ class BaseArgParser(argparse.ArgumentParser):
self.process_arg_group = False self.process_arg_group = False
self.group = self.add_argument_group(_('Common Options')) self.group = self.add_argument_group(_('Common Options'))
if common_help: if common_help:
self.group.add_argument('-h', '--help', action=HelpAction, self.group.add_argument(
help=_('Print this help message')) '-h', '--help', action=HelpAction,
self.group.add_argument('-V', '--version', action='version', version='%(prog)s ' + get_version(), help=_('Print this help message'),
help=_('Print version information')) )
self.group.add_argument('-v', action='version', version='%(prog)s ' + get_version(), self.group.add_argument(
help=argparse.SUPPRESS) # Deprecated arg '-V', '--version', action='version', version='%(prog)s ' + get_version(),
self.group.add_argument('-c', '--config', metavar='<config>', help=_('Print version information'),
help=_('Set the config directory path')) )
self.group.add_argument('-l', '--logfile', metavar='<logfile>', self.group.add_argument(
help=_('Output to specified logfile instead of stdout')) '-v', action='version', version='%(prog)s ' + get_version(),
self.group.add_argument('-L', '--loglevel', choices=[l for k in deluge.log.levels for l in (k, k.upper())], help=argparse.SUPPRESS,
help=_('Set the log level (none, error, warning, info, debug)'), metavar='<level>') ) # Deprecated arg
self.group.add_argument('--logrotate', nargs='?', const='2M', metavar='<max-size>', self.group.add_argument(
help=_('Enable logfile rotation, with optional maximum logfile size, ' '-c', '--config', metavar='<config>',
'default: %(const)s (Logfile rotation count is 5)')) help=_('Set the config directory path'),
self.group.add_argument('-q', '--quiet', action='store_true', )
help=_('Quieten logging output (Same as `--loglevel none`)')) self.group.add_argument(
self.group.add_argument('--profile', metavar='<profile-file>', nargs='?', default=False, '-l', '--logfile', metavar='<logfile>',
help=_('Profile %(prog)s with cProfile. Outputs to stdout ' help=_('Output to specified logfile instead of stdout'),
'unless a filename is specified')) )
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>',
)
self.group.add_argument(
'--logrotate', nargs='?', const='2M', metavar='<max-size>',
help=_(
'Enable logfile rotation, with optional maximum logfile size, '
'default: %(const)s (Logfile rotation count is 5)',
),
)
self.group.add_argument(
'-q', '--quiet', action='store_true',
help=_('Quieten logging output (Same as `--loglevel none`)'),
)
self.group.add_argument(
'--profile', metavar='<profile-file>', nargs='?', default=False,
help=_(
'Profile %(prog)s with cProfile. Outputs to stdout '
'unless a filename is specified',
),
)
def parse_args(self, args=None): def parse_args(self, args=None):
"""Parse UI arguments and handle common and process group options. """Parse UI arguments and handle common and process group options.
@ -251,8 +273,10 @@ class BaseArgParser(argparse.ArgumentParser):
logrotate = common.parse_human_size(options.logrotate) logrotate = common.parse_human_size(options.logrotate)
# Setup the logger # Setup the logger
deluge.log.setup_logger(level=options.loglevel, filename=options.logfile, filemode=logfile_mode, deluge.log.setup_logger(
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: if options.config:
if not set_config_dir(options.config): if not set_config_dir(options.config):
@ -300,14 +324,24 @@ class BaseArgParser(argparse.ArgumentParser):
self.process_arg_group = True self.process_arg_group = True
self.group = self.add_argument_group(_('Process Control Options')) self.group = self.add_argument_group(_('Process Control Options'))
self.group.add_argument('-P', '--pidfile', metavar='<pidfile>', action='store', self.group.add_argument(
help=_('Pidfile to store the process id')) '-P', '--pidfile', metavar='<pidfile>', action='store',
help=_('Pidfile to store the process id'),
)
if not common.windows_check(): if not common.windows_check():
self.group.add_argument('-d', '--do-not-daemonize', dest='donotdaemonize', action='store_true', self.group.add_argument(
help=_('Do not daemonize (fork) this process')) '-d', '--do-not-daemonize', dest='donotdaemonize', action='store_true',
self.group.add_argument('-f', '--fork', dest='donotdaemonize', action='store_false', help=_('Do not daemonize (fork) this process'),
help=argparse.SUPPRESS) # Deprecated arg )
self.group.add_argument('-U', '--user', metavar='<user>', action='store', self.group.add_argument(
help=_('Change to this user on startup (Requires root)')) '-f', '--fork', dest='donotdaemonize', action='store_false',
self.group.add_argument('-g', '--group', metavar='<group>', action='store', help=argparse.SUPPRESS,
help=_('Change to this group on startup (Requires root)')) ) # Deprecated arg
self.group.add_argument(
'-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',
help=_('Change to this group on startup (Requires root)'),
)

View file

@ -98,8 +98,10 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
log.debug('Received invalid message: type is not tuple') log.debug('Received invalid message: type is not tuple')
return return
if len(request) < 3: if len(request) < 3:
log.debug('Received invalid message: number of items in ' log.debug(
'response is %s', len(request)) 'Received invalid message: number of items in '
'response is %s', len(request),
)
return return
message_type = request[0] message_type = request[0]
@ -163,8 +165,10 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
log.debug(msg) log.debug(msg)
except Exception: except Exception:
import traceback import traceback
log.error('Failed to handle RPC_ERROR (Old daemon?): %s\nLocal error: %s', log.error(
request[2], traceback.format_exc()) 'Failed to handle RPC_ERROR (Old daemon?): %s\nLocal error: %s',
request[2], traceback.format_exc(),
)
d.errback(exception) d.errback(exception)
del self.__rpc_requests[request_id] del self.__rpc_requests[request_id]
@ -195,17 +199,23 @@ class DelugeRPCClientFactory(ClientFactory):
self.event_handlers = event_handlers self.event_handlers = event_handlers
def startedConnecting(self, connector): # NOQA: N802 def startedConnecting(self, connector): # NOQA: N802
log.debug('Connecting to daemon at "%s:%s"...', log.debug(
connector.host, connector.port) 'Connecting to daemon at "%s:%s"...',
connector.host, connector.port,
)
def clientConnectionFailed(self, connector, reason): # NOQA: N802 def clientConnectionFailed(self, connector, reason): # NOQA: N802
log.debug('Connection to daemon at "%s:%s" failed: %s', log.debug(
connector.host, connector.port, reason.value) 'Connection to daemon at "%s:%s" failed: %s',
connector.host, connector.port, reason.value,
)
self.daemon.connect_deferred.errback(reason) self.daemon.connect_deferred.errback(reason)
def clientConnectionLost(self, connector, reason): # NOQA: N802 def clientConnectionLost(self, connector, reason): # NOQA: N802
log.debug('Connection lost to daemon at "%s:%s" reason: %s', log.debug(
connector.host, connector.port, reason.value) 'Connection lost to daemon at "%s:%s" reason: %s',
connector.host, connector.port, reason.value,
)
self.daemon.host = None self.daemon.host = None
self.daemon.port = None self.daemon.port = None
self.daemon.username = None self.daemon.username = None
@ -262,9 +272,11 @@ class DaemonSSLProxy(DaemonProxy):
log.debug('sslproxy.connect()') log.debug('sslproxy.connect()')
self.host = host self.host = host
self.port = port self.port = port
self.__connector = reactor.connectSSL(self.host, self.port, self.__connector = reactor.connectSSL(
self.host, self.port,
self.__factory, self.__factory,
ssl.ClientContextFactory()) ssl.ClientContextFactory(),
)
self.connect_deferred = defer.Deferred() self.connect_deferred = defer.Deferred()
self.daemon_info_deferred = defer.Deferred() self.daemon_info_deferred = defer.Deferred()
@ -384,8 +396,10 @@ class DaemonSSLProxy(DaemonProxy):
log.debug('%s.authenticate: %s', self.__class__.__name__, username) log.debug('%s.authenticate: %s', self.__class__.__name__, username)
login_deferred = defer.Deferred() login_deferred = defer.Deferred()
d = self.call('daemon.login', username, password, client_version=get_version()) d = self.call('daemon.login', username, password, client_version=get_version())
d.addCallbacks(self.__on_login, self.__on_login_fail, callbackArgs=[username, login_deferred], d.addCallbacks(
errbackArgs=[login_deferred]) self.__on_login, self.__on_login_fail, callbackArgs=[username, login_deferred],
errbackArgs=[login_deferred],
)
return login_deferred return login_deferred
def __on_login(self, result, username, login_deferred): def __on_login(self, result, username, login_deferred):
@ -396,7 +410,8 @@ class DaemonSSLProxy(DaemonProxy):
if self.__factory.event_handlers: if self.__factory.event_handlers:
self.call('daemon.set_event_interest', list(self.__factory.event_handlers)) self.call('daemon.set_event_interest', list(self.__factory.event_handlers))
self.call('core.get_auth_levels_mappings').addCallback( self.call('core.get_auth_levels_mappings').addCallback(
self.__on_auth_levels_mappings) self.__on_auth_levels_mappings,
)
login_deferred.callback(result) login_deferred.callback(result)
@ -434,9 +449,11 @@ class DaemonStandaloneProxy(DaemonProxy):
self.host = 'localhost' self.host = 'localhost'
self.port = 58846 self.port = 58846
# Running in standalone mode, it's safe to import auth level # Running in standalone mode, it's safe to import auth level
from deluge.core.authmanager import (AUTH_LEVEL_ADMIN, from deluge.core.authmanager import (
AUTH_LEVEL_ADMIN,
AUTH_LEVELS_MAPPING, AUTH_LEVELS_MAPPING,
AUTH_LEVELS_MAPPING_REVERSE) AUTH_LEVELS_MAPPING_REVERSE,
)
self.username = 'localclient' self.username = 'localclient'
self.authentication_level = AUTH_LEVEL_ADMIN self.authentication_level = AUTH_LEVEL_ADMIN
self.auth_levels_mapping = AUTH_LEVELS_MAPPING self.auth_levels_mapping = AUTH_LEVELS_MAPPING
@ -528,8 +545,10 @@ class Client(object):
self.disconnect_callback = None self.disconnect_callback = None
self.__started_standalone = False self.__started_standalone = False
def connect(self, host='127.0.0.1', port=58846, username='', password='', def connect(
skip_authentication=False): self, host='127.0.0.1', port=58846, username='', password='',
skip_authentication=False,
):
""" """
Connects to a daemon process. Connects to a daemon process.
@ -635,8 +654,11 @@ class Client(object):
from errno import ENOENT from errno import ENOENT
if ex.errno == ENOENT: if ex.errno == ENOENT:
log.error( log.error(
_('Deluge cannot find the `deluged` executable, check that ' _(
'the deluged package is installed, or added to your PATH.')) 'Deluge cannot find the `deluged` executable, check that '
'the deluged package is installed, or added to your PATH.',
),
)
else: else:
log.exception(ex) log.exception(ex)
except Exception as ex: except Exception as ex:

View file

@ -134,14 +134,14 @@ TORRENT_DATA_FIELD = {
'pieces': 'pieces':
{'name': _('Pieces'), 'status': ['num_pieces', 'piece_length']}, {'name': _('Pieces'), 'status': ['num_pieces', 'piece_length']},
'seed_rank': 'seed_rank':
{'name': _('Seed Rank'), 'status': ['seed_rank']} {'name': _('Seed Rank'), 'status': ['seed_rank']},
} }
TRACKER_STATUS_TRANSLATION = [ TRACKER_STATUS_TRANSLATION = [
_('Error'), _('Error'),
_('Warning'), _('Warning'),
_('Announce OK'), _('Announce OK'),
_('Announce Sent') _('Announce Sent'),
] ]
PREFS_CATOG_TRANS = { PREFS_CATOG_TRANS = {
@ -154,7 +154,7 @@ PREFS_CATOG_TRANS = {
'cache': _('Cache'), 'cache': _('Cache'),
'other': _('Other'), 'other': _('Other'),
'daemon': _('Daemon'), 'daemon': _('Daemon'),
'plugins': _('Plugins') 'plugins': _('Plugins'),
} }
FILE_PRIORITY = { FILE_PRIORITY = {
@ -178,7 +178,7 @@ del _
DISK_CACHE_KEYS = [ DISK_CACHE_KEYS = [
'disk.num_blocks_read', 'disk.num_blocks_written', 'disk.num_read_ops', 'disk.num_write_ops', '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_cache_hits', 'read_hit_ratio', 'write_hit_ratio', 'disk.disk_blocks_in_use',
'disk.read_cache_blocks' 'disk.read_cache_blocks',
] ]
@ -279,13 +279,13 @@ class TorrentInfo(object):
'type': 'file', 'type': 'file',
'index': 0, 'index': 0,
'length': self.__m_metadata['info']['length'], 'length': self.__m_metadata['info']['length'],
'download': True 'download': True,
} },
} },
} }
else: else:
self.__m_files_tree = { self.__m_files_tree = {
self.__m_name: (0, self.__m_metadata['info']['length'], True) self.__m_name: (0, self.__m_metadata['info']['length'], True),
} }
self.__m_files = [] self.__m_files = []
@ -298,13 +298,13 @@ class TorrentInfo(object):
self.__m_files.append({ self.__m_files.append({
'path': f['path'], 'path': f['path'],
'size': f['length'], 'size': f['length'],
'download': True 'download': True,
}) })
else: else:
self.__m_files.append({ self.__m_files.append({
'path': self.__m_name, 'path': self.__m_name,
'size': self.__m_metadata['info']['length'], 'size': self.__m_metadata['info']['length'],
'download': True 'download': True,
}) })
def as_dict(self, *keys): def as_dict(self, *keys):
@ -315,7 +315,7 @@ class TorrentInfo(object):
:param keys: a number of key strings :param keys: a number of key strings
:type keys: string :type keys: string
""" """
return dict([(key, getattr(self, key)) for key in keys]) return {key: getattr(self, key) for key in keys}
@property @property
def name(self): def name(self):
@ -400,7 +400,7 @@ class FileTree2(object):
if child is None: if child is None:
parent['contents'][directory] = { parent['contents'][directory] = {
'type': 'dir', 'type': 'dir',
'contents': {} 'contents': {},
} }
parent = parent['contents'][directory] parent = parent['contents'][directory]
return parent, path return parent, path
@ -411,12 +411,12 @@ class FileTree2(object):
parent, path = get_parent(path) parent, path = get_parent(path)
parent['contents'][path] = { parent['contents'][path] = {
'type': 'dir', 'type': 'dir',
'contents': {} 'contents': {},
} }
else: else:
parent, path = get_parent(path) parent, path = get_parent(path)
parent['contents'][path] = { parent['contents'][path] = {
'type': 'file' 'type': 'file',
} }
def get_tree(self): def get_tree(self):
@ -443,12 +443,12 @@ class FileTree2(object):
full_path = os.path.join(parent_path, path).replace('\\', '/') full_path = os.path.join(parent_path, path).replace('\\', '/')
if directory['contents'][path]['type'] == 'dir': if directory['contents'][path]['type'] == 'dir':
directory['contents'][path] = callback( directory['contents'][path] = callback(
full_path, directory['contents'][path] full_path, directory['contents'][path],
) or directory['contents'][path] ) or directory['contents'][path]
walk(directory['contents'][path], full_path) walk(directory['contents'][path], full_path)
else: else:
directory['contents'][path] = callback( directory['contents'][path] = callback(
full_path, directory['contents'][path] full_path, directory['contents'][path],
) or directory['contents'][path] ) or directory['contents'][path]
walk(self.tree, '') walk(self.tree, '')

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from deluge.ui.console.cmdline.command import BaseCommand from deluge.ui.console.cmdline.command import BaseCommand

View file

@ -35,8 +35,10 @@ class Command(BaseCommand):
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('-p', '--path', dest='path', help=_('download folder for torrent')) parser.add_argument('-p', '--path', dest='path', help=_('download folder for torrent'))
parser.add_argument('torrents', metavar='<torrent>', nargs='+', parser.add_argument(
help=_('One or more torrent files, URLs or magnet URIs')) 'torrents', metavar='<torrent>', nargs='+',
help=_('One or more torrent files, URLs or magnet URIs'),
)
def handle(self, options): def handle(self, options):
self.console = component.get('ConsoleUI') self.console = component.get('ConsoleUI')
@ -62,11 +64,13 @@ class Command(BaseCommand):
if deluge.common.is_url(torrent): if deluge.common.is_url(torrent):
self.console.write('{!info!}Attempting to add torrent from url: %s' % 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( deferreds.append(client.core.add_torrent_url(torrent, t_options).addCallback(on_success).addErrback(
on_fail)) on_fail,
))
elif deluge.common.is_magnet(torrent): elif deluge.common.is_magnet(torrent):
self.console.write('{!info!}Attempting to add torrent from magnet uri: %s' % 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( deferreds.append(client.core.add_torrent_magnet(torrent, t_options).addCallback(on_success).addErrback(
on_fail)) on_fail,
))
else: else:
# Just a file # Just a file
if urlparse(torrent).scheme == 'file': if urlparse(torrent).scheme == 'file':
@ -85,7 +89,7 @@ class Command(BaseCommand):
deferreds.append( deferreds.append(
client.core.add_torrent_file_async( client.core.add_torrent_file_async(
filename, filedump, t_options, filename, filedump, t_options,
).addCallback(on_success).addErrback(on_fail) ).addCallback(on_success).addErrback(on_fail),
) )
return defer.DeferredList(deferreds) return defer.DeferredList(deferreds)

View file

@ -59,7 +59,7 @@ STATUS_KEYS = [
'total_uploaded', 'total_uploaded',
'total_payload_download', 'total_payload_download',
'total_payload_upload', 'total_payload_upload',
'time_added' 'time_added',
] ]
# Add filter specific state to torrent states # Add filter specific state to torrent states
@ -82,21 +82,33 @@ class Command(BaseCommand):
""" """
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('-v', '--verbose', action='store_true', default=False, dest='verbose', parser.add_argument(
help=_('Show more information per torrent.')) '-v', '--verbose', action='store_true', default=False, dest='verbose',
parser.add_argument('-d', '--detailed', action='store_true', default=False, dest='detailed', help=_('Show more information per torrent.'),
help=_('Show more detailed information including files and peers.')) )
parser.add_argument('-s', '--state', action='store', dest='state', parser.add_argument(
help=_('Show torrents with state STATE: %s.' % (', '.join(STATES)))) '-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',
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', action='store', type=str, default='', dest='sort', help=self.sort_help)
parser.add_argument('--sort-reverse', action='store', type=str, default='', dest='sort_rev', parser.add_argument(
help=_('Same as --sort but items are in reverse order.')) '--sort-reverse', action='store', type=str, default='', dest='sort_rev',
parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='*', help=_('Same as --sort but items are in reverse order.'),
help=_('One or more torrent ids. If none is given, list all')) )
parser.add_argument(
'torrent_ids', metavar='<torrent-id>', nargs='*',
help=_('One or more torrent ids. If none is given, list all'),
)
def add_subparser(self, subparsers): def add_subparser(self, subparsers):
parser = subparsers.add_parser(self.name, prog=self.name, help=self.__doc__, parser = subparsers.add_parser(
description=self.__doc__, epilog=self.epilog) self.name, prog=self.name, help=self.__doc__,
description=self.__doc__, epilog=self.epilog,
)
self.add_arguments(parser) self.add_arguments(parser)
def handle(self, options): def handle(self, options):
@ -251,7 +263,8 @@ class Command(BaseCommand):
colors.state_color['Seeding'], colors.state_color['Seeding'],
fspeed(peer['up_speed']), fspeed(peer['up_speed']),
colors.state_color['Downloading'], colors.state_color['Downloading'],
fspeed(peer['down_speed'])) fspeed(peer['down_speed']),
)
s += '\n' s += '\n'
self.console.write(s[:-1]) self.console.write(s[:-1])
@ -280,10 +293,12 @@ class Command(BaseCommand):
if status['state'] != 'Seeding': if status['state'] != 'Seeding':
s += sep s += sep
s += '{!info!}Down Speed: {!input!}%s' % fspeed( s += '{!info!}Down Speed: {!input!}%s' % fspeed(
status['download_payload_rate'], shortform=True) status['download_payload_rate'], shortform=True,
)
s += sep s += sep
s += '{!info!}Up Speed: {!input!}%s' % fspeed( s += '{!info!}Up Speed: {!input!}%s' % fspeed(
status['upload_payload_rate'], shortform=True) status['upload_payload_rate'], shortform=True,
)
self.console.write(s) self.console.write(s)
if status['state'] in ('Seeding', 'Downloading', 'Queued'): if status['state'] in ('Seeding', 'Downloading', 'Queued'):
@ -294,7 +309,8 @@ class Command(BaseCommand):
s += '{!info!}Availability: {!input!}%.2f' % status['distributed_copies'] s += '{!info!}Availability: {!input!}%.2f' % status['distributed_copies']
s += sep s += sep
s += '{!info!}Seed Rank: {!input!}%s' % f_seedrank_dash( 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) self.console.write(s)
total_done = fsize(status['total_done'], shortform=True) total_done = fsize(status['total_done'], shortform=True)
@ -321,7 +337,8 @@ class Command(BaseCommand):
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 += sep
s += '{!info!}Complete Seen: {!input!}%s' % format_date_never( s += '{!info!}Complete Seen: {!input!}%s' % format_date_never(
status['last_seen_complete']) status['last_seen_complete'],
)
self.console.write(s) self.console.write(s)
s = '{!info!}Tracker: {!input!}%s' % status['tracker_host'] s = '{!info!}Tracker: {!input!}%s' % status['tracker_host']
@ -366,14 +383,18 @@ class Command(BaseCommand):
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: if status['download_payload_rate'] > 0:
dl_info += ' @ %s%s' % (down_color, fspeed( dl_info += ' @ %s%s' % (
status['download_payload_rate'], shortform=True)) down_color,
fspeed(status['download_payload_rate'], shortform=True),
)
ul_info = ' {!info!}UL: {!input!}' 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: if status['upload_payload_rate'] > 0:
ul_info += ' @ %s%s' % (up_color, fspeed( ul_info += ' @ %s%s' % (
status['upload_payload_rate'], shortform=True)) up_color,
fspeed(status['upload_payload_rate'], shortform=True),
)
eta = ' {!info!}ETA: {!magenta!}%s' % format_time(status['eta']) eta = ' {!info!}ETA: {!magenta!}%s' % format_time(status['eta'])

View file

@ -33,7 +33,7 @@ torrent_options = {
'stop_ratio': float, 'stop_ratio': float,
'remove_at_ratio': bool, 'remove_at_ratio': bool,
'move_completed': bool, 'move_completed': bool,
'move_completed_path': str 'move_completed_path': str,
} }
@ -43,8 +43,10 @@ class Command(BaseCommand):
usage = _('Usage: manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]') usage = _('Usage: manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]')
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('torrent', metavar='<torrent>', parser.add_argument(
help=_('an expression matched against torrent ids and torrent names')) 'torrent', metavar='<torrent>',
help=_('an expression matched against torrent ids and torrent names'),
)
set_group = parser.add_argument_group('setting a value') 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('-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('values', metavar='<value>', nargs='+', help=_('Value to set'))

View file

@ -21,8 +21,10 @@ class Command(BaseCommand):
usage = 'pause [ * | <torrent-id> [<torrent-id> ...] ]' usage = 'pause [ * | <torrent-id> [<torrent-id> ...] ]'
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='+', parser.add_argument(
help=_('One or more torrent ids. Use "*" to pause all torrents')) 'torrent_ids', metavar='<torrent-id>', nargs='+',
help=_('One or more torrent ids. Use "*" to pause all torrents'),
)
def handle(self, options): def handle(self, options):
self.console = component.get('ConsoleUI') self.console = component.get('ConsoleUI')

View file

@ -20,14 +20,20 @@ class Command(BaseCommand):
"""Manage plugins""" """Manage plugins"""
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('-l', '--list', action='store_true', default=False, dest='list', parser.add_argument(
help=_('Lists available plugins')) '-l', '--list', action='store_true', default=False, dest='list',
parser.add_argument('-s', '--show', action='store_true', default=False, dest='show', help=_('Lists available plugins'),
help=_('Shows enabled plugins')) )
parser.add_argument(
'-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('-e', '--enable', dest='enable', nargs='+', help=_('Enables a plugin'))
parser.add_argument('-d', '--disable', dest='disable', nargs='+', help=_('Disables 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', parser.add_argument(
help=_('Reload list of available plugins')) '-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): def handle(self, options):

View file

@ -21,8 +21,10 @@ class Command(BaseCommand):
usage = _('Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]') usage = _('Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]')
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='+', parser.add_argument(
help=_('One or more torrent ids. Use "*" to resume all torrents')) 'torrent_ids', metavar='<torrent-id>', nargs='+',
help=_('One or more torrent ids. Use "*" to resume all torrents'),
)
def handle(self, options): def handle(self, options):
self.console = component.get('ConsoleUI') self.console = component.get('ConsoleUI')

View file

@ -25,10 +25,14 @@ class Command(BaseCommand):
aliases = ['del'] aliases = ['del']
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('--remove_data', action='store_true', default=False, parser.add_argument(
help=_('Also removes the torrent data')) '--remove_data', action='store_true', default=False,
parser.add_argument('-c', '--confirm', action='store_true', default=False, help=_('Also removes the torrent data'),
help=_('List the matching torrents without removing.')) )
parser.add_argument(
'-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): def handle(self, options):
@ -36,9 +40,11 @@ class Command(BaseCommand):
torrent_ids = self.console.match_torrents(options.torrent_ids) torrent_ids = self.console.match_torrents(options.torrent_ids)
if not options.confirm: if not options.confirm:
self.console.write('{!info!}%d %s %s{!info!}' % (len(torrent_ids), self.console.write('{!info!}%d %s %s{!info!}' % (
len(torrent_ids),
_n('torrent', 'torrents', len(torrent_ids)), _n('torrent', 'torrents', len(torrent_ids)),
_n('match', 'matches', len(torrent_ids)))) _n('match', 'matches', len(torrent_ids)),
))
for t_id in torrent_ids: for t_id in torrent_ids:
name = self.console.get_torrent_name(t_id) name = self.console.get_torrent_name(t_id)
self.console.write('* %-50s (%s)' % (name, t_id)) self.console.write('* %-50s (%s)' % (name, t_id))

View file

@ -26,11 +26,17 @@ class Command(BaseCommand):
"""Shows various status information from the daemon""" """Shows various status information from the daemon"""
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('-r', '--raw', action='store_true', default=False, dest='raw', parser.add_argument(
help=_('Raw values for upload/download rates (without KiB/s suffix)' '-r', '--raw', action='store_true', default=False, dest='raw',
'(useful for scripts that want to do their own parsing)')) help=_(
parser.add_argument('-n', '--no-torrents', action='store_false', default=True, dest='show_torrents', 'Raw values for upload/download rates (without KiB/s suffix)'
help=_('Do not show torrent status (Improves command speed)')) '(useful for scripts that want to do their own parsing)',
),
)
parser.add_argument(
'-n', '--no-torrents', action='store_false', default=True, dest='show_torrents',
help=_('Do not show torrent status (Improves command speed)'),
)
def handle(self, options): def handle(self, options):
self.console = component.get('ConsoleUI') self.console = component.get('ConsoleUI')

View file

@ -22,8 +22,10 @@ class Command(BaseCommand):
aliases = ['reannounce'] aliases = ['reannounce']
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='+', parser.add_argument(
help='One or more torrent ids. "*" updates all torrents') 'torrent_ids', metavar='<torrent-id>', nargs='+',
help='One or more torrent ids. "*" updates all torrents',
)
def handle(self, options): def handle(self, options):
self.console = component.get('ConsoleUI') self.console = component.get('ConsoleUI')

View file

@ -30,8 +30,12 @@ log = logging.getLogger(__name__)
def load_commands(command_dir): def load_commands(command_dir):
def get_command(name): def get_command(name):
command = getattr(__import__('deluge.ui.console.cmdline.commands.%s' % name, command = getattr(
{}, {}, ['Command']), 'Command')() __import__(
'deluge.ui.console.cmdline.commands.%s' % name,
{}, {}, ['Command'],
), 'Command',
)()
command._name = name command._name = name
return command return command
@ -67,29 +71,45 @@ class Console(UI):
def __init__(self, *args, **kwargs): 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'), group = self.parser.add_argument_group(
_('These daemon connect options will be ' _('Console Options'),
'used for commands, or if console ui autoconnect is enabled.')) _(
group.add_argument('-d', '--daemon', metavar='<ip_addr>', dest='daemon_addr', 'These daemon connect options will be '
help=_('Deluge daemon IP address to connect to (default 127.0.0.1)'), default='127.0.0.1') 'used for commands, or if console ui autoconnect is enabled.',
group.add_argument('-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', group.add_argument(
help=_('Deluge daemon username to use when connecting')) '-d', '--daemon', metavar='<ip_addr>', dest='daemon_addr',
group.add_argument('-P', '--password', metavar='<pass>', dest='daemon_pass', help=_('Deluge daemon IP address to connect to (default 127.0.0.1)'), default='127.0.0.1',
help=_('Deluge daemon password to use when connecting')) )
group.add_argument(
'-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',
help=_('Deluge daemon username to use when connecting'),
)
group.add_argument(
'-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), # 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 # 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, self.console_parser = ConsoleCommandParser(
parents=[self.parser], add_help=False, prog=self.parser.prog,
description='Starts the Deluge console interface', description='Starts the Deluge console interface',
formatter_class=lambda prog: formatter_class=lambda prog:
DelugeTextHelpFormatter(prog, max_help_position=33, width=90)) DelugeTextHelpFormatter(prog, max_help_position=33, width=90),
)
self.parser.subparser = self.console_parser self.parser.subparser = self.console_parser
self.console_parser.base_parser = self.parser self.console_parser.base_parser = self.parser
subparsers = self.console_parser.add_subparsers(title=_('Console Commands'), help=_('Description'), subparsers = self.console_parser.add_subparsers(
title=_('Console Commands'), help=_('Description'),
description=_('The following console commands are available:'), 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 from deluge.ui.console import UI_PATH # Must import here
self.console_cmds = load_commands(os.path.join(UI_PATH, 'cmdline', 'commands')) self.console_cmds = load_commands(os.path.join(UI_PATH, 'cmdline', 'commands'))
for cmd in sorted(self.console_cmds): for cmd in sorted(self.console_cmds):
@ -116,5 +136,7 @@ class Console(UI):
log.exception(ex) log.exception(ex)
raise raise
return deluge.common.run_profiled(run, self.options, output_file=self.options.profile, return deluge.common.run_profiled(
do_profile=self.options.profile) run, self.options, output_file=self.options.profile,
do_profile=self.options.profile,
)

View file

@ -49,7 +49,7 @@ DEFAULT_CONSOLE_PREFS = {
'sidebar_width': 25, 'sidebar_width': 25,
'separate_complete': True, 'separate_complete': True,
'move_selection': True, 'move_selection': True,
'columns': {} 'columns': {},
}, },
'addtorrents': { 'addtorrents': {
'show_misc_files': False, # TODO: Showing/hiding this 'show_misc_files': False, # TODO: Showing/hiding this
@ -63,7 +63,7 @@ DEFAULT_CONSOLE_PREFS = {
'third_tab_lists_all': False, 'third_tab_lists_all': False,
'torrents_per_tab_press': 15, 'torrents_per_tab_press': 15,
'save_command_history': True, 'save_command_history': True,
} },
} }
@ -207,8 +207,10 @@ Please use commands from the command line, e.g.:\n
if not self.interactive and options.parsed_cmds[0].command == 'connect': if not self.interactive and options.parsed_cmds[0].command == 'connect':
d = commander.exec_command(options.parsed_cmds.pop(0)) d = commander.exec_command(options.parsed_cmds.pop(0))
else: else:
log.info('connect: host=%s, port=%s, username=%s, password=%s', log.info(
options.daemon_addr, options.daemon_port, options.daemon_user, options.daemon_pass) 'connect: host=%s, port=%s, username=%s, password=%s',
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.addCallback(on_connect)
d.addErrback(on_connect_fail) d.addErrback(on_connect_fail)
@ -279,8 +281,10 @@ Please use commands from the command line, e.g.:\n
# which can cause issues as the popup's screen will not be destroyed. # which can cause issues as the popup's screen will not be destroyed.
# This can lead to the popup border being visible for short periods # This can lead to the popup border being visible for short periods
# while the current modes' screen is repainted. # while the current modes' screen is repainted.
log.error('Mode "%s" still has popups available after being paused.' log.error(
' Ensure all popups are removed on pause!', mode.popup.title) 'Mode "%s" still has popups available after being paused.'
' Ensure all popups are removed on pause!', mode.popup.title,
)
d.addCallback(on_mode_paused, self.active_mode) d.addCallback(on_mode_paused, self.active_mode)
reactor.removeReader(self.active_mode) reactor.removeReader(self.active_mode)
@ -418,8 +422,10 @@ Please use commands from the command line, e.g.:\n
def tab_complete_path(self, line, path_type='file', ext='', sort='name', dirs_first=True): 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): 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, return self.active_mode.tab_complete_path(
sort=sort, dirs_first=dirs_first) line, path_type=path_type, ext=ext,
sort=sort, dirs_first=dirs_first,
)
def on_client_disconnect(self): def on_client_disconnect(self):
component.stop() component.stop()
@ -474,10 +480,12 @@ Please use commands from the command line, e.g.:\n
config['language'] = DEFAULT_CONSOLE_PREFS['language'] config['language'] = DEFAULT_CONSOLE_PREFS['language']
# Migrate column settings # Migrate column settings
columns = ['queue', 'size', 'state', 'progress', 'seeds', 'peers', 'downspeed', 'upspeed', columns = [
'queue', 'size', 'state', 'progress', 'seeds', 'peers', 'downspeed', 'upspeed',
'eta', 'ratio', 'avail', 'added', 'tracker', 'savepath', 'downloaded', 'uploaded', 'eta', 'ratio', 'avail', 'added', 'tracker', 'savepath', 'downloaded', 'uploaded',
'remaining', 'owner', 'downloading_time', 'seeding_time', 'completed', 'seeds_peers_ratio', 'remaining', 'owner', 'downloading_time', 'seeding_time', 'completed', 'seeds_peers_ratio',
'complete_seen', 'down_limit', 'up_limit', 'shared', 'name'] 'complete_seen', 'down_limit', 'up_limit', 'shared', 'name',
]
column_name_mapping = { column_name_mapping = {
'downspeed': 'download_speed', 'downspeed': 'download_speed',
'upspeed': 'upload_speed', 'upspeed': 'upload_speed',
@ -487,7 +495,7 @@ Please use commands from the command line, e.g.:\n
'complete_seen': 'last_seen_complete', 'complete_seen': 'last_seen_complete',
'down_limit': 'max_download_speed', 'down_limit': 'max_download_speed',
'up_limit': 'max_upload_speed', 'up_limit': 'max_upload_speed',
'downloading_time': 'active_time' 'downloading_time': 'active_time',
} }
from deluge.ui.console.modes.torrentlist.torrentview import default_columns from deluge.ui.console.modes.torrentlist.torrentview import default_columns

View file

@ -81,5 +81,5 @@ def add_torrent(t_file, options, success_cb, fail_cb, ress):
filedump = base64.encodestring(_file.read()) filedump = base64.encodestring(_file.read())
client.core.add_torrent_file_async( client.core.add_torrent_file_async(
filename, filedump, t_options filename, filedump, t_options,
).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress) ).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress)

Some files were not shown because too many files have changed in this diff Show more