diff options
author | Calum Lind <calumlind+deluge@gmail.com> | 2014-09-03 22:28:28 +0100 |
---|---|---|
committer | Calum Lind <calumlind+deluge@gmail.com> | 2014-09-03 23:48:34 +0100 |
commit | 5167e93d12cb9f3c43ab2cecdac3961dac6b0fb2 (patch) | |
tree | 92c4504c3943999836edeeaee2b52af3f04dd6c4 /deluge/ui | |
parent | 5d88504c34bab3d00c338ad7f5da7048beba9223 (diff) | |
download | deluge-5167e93d12cb9f3c43ab2cecdac3961dac6b0fb2.tar.gz deluge-5167e93d12cb9f3c43ab2cecdac3961dac6b0fb2.tar.bz2 deluge-5167e93d12cb9f3c43ab2cecdac3961dac6b0fb2.zip |
Flake8 core and common files
* Added N802 to flake8 ignore as certain inherited funcs cannot be changed
to lowercase and this unresolved warning hides other errors/warnings.
* Include new header
Diffstat (limited to 'deluge/ui')
-rw-r--r-- | deluge/ui/Win32IconImagePlugin.py | 385 | ||||
-rw-r--r-- | deluge/ui/client.py | 60 | ||||
-rw-r--r-- | deluge/ui/common.py | 58 | ||||
-rw-r--r-- | deluge/ui/coreconfig.py | 37 | ||||
-rw-r--r-- | deluge/ui/sessionproxy.py | 42 | ||||
-rw-r--r-- | deluge/ui/tracker_icons.py | 69 | ||||
-rw-r--r-- | deluge/ui/ui.py | 57 | ||||
-rw-r--r-- | deluge/ui/web/__init__.py | 1 | ||||
-rw-r--r-- | deluge/ui/web/auth.py | 6 | ||||
-rw-r--r-- | deluge/ui/web/json_api.py | 2 | ||||
-rw-r--r-- | deluge/ui/web/server.py | 2 |
11 files changed, 282 insertions, 437 deletions
diff --git a/deluge/ui/Win32IconImagePlugin.py b/deluge/ui/Win32IconImagePlugin.py index ae6c6c3e7..d46d057ca 100644 --- a/deluge/ui/Win32IconImagePlugin.py +++ b/deluge/ui/Win32IconImagePlugin.py @@ -57,220 +57,199 @@ _MAGIC = '\0\0\1\0' log = logging.getLogger(__name__) -class Win32IcoFile (object): - """ - Decoder for Microsoft .ico files. - """ - - def __init__ (self, buf): - """ - Args: - buf: file-like object containing ico file data - """ - self.buf = buf - self.entry = [] - - header = struct.unpack('<3H', buf.read(6)) - if (0, 1) != header[:2]: - raise SyntaxError('not an ico file') - - self.nb_items = header[2] - - dir_fields = ('width', 'height', 'nb_color', 'reserved', 'planes', 'bpp', - 'size', 'offset') - for i in xrange(self.nb_items): - directory = list(struct.unpack('<4B2H2I', buf.read(16))) - for j in xrange(3): - if not directory[j]: - directory[j] = 256 - icon_header = dict(zip(dir_fields, directory)) - icon_header['color_depth'] = ( - icon_header['bpp'] or - (icon_header['nb_color'] == 16 and 4)) - icon_header['dim'] = (icon_header['width'], icon_header['height']) - self.entry.append(icon_header) - #end for (read headers) - - # order by size and color depth - self.entry.sort(lambda x, y: \ - cmp(x['width'], y['width']) or cmp(x['color_depth'], y['color_depth'])) - self.entry.reverse() - #end __init__ - - - def sizes (self): - """ - Get a list of all available icon sizes and color depths. - """ - return set((h['width'], h['height']) for h in self.entry) - #end sizes - +class Win32IcoFile(object): + """Decoder for Microsoft .ico files.""" + + def __init__(self, buf): + """ + Args: + buf: file-like object containing ico file data + """ + self.buf = buf + self.entry = [] + + header = struct.unpack('<3H', buf.read(6)) + if (0, 1) != header[:2]: + raise SyntaxError('not an ico file') + + self.nb_items = header[2] + + dir_fields = ('width', 'height', 'nb_color', 'reserved', 'planes', 'bpp', 'size', 'offset') + for i in xrange(self.nb_items): + directory = list(struct.unpack('<4B2H2I', buf.read(16))) + for j in xrange(3): + if not directory[j]: + directory[j] = 256 + icon_header = dict(zip(dir_fields, directory)) + icon_header['color_depth'] = (icon_header['bpp'] or (icon_header['nb_color'] == 16 and 4)) + icon_header['dim'] = (icon_header['width'], icon_header['height']) + self.entry.append(icon_header) + #end for (read headers) + + # order by size and color depth + self.entry.sort(lambda x, y: cmp(x['width'], y['width']) + or cmp(x['color_depth'], y['color_depth'])) + self.entry.reverse() + + def sizes(self): + """Get a list of all available icon sizes and color depths.""" + return set((h['width'], h['height']) for h in self.entry) + + def get_image(self, size, bpp=False): + """Get an image from the icon + + Args: + size: tuple of (width, height) + bpp: color depth + """ + for i in range(self.nb_items): + h = self.entry[i] + if size == h['dim'] and (bpp is False or bpp == h['color_depth']): + return self.frame(i) + + return self.frame(0) + + def frame(self, idx): + """ + Get the icon from frame idx + + Args: + idx: Frame index + + Returns: + PIL.Image + """ + header = self.entry[idx] + self.buf.seek(header['offset']) + data = self.buf.read(8) + self.buf.seek(header['offset']) + if data[:8] == PIL.PngImagePlugin._MAGIC: + # png frame + im = PIL.PngImagePlugin.PngImageFile(self.buf) + + else: + # XOR + AND mask bmp frame + im = PIL.BmpImagePlugin.DibImageFile(self.buf) + log.debug("Loaded image: %s %s %s %s", im.format, im.mode, im.size, im.info) + + # change tile dimension to only encompass XOR image + im.size = im.size[0], im.size[1] / 2 + d, e, o, a = im.tile[0] + im.tile[0] = d, (0, 0) + im.size, o, a + + # figure out where AND mask image starts + mode = a[0] + bpp = 8 + for k in PIL.BmpImagePlugin.BIT2MODE.keys(): + if mode == PIL.BmpImagePlugin.BIT2MODE[k][1]: + bpp = k + break + #end for + log.debug("o:%s, w:%s, h:%s, bpp:%s", o, im.size[0], im.size[1], bpp) + and_mask_offset = o + (im.size[0] * im.size[1] * (bpp / 8.0)) + + if 32 == bpp: + # 32-bit color depth icon image allows semitransparent areas + # PIL's DIB format ignores transparency bits, recover them + # The DIB is packed in BGRX byte order where X is the alpha channel + + # Back up to start of bmp data + self.buf.seek(o) + # extract every 4th byte (eg. 3,7,11,15,...) + alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] + + # convert to an 8bpp grayscale image + mask = PIL.Image.frombuffer( + 'L', # 8bpp + im.size, # (w, h) + alpha_bytes, # source chars + 'raw', # raw decoder + ('L', 0, -1) # 8bpp inverted, unpadded, reversed + ) + + # apply mask image as alpha channel + im = im.convert('RGBA') + im.putalpha(mask) + log.debug("image mode: %s", im.mode) + + else: + # get AND image from end of bitmap + w = im.size[0] + if (w % 32) > 0: + # bitmap row data is aligned to word boundaries + w += 32 - (im.size[0] % 32) + # the total mask data is padded row size * height / bits per char + total_bytes = long((w * im.size[1]) / 8) + log.debug("tot=%d, off=%d, w=%d, size=%d", len(data), and_mask_offset, w, total_bytes) + + self.buf.seek(and_mask_offset) + mask_data = self.buf.read(total_bytes) + + # convert raw data to image + mask = PIL.Image.frombuffer( + '1', # 1 bpp + im.size, # (w, h) + mask_data, # source chars + 'raw', # raw decoder + ('1;I', int(w / 8), -1) # 1bpp inverted, padded, reversed + ) + + # now we have two images, im is XOR image and mask is AND image + # set mask as alpha channel + im = im.convert('RGBA') + im.putalpha(mask) + log.debug("image mode: %s", im.mode) + #end if !'RGBA' + #end if (png)/else(bmp) + + return im + #end frame + + def __repr__(self): + s = 'Microsoft Icon: %d images (max %dx%d %dbpp)' % ( + len(self.entry), self.entry[0]['width'], self.entry[0]['height'], + self.entry[0]['bpp']) + return s +#end Win32IcoFile - def get_image (self, size, bpp=False): - """ - Get an image from the icon - Args: - size: tuple of (width, height) - bpp: color depth +class Win32IconImageFile (PIL.ImageFile.ImageFile): """ - idx = 0 - for i in range(self.nb_items): - h = self.entry[i] - if size == h['dim'] and (bpp == False or bpp == h['color_depth']): - return self.frame(i) + PIL read-only image support for Microsoft .ico files. - return self.frame(0) - #end get_image + By default the largest resolution image in the file will be loaded. This can + be changed by altering the 'size' attribute before calling 'load'. + The info dictionary has a key 'sizes' that is a list of the sizes available + in the icon file. - def frame (self, idx): + Handles classic, XP and Vista icon formats. """ - Get the icon from frame idx - Args: - idx: Frame index - - Returns: - PIL.Image - """ - header = self.entry[idx] - self.buf.seek(header['offset']) - data = self.buf.read(8) - self.buf.seek(header['offset']) - if data[:8] == PIL.PngImagePlugin._MAGIC: - # png frame - im = PIL.PngImagePlugin.PngImageFile(self.buf) - - else: - # XOR + AND mask bmp frame - im = PIL.BmpImagePlugin.DibImageFile(self.buf) - log.debug("Loaded image: %s %s %s %s", im.format, im.mode, im.size, - im.info) - - # change tile dimension to only encompass XOR image - im.size = im.size[0], im.size[1] / 2 - d, e, o, a = im.tile[0] - im.tile[0] = d, (0, 0) + im.size, o, a - - # figure out where AND mask image starts - mode = a[0] - bpp = 8 - for k in PIL.BmpImagePlugin.BIT2MODE.keys(): - if mode == PIL.BmpImagePlugin.BIT2MODE[k][1]: - bpp = k - break - #end for - log.debug("o:%s, w:%s, h:%s, bpp:%s", o, im.size[0], im.size[1], bpp) - and_mask_offset = o + (im.size[0] * im.size[1] * (bpp / 8.0)) - - if 32 == bpp: - # 32-bit color depth icon image allows semitransparent areas - # PIL's DIB format ignores transparency bits, recover them - # The DIB is packed in BGRX byte order where X is the alpha channel - - # Back up to start of bmp data - self.buf.seek(o) - # extract every 4th byte (eg. 3,7,11,15,...) - alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] - - # convert to an 8bpp grayscale image - mask = PIL.Image.frombuffer( - 'L', # 8bpp - im.size, # (w, h) - alpha_bytes, # source chars - 'raw', # raw decoder - ('L', 0, -1) # 8bpp inverted, unpadded, reversed - ) - - # apply mask image as alpha channel - im = im.convert('RGBA') - im.putalpha(mask) - log.debug("image mode: %s", im.mode) - - else: - # get AND image from end of bitmap - w = im.size[0] - if (w % 32) > 0: - # bitmap row data is aligned to word boundaries - w += 32 - (im.size[0] % 32) - # the total mask data is padded row size * height / bits per char - total_bytes = long((w * im.size[1]) / 8) - log.debug("tot=%d, off=%d, w=%d, size=%d", - len(data), and_mask_offset, w, total_bytes) - - self.buf.seek(and_mask_offset) - maskData = self.buf.read(total_bytes) - - # convert raw data to image - mask = PIL.Image.frombuffer( - '1', # 1 bpp - im.size, # (w, h) - maskData, # source chars - 'raw', # raw decoder - ('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed - ) - - # now we have two images, im is XOR image and mask is AND image - # set mask as alpha channel - im = im.convert('RGBA') - im.putalpha(mask) - log.debug("image mode: %s", im.mode) - #end if !'RGBA' - #end if (png)/else(bmp) - - return im - #end frame - - - def __repr__ (self): - s = 'Microsoft Icon: %d images (max %dx%d %dbpp)' % ( - len(self.entry), self.entry[0]['width'], self.entry[0]['height'], - self.entry[0]['bpp']) - return s - #end __repr__ -#end Win32IcoFile - - -class Win32IconImageFile (PIL.ImageFile.ImageFile): - """ - PIL read-only image support for Microsoft .ico files. - - By default the largest resolution image in the file will be loaded. This can - be changed by altering the 'size' attribute before calling 'load'. - - The info dictionary has a key 'sizes' that is a list of the sizes available - in the icon file. - - Handles classic, XP and Vista icon formats. - """ - - format = 'ICO' - format_description = 'Microsoft icon' - - def _open (self): - self.ico = Win32IcoFile(self.fp) - self.info['sizes'] = self.ico.sizes() - self.size = self.ico.entry[0]['dim'] - self.load() - #end _open - - def load (self): - im = self.ico.get_image(self.size) - # if tile is PNG, it won't really be loaded yet - im.load() - self.im = im.im - self.mode = im.mode - self.size = im.size - #end load + format = 'ICO' + format_description = 'Microsoft icon' + + def _open(self): + self.ico = Win32IcoFile(self.fp) + self.info['sizes'] = self.ico.sizes() + self.size = self.ico.entry[0]['dim'] + self.load() + + def load(self): + im = self.ico.get_image(self.size) + # if tile is PNG, it won't really be loaded yet + im.load() + self.im = im.im + self.mode = im.mode + self.size = im.size #end class Win32IconImageFile -def _accept (prefix): - """ - Quick file test helper for Image.open() - """ - return prefix[:4] == _MAGIC +def _accept(prefix): + """ + Quick file test helper for Image.open() + """ + return prefix[:4] == _MAGIC #end _accept diff --git a/deluge/ui/client.py b/deluge/ui/client.py index 9472e0349..f0598ac16 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -1,37 +1,11 @@ -# -# client.py +# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> # Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me> # -# Deluge is free software. -# -# You may redistribute it and/or modify it under the terms of the -# GNU General Public License, as published by the Free Software -# Foundation; either version 3 of the License, or (at your option) -# any later version. -# -# deluge is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with deluge. If not, write to: -# The Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor -# Boston, MA 02110-1301, USA. -# -# In addition, as a special exception, the copyright holders give -# permission to link the code of portions of this program with the OpenSSL -# library. -# You must obey the GNU General Public License in all respects for all of -# the code used other than OpenSSL. If you modify file(s) with this -# exception, you may extend this exception to your version of the file(s), -# but you are not obligated to do so. If you do not wish to do so, delete -# this exception statement from your version. If you delete this exception -# statement from all source files in the program, then also delete it here. -# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. # import logging @@ -43,7 +17,6 @@ from twisted.internet.protocol import ClientFactory import deluge.common from deluge import error -from deluge.event import known_events from deluge.transfer import DelugeTransferProtocol from deluge.ui.common import get_localhost_auth @@ -53,6 +26,7 @@ RPC_EVENT = 3 log = logging.getLogger(__name__) + def format_kwargs(kwargs): return ", ".join([key + "=" + str(value) for key, value in kwargs.items()]) @@ -91,10 +65,8 @@ class DelugeRPCRequest(object): :returns: a properly formated RPCRequest """ - if self.request_id is None or self.method is None or self.args is None \ - or self.kwargs is None: - raise TypeError("You must set the properties of this object " - "before calling format_message!") + if self.request_id is None or self.method is None or self.args is None or self.kwargs is None: + raise TypeError("You must set the properties of this object before calling format_message!") return (self.request_id, self.method, self.args, self.kwargs) @@ -156,7 +128,7 @@ class DelugeRPCProtocol(DelugeTransferProtocol): try: exception_cls = getattr(error, request[2]) exception = exception_cls(*request[3], **request[4]) - except TypeError as err: + except TypeError: log.warn("Received invalid RPC_ERROR (Old daemon?): %s", request[2]) return @@ -189,7 +161,8 @@ class DelugeRPCProtocol(DelugeTransferProtocol): log.debug(msg) except: import traceback - log.error("Failed to handle RPC_ERROR (Old daemon?): %s\nLocal error: %s", request[2], traceback.format_exc()) + log.error("Failed to handle RPC_ERROR (Old daemon?): %s\nLocal error: %s", + request[2], traceback.format_exc()) d.errback(exception) del self.__rpc_requests[request_id] @@ -211,6 +184,7 @@ class DelugeRPCProtocol(DelugeTransferProtocol): except Exception as ex: log.warn("Error occurred when sending message: %s", ex) + class DelugeRPCClientFactory(ClientFactory): protocol = DelugeRPCProtocol @@ -240,9 +214,11 @@ class DelugeRPCClientFactory(ClientFactory): if self.daemon.disconnect_callback: self.daemon.disconnect_callback() + class DaemonProxy(object): pass + class DaemonSSLProxy(DaemonProxy): def __init__(self, event_handlers=None): if event_handlers is None: @@ -445,6 +421,7 @@ class DaemonSSLProxy(DaemonProxy): def get_bytes_sent(self): return self.protocol.get_bytes_sent() + class DaemonClassicProxy(DaemonProxy): def __init__(self, event_handlers=None): if event_handlers is None: @@ -513,6 +490,7 @@ class DaemonClassicProxy(DaemonProxy): """ self.__daemon.core.eventmanager.deregister_event_handler(event, handler) + class DottedObject(object): """ This is used for dotted name calls to client @@ -527,6 +505,7 @@ class DottedObject(object): def __getattr__(self, name): return RemoteMethod(self.daemon, self.base + "." + name) + class RemoteMethod(DottedObject): """ This is used when something like 'client.core.get_something()' is attempted. @@ -534,6 +513,7 @@ class RemoteMethod(DottedObject): def __call__(self, *args, **kwargs): return self.daemon.call(self.base, *args, **kwargs) + class Client(object): """ This class is used to connect to a daemon process and issue RPC requests. @@ -650,7 +630,7 @@ class Client(object): that you forgot to install the deluged package or it's not in your PATH.")) else: log.exception(ex) - raise e + raise ex except Exception as ex: log.error("Unable to start daemon!") log.exception(ex) @@ -665,8 +645,8 @@ that you forgot to install the deluged package or it's not in your PATH.")) :returns: bool, True if connected to a localhost """ - if self._daemon_proxy and self._daemon_proxy.host in ("127.0.0.1", "localhost") or\ - isinstance(self._daemon_proxy, DaemonClassicProxy): + if (self._daemon_proxy and self._daemon_proxy.host in ("127.0.0.1", "localhost") or + isinstance(self._daemon_proxy, DaemonClassicProxy)): return True return False diff --git a/deluge/ui/common.py b/deluge/ui/common.py index 97755837d..f0bc6c05a 100644 --- a/deluge/ui/common.py +++ b/deluge/ui/common.py @@ -1,41 +1,15 @@ # -*- coding: utf-8 -*- # -# deluge/ui/common.py -# # Copyright (C) Damien Churchill 2008-2009 <damoxc@gmail.com> # Copyright (C) Andrew Resch 2009 <andrewresch@gmail.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, write to: -# The Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor -# Boston, MA 02110-1301, USA. -# -# In addition, as a special exception, the copyright holders give -# permission to link the code of portions of this program with the OpenSSL -# library. -# You must obey the GNU General Public License in all respects for all of -# the code used other than OpenSSL. If you modify file(s) with this -# exception, you may extend this exception to your version of the file(s), -# but you are not obligated to do so. If you do not wish to do so, delete -# this exception statement from your version. If you delete this exception -# statement from all source files in the program, then also delete it here. -# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. # """ -The ui common module contains methods and classes that are deemed useful for -all the interfaces. +The ui common module contains methods and classes that are deemed useful for all the interfaces. """ import logging @@ -125,7 +99,8 @@ class TorrentInfo(object): path = os.path.join(prefix, *f["path.utf-8"]) del f["path.utf-8"] else: - path = utf8_encoded(os.path.join(prefix, utf8_encoded(os.path.join(*f["path"]), self.encoding)), self.encoding) + path = utf8_encoded(os.path.join(prefix, utf8_encoded(os.path.join(*f["path"]), + self.encoding)), self.encoding) f["path"] = path f["index"] = index if "sha1" in f and len(f["sha1"]) == 20: @@ -192,7 +167,7 @@ class TorrentInfo(object): "path": self.__m_name, "size": self.__m_metadata["info"]["length"], "download": True - }) + }) def as_dict(self, *keys): """ @@ -267,6 +242,7 @@ class TorrentInfo(object): """ return self.__m_filedata + class FileTree2(object): """ Converts a list of paths in to a file tree. @@ -328,16 +304,17 @@ class FileTree2(object): for path in directory["contents"].keys(): full_path = path_join(parent_path, path) if directory["contents"][path]["type"] == "dir": - directory["contents"][path] = callback(full_path, directory["contents"][path]) or \ - directory["contents"][path] + directory["contents"][path] = callback(full_path, directory["contents"][path] + ) or directory["contents"][path] walk(directory["contents"][path], full_path) else: - directory["contents"][path] = callback(full_path, directory["contents"][path]) or \ - directory["contents"][path] + directory["contents"][path] = callback(full_path, directory["contents"][path] + ) or directory["contents"][path] walk(self.tree, "") def __str__(self): lines = [] + def write(path, item): depth = path.count("/") path = os.path.basename(path) @@ -346,6 +323,7 @@ class FileTree2(object): self.walk(write) return "\n".join(lines) + class FileTree(object): """ Convert a list of paths in a file tree. @@ -404,16 +382,15 @@ class FileTree(object): for path in directory.keys(): full_path = os.path.join(parent_path, path) if type(directory[path]) is dict: - directory[path] = callback(full_path, directory[path]) or \ - directory[path] + directory[path] = callback(full_path, directory[path]) or directory[path] walk(directory[path], full_path) else: - directory[path] = callback(full_path, directory[path]) or \ - directory[path] + directory[path] = callback(full_path, directory[path]) or directory[path] walk(self.tree, "") def __str__(self): lines = [] + def write(path, item): depth = path.count("/") path = os.path.basename(path) @@ -422,6 +399,7 @@ class FileTree(object): self.walk(write) return "\n".join(lines) + def get_localhost_auth(): """ Grabs the localclient auth line from the 'auth' file and creates a localhost uri diff --git a/deluge/ui/coreconfig.py b/deluge/ui/coreconfig.py index 6f90739ec..766466bb9 100644 --- a/deluge/ui/coreconfig.py +++ b/deluge/ui/coreconfig.py @@ -1,38 +1,11 @@ -# -# coreconfig.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # -# Deluge is free software. -# -# You may redistribute it and/or modify it under the terms of the -# GNU General Public License, as published by the Free Software -# Foundation; either version 3 of the License, or (at your option) -# any later version. -# -# deluge is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with deluge. If not, write to: -# The Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor -# Boston, MA 02110-1301, USA. -# -# In addition, as a special exception, the copyright holders give -# permission to link the code of portions of this program with the OpenSSL -# library. -# You must obey the GNU General Public License in all respects for all of -# the code used other than OpenSSL. If you modify file(s) with this -# exception, you may extend this exception to your version of the file(s), -# but you are not obligated to do so. If you do not wish to do so, delete -# this exception statement from your version. If you delete this exception -# statement from all source files in the program, then also delete it here. +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. # -# - import logging @@ -41,11 +14,13 @@ from deluge.ui.client import client log = logging.getLogger(__name__) + class CoreConfig(component.Component): def __init__(self): log.debug("CoreConfig init..") component.Component.__init__(self, "CoreConfig") self.config = {} + def on_configvaluechanged_event(key, value): self.config[key] = value client.register_event_handler("ConfigValueChangedEvent", on_configvaluechanged_event) diff --git a/deluge/ui/sessionproxy.py b/deluge/ui/sessionproxy.py index 926193e77..ce3d0b627 100644 --- a/deluge/ui/sessionproxy.py +++ b/deluge/ui/sessionproxy.py @@ -1,38 +1,13 @@ -# -# sessionproxy.py +# -*- coding: utf-8 -*- # # Copyright (C) 2010 Andrew Resch <andrewresch@gmail.com> # -# Deluge is free software. -# -# You may redistribute it and/or modify it under the terms of the -# GNU General Public License, as published by the Free Software -# Foundation; either version 3 of the License, or (at your option) -# any later version. -# -# deluge is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with deluge. If not, write to: -# The Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor -# Boston, MA 02110-1301, USA. -# -# In addition, as a special exception, the copyright holders give -# permission to link the code of portions of this program with the OpenSSL -# library. -# You must obey the GNU General Public License in all respects for all of -# the code used other than OpenSSL. If you modify file(s) with this -# exception, you may extend this exception to your version of the file(s), -# but you are not obligated to do so. If you do not wish to do so, delete -# this exception statement from your version. If you delete this exception -# statement from all source files in the program, then also delete it here. -# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. # + import logging import time @@ -43,6 +18,7 @@ from deluge.ui.client import client log = logging.getLogger(__name__) + class SessionProxy(component.Component): """ The SessionProxy component is used to cache session information client-side @@ -101,7 +77,7 @@ class SessionProxy(component.Component): """ sd = {} keys = set(keys) - keys_len = -1 # The number of keys for the current cache (not the len of keys_diff_cached) + keys_len = -1 # The number of keys for the current cache (not the len of keys_diff_cached) keys_diff_cached = [] for torrent_id in torrent_ids: @@ -161,6 +137,7 @@ class SessionProxy(component.Component): ) else: d = client.core.get_torrent_status(torrent_id, keys_to_get, True) + def on_status(result, torrent_id): t = time.time() self.torrents[torrent_id][0] = t @@ -171,6 +148,7 @@ class SessionProxy(component.Component): return d.addCallback(on_status, torrent_id) else: d = client.core.get_torrent_status(torrent_id, keys, True) + def on_status(result): if result: t = time.time() @@ -247,7 +225,6 @@ class SessionProxy(component.Component): # Don't need to fetch anything return maybeDeferred(self.create_status_dict, self.torrents.keys(), keys) - if len(filter_dict) == 1 and "id" in filter_dict: # At this point we should have a filter with just "id" in it to_fetch = find_torrents_to_fetch(filter_dict["id"]) @@ -271,6 +248,7 @@ class SessionProxy(component.Component): def on_torrent_added(self, torrent_id, from_state): self.torrents[torrent_id] = [time.time() - self.cache_time - 1, {}] self.cache_times[torrent_id] = {} + def on_status(status): self.torrents[torrent_id][1].update(status) t = time.time() diff --git a/deluge/ui/tracker_icons.py b/deluge/ui/tracker_icons.py index 204954483..61db43427 100644 --- a/deluge/ui/tracker_icons.py +++ b/deluge/ui/tracker_icons.py @@ -1,36 +1,10 @@ -# -# tracker_icons.py +# -*- coding: utf-8 -*- # # Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com> # -# Deluge is free software. -# -# You may redistribute it and/or modify it under the terms of the -# GNU General Public License, as published by the Free Software -# Foundation; either version 3 of the License, or (at your option) -# any later version. -# -# deluge is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with deluge. If not, write to: -# The Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor -# Boston, MA 02110-1301, USA. -# -# In addition, as a special exception, the copyright holders give -# permission to link the code of portions of this program with the OpenSSL -# library. -# You must obey the GNU General Public License in all respects for all of -# the code used other than OpenSSL. If you modify file(s) with this -# exception, you may extend this exception to your version of the file(s), -# but you are not obligated to do so. If you do not wish to do so, delete -# this exception statement from your version. If you delete this exception -# statement from all source files in the program, then also delete it here. -# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. # import logging @@ -53,11 +27,10 @@ except ImportError: # twisted 8 from twisted.web.error import NoResource, ForbiddenResource - - try: import PIL.Image as Image import deluge.ui.Win32IconImagePlugin + assert deluge.ui.Win32IconImagePlugin # silence pyflakes except ImportError: PIL_INSTALLED = False else: @@ -65,6 +38,7 @@ else: log = logging.getLogger(__name__) + class TrackerIcon(object): """ Represents a tracker's icon @@ -77,7 +51,7 @@ class TrackerIcon(object): :type filename: string """ self.filename = os.path.abspath(filename) - self.mimetype = extension_to_mimetype(self.filename.rpartition('.')[2]) + self.mimetype = extension_to_mimetype(self.filename.rpartition(".")[2]) self.data = None self.icon_cache = None @@ -90,9 +64,9 @@ class TrackerIcon(object): :returns: whether or not they're equal :rtype: boolean """ - return os.path.samefile(self.filename, other.filename) or \ - self.get_mimetype() == other.get_mimetype() and \ - self.get_data() == other.get_data() + return (os.path.samefile(self.filename, other.filename) or + self.get_mimetype() == other.get_mimetype() and + self.get_data() == other.get_data()) def get_mimetype(self): """ @@ -142,6 +116,7 @@ class TrackerIcon(object): """ return self.icon_cache + class TrackerIcons(Component): """ A TrackerIcon factory class @@ -175,7 +150,7 @@ class TrackerIcons(Component): self.icons[None] = TrackerIcon(no_icon) else: self.icons[None] = None - self.icons[''] = self.icons[None] + self.icons[""] = self.icons[None] self.pending = {} self.redirects = {} @@ -433,7 +408,7 @@ class TrackerIcons(Component): if f.check(PageRedirect): # Handle redirect errors location = urljoin(self.host_to_url(host), error_msg.split(" to ")[1]) - d = self.download_icon([(location, extension_to_mimetype(location.rpartition('.')[2]))] + icons, host) + d = self.download_icon([(location, extension_to_mimetype(location.rpartition(".")[2]))] + icons, host) if not icons: d.addCallbacks(self.on_download_icon_complete, self.on_download_icon_fail, callbackArgs=(host,), errbackArgs=(host,)) @@ -441,7 +416,8 @@ class TrackerIcons(Component): d = self.download_icon(icons, host) elif f.check(NoIconsError, HTMLParseError): # No icons, try favicon.ico as an act of desperation - d = self.download_icon([(urljoin(self.host_to_url(host), "favicon.ico"), extension_to_mimetype("ico"))], host) + d = self.download_icon([(urljoin(self.host_to_url(host), "favicon.ico"), + extension_to_mimetype("ico"))], host) d.addCallbacks(self.on_download_icon_complete, self.on_download_icon_fail, callbackArgs=(host,), errbackArgs=(host,)) else: @@ -465,7 +441,7 @@ class TrackerIcons(Component): filename = icon.get_filename() img = Image.open(filename) if img.size > (16, 16): - new_filename = filename.rpartition('.')[0]+".png" + new_filename = filename.rpartition(".")[0] + ".png" img = img.resize((16, 16), Image.ANTIALIAS) img.save(new_filename) if new_filename != filename: @@ -506,6 +482,7 @@ class TrackerIcons(Component): ################################ HELPER CLASSES ############################### + class FaviconParser(HTMLParser): """ A HTMLParser which extracts favicons from a HTML page @@ -526,7 +503,7 @@ class FaviconParser(HTMLParser): type = value if href: try: - mimetype = extension_to_mimetype(href.rpartition('.')[2]) + mimetype = extension_to_mimetype(href.rpartition(".")[2]) except KeyError: pass else: @@ -561,6 +538,7 @@ def url_to_host(url): """ return urlparse(url).hostname + def host_to_icon_name(host, mimetype): """ Given a host, returns the appropriate icon name @@ -573,7 +551,8 @@ def host_to_icon_name(host, mimetype): :rtype: string """ - return host+'.'+mimetype_to_extension(mimetype) + return host + "." + mimetype_to_extension(mimetype) + def icon_name_to_host(icon): """ @@ -584,7 +563,7 @@ def icon_name_to_host(icon): :returns: the host name :rtype: string """ - return icon.rpartition('.')[0] + return icon.rpartition(".")[0] MIME_MAP = { "image/gif": "gif", @@ -599,6 +578,7 @@ MIME_MAP = { "ico": "image/vnd.microsoft.icon", } + def mimetype_to_extension(mimetype): """ Given a mimetype, returns the appropriate filename extension @@ -611,6 +591,7 @@ def mimetype_to_extension(mimetype): """ return MIME_MAP[mimetype.lower()] + def extension_to_mimetype(extension): """ Given a filename extension, returns the appropriate mimetype @@ -625,8 +606,10 @@ def extension_to_mimetype(extension): ################################## EXCEPTIONS ################################# + class NoIconsError(Exception): pass + class InvalidIconError(Exception): pass diff --git a/deluge/ui/ui.py b/deluge/ui/ui.py index 83cb01b3e..62fff5e76 100644 --- a/deluge/ui/ui.py +++ b/deluge/ui/ui.py @@ -1,36 +1,10 @@ -# -# ui.py +# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # -# Deluge is free software. -# -# You may redistribute it and/or modify it under the terms of the -# GNU General Public License, as published by the Free Software -# Foundation; either version 3 of the License, or (at your option) -# any later version. -# -# deluge is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with deluge. If not, write to: -# The Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor -# Boston, MA 02110-1301, USA. -# -# In addition, as a special exception, the copyright holders give -# permission to link the code of portions of this program with the OpenSSL -# library. -# You must obey the GNU General Public License in all respects for all of -# the code used other than OpenSSL. If you modify file(s) with this -# exception, you may extend this exception to your version of the file(s), -# but you are not obligated to do so. If you do not wish to do so, delete -# this exception statement from your version. If you delete this exception -# statement from all source files in the program, then also delete it here. -# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. # from __future__ import print_function @@ -75,18 +49,15 @@ class _UI(object): self.__parser = OptionParser(usage="%prog [options] [actions]") self.__parser.add_option("-v", "--version", action="callback", callback=version_callback, - help="Show program's version number and exit") + help="Show program's version number and exit") group = OptionGroup(self.__parser, "Common Options") - group.add_option("-c", "--config", dest="config", - help="Set the config folder location", action="store", type="str") - group.add_option("-l", "--logfile", dest="logfile", - help="Output to designated logfile instead of stdout", action="store", type="str") + group.add_option("-c", "--config", dest="config", help="Set the config folder location") + group.add_option("-l", "--logfile", dest="logfile", help="Output to designated logfile instead of stdout") group.add_option("-L", "--loglevel", dest="loglevel", - help="Set the log level: none, info, warning, error, critical, debug", action="store", type="str") - group.add_option("-q", "--quiet", dest="quiet", - help="Sets the log level to 'none', this is the same as `-L none`", action="store_true", default=False) - group.add_option("-r", "--rotate-logs", - help="Rotate logfiles.", action="store_true", default=False) + help="Set the log level: none, info, warning, error, critical, debug") + group.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False, + help="Sets the log level to 'none', this is the same as `-L none`") + group.add_option("-r", "--rotate-logs", help="Rotate logfiles.", action="store_true", default=False) self.__parser.add_option_group(group) @property @@ -171,15 +142,15 @@ class UI: if selected_ui == "gtk": log.info("Starting GtkUI..") from deluge.ui.gtkui.gtkui import GtkUI - ui = GtkUI(args) + GtkUI(args) elif selected_ui == "web": log.info("Starting WebUI..") from deluge.ui.web.web import WebUI - ui = WebUI(args) + WebUI(args) elif selected_ui == "console": log.info("Starting ConsoleUI..") from deluge.ui.console.main import ConsoleUI - ui = ConsoleUI(ui_args) + ConsoleUI(ui_args) except ImportError as ex: import sys import traceback diff --git a/deluge/ui/web/__init__.py b/deluge/ui/web/__init__.py index f70f6a9c8..763684964 100644 --- a/deluge/ui/web/__init__.py +++ b/deluge/ui/web/__init__.py @@ -1 +1,2 @@ from web import start +assert start # silence pyflakes diff --git a/deluge/ui/web/auth.py b/deluge/ui/web/auth.py index 3bf4e2b92..22dffd11a 100644 --- a/deluge/ui/web/auth.py +++ b/deluge/ui/web/auth.py @@ -42,7 +42,7 @@ class AuthError(Exception): def make_checksum(session_id): - return reduce(lambda x, y: x+y, map(ord, session_id)) + return reduce(lambda x, y: x + y, map(ord, session_id)) def get_session_id(session_id): @@ -118,7 +118,7 @@ class Auth(JSONComponent): checksum = str(make_checksum(session_id)) request.addCookie('_session_id', session_id + checksum, - path=request.base+"json", expires=expires_str) + path=request.base + "json", expires=expires_str) log.debug("Creating session for %s", login) config = component.get("DelugeWeb").config @@ -215,7 +215,7 @@ class Auth(JSONComponent): _session_id = request.getCookie("_session_id") request.addCookie('_session_id', _session_id, - path=request.base+"json", expires=expires_str) + path=request.base + "json", expires=expires_str) if method: if not hasattr(method, "_json_export"): diff --git a/deluge/ui/web/json_api.py b/deluge/ui/web/json_api.py index 9b435819b..c315a0691 100644 --- a/deluge/ui/web/json_api.py +++ b/deluge/ui/web/json_api.py @@ -757,7 +757,7 @@ class WebApi(JSONComponent): Return the hosts in the hostlist. """ log.debug("get_hosts called") - return [(tuple(host[HOSTS_ID:HOSTS_PORT+1]) + (_("Offline"),)) for host in self.host_list["hosts"]] + return [(tuple(host[HOSTS_ID:HOSTS_PORT + 1]) + (_("Offline"),)) for host in self.host_list["hosts"]] @export def get_host_status(self, host_id): diff --git a/deluge/ui/web/server.py b/deluge/ui/web/server.py index 6fd61630d..aa5fc2d08 100644 --- a/deluge/ui/web/server.py +++ b/deluge/ui/web/server.py @@ -354,7 +354,7 @@ class ScriptResource(resource.Resource, component.Component): except: pass - dirpath = dirpath[len(filepath)+1:] + dirpath = dirpath[len(filepath) + 1:] if dirpath: scripts.extend(['js/' + path + '/' + dirpath + '/' + f for f in files]) else: |