diff --git a/core/linktastic/linktastic.py b/core/linktastic/linktastic.py deleted file mode 100644 index 95d2f8c6..00000000 --- a/core/linktastic/linktastic.py +++ /dev/null @@ -1,123 +0,0 @@ -# coding=utf-8 -# Linktastic Module -# - A python2/3 compatible module that can create hardlinks/symlinks on windows-based systems -# -# Linktastic is distributed under the MIT License. The follow are the terms and conditions of using Linktastic. -# -# The MIT License (MIT) -# Copyright (c) 2012 Solipsis Development -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -# associated documentation files (the "Software"), to deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all copies or substantial -# portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import subprocess -from subprocess import CalledProcessError -import os - -if os.name == 'nt': - info = subprocess.STARTUPINFO() - info.dwFlags |= subprocess.STARTF_USESHOWWINDOW - - -# Prevent spaces from messing with us! -def _escape_param(param): - return '"{0}"'.format(param) - - -# Private function to create link on nt-based systems -def _link_windows(src, dest): - try: - subprocess.check_output( - 'cmd /C mklink /H {0} {1}'.format(_escape_param(dest), _escape_param(src)), - stderr=subprocess.STDOUT, startupinfo=info) - except CalledProcessError as err: - - raise IOError(err.output.decode('utf-8')) - - # TODO, find out what kind of messages Windows sends us from mklink - # print(stdout) - # assume if they ret-coded 0 we're good - - -def _symlink_windows(src, dest): - try: - subprocess.check_output( - 'cmd /C mklink {0} {1}'.format(_escape_param(dest), _escape_param(src)), - stderr=subprocess.STDOUT, startupinfo=info) - except CalledProcessError as err: - raise IOError(err.output.decode('utf-8')) - - # TODO, find out what kind of messages Windows sends us from mklink - # print(stdout) - # assume if they ret-coded 0 we're good - - -def _dirlink_windows(src, dest): - try: - subprocess.check_output( - 'cmd /C mklink /J {0} {1}'.format(_escape_param(dest), _escape_param(src)), - stderr=subprocess.STDOUT, startupinfo=info) - except CalledProcessError as err: - raise IOError(err.output.decode('utf-8')) - - # TODO, find out what kind of messages Windows sends us from mklink - # print(stdout) - # assume if they ret-coded 0 we're good - - -def _junctionlink_windows(src, dest): - try: - subprocess.check_output( - 'cmd /C mklink /D {0} {1}'.format(_escape_param(dest), _escape_param(src)), - stderr=subprocess.STDOUT, startupinfo=info) - except CalledProcessError as err: - raise IOError(err.output.decode('utf-8')) - - # TODO, find out what kind of messages Windows sends us from mklink - # print(stdout) - # assume if they ret-coded 0 we're good - - -# Create a hard link to src named as dest -# This version of link, unlike os.link, supports nt systems as well -def link(src, dest): - if os.name == 'nt': - _link_windows(src, dest) - else: - os.link(src, dest) - - -# Create a symlink to src named as dest, but don't fail if you're on nt -def symlink(src, dest): - if os.name == 'nt': - _symlink_windows(src, dest) - else: - os.symlink(src, dest) - - -# Create a symlink to src named as dest, but don't fail if you're on nt -def dirlink(src, dest): - if os.name == 'nt': - _dirlink_windows(src, dest) - else: - os.symlink(src, dest) - - -# Create a symlink to src named as dest, but don't fail if you're on nt -def junctionlink(src, dest): - if os.name == 'nt': - _junctionlink_windows(src, dest) - else: - os.symlink(src, dest) diff --git a/core/nzbToMediaUtil.py b/core/nzbToMediaUtil.py index 7414b9ac..4b52abbd 100644 --- a/core/nzbToMediaUtil.py +++ b/core/nzbToMediaUtil.py @@ -14,19 +14,19 @@ import time import beets import guessit +import linktastic import requests import subliminal from babelfish import Language +from qbittorrent import Client as qBittorrentClient from six import text_type +from synchronousdeluge.client import DelugeClient +from transmissionrpc.client import Client as TransmissionClient +from utorrent.client import UTorrentClient import core from core import logger, nzbToMediaDB from core.extractor import extractor -from core.linktastic import linktastic -from core.qbittorrent.client import Client as qBittorrentClient -from core.synchronousdeluge.client import DelugeClient -from core.transmissionrpc.client import Client as TransmissionClient -from core.utorrent.client import UTorrentClient requests.packages.urllib3.disable_warnings() diff --git a/core/qbittorrent/__init__.py b/core/qbittorrent/__init__.py deleted file mode 100644 index bf893c06..00000000 --- a/core/qbittorrent/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# coding=utf-8 \ No newline at end of file diff --git a/core/transmissionrpc/__init__.py b/core/transmissionrpc/__init__.py deleted file mode 100644 index c0ced381..00000000 --- a/core/transmissionrpc/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2008-2013 Erik Svensson -# Licensed under the MIT license. - -from core.transmissionrpc.constants import DEFAULT_PORT, DEFAULT_TIMEOUT, PRIORITY, RATIO_LIMIT, LOGGER -from core.transmissionrpc.error import TransmissionError, HTTPHandlerError -from core.transmissionrpc.httphandler import HTTPHandler, DefaultHTTPHandler -from core.transmissionrpc.torrent import Torrent -from core.transmissionrpc.session import Session -from core.transmissionrpc.client import Client -from core.transmissionrpc.utils import add_stdout_logger, add_file_logger - -__author__ = 'Erik Svensson ' -__version_major__ = 0 -__version_minor__ = 11 -__version__ = '{0}.{1}'.format(__version_major__, __version_minor__) -__copyright__ = 'Copyright (c) 2008-2013 Erik Svensson' -__license__ = 'MIT' diff --git a/core/utorrent/__init__.py b/core/utorrent/__init__.py deleted file mode 100644 index 9bad5790..00000000 --- a/core/utorrent/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# coding=utf-8 diff --git a/libs/linktastic.py b/libs/linktastic.py new file mode 100644 index 00000000..88c34106 --- /dev/null +++ b/libs/linktastic.py @@ -0,0 +1,75 @@ +# Linktastic Module +# - A python2/3 compatible module that can create hardlinks/symlinks on windows-based systems +# +# Linktastic is distributed under the MIT License. The follow are the terms and conditions of using Linktastic. +# +# The MIT License (MIT) +# Copyright (c) 2012 Solipsis Development +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +# associated documentation files (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import subprocess +from subprocess import CalledProcessError +import os + + +# Prevent spaces from messing with us! +def _escape_param(param): + return '"%s"' % param + + +# Private function to create link on nt-based systems +def _link_windows(src, dest): + try: + subprocess.check_output( + 'cmd /C mklink /H %s %s' % (_escape_param(dest), _escape_param(src)), + stderr=subprocess.STDOUT) + except CalledProcessError as err: + raise IOError(err.output.decode('utf-8')) + + # TODO, find out what kind of messages Windows sends us from mklink + # print(stdout) + # assume if they ret-coded 0 we're good + + +def _symlink_windows(src, dest): + try: + subprocess.check_output( + 'cmd /C mklink %s %s' % (_escape_param(dest), _escape_param(src)), + stderr=subprocess.STDOUT) + except CalledProcessError as err: + raise IOError(err.output.decode('utf-8')) + + # TODO, find out what kind of messages Windows sends us from mklink + # print(stdout) + # assume if they ret-coded 0 we're good + + +# Create a hard link to src named as dest +# This version of link, unlike os.link, supports nt systems as well +def link(src, dest): + if os.name == 'nt': + _link_windows(src, dest) + else: + os.link(src, dest) + + +# Create a symlink to src named as dest, but don't fail if you're on nt +def symlink(src, dest): + if os.name == 'nt': + _symlink_windows(src, dest) + else: + os.symlink(src, dest) diff --git a/libs/qbittorrent/__init__.py b/libs/qbittorrent/__init__.py new file mode 100644 index 00000000..4e7aa816 --- /dev/null +++ b/libs/qbittorrent/__init__.py @@ -0,0 +1 @@ +from qbittorrent.client import Client diff --git a/core/qbittorrent/client.py b/libs/qbittorrent/client.py similarity index 100% rename from core/qbittorrent/client.py rename to libs/qbittorrent/client.py diff --git a/libs/rencode/__init__.py b/libs/rencode/__init__.py new file mode 100644 index 00000000..94a54658 --- /dev/null +++ b/libs/rencode/__init__.py @@ -0,0 +1,12 @@ +try: + from rencode._rencode import * + from rencode._rencode import __version__ +except ImportError: + import rencode.rencode_orig + prev_all = rencode.rencode_orig.__all__[:] + del rencode.rencode_orig.__all__ + from rencode.rencode_orig import * + from rencode.rencode_orig import __version__ + rencode.rencode_orig.__all__ = prev_all + +__all__ = ['dumps', 'loads'] diff --git a/core/synchronousdeluge/rencode.py b/libs/rencode/rencode_orig.py similarity index 57% rename from core/synchronousdeluge/rencode.py rename to libs/rencode/rencode_orig.py index 8ab01375..83f0de33 100644 --- a/core/synchronousdeluge/rencode.py +++ b/libs/rencode/rencode_orig.py @@ -1,34 +1,3 @@ -# coding=utf-8 -""" -rencode -- Web safe object pickling/unpickling. - -Public domain, Connelly Barnes 2006-2007. - -The rencode module is a modified version of bencode from the -BitTorrent project. For complex, heterogeneous data structures with -many small elements, r-encodings take up significantly less space than -b-encodings: - - >>> len(rencode.dumps({'a': 0, 'b': [1, 2], 'c': 99})) - 13 - >>> len(bencode.bencode({'a': 0, 'b': [1, 2], 'c': 99})) - 26 - -The rencode format is not standardized, and may change with different -rencode module versions, so you should check that you are using the -same rencode version throughout your project. -""" - -import struct -from threading import Lock -from six import PY3 - -if PY3: - long = int - -__version__ = '1.0.1' -__all__ = ['dumps', 'loads'] - # Original bencode module by Petru Paler, et al. # # Modifications by Connelly Barnes: @@ -68,6 +37,45 @@ __all__ = ['dumps', 'loads'] # # (The rencode module is licensed under the above license as well). # +# pylint: disable=redefined-builtin + +""" +rencode -- Web safe object pickling/unpickling. + +Public domain, Connelly Barnes 2006-2007. + +The rencode module is a modified version of bencode from the +BitTorrent project. For complex, heterogeneous data structures with +many small elements, r-encodings take up significantly less space than +b-encodings: + + >>> len(rencode.dumps({'a':0, 'b':[1,2], 'c':99})) + 13 + >>> len(bencode.bencode({'a':0, 'b':[1,2], 'c':99})) + 26 + +The rencode format is not standardized, and may change with different +rencode module versions, so you should check that you are using the +same rencode version throughout your project. +""" + +import struct +import sys +from threading import Lock + +__version__ = ("Python", 1, 0, 6) +__all__ = ('dumps', 'loads') + +py3 = sys.version_info[0] >= 3 +if py3: + long = int + unicode = str + + def int2byte(c): + return bytes([c]) +else: + def int2byte(c): + return chr(c) # Default number of bits for serialized floats, either 32 or 64 (also a parameter for dumps()). DEFAULT_FLOAT_BITS = 32 @@ -77,19 +85,19 @@ MAX_INT_LENGTH = 64 # The bencode 'typecodes' such as i, d, etc have been extended and # relocated on the base-256 character set. -CHR_LIST = chr(59) -CHR_DICT = chr(60) -CHR_INT = chr(61) -CHR_INT1 = chr(62) -CHR_INT2 = chr(63) -CHR_INT4 = chr(64) -CHR_INT8 = chr(65) -CHR_FLOAT32 = chr(66) -CHR_FLOAT64 = chr(44) -CHR_TRUE = chr(67) -CHR_FALSE = chr(68) -CHR_NONE = chr(69) -CHR_TERM = chr(127) +CHR_LIST = int2byte(59) +CHR_DICT = int2byte(60) +CHR_INT = int2byte(61) +CHR_INT1 = int2byte(62) +CHR_INT2 = int2byte(63) +CHR_INT4 = int2byte(64) +CHR_INT8 = int2byte(65) +CHR_FLOAT32 = int2byte(66) +CHR_FLOAT64 = int2byte(44) +CHR_TRUE = int2byte(67) +CHR_FALSE = int2byte(68) +CHR_NONE = int2byte(69) +CHR_TERM = int2byte(127) # Positive integers with value embedded in typecode. INT_POS_FIXED_START = 0 @@ -111,6 +119,9 @@ STR_FIXED_COUNT = 64 LIST_FIXED_START = STR_FIXED_START + STR_FIXED_COUNT LIST_FIXED_COUNT = 64 +# Whether strings should be decoded when loading +_decode_utf8 = False + def decode_int(x, f): f += 1 @@ -121,48 +132,49 @@ def decode_int(x, f): n = int(x[f:newf]) except (OverflowError, ValueError): n = long(x[f:newf]) - if x[f] == '-': - if x[f + 1] == '0': + if x[f:f + 1] == '-': + if x[f + 1:f + 2] == '0': raise ValueError - elif x[f] == '0' and newf != f + 1: + elif x[f:f + 1] == '0' and newf != f + 1: raise ValueError - return n, newf + 1 + return (n, newf + 1) def decode_intb(x, f): f += 1 - return struct.unpack('!b', x[f:f + 1])[0], f + 1 + return (struct.unpack('!b', x[f:f + 1])[0], f + 1) def decode_inth(x, f): f += 1 - return struct.unpack('!h', x[f:f + 2])[0], f + 2 + return (struct.unpack('!h', x[f:f + 2])[0], f + 2) def decode_intl(x, f): f += 1 - return struct.unpack('!l', x[f:f + 4])[0], f + 4 + + return (struct.unpack('!l', x[f:f + 4])[0], f + 4) def decode_intq(x, f): f += 1 - return struct.unpack('!q', x[f:f + 8])[0], f + 8 + return (struct.unpack('!q', x[f:f + 8])[0], f + 8) def decode_float32(x, f): f += 1 n = struct.unpack('!f', x[f:f + 4])[0] - return n, f + 4 + return (n, f + 4) def decode_float64(x, f): f += 1 n = struct.unpack('!d', x[f:f + 8])[0] - return n, f + 8 + return (n, f + 8) def decode_string(x, f): - colon = x.index(':', f) + colon = x.index(b':', f) try: n = int(x[f:colon]) except (OverflowError, ValueError): @@ -171,86 +183,73 @@ def decode_string(x, f): raise ValueError colon += 1 s = x[colon:colon + n] - try: - t = s.decode("utf8") - if len(t) != len(s): - s = t - except UnicodeDecodeError: - pass - return s, colon + n + if _decode_utf8: + s = s.decode('utf8') + return (s, colon + n) def decode_list(x, f): r, f = [], f + 1 - while x[f] != CHR_TERM: - v, f = decode_func[x[f]](x, f) + while x[f:f + 1] != CHR_TERM: + v, f = decode_func[x[f:f + 1]](x, f) r.append(v) - return tuple(r), f + 1 + return (tuple(r), f + 1) def decode_dict(x, f): r, f = {}, f + 1 - while x[f] != CHR_TERM: - k, f = decode_func[x[f]](x, f) - r[k], f = decode_func[x[f]](x, f) - return r, f + 1 + while x[f:f + 1] != CHR_TERM: + k, f = decode_func[x[f:f + 1]](x, f) + r[k], f = decode_func[x[f:f + 1]](x, f) + return (r, f + 1) def decode_true(x, f): - return True, f + 1 + return (True, f + 1) def decode_false(x, f): - return False, f + 1 + return (False, f + 1) def decode_none(x, f): - return None, f + 1 + return (None, f + 1) - -decode_func = { - '0': decode_string, - '1': decode_string, - '2': decode_string, - '3': decode_string, - '4': decode_string, - '5': decode_string, - '6': decode_string, - '7': decode_string, - '8': decode_string, - '9': decode_string, - CHR_LIST: decode_list, - CHR_DICT: decode_dict, - CHR_INT: decode_int, - CHR_INT1: decode_intb, - CHR_INT2: decode_inth, - CHR_INT4: decode_intl, - CHR_INT8: decode_intq, - CHR_FLOAT32: decode_float32, - CHR_FLOAT64: decode_float64, - CHR_TRUE: decode_true, - CHR_FALSE: decode_false, - CHR_NONE: decode_none, -} +decode_func = {} +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 +decode_func[CHR_LIST] = decode_list +decode_func[CHR_DICT] = decode_dict +decode_func[CHR_INT] = decode_int +decode_func[CHR_INT1] = decode_intb +decode_func[CHR_INT2] = decode_inth +decode_func[CHR_INT4] = decode_intl +decode_func[CHR_INT8] = decode_intq +decode_func[CHR_FLOAT32] = decode_float32 +decode_func[CHR_FLOAT64] = decode_float64 +decode_func[CHR_TRUE] = decode_true +decode_func[CHR_FALSE] = decode_false +decode_func[CHR_NONE] = decode_none def make_fixed_length_string_decoders(): def make_decoder(slen): def f(x, f): s = x[f + 1:f + 1 + slen] - try: - t = s.decode("utf8") - if len(t) != len(s): - s = t - except UnicodeDecodeError: - pass - return s, f + 1 + slen - + if _decode_utf8: + s = s.decode("utf8") + return (s, f + 1 + slen) return f - for i in range(STR_FIXED_COUNT): - decode_func[chr(STR_FIXED_START + i)] = make_decoder(i) - + decode_func[int2byte(STR_FIXED_START + i)] = make_decoder(i) make_fixed_length_string_decoders() @@ -259,16 +258,13 @@ def make_fixed_length_list_decoders(): def make_decoder(slen): def f(x, f): r, f = [], f + 1 - for i in range(slen): - v, f = decode_func[x[f]](x, f) + for _ in range(slen): + v, f = decode_func[x[f:f + 1]](x, f) r.append(v) - return tuple(r), f - + return (tuple(r), f) return f - for i in range(LIST_FIXED_COUNT): - decode_func[chr(LIST_FIXED_START + i)] = make_decoder(i) - + decode_func[int2byte(LIST_FIXED_START + i)] = make_decoder(i) make_fixed_length_list_decoders() @@ -276,15 +272,12 @@ make_fixed_length_list_decoders() def make_fixed_length_int_decoders(): def make_decoder(j): def f(x, f): - return j, f + 1 - + return (j, f + 1) return f - for i in range(INT_POS_FIXED_COUNT): - decode_func[chr(INT_POS_FIXED_START + i)] = make_decoder(i) + decode_func[int2byte(INT_POS_FIXED_START + i)] = make_decoder(i) for i in range(INT_NEG_FIXED_COUNT): - decode_func[chr(INT_NEG_FIXED_START + i)] = make_decoder(-1 - i) - + decode_func[int2byte(INT_NEG_FIXED_START + i)] = make_decoder(-1 - i) make_fixed_length_int_decoders() @@ -293,31 +286,22 @@ def make_fixed_length_dict_decoders(): def make_decoder(slen): def f(x, f): r, f = {}, f + 1 - for j in range(slen): - k, f = decode_func[x[f]](x, f) - r[k], f = decode_func[x[f]](x, f) - return r, f - + for _ in range(slen): + k, f = decode_func[x[f:f + 1]](x, f) + r[k], f = decode_func[x[f:f + 1]](x, f) + return (r, f) return f - for i in range(DICT_FIXED_COUNT): - decode_func[chr(DICT_FIXED_START + i)] = make_decoder(i) - + decode_func[int2byte(DICT_FIXED_START + i)] = make_decoder(i) make_fixed_length_dict_decoders() -def encode_dict(x, r): - r.append(CHR_DICT) - for k, v in x.items(): - encode_func[type(k)](k, r) - encode_func[type(v)](v, r) - r.append(CHR_TERM) - - -def loads(x): +def loads(x, decode_utf8=False): + global _decode_utf8 + _decode_utf8 = decode_utf8 try: - r, l = decode_func[x[0]](x, 0) + r, l = decode_func[x[0:1]](x, 0) except (IndexError, KeyError): raise ValueError if l != len(x): @@ -325,14 +309,11 @@ def loads(x): return r -from types import StringType, IntType, LongType, DictType, ListType, TupleType, FloatType, NoneType, UnicodeType - - def encode_int(x, r): if 0 <= x < INT_POS_FIXED_COUNT: - r.append(chr(INT_POS_FIXED_START + x)) + r.append(int2byte(INT_POS_FIXED_START + x)) elif -INT_NEG_FIXED_COUNT <= x < 0: - r.append(chr(INT_NEG_FIXED_START - 1 - x)) + r.append(int2byte(INT_NEG_FIXED_START - 1 - x)) elif -128 <= x < 128: r.extend((CHR_INT1, struct.pack('!b', x))) elif -32768 <= x < 32768: @@ -343,6 +324,9 @@ def encode_int(x, r): r.extend((CHR_INT8, struct.pack('!q', x))) else: s = str(x) + if py3: + s = bytes(s, "ascii") + if len(s) >= MAX_INT_LENGTH: raise ValueError('overflow') r.extend((CHR_INT, s, CHR_TERM)) @@ -357,18 +341,21 @@ def encode_float64(x, r): def encode_bool(x, r): - r.extend({False: CHR_FALSE, True: CHR_TRUE}[bool(x)]) + r.append({False: CHR_FALSE, True: CHR_TRUE}[bool(x)]) def encode_none(x, r): - r.extend(CHR_NONE) + r.append(CHR_NONE) def encode_string(x, r): if len(x) < STR_FIXED_COUNT: - r.extend((chr(STR_FIXED_START + len(x)), x)) + r.extend((int2byte(STR_FIXED_START + len(x)), x)) else: - r.extend((str(len(x)), ':', x)) + s = str(len(x)) + if py3: + s = bytes(s, "ascii") + r.extend((s, b':', x)) def encode_unicode(x, r): @@ -377,7 +364,7 @@ def encode_unicode(x, r): def encode_list(x, r): if len(x) < LIST_FIXED_COUNT: - r.append(chr(LIST_FIXED_START + len(x))) + r.append(int2byte(LIST_FIXED_START + len(x))) for i in x: encode_func[type(i)](i, r) else: @@ -389,7 +376,7 @@ def encode_list(x, r): def encode_dict(x, r): if len(x) < DICT_FIXED_COUNT: - r.append(chr(DICT_FIXED_START + len(x))) + r.append(int2byte(DICT_FIXED_START + len(x))) for k, v in x.items(): encode_func[type(k)](k, r) encode_func[type(v)](v, r) @@ -400,27 +387,19 @@ def encode_dict(x, r): encode_func[type(v)](v, r) r.append(CHR_TERM) - -encode_func = { - IntType: encode_int, - LongType: encode_int, - StringType: encode_string, - ListType: encode_list, - TupleType: encode_list, - DictType: encode_dict, - NoneType: encode_none, - UnicodeType: encode_unicode, -} +encode_func = {} +encode_func[int] = encode_int +encode_func[long] = encode_int +encode_func[bytes] = encode_string +encode_func[list] = encode_list +encode_func[tuple] = encode_list +encode_func[dict] = encode_dict +encode_func[type(None)] = encode_none +encode_func[unicode] = encode_unicode +encode_func[bool] = encode_bool lock = Lock() -try: - from types import BooleanType - - encode_func[BooleanType] = encode_bool -except ImportError: - pass - def dumps(x, float_bits=DEFAULT_FLOAT_BITS): """ @@ -428,60 +407,55 @@ def dumps(x, float_bits=DEFAULT_FLOAT_BITS): Here float_bits is either 32 or 64. """ - lock.acquire() - try: + with lock: if float_bits == 32: - encode_func[FloatType] = encode_float32 + encode_func[float] = encode_float32 elif float_bits == 64: - encode_func[FloatType] = encode_float64 + encode_func[float] = encode_float64 else: - raise ValueError('Float bits ({0:d}) is not 32 or 64'.format(float_bits)) + raise ValueError('Float bits (%d) is not 32 or 64' % float_bits) r = [] encode_func[type(x)](x, r) - finally: - lock.release() - return ''.join(r) + return b''.join(r) def test(): f1 = struct.unpack('!f', struct.pack('!f', 25.5))[0] f2 = struct.unpack('!f', struct.pack('!f', 29.3))[0] f3 = struct.unpack('!f', struct.pack('!f', -0.6))[0] - L = (({'a': 15, 'bb': f1, 'ccc': f2, '': (f3, (), False, True, '')}, ('a', 10 ** 20), tuple(range(-100000, 100000)), - 'b' * 31, 'b' * 62, 'b' * 64, 2 ** 30, 2 ** 33, 2 ** 62, 2 ** 64, 2 ** 30, 2 ** 33, 2 ** 62, 2 ** 64, False, - False, True, -1, 2, 0),) - assert loads(dumps(L)) == L + ld = (({b'a': 15, b'bb': f1, b'ccc': f2, b'': (f3, (), False, True, b'')}, (b'a', 10**20), + tuple(range(-100000, 100000)), b'b' * 31, b'b' * 62, b'b' * 64, 2**30, 2**33, 2**62, + 2**64, 2**30, 2**33, 2**62, 2**64, False, False, True, -1, 2, 0),) + assert loads(dumps(ld)) == ld d = dict(zip(range(-100000, 100000), range(-100000, 100000))) - d.update({'a': 20, 20: 40, 40: 41, f1: f2, f2: f3, f3: False, False: True, True: False}) - L = (d, {}, {5: 6}, {7: 7, True: 8}, {9: 10, 22: 39, 49: 50, 44: ''}) - assert loads(dumps(L)) == L - L = ('', 'a' * 10, 'a' * 100, 'a' * 1000, 'a' * 10000, 'a' * 100000, 'a' * 1000000, 'a' * 10000000) - assert loads(dumps(L)) == L - L = tuple([dict(zip(range(n), range(n))) for n in range(100)]) + ('b',) - assert loads(dumps(L)) == L - L = tuple([dict(zip(range(n), range(-n, 0))) for n in range(100)]) + ('b',) - assert loads(dumps(L)) == L - L = tuple([tuple(range(n)) for n in range(100)]) + ('b',) - assert loads(dumps(L)) == L - L = tuple(['a' * n for n in range(1000)]) + ('b',) - assert loads(dumps(L)) == L - L = tuple(['a' * n for n in range(1000)]) + (None, True, None) - assert loads(dumps(L)) == L + d.update({b'a': 20, 20: 40, 40: 41, f1: f2, f2: f3, f3: False, False: True, True: False}) + ld = (d, {}, {5: 6}, {7: 7, True: 8}, {9: 10, 22: 39, 49: 50, 44: b''}) + assert loads(dumps(ld)) == ld + ld = (b'', b'a' * 10, b'a' * 100, b'a' * 1000, b'a' * 10000, b'a' * 100000, b'a' * 1000000, b'a' * 10000000) + assert loads(dumps(ld)) == ld + ld = tuple([dict(zip(range(n), range(n))) for n in range(100)]) + (b'b',) + assert loads(dumps(ld)) == ld + ld = tuple([dict(zip(range(n), range(-n, 0))) for n in range(100)]) + (b'b',) + assert loads(dumps(ld)) == ld + ld = tuple([tuple(range(n)) for n in range(100)]) + (b'b',) + assert loads(dumps(ld)) == ld + ld = tuple([b'a' * n for n in range(1000)]) + (b'b',) + assert loads(dumps(ld)) == ld + ld = tuple([b'a' * n for n in range(1000)]) + (None, True, None) + assert loads(dumps(ld)) == ld assert loads(dumps(None)) is None assert loads(dumps({None: None})) == {None: None} assert 1e-10 < abs(loads(dumps(1.1)) - 1.1) < 1e-6 assert 1e-10 < abs(loads(dumps(1.1, 32)) - 1.1) < 1e-6 assert abs(loads(dumps(1.1, 64)) - 1.1) < 1e-12 - assert loads(dumps(u"Hello World!!")) - - + assert loads(dumps("Hello World!!"), decode_utf8=True) try: import psyco - psyco.bind(dumps) psyco.bind(loads) except ImportError: pass + if __name__ == '__main__': test() diff --git a/core/synchronousdeluge/__init__.py b/libs/synchronousdeluge/__init__.py similarity index 88% rename from core/synchronousdeluge/__init__.py rename to libs/synchronousdeluge/__init__.py index 9d4d8c77..fbafe6fe 100644 --- a/core/synchronousdeluge/__init__.py +++ b/libs/synchronousdeluge/__init__.py @@ -15,7 +15,7 @@ Example usage: download_location = client.core.get_config_value("download_location").get() """ -from core.synchronousdeluge.exceptions import DelugeRPCError +from .exceptions import DelugeRPCError __title__ = "synchronous-deluge" diff --git a/core/synchronousdeluge/client.py b/libs/synchronousdeluge/client.py similarity index 99% rename from core/synchronousdeluge/client.py rename to libs/synchronousdeluge/client.py index cecb2a88..7d0bbba0 100644 --- a/core/synchronousdeluge/client.py +++ b/libs/synchronousdeluge/client.py @@ -1,9 +1,10 @@ # coding=utf-8 import os import platform - from collections import defaultdict -from itertools import imap + +from six.moves import map as imap + from .exceptions import DelugeRPCError from .protocol import DelugeRPCRequest, DelugeRPCResponse from .transfer import DelugeTransfer diff --git a/core/synchronousdeluge/exceptions.py b/libs/synchronousdeluge/exceptions.py similarity index 90% rename from core/synchronousdeluge/exceptions.py rename to libs/synchronousdeluge/exceptions.py index 95bf7f04..6da4bdde 100644 --- a/core/synchronousdeluge/exceptions.py +++ b/libs/synchronousdeluge/exceptions.py @@ -1,5 +1,4 @@ # coding=utf-8 -__all__ = ["DelugeRPCError"] class DelugeRPCError(Exception): diff --git a/core/synchronousdeluge/protocol.py b/libs/synchronousdeluge/protocol.py similarity index 94% rename from core/synchronousdeluge/protocol.py rename to libs/synchronousdeluge/protocol.py index 98084d4f..6dcdb2a1 100644 --- a/core/synchronousdeluge/protocol.py +++ b/libs/synchronousdeluge/protocol.py @@ -1,5 +1,4 @@ # coding=utf-8 -__all__ = ["DelugeRPCRequest", "DelugeRPCResponse"] class DelugeRPCRequest(object): diff --git a/core/synchronousdeluge/transfer.py b/libs/synchronousdeluge/transfer.py similarity index 96% rename from core/synchronousdeluge/transfer.py rename to libs/synchronousdeluge/transfer.py index 50054757..42863b83 100644 --- a/core/synchronousdeluge/transfer.py +++ b/libs/synchronousdeluge/transfer.py @@ -1,10 +1,10 @@ # coding=utf-8 -import zlib -import struct import socket import ssl +import struct +import zlib -from core.synchronousdeluge import rencode +import rencode __all__ = ["DelugeTransfer"] diff --git a/libs/transmissionrpc/__init__.py b/libs/transmissionrpc/__init__.py new file mode 100644 index 00000000..ef4ad49c --- /dev/null +++ b/libs/transmissionrpc/__init__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2008-2013 Erik Svensson +# Licensed under the MIT license. + +from transmissionrpc.constants import DEFAULT_PORT, DEFAULT_TIMEOUT, PRIORITY, RATIO_LIMIT, LOGGER +from transmissionrpc.error import TransmissionError, HTTPHandlerError +from transmissionrpc.httphandler import HTTPHandler, DefaultHTTPHandler +from transmissionrpc.torrent import Torrent +from transmissionrpc.session import Session +from transmissionrpc.client import Client +from transmissionrpc.utils import add_stdout_logger, add_file_logger + +__author__ = 'Erik Svensson ' +__version_major__ = 0 +__version_minor__ = 11 +__version__ = '{0}.{1}'.format(__version_major__, __version_minor__) +__copyright__ = 'Copyright (c) 2008-2013 Erik Svensson' +__license__ = 'MIT' diff --git a/core/transmissionrpc/client.py b/libs/transmissionrpc/client.py similarity index 98% rename from core/transmissionrpc/client.py rename to libs/transmissionrpc/client.py index 6b2bc7f8..c675c59f 100644 --- a/core/transmissionrpc/client.py +++ b/libs/transmissionrpc/client.py @@ -2,24 +2,24 @@ # Copyright (c) 2008-2013 Erik Svensson # Licensed under the MIT license. -import re -import time -import operator -import warnings -import os import base64 import json +import operator +import os +import re +import time +import warnings -from six import PY3, integer_types, string_types, iteritems +from six import PY3, integer_types, iteritems, string_types from six.moves.urllib_parse import urlparse from six.moves.urllib_request import urlopen -from core.transmissionrpc.constants import DEFAULT_PORT, DEFAULT_TIMEOUT -from core.transmissionrpc.error import TransmissionError, HTTPHandlerError -from core.transmissionrpc.utils import LOGGER, get_arguments, make_rpc_name, argument_value_convert, rpc_bool -from core.transmissionrpc.httphandler import DefaultHTTPHandler -from core.transmissionrpc.torrent import Torrent -from core.transmissionrpc.session import Session +from .constants import DEFAULT_PORT, DEFAULT_TIMEOUT +from .error import HTTPHandlerError, TransmissionError +from .httphandler import DefaultHTTPHandler +from .session import Session +from .torrent import Torrent +from .utils import LOGGER, argument_value_convert, get_arguments, make_rpc_name, rpc_bool def debug_httperror(error): diff --git a/core/transmissionrpc/constants.py b/libs/transmissionrpc/constants.py similarity index 100% rename from core/transmissionrpc/constants.py rename to libs/transmissionrpc/constants.py diff --git a/core/transmissionrpc/error.py b/libs/transmissionrpc/error.py similarity index 97% rename from core/transmissionrpc/error.py rename to libs/transmissionrpc/error.py index a7da7307..4ad079be 100644 --- a/core/transmissionrpc/error.py +++ b/libs/transmissionrpc/error.py @@ -2,7 +2,7 @@ # Copyright (c) 2008-2013 Erik Svensson # Licensed under the MIT license. -from six import string_types, integer_types +from six import integer_types, string_types class TransmissionError(Exception): diff --git a/core/transmissionrpc/httphandler.py b/libs/transmissionrpc/httphandler.py similarity index 94% rename from core/transmissionrpc/httphandler.py rename to libs/transmissionrpc/httphandler.py index 02d65fa7..5ec981a3 100644 --- a/core/transmissionrpc/httphandler.py +++ b/libs/transmissionrpc/httphandler.py @@ -4,15 +4,17 @@ import sys -from six.moves.urllib_request import ( - build_opener, install_opener, - HTTPBasicAuthHandler, HTTPDigestAuthHandler, HTTPPasswordMgrWithDefaultRealm, - Request, -) -from six.moves.urllib_error import HTTPError, URLError from six.moves.http_client import BadStatusLine +from six.moves.urllib_error import HTTPError, URLError +from six.moves.urllib_request import ( + HTTPBasicAuthHandler, + HTTPDigestAuthHandler, + HTTPPasswordMgrWithDefaultRealm, + Request, + build_opener, +) -from core.transmissionrpc.error import HTTPHandlerError +from .error import HTTPHandlerError class HTTPHandler(object): diff --git a/core/transmissionrpc/session.py b/libs/transmissionrpc/session.py similarity index 97% rename from core/transmissionrpc/session.py rename to libs/transmissionrpc/session.py index 96c2c359..2e13a06b 100644 --- a/core/transmissionrpc/session.py +++ b/libs/transmissionrpc/session.py @@ -2,9 +2,9 @@ # Copyright (c) 2008-2013 Erik Svensson # Licensed under the MIT license. -from six import iteritems, integer_types +from six import integer_types, iteritems -from core.transmissionrpc.utils import Field +from .utils import Field class Session(object): diff --git a/core/transmissionrpc/torrent.py b/libs/transmissionrpc/torrent.py similarity index 98% rename from core/transmissionrpc/torrent.py rename to libs/transmissionrpc/torrent.py index 124e42c9..56821b05 100644 --- a/core/transmissionrpc/torrent.py +++ b/libs/transmissionrpc/torrent.py @@ -2,13 +2,13 @@ # Copyright (c) 2008-2013 Erik Svensson # Licensed under the MIT license. -import sys import datetime +import sys -from six import integer_types, string_types, text_type, iteritems +from six import integer_types, iteritems, string_types, text_type -from core.transmissionrpc.constants import PRIORITY, RATIO_LIMIT, IDLE_LIMIT -from core.transmissionrpc.utils import Field, format_timedelta +from .constants import IDLE_LIMIT, PRIORITY, RATIO_LIMIT +from .utils import Field, format_timedelta def get_status_old(code): diff --git a/core/transmissionrpc/utils.py b/libs/transmissionrpc/utils.py similarity index 99% rename from core/transmissionrpc/utils.py rename to libs/transmissionrpc/utils.py index 9103d534..e2ad309c 100644 --- a/core/transmissionrpc/utils.py +++ b/libs/transmissionrpc/utils.py @@ -2,7 +2,6 @@ # Copyright (c) 2008-2013 Erik Svensson # Licensed under the MIT license. -import constants import datetime import logging import socket @@ -10,7 +9,8 @@ from collections import namedtuple from six import iteritems, string_types -from constants import LOGGER +from . import constants +from .constants import LOGGER UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'] diff --git a/core/linktastic/__init__.py b/libs/utorrent/__init__.py similarity index 100% rename from core/linktastic/__init__.py rename to libs/utorrent/__init__.py diff --git a/core/utorrent/client.py b/libs/utorrent/client.py similarity index 84% rename from core/utorrent/client.py rename to libs/utorrent/client.py index 2d5a5741..12ff919c 100644 --- a/core/utorrent/client.py +++ b/libs/utorrent/client.py @@ -1,17 +1,25 @@ # coding=utf8 -import urllib -import urllib2 -import urlparse -import cookielib import re -import StringIO +import urllib + +from six import StringIO +from six.moves.http_cookiejar import CookieJar +from six.moves.urllib.request import ( + HTTPBasicAuthHandler, + HTTPCookieProcessor, + Request, + build_opener, + install_opener, +) +from six.moves.urllib_parse import urljoin + +from .upload import MultiPartForm + try: import json except ImportError: import simplejson as json -from upload import MultiPartForm - class UTorrentClient(object): def __init__(self, base_url, username, password): @@ -25,23 +33,23 @@ class UTorrentClient(object): def _make_opener(self, realm, base_url, username, password): '''uTorrent API need HTTP Basic Auth and cookie support for token verify.''' - auth_handler = urllib2.HTTPBasicAuthHandler() + auth_handler = HTTPBasicAuthHandler() auth_handler.add_password(realm=realm, uri=base_url, user=username, passwd=password) - opener = urllib2.build_opener(auth_handler) - urllib2.install_opener(opener) + opener = build_opener(auth_handler) + install_opener(opener) - cookie_jar = cookielib.CookieJar() - cookie_handler = urllib2.HTTPCookieProcessor(cookie_jar) + cookie_jar = CookieJar() + cookie_handler = HTTPCookieProcessor(cookie_jar) handlers = [auth_handler, cookie_handler] - opener = urllib2.build_opener(*handlers) + opener = build_opener(*handlers) return opener def _get_token(self): - url = urlparse.urljoin(self.base_url, 'token.html') + url = urljoin(self.base_url, 'token.html') response = self.opener.open(url) token_re = "" match = re.search(token_re, response.read()) @@ -131,7 +139,7 @@ class UTorrentClient(object): def _action(self, params, body=None, content_type=None): #about token, see https://github.com/bittorrent/webui/wiki/TokenSystem url = self.base_url + '?token=' + self.token + '&' + urllib.urlencode(params) - request = urllib2.Request(url) + request = Request(url) if body: request.add_data(body) @@ -139,8 +147,5 @@ class UTorrentClient(object): if content_type: request.add_header('Content-type', content_type) - try: - response = self.opener.open(request) - return response.code, json.loads(response.read()) - except urllib2.HTTPError,e: - raise \ No newline at end of file + response = self.opener.open(request) + return response.code, json.loads(response.read()) diff --git a/core/utorrent/upload.py b/libs/utorrent/upload.py similarity index 94% rename from core/utorrent/upload.py rename to libs/utorrent/upload.py index 8b502532..f5659f41 100644 --- a/core/utorrent/upload.py +++ b/libs/utorrent/upload.py @@ -2,11 +2,9 @@ # code copied from http://www.doughellmann.com/PyMOTW/urllib2/ import itertools -import mimetools import mimetypes -from cStringIO import StringIO -import urllib -import urllib2 +from email.generator import _make_boundary as choose_boundary + class MultiPartForm(object): """Accumulate the data to be used when posting a form.""" @@ -14,7 +12,7 @@ class MultiPartForm(object): def __init__(self): self.form_fields = [] self.files = [] - self.boundary = mimetools.choose_boundary() + self.boundary = choose_boundary() return def get_content_type(self):