[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:
parent
ede0f710f8
commit
481f779349
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'):
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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'):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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__)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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__)
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue