mirror of
https://git.deluge-torrent.org/deluge
synced 2025-08-10 10:28:39 +00:00
[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
27 changed files with 216 additions and 145 deletions
|
@ -9,32 +9,39 @@
|
||||||
# License.
|
# License.
|
||||||
|
|
||||||
# Written by Petru Paler
|
# 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):
|
class BTFailure(Exception):
|
||||||
pass
|
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):
|
def decode_int(x, f):
|
||||||
f += 1
|
f += 1
|
||||||
newf = x.index('e', f)
|
newf = x.index(END_DELIM, f)
|
||||||
n = int(x[f:newf])
|
n = int(x[f:newf])
|
||||||
if x[f] == '-':
|
if x[f:f+1] == b'-' and x[f+1:f+2] == b'0':
|
||||||
if x[f + 1] == '0':
|
raise ValueError
|
||||||
raise ValueError
|
elif x[f:f+1] == b'0' and newf != f + 1:
|
||||||
elif x[f] == '0' and newf != f + 1:
|
|
||||||
raise ValueError
|
raise ValueError
|
||||||
return (n, newf + 1)
|
return (n, newf + 1)
|
||||||
|
|
||||||
|
|
||||||
def decode_string(x, f):
|
def decode_string(x, f):
|
||||||
colon = x.index(':', f)
|
colon = x.index(BYTE_SEP, f)
|
||||||
n = int(x[f:colon])
|
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
|
raise ValueError
|
||||||
colon += 1
|
colon += 1
|
||||||
return (x[colon:colon + n], colon + n)
|
return (x[colon:colon + n], colon + n)
|
||||||
|
@ -42,43 +49,43 @@ def decode_string(x, f):
|
||||||
|
|
||||||
def decode_list(x, f):
|
def decode_list(x, f):
|
||||||
r, f = [], f + 1
|
r, f = [], f + 1
|
||||||
while x[f] != 'e':
|
while x[f:f+1] != END_DELIM:
|
||||||
v, f = decode_func[x[f]](x, f)
|
v, f = decode_func[x[f:f+1]](x, f)
|
||||||
r.append(v)
|
r.append(v)
|
||||||
return (r, f + 1)
|
return (r, f + 1)
|
||||||
|
|
||||||
|
|
||||||
def decode_dict(x, f):
|
def decode_dict(x, f):
|
||||||
r, f = {}, f + 1
|
r, f = {}, f + 1
|
||||||
while x[f] != 'e':
|
while x[f:f+1] != END_DELIM:
|
||||||
k, f = decode_string(x, f)
|
k, f = decode_string(x, f)
|
||||||
r[k], f = decode_func[x[f]](x, f)
|
r[k], f = decode_func[x[f:f+1]](x, f)
|
||||||
return (r, f + 1)
|
return (r, f + 1)
|
||||||
|
|
||||||
|
|
||||||
decode_func = {}
|
decode_func = {}
|
||||||
decode_func['l'] = decode_list
|
decode_func[LIST_DELIM] = decode_list
|
||||||
decode_func['d'] = decode_dict
|
decode_func[DICT_DELIM] = decode_dict
|
||||||
decode_func['i'] = decode_int
|
decode_func[INT_DELIM] = decode_int
|
||||||
decode_func['0'] = decode_string
|
decode_func[b'0'] = decode_string
|
||||||
decode_func['1'] = decode_string
|
decode_func[b'1'] = decode_string
|
||||||
decode_func['2'] = decode_string
|
decode_func[b'2'] = decode_string
|
||||||
decode_func['3'] = decode_string
|
decode_func[b'3'] = decode_string
|
||||||
decode_func['4'] = decode_string
|
decode_func[b'4'] = decode_string
|
||||||
decode_func['5'] = decode_string
|
decode_func[b'5'] = decode_string
|
||||||
decode_func['6'] = decode_string
|
decode_func[b'6'] = decode_string
|
||||||
decode_func['7'] = decode_string
|
decode_func[b'7'] = decode_string
|
||||||
decode_func['8'] = decode_string
|
decode_func[b'8'] = decode_string
|
||||||
decode_func['9'] = decode_string
|
decode_func[b'9'] = decode_string
|
||||||
|
|
||||||
|
|
||||||
def bdecode(x):
|
def bdecode(x):
|
||||||
try:
|
try:
|
||||||
r, l = decode_func[x[0]](x, 0)
|
r, l = decode_func[x[0:1]](x, 0)
|
||||||
except (IndexError, KeyError, ValueError):
|
except (IndexError, KeyError, ValueError):
|
||||||
raise BTFailure('not a valid bencoded string')
|
raise BTFailure('Not a valid bencoded string')
|
||||||
|
else:
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
class Bencached(object):
|
class Bencached(object):
|
||||||
|
@ -94,53 +101,52 @@ def encode_bencached(x, r):
|
||||||
|
|
||||||
|
|
||||||
def encode_int(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):
|
def encode_bool(x, r):
|
||||||
if x:
|
encode_int(1 if x else 0, r)
|
||||||
encode_int(1, r)
|
|
||||||
else:
|
|
||||||
encode_int(0, r)
|
|
||||||
|
|
||||||
|
|
||||||
def encode_string(x, 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):
|
def encode_list(x, r):
|
||||||
r.append('l')
|
r.append(LIST_DELIM)
|
||||||
for i in x:
|
for i in x:
|
||||||
encode_func[type(i)](i, r)
|
encode_func[type(i)](i, r)
|
||||||
r.append('e')
|
r.append(END_DELIM)
|
||||||
|
|
||||||
|
|
||||||
def encode_dict(x, r):
|
def encode_dict(x, r):
|
||||||
r.append('d')
|
r.append(DICT_DELIM)
|
||||||
ilist = sorted(x.items())
|
for k, v in sorted(x.items()):
|
||||||
for k, v in ilist:
|
r.extend((str(len(k)).encode('utf8'), BYTE_SEP, k))
|
||||||
r.extend((str(len(k)), ':', k))
|
|
||||||
encode_func[type(v)](v, r)
|
encode_func[type(v)](v, r)
|
||||||
r.append('e')
|
r.append(END_DELIM)
|
||||||
|
|
||||||
|
|
||||||
encode_func = {}
|
encode_func = {}
|
||||||
encode_func[Bencached] = encode_bencached
|
encode_func[Bencached] = encode_bencached
|
||||||
encode_func[IntType] = encode_int
|
encode_func[int] = encode_int
|
||||||
encode_func[LongType] = encode_int
|
encode_func[list] = encode_list
|
||||||
encode_func[StringType] = encode_string
|
encode_func[tuple] = encode_list
|
||||||
encode_func[ListType] = encode_list
|
encode_func[dict] = encode_dict
|
||||||
encode_func[TupleType] = encode_list
|
encode_func[bool] = encode_bool
|
||||||
encode_func[DictType] = encode_dict
|
encode_func[str] = encode_string
|
||||||
|
encode_func[bytes] = encode_bytes
|
||||||
try:
|
if PY2:
|
||||||
from types import BooleanType
|
encode_func[long] = encode_int
|
||||||
encode_func[BooleanType] = encode_bool
|
encode_func[str] = encode_bytes
|
||||||
except ImportError:
|
encode_func[unicode] = encode_string
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def bencode(x):
|
def bencode(x):
|
||||||
r = []
|
r = []
|
||||||
encode_func[type(x)](x, r)
|
encode_func[type(x)](x, r)
|
||||||
return ''.join(r)
|
return b''.join(r)
|
||||||
|
|
|
@ -62,6 +62,9 @@ TORRENT_STATE = [
|
||||||
'Moving'
|
'Moving'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# The output formatting for json.dump
|
||||||
|
JSON_FORMAT = {'indent': 4, 'sort_keys': True, 'ensure_ascii': False}
|
||||||
|
|
||||||
PY2 = sys.version_info.major == 2
|
PY2 = sys.version_info.major == 2
|
||||||
|
|
||||||
|
|
||||||
|
@ -678,8 +681,12 @@ def create_magnet_uri(infohash, name=None, trackers=None):
|
||||||
str: A magnet uri string.
|
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:
|
if name:
|
||||||
uri.extend(['&', DN_PARAM, name])
|
uri.extend(['&', DN_PARAM, name])
|
||||||
if trackers:
|
if trackers:
|
||||||
|
@ -983,7 +990,7 @@ def create_localclient_account(append=False):
|
||||||
with open(auth_file, 'a' if append else 'w') as _file:
|
with open(auth_file, 'a' if append else 'w') as _file:
|
||||||
_file.write(':'.join([
|
_file.write(':'.join([
|
||||||
'localclient',
|
'localclient',
|
||||||
sha(str(random.random())).hexdigest(),
|
sha(str(random.random()).encode('utf8')).hexdigest(),
|
||||||
str(AUTH_LEVEL_ADMIN)
|
str(AUTH_LEVEL_ADMIN)
|
||||||
]) + '\n')
|
]) + '\n')
|
||||||
_file.flush()
|
_file.flush()
|
||||||
|
@ -1090,7 +1097,14 @@ def unicode_argv():
|
||||||
# As a last resort, just default to utf-8
|
# As a last resort, just default to utf-8
|
||||||
encoding = encoding or '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):
|
def run_profiled(func, *args, **kwargs):
|
||||||
|
@ -1116,8 +1130,8 @@ def run_profiled(func, *args, **kwargs):
|
||||||
print('Profile stats saved to %s' % output_file)
|
print('Profile stats saved to %s' % output_file)
|
||||||
else:
|
else:
|
||||||
import pstats
|
import pstats
|
||||||
import StringIO
|
from io import StringIO
|
||||||
strio = StringIO.StringIO()
|
strio = StringIO()
|
||||||
ps = pstats.Stats(profiler, stream=strio).sort_stats('cumulative')
|
ps = pstats.Stats(profiler, stream=strio).sort_stats('cumulative')
|
||||||
ps.print_stats()
|
ps.print_stats()
|
||||||
print(strio.getvalue())
|
print(strio.getvalue())
|
||||||
|
|
|
@ -46,8 +46,10 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
callLater = None # Necessary for the config tests
|
callLater = None # Necessary for the config tests
|
||||||
|
@ -172,11 +174,6 @@ class Config(object):
|
||||||
5
|
5
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
value = value.encode('utf8')
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if key not in self.__config:
|
if key not in self.__config:
|
||||||
self.__config[key] = value
|
self.__config[key] = value
|
||||||
log.debug('Setting key "%s" to: %s (of type: %s)', key, value, type(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)
|
log.warning('Value Type "%s" invalid for key: %s', type(value), key)
|
||||||
raise
|
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
|
self.__config[key] = value
|
||||||
|
|
||||||
global callLater
|
global callLater
|
||||||
|
@ -243,10 +242,7 @@ class Config(object):
|
||||||
5
|
5
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
return self.__config[key]
|
||||||
return self.__config[key].decode('utf8')
|
|
||||||
except AttributeError:
|
|
||||||
return self.__config[key]
|
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
"""Gets the value of item 'key' if key is in the config, else default.
|
"""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
|
filename = self.__config_file
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(filename, 'rb') as _file:
|
with open(filename, 'r', encoding='utf8') as _file:
|
||||||
data = _file.read()
|
data = _file.read()
|
||||||
except IOError as ex:
|
except IOError as ex:
|
||||||
log.warning('Unable to open config file %s: %s', filename, 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
|
# 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
|
# We will only write a new config file if there is a difference
|
||||||
try:
|
try:
|
||||||
with open(filename, 'rb') as _file:
|
with open(filename, 'r', encoding='utf8') as _file:
|
||||||
data = _file.read()
|
data = _file.read()
|
||||||
objects = find_json_objects(data)
|
objects = find_json_objects(data)
|
||||||
start, end = objects[0]
|
start, end = objects[0]
|
||||||
|
@ -463,8 +459,8 @@ class Config(object):
|
||||||
try:
|
try:
|
||||||
log.debug('Saving new config file %s', filename + '.new')
|
log.debug('Saving new config file %s', filename + '.new')
|
||||||
with open(filename + '.new', 'wb') as _file:
|
with open(filename + '.new', 'wb') as _file:
|
||||||
json.dump(self.__version, _file, indent=2)
|
json.dump(self.__version, getwriter('utf8')(_file), **JSON_FORMAT)
|
||||||
json.dump(self.__config, _file, indent=2, sort_keys=True)
|
json.dump(self.__config, getwriter('utf8')(_file), **JSON_FORMAT)
|
||||||
_file.flush()
|
_file.flush()
|
||||||
os.fsync(_file.fileno())
|
os.fsync(_file.fileno())
|
||||||
except IOError as ex:
|
except IOError as ex:
|
||||||
|
|
|
@ -13,6 +13,7 @@ from __future__ import unicode_literals
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
from io import open
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.configmanager as configmanager
|
import deluge.configmanager as configmanager
|
||||||
|
@ -178,7 +179,7 @@ class AuthManager(component.Component):
|
||||||
else:
|
else:
|
||||||
log.info('Saving the %s at: %s', filename, filepath)
|
log.info('Saving the %s at: %s', filename, filepath)
|
||||||
try:
|
try:
|
||||||
with open(filepath_tmp, 'wb') as _file:
|
with open(filepath_tmp, 'w', encoding='utf8') as _file:
|
||||||
for account in self.__auth.values():
|
for account in self.__auth.values():
|
||||||
_file.write('%(username)s:%(password)s:%(authlevel_int)s\n' % account.data())
|
_file.write('%(username)s:%(password)s:%(authlevel_int)s\n' % account.data())
|
||||||
_file.flush()
|
_file.flush()
|
||||||
|
@ -213,7 +214,7 @@ class AuthManager(component.Component):
|
||||||
for _filepath in (auth_file, auth_file_bak):
|
for _filepath in (auth_file, auth_file_bak):
|
||||||
log.info('Opening %s for load: %s', filename, _filepath)
|
log.info('Opening %s for load: %s', filename, _filepath)
|
||||||
try:
|
try:
|
||||||
with open(_filepath, 'rb') as _file:
|
with open(_filepath, 'r', encoding='utf8') as _file:
|
||||||
file_data = _file.readlines()
|
file_data = _file.readlines()
|
||||||
except IOError as ex:
|
except IOError as ex:
|
||||||
log.warning('Unable to load %s: %s', _filepath, ex)
|
log.warning('Unable to load %s: %s', _filepath, ex)
|
||||||
|
|
|
@ -45,7 +45,7 @@ def is_daemon_running(pid_file):
|
||||||
try:
|
try:
|
||||||
with open(pid_file) as _file:
|
with open(pid_file) as _file:
|
||||||
pid, port = [int(x) for x in _file.readline().strip().split(';')]
|
pid, port = [int(x) for x in _file.readline().strip().split(';')]
|
||||||
except EnvironmentError:
|
except (EnvironmentError, ValueError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if is_process_running(pid):
|
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.
|
# Create pid file to track if deluged is running, also includes the port number.
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
log.debug('Storing pid %s & port %s in: %s', pid, self.port, self.pid_file)
|
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))
|
_file.write('%s;%s\n' % (pid, self.port))
|
||||||
|
|
||||||
component.start()
|
component.start()
|
||||||
|
|
|
@ -563,7 +563,9 @@ def generate_ssl_keys():
|
||||||
"""
|
"""
|
||||||
This method generates a new SSL key/cert.
|
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
|
# Generate key pair
|
||||||
pkey = crypto.PKey()
|
pkey = crypto.PKey()
|
||||||
pkey.generate_key(crypto.TYPE_RSA, 2048)
|
pkey.generate_key(crypto.TYPE_RSA, 2048)
|
||||||
|
@ -587,9 +589,9 @@ def generate_ssl_keys():
|
||||||
|
|
||||||
# Write out files
|
# Write out files
|
||||||
ssl_dir = deluge.configmanager.get_config_dir('ssl')
|
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))
|
_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))
|
_file.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
|
||||||
# Make the files only readable by this user
|
# Make the files only readable by this user
|
||||||
for f in ('daemon.pkey', 'daemon.cert'):
|
for f in ('daemon.pkey', 'daemon.cert'):
|
||||||
|
|
|
@ -19,8 +19,6 @@ from __future__ import division, unicode_literals
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
from future_builtins import zip
|
|
||||||
from urlparse import urlparse
|
|
||||||
|
|
||||||
from twisted.internet.defer import Deferred, DeferredList
|
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.decorators import deprecated
|
||||||
from deluge.event import TorrentFolderRenamedEvent, TorrentStateChangedEvent, TorrentTrackerStatusEvent
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
LT_TORRENT_STATE_MAP = {
|
LT_TORRENT_STATE_MAP = {
|
||||||
|
@ -95,9 +105,14 @@ def convert_lt_files(files):
|
||||||
"""
|
"""
|
||||||
filelist = []
|
filelist = []
|
||||||
for index, _file in enumerate(files):
|
for index, _file in enumerate(files):
|
||||||
|
try:
|
||||||
|
file_path = _file.path.decode('utf8')
|
||||||
|
except AttributeError:
|
||||||
|
file_path = _file.path
|
||||||
|
|
||||||
filelist.append({
|
filelist.append({
|
||||||
'index': index,
|
'index': index,
|
||||||
'path': _file.path.decode('utf8').replace('\\', '/'),
|
'path': file_path.replace('\\', '/'),
|
||||||
'size': _file.size,
|
'size': _file.size,
|
||||||
'offset': _file.offset
|
'offset': _file.offset
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,7 +12,6 @@ from __future__ import unicode_literals
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
import zlib
|
import zlib
|
||||||
from urlparse import urljoin
|
|
||||||
|
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
from twisted.python.failure import Failure
|
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
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,7 +57,7 @@ class HTTPDownloader(client.HTTPDownloader):
|
||||||
self.force_filename = force_filename
|
self.force_filename = force_filename
|
||||||
self.allow_compression = allow_compression
|
self.allow_compression = allow_compression
|
||||||
self.code = None
|
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)
|
client.HTTPDownloader.__init__(self, url, filename, headers=headers, agent=agent)
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ class Logging(LoggingLoggerClass):
|
||||||
def exception(self, msg, *args, **kwargs):
|
def exception(self, msg, *args, **kwargs):
|
||||||
yield LoggingLoggerClass.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
|
f = logging.currentframe().f_back
|
||||||
rv = '(unknown file)', 0, '(unknown function)'
|
rv = '(unknown file)', 0, '(unknown function)'
|
||||||
while hasattr(f, 'f_code'):
|
while hasattr(f, 'f_code'):
|
||||||
|
|
|
@ -16,7 +16,6 @@ import shutil
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from email.utils import formatdate
|
from email.utils import formatdate
|
||||||
from urlparse import urljoin
|
|
||||||
|
|
||||||
from twisted.internet import defer, threads
|
from twisted.internet import defer, threads
|
||||||
from twisted.internet.task import LoopingCall
|
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 .detect import UnknownFormatError, create_reader, detect_compression, detect_format
|
||||||
from .readers import ReaderParseError
|
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: check return values for deferred callbacks
|
||||||
# TODO: review class attributes for redundancy
|
# TODO: review class attributes for redundancy
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,7 @@ def Zipped(reader): # NOQA: N802
|
||||||
"""Blocklist reader for zipped blocklists"""
|
"""Blocklist reader for zipped blocklists"""
|
||||||
def open(self):
|
def open(self):
|
||||||
z = zipfile.ZipFile(self.file)
|
z = zipfile.ZipFile(self.file)
|
||||||
if hasattr(z, 'open'):
|
f = z.open(z.namelist()[0])
|
||||||
f = z.open(z.namelist()[0])
|
|
||||||
else:
|
|
||||||
# Handle python 2.5
|
|
||||||
import cStringIO
|
|
||||||
f = cStringIO.StringIO(z.read(z.namelist()[0]))
|
|
||||||
return f
|
return f
|
||||||
reader.open = open
|
reader.open = open
|
||||||
return reader
|
return reader
|
||||||
|
|
|
@ -60,9 +60,14 @@ same rencode version throughout your project.
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
from future_builtins import zip
|
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
|
try:
|
||||||
|
from future_builtins import zip
|
||||||
|
except ImportError:
|
||||||
|
# Ignore on Py3.
|
||||||
|
pass
|
||||||
|
|
||||||
__version__ = ('Python', 1, 0, 4)
|
__version__ = ('Python', 1, 0, 4)
|
||||||
__all__ = ['dumps', 'loads']
|
__all__ = ['dumps', 'loads']
|
||||||
|
|
||||||
|
|
|
@ -185,6 +185,7 @@ class ProcessOutputHandler(protocol.ProcessProtocol):
|
||||||
|
|
||||||
def outReceived(self, data): # NOQA: N802
|
def outReceived(self, data): # NOQA: N802
|
||||||
"""Process output from stdout"""
|
"""Process output from stdout"""
|
||||||
|
data = data.decode('utf8')
|
||||||
self.log_output += data
|
self.log_output += data
|
||||||
if self.check_callbacks(data):
|
if self.check_callbacks(data):
|
||||||
pass
|
pass
|
||||||
|
@ -193,6 +194,7 @@ class ProcessOutputHandler(protocol.ProcessProtocol):
|
||||||
|
|
||||||
def errReceived(self, data): # NOQA: N802
|
def errReceived(self, data): # NOQA: N802
|
||||||
"""Process output from stderr"""
|
"""Process output from stderr"""
|
||||||
|
data = data.decode('utf8')
|
||||||
self.log_output += data
|
self.log_output += data
|
||||||
self.stderr_out += data
|
self.stderr_out += data
|
||||||
self.check_callbacks(data, cb_type='stderr')
|
self.check_callbacks(data, cb_type='stderr')
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from codecs import getwriter
|
||||||
|
|
||||||
from twisted.internet import task
|
from twisted.internet import task
|
||||||
from twisted.trial import unittest
|
from twisted.trial import unittest
|
||||||
|
|
||||||
import deluge.config
|
import deluge.config
|
||||||
|
from deluge.common import JSON_FORMAT
|
||||||
from deluge.config import Config
|
from deluge.config import Config
|
||||||
|
|
||||||
from .common import set_tmp_config_dir
|
from .common import set_tmp_config_dir
|
||||||
|
@ -88,17 +90,10 @@ class ConfigTestCase(unittest.TestCase):
|
||||||
self.assertEqual(config['string'], 'foobar')
|
self.assertEqual(config['string'], 'foobar')
|
||||||
self.assertEqual(config['float'], 0.435)
|
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
|
# Test opening a previous 1.2 config file of just a json object
|
||||||
import json
|
import json
|
||||||
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
|
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()
|
check_config()
|
||||||
|
|
||||||
|
@ -107,15 +102,15 @@ class ConfigTestCase(unittest.TestCase):
|
||||||
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
|
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
|
||||||
_file.write(str(1) + '\n')
|
_file.write(str(1) + '\n')
|
||||||
_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()
|
check_config()
|
||||||
|
|
||||||
# Test the 1.2 config format
|
# Test the 1.2 config format
|
||||||
version = {'format': 1, 'file': 1}
|
version = {'format': 1, 'file': 1}
|
||||||
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
|
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
|
||||||
json.dump(version, _file, indent=2)
|
json.dump(version, getwriter('utf8')(_file), **JSON_FORMAT)
|
||||||
json.dump(DEFAULTS, _file, indent=2)
|
json.dump(DEFAULTS, getwriter('utf8')(_file), **JSON_FORMAT)
|
||||||
|
|
||||||
check_config()
|
check_config()
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ class CookieResource(Resource):
|
||||||
return
|
return
|
||||||
|
|
||||||
request.setHeader(b'Content-Type', b'application/x-bittorrent')
|
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()
|
data = _file.read()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class CookieResource(Resource):
|
||||||
class PartialDownload(Resource):
|
class PartialDownload(Resource):
|
||||||
|
|
||||||
def render(self, request):
|
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()
|
data = _file.read()
|
||||||
request.setHeader(b'Content-Type', len(data))
|
request.setHeader(b'Content-Type', len(data))
|
||||||
request.setHeader(b'Content-Type', b'application/x-bittorrent')
|
request.setHeader(b'Content-Type', b'application/x-bittorrent')
|
||||||
|
@ -119,7 +119,7 @@ class CoreTestCase(BaseTestCase):
|
||||||
files_to_add = []
|
files_to_add = []
|
||||||
for f in filenames:
|
for f in filenames:
|
||||||
filename = common.get_test_data_file(f)
|
filename = common.get_test_data_file(f)
|
||||||
with open(filename) as _file:
|
with open(filename, 'rb') as _file:
|
||||||
filedump = base64.encodestring(_file.read())
|
filedump = base64.encodestring(_file.read())
|
||||||
files_to_add.append((filename, filedump, options))
|
files_to_add.append((filename, filedump, options))
|
||||||
errors = yield self.core.add_torrent_files(files_to_add)
|
errors = yield self.core.add_torrent_files(files_to_add)
|
||||||
|
@ -132,7 +132,7 @@ class CoreTestCase(BaseTestCase):
|
||||||
files_to_add = []
|
files_to_add = []
|
||||||
for f in filenames:
|
for f in filenames:
|
||||||
filename = common.get_test_data_file(f)
|
filename = common.get_test_data_file(f)
|
||||||
with open(filename) as _file:
|
with open(filename, 'rb') as _file:
|
||||||
filedump = base64.encodestring(_file.read())
|
filedump = base64.encodestring(_file.read())
|
||||||
files_to_add.append((filename, filedump, options))
|
files_to_add.append((filename, filedump, options))
|
||||||
errors = yield self.core.add_torrent_files(files_to_add)
|
errors = yield self.core.add_torrent_files(files_to_add)
|
||||||
|
@ -143,15 +143,15 @@ class CoreTestCase(BaseTestCase):
|
||||||
def test_add_torrent_file(self):
|
def test_add_torrent_file(self):
|
||||||
options = {}
|
options = {}
|
||||||
filename = common.get_test_data_file('test.torrent')
|
filename = common.get_test_data_file('test.torrent')
|
||||||
with open(filename) as _file:
|
with open(filename, 'rb') as _file:
|
||||||
filedump = base64.encodestring(_file.read())
|
filedump = base64.encodestring(_file.read())
|
||||||
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
|
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
|
||||||
|
|
||||||
# Get the info hash from the test.torrent
|
# Get the info hash from the test.torrent
|
||||||
from deluge.bencode import bdecode, bencode
|
from deluge.bencode import bdecode, bencode
|
||||||
with open(filename) as _file:
|
with open(filename, 'rb') as _file:
|
||||||
info_hash = sha(bencode(bdecode(_file.read())['info'])).hexdigest()
|
info_hash = sha(bencode(bdecode(_file.read())[b'info'])).hexdigest()
|
||||||
self.assertEqual(torrent_id, info_hash)
|
self.assertEquals(torrent_id, info_hash)
|
||||||
|
|
||||||
def test_add_torrent_file_invalid_filedump(self):
|
def test_add_torrent_file_invalid_filedump(self):
|
||||||
options = {}
|
options = {}
|
||||||
|
@ -211,7 +211,7 @@ class CoreTestCase(BaseTestCase):
|
||||||
def test_remove_torrent(self):
|
def test_remove_torrent(self):
|
||||||
options = {}
|
options = {}
|
||||||
filename = common.get_test_data_file('test.torrent')
|
filename = common.get_test_data_file('test.torrent')
|
||||||
with open(filename) as _file:
|
with open(filename, 'rb') as _file:
|
||||||
filedump = base64.encodestring(_file.read())
|
filedump = base64.encodestring(_file.read())
|
||||||
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
|
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
|
||||||
removed = self.core.remove_torrent(torrent_id, True)
|
removed = self.core.remove_torrent(torrent_id, True)
|
||||||
|
@ -225,12 +225,12 @@ class CoreTestCase(BaseTestCase):
|
||||||
def test_remove_torrents(self):
|
def test_remove_torrents(self):
|
||||||
options = {}
|
options = {}
|
||||||
filename = common.get_test_data_file('test.torrent')
|
filename = common.get_test_data_file('test.torrent')
|
||||||
with open(filename) as _file:
|
with open(filename, 'rb') as _file:
|
||||||
filedump = base64.encodestring(_file.read())
|
filedump = base64.encodestring(_file.read())
|
||||||
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
|
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
|
||||||
|
|
||||||
filename2 = common.get_test_data_file('unicode_filenames.torrent')
|
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())
|
filedump = base64.encodestring(_file.read())
|
||||||
torrent_id2 = yield self.core.add_torrent_file(filename2, filedump, options)
|
torrent_id2 = yield self.core.add_torrent_file(filename2, filedump, options)
|
||||||
d = self.core.remove_torrents([torrent_id, torrent_id2], True)
|
d = self.core.remove_torrents([torrent_id, torrent_id2], True)
|
||||||
|
@ -248,7 +248,7 @@ class CoreTestCase(BaseTestCase):
|
||||||
def test_remove_torrents_invalid(self):
|
def test_remove_torrents_invalid(self):
|
||||||
options = {}
|
options = {}
|
||||||
filename = common.get_test_data_file('test.torrent')
|
filename = common.get_test_data_file('test.torrent')
|
||||||
with open(filename) as _file:
|
with open(filename, 'rb') as _file:
|
||||||
filedump = base64.encodestring(_file.read())
|
filedump = base64.encodestring(_file.read())
|
||||||
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
|
torrent_id = yield self.core.add_torrent_file(filename, filedump, options)
|
||||||
val = yield self.core.remove_torrents(['invalidid1', 'invalidid2', torrent_id], False)
|
val = yield self.core.remove_torrents(['invalidid1', 'invalidid2', torrent_id], False)
|
||||||
|
|
|
@ -10,9 +10,8 @@
|
||||||
from __future__ import print_function, unicode_literals
|
from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import exceptions
|
|
||||||
import StringIO
|
|
||||||
import sys
|
import sys
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -45,13 +44,14 @@ sys_stdout = sys.stdout
|
||||||
class StringFileDescriptor(object):
|
class StringFileDescriptor(object):
|
||||||
"""File descriptor that writes to string buffer"""
|
"""File descriptor that writes to string buffer"""
|
||||||
def __init__(self, fd):
|
def __init__(self, fd):
|
||||||
self.out = StringIO.StringIO()
|
self.out = StringIO()
|
||||||
self.fd = fd
|
self.fd = fd
|
||||||
for a in ['encoding']:
|
for a in ['encoding']:
|
||||||
setattr(self, a, getattr(sys_stdout, a))
|
setattr(self, a, getattr(sys_stdout, a))
|
||||||
|
|
||||||
def write(self, *data, **kwargs):
|
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):
|
def flush(self):
|
||||||
self.out.flush()
|
self.out.flush()
|
||||||
|
@ -113,7 +113,7 @@ class DelugeEntryTestCase(BaseTestCase):
|
||||||
self.patch(argparse._sys, 'stdout', fd)
|
self.patch(argparse._sys, 'stdout', fd)
|
||||||
|
|
||||||
with mock.patch('deluge.ui.console.main.ConsoleUI'):
|
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('usage: deluge' in fd.out.getvalue())
|
||||||
self.assertTrue('UI Options:' in fd.out.getvalue())
|
self.assertTrue('UI Options:' in fd.out.getvalue())
|
||||||
self.assertTrue('* console' in fd.out.getvalue())
|
self.assertTrue('* console' in fd.out.getvalue())
|
||||||
|
@ -292,7 +292,7 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
|
||||||
self.patch(argparse._sys, 'stdout', fd)
|
self.patch(argparse._sys, 'stdout', fd)
|
||||||
|
|
||||||
with mock.patch('deluge.ui.console.main.ConsoleUI'):
|
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()
|
std_output = fd.out.getvalue()
|
||||||
self.assertTrue(('usage: %s' % self.var['cmd_name']) in std_output) # Check command name
|
self.assertTrue(('usage: %s' % self.var['cmd_name']) in std_output) # Check command name
|
||||||
self.assertTrue('Common Options:' in std_output)
|
self.assertTrue('Common Options:' in std_output)
|
||||||
|
@ -314,7 +314,7 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
|
||||||
self.patch(argparse._sys, 'stdout', fd)
|
self.patch(argparse._sys, 'stdout', fd)
|
||||||
|
|
||||||
with mock.patch('deluge.ui.console.main.ConsoleUI'):
|
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()
|
std_output = fd.out.getvalue()
|
||||||
self.assertTrue('usage: info' in std_output)
|
self.assertTrue('usage: info' in std_output)
|
||||||
self.assertTrue('Show information about the torrents' in std_output)
|
self.assertTrue('Show information about the torrents' in std_output)
|
||||||
|
@ -324,7 +324,7 @@ class ConsoleUIBaseTestCase(UIBaseTestCase):
|
||||||
fd = StringFileDescriptor(sys.stdout)
|
fd = StringFileDescriptor(sys.stdout)
|
||||||
self.patch(argparse._sys, 'stderr', fd)
|
self.patch(argparse._sys, 'stderr', fd)
|
||||||
with mock.patch('deluge.ui.console.main.ConsoleUI'):
|
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())
|
self.assertTrue('unrecognized arguments: --ui' in fd.out.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from StringIO import StringIO
|
from io import BytesIO
|
||||||
|
|
||||||
from twisted.internet import defer, reactor
|
from twisted.internet import defer, reactor
|
||||||
from twisted.python.failure import Failure
|
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,
|
b'http://127.0.0.1:%s/json' % self.webserver_listen_port,
|
||||||
Headers({b'User-Agent': [b'Twisted Web Client Example'],
|
Headers({b'User-Agent': [b'Twisted Web Client Example'],
|
||||||
b'Content-Type': [b'application/json']}),
|
b'Content-Type': [b'application/json']}),
|
||||||
FileBodyProducer(StringIO(bad_body)))
|
FileBodyProducer(BytesIO(bad_body)))
|
||||||
yield d
|
yield d
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import json as json_lib
|
import json as json_lib
|
||||||
from StringIO import StringIO
|
from io import BytesIO
|
||||||
|
|
||||||
import twisted.web.client
|
import twisted.web.client
|
||||||
from twisted.internet import defer, reactor
|
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
|
url = 'http://127.0.0.1:%s/json' % self.webserver_listen_port
|
||||||
|
|
||||||
d = yield agent.request(b'POST', url.encode('utf-8'), Headers(utf8_encode_structure(headers)),
|
d = yield agent.request(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:
|
try:
|
||||||
body = yield twisted.web.client.readBody(d)
|
body = yield twisted.web.client.readBody(d)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
|
@ -48,7 +48,6 @@ from __future__ import division, unicode_literals
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
from future_builtins import zip
|
|
||||||
|
|
||||||
import PIL.BmpImagePlugin
|
import PIL.BmpImagePlugin
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
|
@ -56,6 +55,12 @@ import PIL.ImageChops
|
||||||
import PIL.ImageFile
|
import PIL.ImageFile
|
||||||
import PIL.PngImagePlugin
|
import PIL.PngImagePlugin
|
||||||
|
|
||||||
|
try:
|
||||||
|
from future_builtins import zip
|
||||||
|
except ImportError:
|
||||||
|
# Ignore on Py3.
|
||||||
|
pass
|
||||||
|
|
||||||
_MAGIC = '\0\0\1\0'
|
_MAGIC = '\0\0\1\0'
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -166,7 +166,7 @@ del _
|
||||||
DEFAULT_HOST = '127.0.0.1'
|
DEFAULT_HOST = '127.0.0.1'
|
||||||
DEFAULT_PORT = 58846
|
DEFAULT_PORT = 58846
|
||||||
DEFAULT_HOSTS = {
|
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.
|
# The keys from session statistics for cache status.
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import cStringIO
|
|
||||||
import logging
|
import logging
|
||||||
import tokenize
|
import tokenize
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.utils.colors as colors
|
import deluge.ui.console.utils.colors as colors
|
||||||
|
@ -58,7 +58,7 @@ def atom(src, token):
|
||||||
def simple_eval(source):
|
def simple_eval(source):
|
||||||
""" evaluates the 'source' string into a combination of primitive python objects
|
""" evaluates the 'source' string into a combination of primitive python objects
|
||||||
taken from http://effbot.org/zone/simple-iterator-parser.htm"""
|
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 = tokenize.generate_tokens(src)
|
||||||
src = (token for token in src if token[0] is not tokenize.NL)
|
src = (token for token in src if token[0] is not tokenize.NL)
|
||||||
res = atom(src, next(src))
|
res = atom(src, next(src))
|
||||||
|
|
|
@ -12,7 +12,6 @@ from __future__ import unicode_literals
|
||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from future_builtins import zip
|
|
||||||
|
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import deluge.component as component
|
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.utils import format_utils
|
||||||
from deluge.ui.console.widgets.popup import InputPopup, MessagePopup
|
from deluge.ui.console.widgets.popup import InputPopup, MessagePopup
|
||||||
|
|
||||||
|
try:
|
||||||
|
from future_builtins import zip
|
||||||
|
except ImportError:
|
||||||
|
# Ignore on Py3.
|
||||||
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import curses
|
import curses
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
@ -14,7 +14,6 @@ import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from socket import gaierror, gethostbyname
|
from socket import gaierror, gethostbyname
|
||||||
from urlparse import urlparse
|
|
||||||
|
|
||||||
import gtk
|
import gtk
|
||||||
from twisted.internet import reactor
|
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.common import get_clipboard_text, get_deluge_icon, get_logo
|
||||||
from deluge.ui.gtkui.dialogs import AuthenticationDialog, ErrorDialog
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEFAULT_HOST = '127.0.0.1'
|
DEFAULT_HOST = '127.0.0.1'
|
||||||
|
|
|
@ -11,7 +11,6 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
from future_builtins import zip
|
|
||||||
|
|
||||||
from gtk import (TREE_VIEW_COLUMN_FIXED, Builder, CellRendererPixbuf, CellRendererProgress, CellRendererText, ListStore,
|
from gtk import (TREE_VIEW_COLUMN_FIXED, Builder, CellRendererPixbuf, CellRendererProgress, CellRendererText, ListStore,
|
||||||
TreeViewColumn)
|
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.torrentdetails import Tab
|
||||||
from deluge.ui.gtkui.torrentview_data_funcs import cell_data_peer_progress, cell_data_speed_down, cell_data_speed_up
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ from __future__ import unicode_literals
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from hashlib import sha1 as sha
|
from hashlib import sha1 as sha
|
||||||
from urlparse import urlparse
|
|
||||||
|
|
||||||
import gtk
|
import gtk
|
||||||
from gtk.gdk import Color
|
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.gtkui.path_chooser import PathChooser
|
||||||
from deluge.ui.translations_util import get_languages
|
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:
|
try:
|
||||||
import appindicator
|
import appindicator
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
@ -11,9 +11,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from HTMLParser import HTMLParseError, HTMLParser
|
|
||||||
from tempfile import mkstemp
|
from tempfile import mkstemp
|
||||||
from urlparse import urljoin, urlparse
|
|
||||||
|
|
||||||
from twisted.internet import defer, threads
|
from twisted.internet import defer, threads
|
||||||
from twisted.web.error import PageRedirect
|
from twisted.web.error import PageRedirect
|
||||||
|
@ -24,6 +22,14 @@ from deluge.configmanager import get_config_dir
|
||||||
from deluge.decorators import proxy
|
from deluge.decorators import proxy
|
||||||
from deluge.httpdownloader import download_file
|
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:
|
try:
|
||||||
import PIL.Image as Image
|
import PIL.Image as Image
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -411,7 +417,7 @@ class TrackerIcons(Component):
|
||||||
callbackArgs=(host,), errbackArgs=(host,))
|
callbackArgs=(host,), errbackArgs=(host,))
|
||||||
elif f.check(NoResource, ForbiddenResource) and icons:
|
elif f.check(NoResource, ForbiddenResource) and icons:
|
||||||
d = self.download_icon(icons, host)
|
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
|
# No icons, try favicon.ico as an act of desperation
|
||||||
d = self.download_icon([(urljoin(self.host_to_url(host), 'favicon.ico'),
|
d = self.download_icon([(urljoin(self.host_to_url(host), 'favicon.ico'),
|
||||||
extension_to_mimetype('ico'))], host)
|
extension_to_mimetype('ico'))], host)
|
||||||
|
|
|
@ -20,8 +20,7 @@ known_third_party =
|
||||||
# Ignore Windows specific modules.
|
# Ignore Windows specific modules.
|
||||||
bbfreeze, win32verstamp,
|
bbfreeze, win32verstamp,
|
||||||
# Ignore gtk modules, primarily for tox testing.
|
# Ignore gtk modules, primarily for tox testing.
|
||||||
pygtk, gtk, gobject, gtk.gdk, pango, cairo, pangocairo,
|
pygtk, gtk, gobject, gtk.gdk, pango, cairo, pangocairo
|
||||||
six
|
|
||||||
known_first_party = msgfmt, deluge
|
known_first_party = msgfmt, deluge
|
||||||
order_by_type = true
|
order_by_type = true
|
||||||
line_length = 120
|
line_length = 120
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue