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

@ -31,9 +31,9 @@ def decode_int(x, f):
f += 1 f += 1
newf = x.index(END_DELIM, f) newf = x.index(END_DELIM, f)
n = int(x[f:newf]) n = int(x[f:newf])
if x[f:f+1] == b'-' and x[f+1:f+2] == b'0': if x[f:f + 1] == b'-' and x[f + 1:f + 2] == b'0':
raise ValueError raise ValueError
elif x[f:f+1] == b'0' and newf != f + 1: elif x[f:f + 1] == b'0' and newf != f + 1:
raise ValueError raise ValueError
return (n, newf + 1) return (n, newf + 1)
@ -41,7 +41,7 @@ def decode_int(x, f):
def decode_string(x, f): def decode_string(x, f):
colon = x.index(BYTE_SEP, f) colon = x.index(BYTE_SEP, f)
n = int(x[f:colon]) n = int(x[f:colon])
if x[f:f+1] == b'0' and colon != f + 1: if x[f:f + 1] == b'0' and colon != f + 1:
raise ValueError raise ValueError
colon += 1 colon += 1
return (x[colon:colon + n], colon + n) return (x[colon:colon + n], colon + n)
@ -49,17 +49,17 @@ def decode_string(x, f):
def decode_list(x, f): def decode_list(x, f):
r, f = [], f + 1 r, f = [], f + 1
while x[f:f+1] != END_DELIM: while x[f:f + 1] != END_DELIM:
v, f = decode_func[x[f:f+1]](x, f) v, f = decode_func[x[f:f + 1]](x, f)
r.append(v) r.append(v)
return (r, f + 1) return (r, f + 1)
def decode_dict(x, f): def decode_dict(x, f):
r, f = {}, f + 1 r, f = {}, f + 1
while x[f:f+1] != END_DELIM: while x[f:f + 1] != END_DELIM:
k, f = decode_string(x, f) k, f = decode_string(x, f)
r[k], f = decode_func[x[f:f+1]](x, f) r[k], f = decode_func[x[f:f + 1]](x, f)
return (r, f + 1) return (r, f + 1)

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: ('iso-8859-1', 'strict'), lambda: ('utf8', 'strict'),
lambda: (chardet.detect(byte_str)['encoding'], 'strict'), lambda: ('iso-8859-1', 'strict'),
lambda: (encoding, 'ignore')] lambda: (chardet.detect(byte_str)['encoding'], 'strict'),
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(
'localclient', ':'.join([
sha(str(random.random()).encode('utf8')).hexdigest(), 'localclient',
str(AUTH_LEVEL_ADMIN) sha(str(random.random()).encode('utf8')).hexdigest(),
]) + '\n') str(AUTH_LEVEL_ADMIN),
]) + '\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(
'not in a stopped state. Current state: %s' % 'Trying to start component "%s" but it is '
(self._component_name, self._component_state), 'not in a stopped state. Current state: %s' %
traceback.format_stack(limit=4))) (self._component_name, self._component_state),
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(
'not in a started state. Current state: %s' % 'Trying to pause component "%s" but it is '
(self._component_name, self._component_state), 'not in a started state. Current state: %s' %
traceback.format_stack(limit=4))) (self._component_name, self._component_state),
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(
'not in a paused state. Current state: %s' % 'Trying to resume component "%s" but it is '
(self._component_name, self._component_state), 'not in a paused state. Current state: %s' %
traceback.format_stack(limit=4))) (self._component_name, self._component_state),
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_dir=self.config_directory, config_file, defaults,
file_version=file_version) config_dir=self.config_directory,
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.port_mapping_notification | lt.alert.category_t.error_notification |
lt.alert.category_t.storage_notification | lt.alert.category_t.port_mapping_notification |
lt.alert.category_t.tracker_notification | lt.alert.category_t.storage_notification |
lt.alert.category_t.status_notification | lt.alert.category_t.tracker_notification |
lt.alert.category_t.ip_block_notification | lt.alert.category_t.status_notification |
lt.alert.category_t.performance_warning) lt.alert.category_t.ip_block_notification |
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,25 +875,32 @@ 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(
args=( target=self._create_torrent_thread,
path, args=(
tracker, path,
piece_length, tracker,
comment, piece_length,
target, comment,
webseeds, target,
private, webseeds,
created_by, private,
trackers, created_by,
add_to_session)).start() trackers,
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(
outgoing_interface=outgoing_interface, listen_interface=listen_interface,
read_only_config_keys=read_only_config_keys) outgoing_interface=outgoing_interface,
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(
outgoing_interface=options.outgoing_interface, listen_interface=options.listen_interface,
interface=options.ui_interface, outgoing_interface=options.outgoing_interface,
port=options.port, interface=options.ui_interface,
read_only_config_keys=options.read_only_config_keys.split(',')) port=options.port,
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(
' Check for other running daemons or services using this port: %s:%s', 'Cannot start deluged, listen port in use.\n'
ex.interface, ex.port) ' Check for other running daemons or services using this port: %s:%s',
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']), {
'in_enc_policy': lt.enc_policy(self.config['enc_in_policy']), 'out_enc_policy': lt.enc_policy(self.config['enc_out_policy']),
'allowed_enc_level': lt.enc_level(pe_enc_level[self.config['enc_level']]), 'in_enc_policy': lt.enc_policy(self.config['enc_in_policy']),
'prefer_rc4': True}) 'allowed_enc_level': lt.enc_level(pe_enc_level[self.config['enc_level']]),
'prefer_rc4': True,
},
)
def _on_set_max_connections_global(self, key, value): 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(
'client. Wrapping it and resending. Error to ' 'An exception occurred while sending RPC_ERROR to '
'send(causing exception goes next):\n%s', formated_tb) 'client. Wrapping it and resending. Error to '
'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(
'not exist and could not create the directory: %s', 'Could not move storage for torrent %s since %s does '
self.torrent_id, dest, ex) 'not exist and could not create the directory: %s',
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,33 +49,35 @@ 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__(
torrent_id=None, self,
filename=None, torrent_id=None,
trackers=None, filename=None,
storage_mode='sparse', trackers=None,
paused=False, storage_mode='sparse',
save_path=None, paused=False,
max_connections=-1, save_path=None,
max_upload_slots=-1, max_connections=-1,
max_upload_speed=-1.0, max_upload_slots=-1,
max_download_speed=-1.0, max_upload_speed=-1.0,
prioritize_first_last=False, max_download_speed=-1.0,
sequential_download=False, prioritize_first_last=False,
file_priorities=None, sequential_download=False,
queue=None, file_priorities=None,
auto_managed=True, queue=None,
is_finished=False, auto_managed=True,
stop_ratio=2.00, is_finished=False,
stop_at_ratio=False, stop_ratio=2.00,
remove_at_ratio=False, stop_at_ratio=False,
move_completed=False, remove_at_ratio=False,
move_completed_path=None, move_completed=False,
magnet=None, move_completed_path=None,
owner=None, magnet=None,
shared=False, owner=None,
super_seeding=False, shared=False,
name=None): super_seeding=False,
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.add_torrent_params_flags_t.flag_duplicate_is_error | (
lt.add_torrent_params_flags_t.flag_override_resume_data) ^ LT_DEFAULT_ADD_TORRENT_FLAGS |
lt.add_torrent_params_flags_t.flag_auto_managed) lt.add_torrent_params_flags_t.flag_duplicate_is_error |
lt.add_torrent_params_flags_t.flag_override_resume_data
) ^
lt.add_torrent_params_flags_t.flag_auto_managed
)
if options['seed_mode']: 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(
name_and_owner['name'], 'Torrent %s from user "%s" %s',
name_and_owner['owner'], name_and_owner['name'],
from_state and 'loaded' or 'added') name_and_owner['owner'],
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.is_finished, total_download, torrent.options['move_completed'], 'Torrent settings: is_finished: %s, total_download: %s, move_completed: %s, move_path: %s',
torrent.options['move_completed_path']) torrent.is_finished, total_download, torrent.options['move_completed'],
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(
force_filename=force_filename, new_url, filename, callback=callback, headers=headers,
allow_compression=allow_compression) force_filename=force_filename,
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(
module_stack[1], module_stack[2], DEPRECATION_WARNING, DeprecationWarning,
caller_module_name) module_stack[1], module_stack[2],
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(
module_stack[1], module_stack[2], DEPRECATION_WARNING, DeprecationWarning,
caller_module_name) module_stack[1], module_stack[2],
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(
safe=None, content_type=None, target=None, webseeds=None, name=None, path, url, piece_length, progress=None, title=None, comment=None,
private=False, created_by=None, trackers=None): safe=None, content_type=None, target=None, webseeds=None, name=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 {
'piece length': piece_length, 'pieces': b''.join(pieces),
'files': fs, 'piece length': piece_length,
'name': name.encode('utf8'), 'files': fs,
'private': private} 'name': name.encode('utf8'),
'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 {
'piece length': piece_length, 'length': size, 'pieces': b''.join(pieces),
'name': name,
'content_type': content_type,
'private': private}
return {'pieces': b''.join(pieces),
'piece length': piece_length, 'length': size, 'piece length': piece_length, 'length': size,
'name': name, 'name': name,
'private': private} 'content_type': content_type,
'private': private,
}
return {
'pieces': b''.join(pieces),
'piece length': piece_length, 'length': size,
'name': name,
'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(
self.pkg_env[name][0].project_name, 'Found plugin: %s %s at %s',
self.pkg_env[name][0].version, self.pkg_env[name][0].project_name,
self.pkg_env[name][0].location) self.pkg_env[name][0].version,
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(
'was already deleted from the TorrentManager', 'Unable to remove torrent file for torrent id %s. It'
torrent_id) 'was already deleted from the TorrentManager',
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 = [
'move_completed', 'label', 'max_download_speed', 'download_location', 'append_extension',
'max_upload_speed', 'max_connections', 'move_completed', 'label', 'max_download_speed',
'max_upload_slots', 'add_paused', 'auto_managed', 'max_upload_speed', 'max_connections',
'stop_at_ratio', 'queue_to_top', 'copy_torrent'] 'max_upload_slots', 'add_paused', 'auto_managed',
'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 [
'label_toggle', 'copy_torrent_toggle', 'append_extension_toggle', 'download_location_toggle',
'delete_copy_torrent_toggle', 'seed_mode']: 'label_toggle', 'copy_torrent_toggle',
'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(
'do not know if they were blocked before. Re-import ' 'IP addresses were removed from the whitelist. Since we '
'current blocklist and re-add whitelisted.') 'do not know if they were blocked before. Re-import '
'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(
text=RECIPIENT_FIELD, 'Recipients', renderer,
editable=RECIPIENT_EDIT) text=RECIPIENT_FIELD,
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(
RECIPIENT_FIELD, recipient, self.recipients_model.append(),
RECIPIENT_EDIT, False) RECIPIENT_FIELD, recipient,
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(
RECIPIENT_FIELD, 'USER@HOST', model.append(),
RECIPIENT_EDIT, True) RECIPIENT_FIELD, 'USER@HOST',
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(
SND_PATH, self.config['sound_path'], selected_iter,
SND_NAME, basename(self.config['sound_path'])) SND_PATH, self.config['sound_path'],
SND_NAME, basename(self.config['sound_path']),
)
def on_sounds_edit_button_clicked(self, widget): 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.RESPONSE_CANCEL, gtk.STOCK_CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_CANCEL,
gtk.RESPONSE_OK) gtk.STOCK_OPEN,
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(
SND_PATH, new_filename, selected_iter,
SND_NAME, basename(new_filename)) SND_PATH, 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_pass', 'smtp_tls', 'smtp_from', 'smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass',
'smtp_recipients'): 'smtp_pass', 'smtp_tls', 'smtp_from',
self.builder.get_object(widget_name).set_property('sensitive', 'smtp_recipients',
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 = [
[237 / 255, 212 / 255, 0 / 255], [115 / 255, 210 / 255, 22 / 255],
[204 / 255, 0 / 255, 0 / 255]] [237 / 255, 212 / 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]][1], self.colors[self.button_state[x][y]][0],
self.colors[self.button_state[x][y]][2], 0.7) self.colors[self.button_state[x][y]][1],
context.rectangle(width * (6 * x / 145 + 1 / 145), height * (6 * y / 43 + 1 / 43), self.colors[self.button_state[x][y]][2], 0.7,
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(
' ' + str(self.hover_point[0]) + self.hover_days[self.hover_point[1]] +
':00 - ' + str(self.hover_point[0]) + ':59') ' ' + str(self.hover_point[0]) +
':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_upload', 'max_download',
'max_num_connections'])) 'max_upload',
'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(
self.graph_widget.allocation.width, context,
self.graph_widget.allocation.height) self.graph_widget.allocation.width,
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(
None, # This is unused self.notebook,
self.notebook.get_current_page()) None, # This is unused
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 = (
tuple(range(-100000, 100000)), b'b' * 31, b'b' * 62, b'b' * 64, 2**30, 2**33, 2**62, (
2**64, 2**30, 2**33, 2**62, 2**64, False, False, True, -1, 2, 0),) {b'a': 15, b'bb': f1, b'ccc': f2, b'': (f3, (), False, True, b'')}, (b'a', 10**20),
tuple(range(-100000, 100000)), b'b' * 31, b'b' * 62, b'b' * 64, 2**30, 2**33, 2**62,
2**64, 2**30, 2**33, 2**62, 2**64, False, False, True, -1, 2, 0,
),
)
assert loads(dumps(ld)) == ld 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(
'This is probably caused by another test that did not clean up after finishing!: %s' % 'The component._ComponentRegistry.components is not empty on test setup.\n'
component._ComponentRegistry.components) 'This is probably caused by another test that did not clean up after finishing!: %s' %
component._ComponentRegistry.components,
)
d = maybeDeferred(self.set_up) 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(
timeout=timeout, timeout_msg='Timeout!', listen_port=self.listen_port, logfile=logfile,
custom_script=custom_script, timeout=timeout, timeout_msg='Timeout!',
print_stdout=print_stdout, custom_script=custom_script,
print_stderr=print_stderr, print_stdout=print_stdout,
extra_callbacks=extra_callbacks) print_stderr=print_stderr,
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,17 +116,19 @@ 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 = [
('10 bytes', 10), ('1', 1),
('2048 bytes', 2048), ('10 bytes', 10),
('1MiB', 2**(10 * 2)), ('2048 bytes', 2048),
('1 MiB', 2**(10 * 2)), ('1MiB', 2**(10 * 2)),
('1 GiB', 2**(10 * 3)), ('1 MiB', 2**(10 * 2)),
('1 GiB', 2**(10 * 3)), ('1 GiB', 2**(10 * 3)),
('1M', 10**6), ('1 GiB', 2**(10 * 3)),
('1MB', 10**6), ('1M', 10**6),
('1 GB', 10**9), ('1MB', 10**6),
('1 TB', 10**12)] ('1 GB', 10**9),
('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 = {
'timeout': 10, 'deferred': Deferred(), 'types': ['stderr'],
'triggers': [{'expr': 'in test_raise_error', 'timeout': 10,
'value': lambda reader, data, data_all: 'Test'}]} 'triggers': [{
'expr': 'in test_raise_error',
'value': lambda reader, data, data_all: 'Test',
}],
}
def on_test_raise(*args): 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,26 +42,30 @@ setup_translations()
@pytest.mark.gtkui @pytest.mark.gtkui
class TorrentviewTestCase(BaseTestCase): class TorrentviewTestCase(BaseTestCase):
default_column_index = ['filter', 'torrent_id', 'dirty', '#', default_column_index = [
'Name', 'filter', 'torrent_id', 'dirty', '#',
'Size', 'Downloaded', 'Uploaded', 'Remaining', 'Name',
'Progress', 'Size', 'Downloaded', 'Uploaded', 'Remaining',
'Seeds', 'Peers', 'Seeds:Peers', 'Progress',
'Down Speed', 'Up Speed', 'Down Limit', 'Up Limit', 'Seeds', 'Peers', 'Seeds:Peers',
'ETA', 'Ratio', 'Avail', 'Down Speed', 'Up Speed', 'Down Limit', 'Up Limit',
'Added', 'Completed', 'Complete Seen', 'ETA', 'Ratio', 'Avail',
'Tracker', 'Download Folder', 'Owner', 'Shared'] 'Added', 'Completed', 'Complete Seen',
default_liststore_columns = [bool, str, bool, int, 'Tracker', 'Download Folder', 'Owner', 'Shared',
str, str, # Name ]
TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, default_liststore_columns = [
float, str, # Progress bool, str, bool, int,
int, int, int, int, float, # Seeds, Peers str, str, # Name
int, int, float, float, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64,
int, float, float, # ETA, Ratio, Avail float, str, # Progress
int, int, int, int, int, int, int, float, # Seeds, Peers
str, str, # Tracker int, int, float, float,
str, str, int, float, float, # ETA, Ratio, Avail
bool] # shared int, int, int,
str, str, # Tracker
str, str,
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 = (
{'key_unicode': 'some string'}, 2, 3, {'key_float': 12424.29423},
{'key_dict_with_tuple': {'key_tuple': (1, 2, 3)}}, {'key_unicode': 'some string'},
{'keylist': [4, '5', 6.7]}) {'key_dict_with_tuple': {'key_tuple': (1, 2, 3)}},
{'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(
'twisted.plugins.delugereporter', 'Deluge reporter that suppresses Stacktrace from TODO tests',
description='Deluge Reporter', 'twisted.plugins.delugereporter',
longOpt='deluge-reporter', description='Deluge Reporter',
shortOpt=None, longOpt='deluge-reporter',
klass='DelugeReporter') shortOpt=None,
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.__factory, self.host, self.port,
ssl.ClientContextFactory()) self.__factory,
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_LEVELS_MAPPING, AUTH_LEVEL_ADMIN,
AUTH_LEVELS_MAPPING_REVERSE) AUTH_LEVELS_MAPPING,
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,13 +443,13 @@ 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, '')
def __str__(self): def __str__(self):

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!}' % (
_n('torrent', 'torrents', len(torrent_ids)), len(torrent_ids),
_n('match', 'matches', len(torrent_ids)))) _n('torrent', 'torrents', 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(
description='Starts the Deluge console interface', parents=[self.parser], add_help=False, prog=self.parser.prog,
formatter_class=lambda prog: description='Starts the Deluge console interface',
DelugeTextHelpFormatter(prog, max_help_position=33, width=90)) formatter_class=lambda prog:
DelugeTextHelpFormatter(prog, max_help_position=33, width=90),
)
self.parser.subparser = self.console_parser self.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(
description=_('The following console commands are available:'), title=_('Console Commands'), help=_('Description'),
metavar=_('Command'), dest='command') description=_('The following console commands are available:'),
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 = [
'eta', 'ratio', 'avail', 'added', 'tracker', 'savepath', 'downloaded', 'uploaded', 'queue', 'size', 'state', 'progress', 'seeds', 'peers', 'downspeed', 'upspeed',
'remaining', 'owner', 'downloading_time', 'seeding_time', 'completed', 'seeds_peers_ratio', 'eta', 'ratio', 'avail', 'added', 'tracker', 'savepath', 'downloaded', 'uploaded',
'complete_seen', 'down_limit', 'up_limit', 'shared', 'name'] 'remaining', 'owner', 'downloading_time', 'seeding_time', 'completed', 'seeds_peers_ratio',
'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

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