[Python3] Fixes to make code backward compatible

* Continuation of updating code to Python 3 with Python 2 fallback.
 * Using io.open allows files to be encoded and decoded automatically on write and read. This
 maintains the python boundaries of unicode in code and bytes for output/files so less
 explicit encoding or decoding.
 * io.StringIO is the replacement for StringIO and will only accept unicode strings.
 * io.BytesIO is used where bytes output is required by the enclosing method.
 * Update bencode for full compatibility.
This commit is contained in:
Calum Lind 2017-03-05 09:29:51 +00:00
parent ede0f710f8
commit 481f779349
27 changed files with 216 additions and 145 deletions

View File

@ -9,32 +9,39 @@
# License.
# Written by Petru Paler
# Updated by Calum Lind to support both Python 2 and Python 3.
# Minor modifications made by Andrew Resch to replace the BTFailure errors with Exceptions
from sys import version_info
from types import DictType, IntType, ListType, LongType, StringType, TupleType
PY2 = version_info.major == 2
class BTFailure(Exception):
pass
DICT_DELIM = b'd'
END_DELIM = b'e'
INT_DELIM = b'i'
LIST_DELIM = b'l'
BYTE_SEP = b':'
def decode_int(x, f):
f += 1
newf = x.index('e', f)
newf = x.index(END_DELIM, f)
n = int(x[f:newf])
if x[f] == '-':
if x[f + 1] == '0':
raise ValueError
elif x[f] == '0' and newf != f + 1:
if x[f:f+1] == b'-' and x[f+1:f+2] == b'0':
raise ValueError
elif x[f:f+1] == b'0' and newf != f + 1:
raise ValueError
return (n, newf + 1)
def decode_string(x, f):
colon = x.index(':', f)
colon = x.index(BYTE_SEP, f)
n = int(x[f:colon])
if x[f] == '0' and colon != f + 1:
if x[f:f+1] == b'0' and colon != f + 1:
raise ValueError
colon += 1
return (x[colon:colon + n], colon + n)
@ -42,43 +49,43 @@ def decode_string(x, f):
def decode_list(x, f):
r, f = [], f + 1
while x[f] != 'e':
v, f = decode_func[x[f]](x, f)
while x[f:f+1] != END_DELIM:
v, f = decode_func[x[f:f+1]](x, f)
r.append(v)
return (r, f + 1)
def decode_dict(x, f):
r, f = {}, f + 1
while x[f] != 'e':
while x[f:f+1] != END_DELIM:
k, f = decode_string(x, f)
r[k], f = decode_func[x[f]](x, f)
r[k], f = decode_func[x[f:f+1]](x, f)
return (r, f + 1)
decode_func = {}
decode_func['l'] = decode_list
decode_func['d'] = decode_dict
decode_func['i'] = decode_int
decode_func['0'] = decode_string
decode_func['1'] = decode_string
decode_func['2'] = decode_string
decode_func['3'] = decode_string
decode_func['4'] = decode_string
decode_func['5'] = decode_string
decode_func['6'] = decode_string
decode_func['7'] = decode_string
decode_func['8'] = decode_string
decode_func['9'] = decode_string
decode_func[LIST_DELIM] = decode_list
decode_func[DICT_DELIM] = decode_dict
decode_func[INT_DELIM] = decode_int
decode_func[b'0'] = decode_string
decode_func[b'1'] = decode_string
decode_func[b'2'] = decode_string
decode_func[b'3'] = decode_string
decode_func[b'4'] = decode_string
decode_func[b'5'] = decode_string
decode_func[b'6'] = decode_string
decode_func[b'7'] = decode_string
decode_func[b'8'] = decode_string
decode_func[b'9'] = decode_string
def bdecode(x):
try:
r, l = decode_func[x[0]](x, 0)
r, l = decode_func[x[0:1]](x, 0)
except (IndexError, KeyError, ValueError):
raise BTFailure('not a valid bencoded string')
return r
raise BTFailure('Not a valid bencoded string')
else:
return r
class Bencached(object):
@ -94,53 +101,52 @@ def encode_bencached(x, r):
def encode_int(x, r):
r.extend(('i', str(x), 'e'))
r.extend((INT_DELIM, str(x).encode('utf8'), END_DELIM))
def encode_bool(x, r):
if x:
encode_int(1, r)
else:
encode_int(0, r)
encode_int(1 if x else 0, r)
def encode_string(x, r):
r.extend((str(len(x)), ':', x))
encode_string(x.encode('utf8'), r)
def encode_bytes(x, r):
r.extend((str(len(x)).encode('utf8'), BYTE_SEP, x))
def encode_list(x, r):
r.append('l')
r.append(LIST_DELIM)
for i in x:
encode_func[type(i)](i, r)
r.append('e')
r.append(END_DELIM)
def encode_dict(x, r):
r.append('d')
ilist = sorted(x.items())
for k, v in ilist:
r.extend((str(len(k)), ':', k))
r.append(DICT_DELIM)
for k, v in sorted(x.items()):
r.extend((str(len(k)).encode('utf8'), BYTE_SEP, k))
encode_func[type(v)](v, r)
r.append('e')
r.append(END_DELIM)
encode_func = {}
encode_func[Bencached] = encode_bencached
encode_func[IntType] = encode_int
encode_func[LongType] = encode_int
encode_func[StringType] = encode_string
encode_func[ListType] = encode_list
encode_func[TupleType] = encode_list
encode_func[DictType] = encode_dict
try:
from types import BooleanType
encode_func[BooleanType] = encode_bool
except ImportError:
pass
encode_func[int] = encode_int
encode_func[list] = encode_list
encode_func[tuple] = encode_list
encode_func[dict] = encode_dict
encode_func[bool] = encode_bool
encode_func[str] = encode_string
encode_func[bytes] = encode_bytes
if PY2:
encode_func[long] = encode_int
encode_func[str] = encode_bytes
encode_func[unicode] = encode_string
def bencode(x):
r = []
encode_func[type(x)](x, r)
return ''.join(r)
return b''.join(r)

View File

@ -62,6 +62,9 @@ TORRENT_STATE = [
'Moving'
]
# The output formatting for json.dump
JSON_FORMAT = {'indent': 4, 'sort_keys': True, 'ensure_ascii': False}
PY2 = sys.version_info.major == 2
@ -678,8 +681,12 @@ def create_magnet_uri(infohash, name=None, trackers=None):
str: A magnet uri string.
"""
try:
infohash = infohash.decode('hex')
except AttributeError:
pass
uri = [MAGNET_SCHEME, XT_BTIH_PARAM, base64.b32encode(infohash.decode('hex'))]
uri = [MAGNET_SCHEME, XT_BTIH_PARAM, base64.b32encode(infohash)]
if name:
uri.extend(['&', DN_PARAM, name])
if trackers:
@ -983,7 +990,7 @@ def create_localclient_account(append=False):
with open(auth_file, 'a' if append else 'w') as _file:
_file.write(':'.join([
'localclient',
sha(str(random.random())).hexdigest(),
sha(str(random.random()).encode('utf8')).hexdigest(),
str(AUTH_LEVEL_ADMIN)
]) + '\n')
_file.flush()
@ -1090,7 +1097,14 @@ def unicode_argv():
# As a last resort, just default to utf-8
encoding = encoding or 'utf-8'
return [arg.decode(encoding) for arg in sys.argv]
arg_list = []
for arg in sys.argv:
try:
arg_list.append(arg.decode(encoding))
except AttributeError:
arg_list.append(arg)
return arg_list
def run_profiled(func, *args, **kwargs):
@ -1116,8 +1130,8 @@ def run_profiled(func, *args, **kwargs):
print('Profile stats saved to %s' % output_file)
else:
import pstats
import StringIO
strio = StringIO.StringIO()
from io import StringIO
strio = StringIO()
ps = pstats.Stats(profiler, stream=strio).sort_stats('cumulative')
ps.print_stats()
print(strio.getvalue())

View File

@ -46,8 +46,10 @@ import json
import logging
import os
import shutil
from codecs import getwriter
from io import open
from deluge.common import get_default_config_dir
from deluge.common import JSON_FORMAT, get_default_config_dir
log = logging.getLogger(__name__)
callLater = None # Necessary for the config tests
@ -172,11 +174,6 @@ class Config(object):
5
"""
try:
value = value.encode('utf8')
except AttributeError:
pass
if key not in self.__config:
self.__config[key] = value
log.debug('Setting key "%s" to: %s (of type: %s)', key, value, type(value))
@ -195,8 +192,10 @@ class Config(object):
log.warning('Value Type "%s" invalid for key: %s', type(value), key)
raise
log.debug('Setting key "%s" to: %s (of type: %s)', key, value, type(value))
if isinstance(value, bytes):
value.decode('utf8')
log.debug('Setting key "%s" to: %s (of type: %s)', key, value, type(value))
self.__config[key] = value
global callLater
@ -243,10 +242,7 @@ class Config(object):
5
"""
try:
return self.__config[key].decode('utf8')
except AttributeError:
return self.__config[key]
return self.__config[key]
def get(self, key, default=None):
"""Gets the value of item 'key' if key is in the config, else default.
@ -394,7 +390,7 @@ class Config(object):
filename = self.__config_file
try:
with open(filename, 'rb') as _file:
with open(filename, 'r', encoding='utf8') as _file:
data = _file.read()
except IOError as ex:
log.warning('Unable to open config file %s: %s', filename, ex)
@ -444,7 +440,7 @@ class Config(object):
# Check to see if the current config differs from the one on disk
# We will only write a new config file if there is a difference
try:
with open(filename, 'rb') as _file:
with open(filename, 'r', encoding='utf8') as _file:
data = _file.read()
objects = find_json_objects(data)
start, end = objects[0]
@ -463,8 +459,8 @@ class Config(object):
try:
log.debug('Saving new config file %s', filename + '.new')
with open(filename + '.new', 'wb') as _file:
json.dump(self.__version, _file, indent=2)
json.dump(self.__config, _file, indent=2, sort_keys=True)
json.dump(self.__version, getwriter('utf8')(_file), **JSON_FORMAT)
json.dump(self.__config, getwriter('utf8')(_file), **JSON_FORMAT)
_file.flush()
os.fsync(_file.fileno())
except IOError as ex:

View File

@ -13,6 +13,7 @@ from __future__ import unicode_literals
import logging
import os
import shutil
from io import open
import deluge.component as component
import deluge.configmanager as configmanager
@ -178,7 +179,7 @@ class AuthManager(component.Component):
else:
log.info('Saving the %s at: %s', filename, filepath)
try:
with open(filepath_tmp, 'wb') as _file:
with open(filepath_tmp, 'w', encoding='utf8') as _file:
for account in self.__auth.values():
_file.write('%(username)s:%(password)s:%(authlevel_int)s\n' % account.data())
_file.flush()
@ -213,7 +214,7 @@ class AuthManager(component.Component):
for _filepath in (auth_file, auth_file_bak):
log.info('Opening %s for load: %s', filename, _filepath)
try:
with open(_filepath, 'rb') as _file:
with open(_filepath, 'r', encoding='utf8') as _file:
file_data = _file.readlines()
except IOError as ex:
log.warning('Unable to load %s: %s', _filepath, ex)

View File

@ -45,7 +45,7 @@ def is_daemon_running(pid_file):
try:
with open(pid_file) as _file:
pid, port = [int(x) for x in _file.readline().strip().split(';')]
except EnvironmentError:
except (EnvironmentError, ValueError):
return False
if is_process_running(pid):
@ -130,7 +130,7 @@ class Daemon(object):
# Create pid file to track if deluged is running, also includes the port number.
pid = os.getpid()
log.debug('Storing pid %s & port %s in: %s', pid, self.port, self.pid_file)
with open(self.pid_file, 'wb') as _file:
with open(self.pid_file, 'w') as _file:
_file.write('%s;%s\n' % (pid, self.port))
component.start()

View File

@ -563,7 +563,9 @@ def generate_ssl_keys():
"""
This method generates a new SSL key/cert.
"""
digest = b'sha256'
from deluge.common import PY2
digest = 'sha256' if not PY2 else b'sha256'
# Generate key pair
pkey = crypto.PKey()
pkey.generate_key(crypto.TYPE_RSA, 2048)
@ -587,9 +589,9 @@ def generate_ssl_keys():
# Write out files
ssl_dir = deluge.configmanager.get_config_dir('ssl')
with open(os.path.join(ssl_dir, 'daemon.pkey'), 'w') as _file:
with open(os.path.join(ssl_dir, 'daemon.pkey'), 'wb') as _file:
_file.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
with open(os.path.join(ssl_dir, 'daemon.cert'), 'w') as _file:
with open(os.path.join(ssl_dir, 'daemon.cert'), 'wb') as _file:
_file.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
# Make the files only readable by this user
for f in ('daemon.pkey', 'daemon.cert'):

View File

@ -19,8 +19,6 @@ from __future__ import division, unicode_literals
import logging
import os
import socket
from future_builtins import zip
from urlparse import urlparse
from twisted.internet.defer import Deferred, DeferredList
@ -32,6 +30,18 @@ from deluge.core.authmanager import AUTH_LEVEL_ADMIN
from deluge.decorators import deprecated
from deluge.event import TorrentFolderRenamedEvent, TorrentStateChangedEvent, TorrentTrackerStatusEvent
try:
from urllib.parse import urlparse
except ImportError:
# PY2 fallback
from urlparse import urlparse # pylint: disable=ungrouped-imports
try:
from future_builtins import zip
except ImportError:
# Ignore on Py3.
pass
log = logging.getLogger(__name__)
LT_TORRENT_STATE_MAP = {
@ -95,9 +105,14 @@ def convert_lt_files(files):
"""
filelist = []
for index, _file in enumerate(files):
try:
file_path = _file.path.decode('utf8')
except AttributeError:
file_path = _file.path
filelist.append({
'index': index,
'path': _file.path.decode('utf8').replace('\\', '/'),
'path': file_path.replace('\\', '/'),
'size': _file.size,
'offset': _file.offset
})

View File

@ -12,7 +12,6 @@ from __future__ import unicode_literals
import logging
import os.path
import zlib
from urlparse import urljoin
from twisted.internet import reactor
from twisted.python.failure import Failure
@ -21,6 +20,12 @@ from twisted.web.error import PageRedirect
from deluge.common import get_version, utf8_encode_structure
try:
from urllib.parse import urljoin
except ImportError:
# PY2 fallback
from urlparse import urljoin # pylint: disable=ungrouped-imports
log = logging.getLogger(__name__)
@ -52,7 +57,7 @@ class HTTPDownloader(client.HTTPDownloader):
self.force_filename = force_filename
self.allow_compression = allow_compression
self.code = None
agent = b'Deluge/%s (http://deluge-torrent.org)' % get_version()
agent = b'Deluge/%s (http://deluge-torrent.org)' % get_version().encode('utf8')
client.HTTPDownloader.__init__(self, url, filename, headers=headers, agent=agent)

View File

@ -82,7 +82,7 @@ class Logging(LoggingLoggerClass):
def exception(self, msg, *args, **kwargs):
yield LoggingLoggerClass.exception(self, msg, *args, **kwargs)
def findCaller(self): # NOQA: N802
def findCaller(self, stack_info=False): # NOQA: N802
f = logging.currentframe().f_back
rv = '(unknown file)', 0, '(unknown function)'
while hasattr(f, 'f_code'):

View File

@ -16,7 +16,6 @@ import shutil
import time
from datetime import datetime, timedelta
from email.utils import formatdate
from urlparse import urljoin
from twisted.internet import defer, threads
from twisted.internet.task import LoopingCall
@ -33,6 +32,12 @@ from .common import IP, BadIP
from .detect import UnknownFormatError, create_reader, detect_compression, detect_format
from .readers import ReaderParseError
try:
from urllib.parse import urljoin
except ImportError:
# PY2 fallback
from urlparse import urljoin # pylint: disable=ungrouped-imports
# TODO: check return values for deferred callbacks
# TODO: review class attributes for redundancy

View File

@ -19,12 +19,7 @@ def Zipped(reader): # NOQA: N802
"""Blocklist reader for zipped blocklists"""
def open(self):
z = zipfile.ZipFile(self.file)
if hasattr(z, 'open'):
f = z.open(z.namelist()[0])
else:
# Handle python 2.5
import cStringIO
f = cStringIO.StringIO(z.read(z.namelist()[0]))
f = z.open(z.namelist()[0])
return f
reader.open = open
return reader

View File

@ -60,9 +60,14 @@ same rencode version throughout your project.
import struct
import sys
from future_builtins import zip
from threading import Lock
try:
from future_builtins import zip
except ImportError:
# Ignore on Py3.
pass
__version__ = ('Python', 1, 0, 4)
__all__ = ['dumps', 'loads']

View File

@ -185,6 +185,7 @@ class ProcessOutputHandler(protocol.ProcessProtocol):
def outReceived(self, data): # NOQA: N802
"""Process output from stdout"""
data = data.decode('utf8')
self.log_output += data
if self.check_callbacks(data):
pass
@ -193,6 +194,7 @@ class ProcessOutputHandler(protocol.ProcessProtocol):
def errReceived(self, data): # NOQA: N802
"""Process output from stderr"""
data = data.decode('utf8')
self.log_output += data
self.stderr_out += data
self.check_callbacks(data, cb_type='stderr')

View File

@ -8,11 +8,13 @@
from __future__ import unicode_literals
import os
from codecs import getwriter
from twisted.internet import task
from twisted.trial import unittest
import deluge.config
from deluge.common import JSON_FORMAT
from deluge.config import Config
from .common import set_tmp_config_dir
@ -88,17 +90,10 @@ class ConfigTestCase(unittest.TestCase):
self.assertEqual(config['string'], 'foobar')
self.assertEqual(config['float'], 0.435)
# Test loading an old config from 1.1.x
import pickle
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
pickle.dump(DEFAULTS, _file)
check_config()
# Test opening a previous 1.2 config file of just a json object
import json
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
json.dump(DEFAULTS, _file, indent=2)
json.dump(DEFAULTS, getwriter('utf8')(_file), **JSON_FORMAT)
check_config()
@ -107,15 +102,15 @@ class ConfigTestCase(unittest.TestCase):
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
_file.write(str(1) + '\n')
_file.write(str(1) + '\n')
json.dump(DEFAULTS, _file, indent=2)
json.dump(DEFAULTS, getwriter('utf8')(_file), **JSON_FORMAT)
check_config()
# Test the 1.2 config format
version = {'format': 1, 'file': 1}
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
json.dump(version, _file, indent=2)
json.dump(DEFAULTS, _file, indent=2)
json.dump(version, getwriter('utf8')(_file), **JSON_FORMAT)
json.dump(DEFAULTS, getwriter('utf8')(_file), **JSON_FORMAT)
check_config()

View File

@ -42,7 +42,7 @@ class CookieResource(Resource):
return
request.setHeader(b'Content-Type', b'application/x-bittorrent')
with open(common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent')) as _file:
with open(common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent'), 'rb') as _file:
data = _file.read()
return data
@ -50,7 +50,7 @@ class CookieResource(Resource):
class PartialDownload(Resource):
def render(self, request):
with open(common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent')) as _file:
with open(common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent'), 'rb') as _file:
data = _file.read()
request.setHeader(b'Content-Type', len(data))
request.setHeader(b'Content-Type', b'application/x-bittorrent')
@ -119,7 +119,7 @@ class CoreTestCase(BaseTestCase):
files_to_add = []
for f in filenames:
filename = common.get_test_data_file(f)
with open(filename) as _file:
with open(filename, 'rb') as _file:
filedump = base64.encodestring(_file.read())
files_to_add.append((filename, filedump, options))
errors = yield self.core.add_torrent_files(files_to_add)
@ -132,7 +132,7 @@ class CoreTestCase(BaseTestCase):
files_to_add = []
for f in filenames:
filename = common.get_test_data_file(f)
with open(filename) as _file:
with open(filename, 'rb') as _file:
filedump = base64.encodestring(_file.read())
files_to_add.append((filename, filedump, options))
errors = yield self.core.add_torrent_files(files_to_add)
@ -143,15 +143,15 @@ class CoreTestCase(BaseTestCase):
def test_add_torrent_file(self):
options = {}
filename = common.get_test_data_file('test.torrent')
with open(filename) as _file:
with open(filename, 'rb') as _file:
filedump = base64.encodestring(_file.read())
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
# Get the info hash from the test.torrent
from deluge.bencode import bdecode, bencode
with open(filename) as _file:
info_hash = sha(bencode(bdecode(_file.read())['info'])).hexdigest()
self.assertEqual(torrent_id, info_hash)
with open(filename, 'rb') as _file:
info_hash = sha(bencode(bdecode(_file.read())[b'info'])).hexdigest()
self.assertEquals(torrent_id, info_hash)
def test_add_torrent_file_invalid_filedump(self):
options = {}
@ -211,7 +211,7 @@ class CoreTestCase(BaseTestCase):
def test_remove_torrent(self):
options = {}
filename = common.get_test_data_file('test.torrent')
with open(filename) as _file:
with open(filename, 'rb') as _file:
filedump = base64.encodestring(_file.read())
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
removed = self.core.remove_torrent(torrent_id, True)
@ -225,12 +225,12 @@ class CoreTestCase(BaseTestCase):
def test_remove_torrents(self):
options = {}
filename = common.get_test_data_file('test.torrent')
with open(filename) as _file:
with open(filename, 'rb') as _file:
filedump = base64.encodestring(_file.read())
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
filename2 = common.get_test_data_file('unicode_filenames.torrent')
with open(filename2) as _file:
with open(filename2, 'rb') as _file:
filedump = base64.encodestring(_file.read())
torrent_id2 = yield self.core.add_torrent_file(filename2, filedump, options)
d = self.core.remove_torrents([torrent_id, torrent_id2], True)
@ -248,7 +248,7 @@ class CoreTestCase(BaseTestCase):
def test_remove_torrents_invalid(self):
options = {}
filename = common.get_test_data_file('test.torrent')
with open(filename) as _file:
with open(filename, 'rb') as _file:
filedump = base64.encodestring(_file.read())
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
val = yield self.core.remove_torrents(['invalidid1', 'invalidid2', torrent_id], False)

View File

@ -10,9 +10,8 @@
from __future__ import print_function, unicode_literals
import argparse
import exceptions
import StringIO
import sys
from io import StringIO
import mock
import pytest
@ -45,13 +44,14 @@ sys_stdout = sys.stdout
class StringFileDescriptor(object):
"""File descriptor that writes to string buffer"""
def __init__(self, fd):
self.out = StringIO.StringIO()
self.out = StringIO()
self.fd = fd
for a in ['encoding']:
setattr(self, a, getattr(sys_stdout, a))
def write(self, *data, **kwargs):
print(*data, file=self.out, end='')
# io.StringIO requires unicode strings.
print(unicode(*data), file=self.out, end='')
def flush(self):
self.out.flush()
@ -113,7 +113,7 @@ class DelugeEntryTestCase(BaseTestCase):
self.patch(argparse._sys, 'stdout', fd)
with mock.patch('deluge.ui.console.main.ConsoleUI'):
self.assertRaises(exceptions.SystemExit, ui_entry.start_ui)
self.assertRaises(SystemExit, ui_entry.start_ui)
self.assertTrue('usage: deluge' in fd.out.getvalue())
self.assertTrue('UI Options:' in fd.out.getvalue())
self.assertTrue('* console' in fd.out.getvalue())
@ -292,7 +292,7 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
self.patch(argparse._sys, 'stdout', fd)
with mock.patch('deluge.ui.console.main.ConsoleUI'):
self.assertRaises(exceptions.SystemExit, self.exec_command)
self.assertRaises(SystemExit, self.exec_command)
std_output = fd.out.getvalue()
self.assertTrue(('usage: %s' % self.var['cmd_name']) in std_output) # Check command name
self.assertTrue('Common Options:' in std_output)
@ -314,7 +314,7 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
self.patch(argparse._sys, 'stdout', fd)
with mock.patch('deluge.ui.console.main.ConsoleUI'):
self.assertRaises(exceptions.SystemExit, self.exec_command)
self.assertRaises(SystemExit, self.exec_command)
std_output = fd.out.getvalue()
self.assertTrue('usage: info' in std_output)
self.assertTrue('Show information about the torrents' in std_output)
@ -324,7 +324,7 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
fd = StringFileDescriptor(sys.stdout)
self.patch(argparse._sys, 'stderr', fd)
with mock.patch('deluge.ui.console.main.ConsoleUI'):
self.assertRaises(exceptions.SystemExit, self.exec_command)
self.assertRaises(SystemExit, self.exec_command)
self.assertTrue('unrecognized arguments: --ui' in fd.out.getvalue())

View File

@ -9,7 +9,7 @@
from __future__ import unicode_literals
from StringIO import StringIO
from io import BytesIO
from twisted.internet import defer, reactor
from twisted.python.failure import Failure
@ -172,5 +172,5 @@ class WebAPITestCase(WebServerTestBase):
b'http://127.0.0.1:%s/json' % self.webserver_listen_port,
Headers({b'User-Agent': [b'Twisted Web Client Example'],
b'Content-Type': [b'application/json']}),
FileBodyProducer(StringIO(bad_body)))
FileBodyProducer(BytesIO(bad_body)))
yield d

View File

@ -10,7 +10,7 @@
from __future__ import unicode_literals
import json as json_lib
from StringIO import StringIO
from io import BytesIO
import twisted.web.client
from twisted.internet import defer, reactor
@ -47,7 +47,7 @@ class WebServerTestCase(WebServerTestBase, WebServerMockBase):
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)),
FileBodyProducer(StringIO(input_file.encode('utf-8'))))
FileBodyProducer(BytesIO(input_file.encode('utf-8'))))
try:
body = yield twisted.web.client.readBody(d)
except AttributeError:

View File

@ -48,7 +48,6 @@ from __future__ import division, unicode_literals
import logging
import struct
from future_builtins import zip
import PIL.BmpImagePlugin
import PIL.Image
@ -56,6 +55,12 @@ import PIL.ImageChops
import PIL.ImageFile
import PIL.PngImagePlugin
try:
from future_builtins import zip
except ImportError:
# Ignore on Py3.
pass
_MAGIC = '\0\0\1\0'
log = logging.getLogger(__name__)

View File

@ -166,7 +166,7 @@ del _
DEFAULT_HOST = '127.0.0.1'
DEFAULT_PORT = 58846
DEFAULT_HOSTS = {
'hosts': [(sha(str(time.time())).hexdigest(), DEFAULT_HOST, DEFAULT_PORT, '', '')]
'hosts': [(sha(str(time.time()).encode('utf8')).hexdigest(), DEFAULT_HOST, DEFAULT_PORT, '', '')]
}
# The keys from session statistics for cache status.

View File

@ -10,9 +10,9 @@
from __future__ import unicode_literals
import cStringIO
import logging
import tokenize
from io import StringIO
import deluge.component as component
import deluge.ui.console.utils.colors as colors
@ -58,7 +58,7 @@ def atom(src, token):
def simple_eval(source):
""" evaluates the 'source' string into a combination of primitive python objects
taken from http://effbot.org/zone/simple-iterator-parser.htm"""
src = cStringIO.StringIO(source).readline
src = StringIO(source).readline
src = tokenize.generate_tokens(src)
src = (token for token in src if token[0] is not tokenize.NL)
res = atom(src, next(src))

View File

@ -12,7 +12,6 @@ from __future__ import unicode_literals
import base64
import logging
import os
from future_builtins import zip
import deluge.common
import deluge.component as component
@ -24,6 +23,12 @@ from deluge.ui.console.utils import curses_util as util
from deluge.ui.console.utils import format_utils
from deluge.ui.console.widgets.popup import InputPopup, MessagePopup
try:
from future_builtins import zip
except ImportError:
# Ignore on Py3.
pass
try:
import curses
except ImportError:

View File

@ -14,7 +14,6 @@ import logging
import os
import time
from socket import gaierror, gethostbyname
from urlparse import urlparse
import gtk
from twisted.internet import reactor
@ -28,6 +27,12 @@ from deluge.ui.common import get_localhost_auth
from deluge.ui.gtkui.common import get_clipboard_text, get_deluge_icon, get_logo
from deluge.ui.gtkui.dialogs import AuthenticationDialog, ErrorDialog
try:
from urllib.parse import urlparse
except ImportError:
# PY2 fallback
from urlparse import urlparse # pylint: disable=ungrouped-imports
log = logging.getLogger(__name__)
DEFAULT_HOST = '127.0.0.1'

View File

@ -11,7 +11,6 @@ from __future__ import unicode_literals
import logging
import os.path
from future_builtins import zip
from gtk import (TREE_VIEW_COLUMN_FIXED, Builder, CellRendererPixbuf, CellRendererProgress, CellRendererText, ListStore,
TreeViewColumn)
@ -25,6 +24,12 @@ from deluge.ui.gtkui.common import icon_downloading, icon_seeding, load_pickled_
from deluge.ui.gtkui.torrentdetails import Tab
from deluge.ui.gtkui.torrentview_data_funcs import cell_data_peer_progress, cell_data_speed_down, cell_data_speed_up
try:
from future_builtins import zip
except ImportError:
# Ignore on Py3.
pass
log = logging.getLogger(__name__)

View File

@ -13,7 +13,6 @@ from __future__ import unicode_literals
import logging
import os
from hashlib import sha1 as sha
from urlparse import urlparse
import gtk
from gtk.gdk import Color
@ -29,6 +28,12 @@ from deluge.ui.gtkui.dialogs import AccountDialog, ErrorDialog, InformationDialo
from deluge.ui.gtkui.path_chooser import PathChooser
from deluge.ui.translations_util import get_languages
try:
from urllib.parse import urlparse
except ImportError:
# PY2 fallback
from urlparse import urlparse # pylint: disable=ungrouped-imports
try:
import appindicator
except ImportError:

View File

@ -11,9 +11,7 @@ from __future__ import unicode_literals
import logging
import os
from HTMLParser import HTMLParseError, HTMLParser
from tempfile import mkstemp
from urlparse import urljoin, urlparse
from twisted.internet import defer, threads
from twisted.web.error import PageRedirect
@ -24,6 +22,14 @@ from deluge.configmanager import get_config_dir
from deluge.decorators import proxy
from deluge.httpdownloader import download_file
try:
from html.parser import HTMLParser
from urllib.parse import urljoin, urlparse
except ImportError:
# PY2 fallback
from HTMLParser import HTMLParser
from urlparse import urljoin, urlparse # pylint: disable=ungrouped-imports
try:
import PIL.Image as Image
except ImportError:
@ -411,7 +417,7 @@ class TrackerIcons(Component):
callbackArgs=(host,), errbackArgs=(host,))
elif f.check(NoResource, ForbiddenResource) and icons:
d = self.download_icon(icons, host)
elif f.check(NoIconsError, HTMLParseError):
elif f.check(NoIconsError):
# No icons, try favicon.ico as an act of desperation
d = self.download_icon([(urljoin(self.host_to_url(host), 'favicon.ico'),
extension_to_mimetype('ico'))], host)

View File

@ -20,8 +20,7 @@ known_third_party =
# Ignore Windows specific modules.
bbfreeze, win32verstamp,
# Ignore gtk modules, primarily for tox testing.
pygtk, gtk, gobject, gtk.gdk, pango, cairo, pangocairo,
six
pygtk, gtk, gobject, gtk.gdk, pango, cairo, pangocairo
known_first_party = msgfmt, deluge
order_by_type = true
line_length = 120