diff --git a/CHANGES.md b/CHANGES.md index cfa1c48..2f1f2a1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -51,9 +51,16 @@ * Update urllib3 release 1.25.6 (4a6c288) to 1.25.7 (37ba61a) +### 0.21.29 (2020-04-29 02:10:00 UTC) + +* Change update fallback timezone info file to 2020a +* Fix TVEpisodeSample to fix comparison on patterns with limited multi ep naming +* Update Js2Py 0.64 (7858d1d) to 0.70 (f297498) + + ### 0.21.28 (2020-04-24 09:40:00 UTC) -* Change improve Cloudflare connectivity +* Change improve Cloudflare connectivity ### 0.21.27 (2020-04-22 20:35:00 UTC) diff --git a/lib/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/lib/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz index 5e8c11a..c77594d 100644 Binary files a/lib/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz and b/lib/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz differ diff --git a/lib/js2py/base.py b/lib/js2py/base.py index cf1eca0..f4ee721 100644 --- a/lib/js2py/base.py +++ b/lib/js2py/base.py @@ -126,7 +126,7 @@ def HJs(val): except Exception as e: message = 'your Python function failed! ' try: - message += e.message + message += str(e) except: pass raise MakeError('Error', message) @@ -319,7 +319,7 @@ class PyJs(object): #prop = prop.value if self.Class == 'Undefined' or self.Class == 'Null': raise MakeError('TypeError', - 'Undefined and null dont have properties!') + 'Undefined and null dont have properties (tried getting property %s)' % repr(prop)) if not isinstance(prop, basestring): prop = prop.to_string().value if not isinstance(prop, basestring): raise RuntimeError('Bug') @@ -361,7 +361,7 @@ class PyJs(object): * / % + - << >> & ^ |''' if self.Class == 'Undefined' or self.Class == 'Null': raise MakeError('TypeError', - 'Undefined and null dont have properties!') + 'Undefined and null don\'t have properties (tried setting property %s)' % repr(prop)) if not isinstance(prop, basestring): prop = prop.to_string().value if NUMPY_AVAILABLE and prop.isdigit(): @@ -991,7 +991,8 @@ class PyJs(object): cand = self.get(prop) if not cand.is_callable(): raise MakeError('TypeError', - '%s is not a function' % cand.typeof()) + '%s is not a function (tried calling property %s of %s)' % ( + cand.typeof(), repr(prop), repr(self.Class))) return cand.call(self, args) def to_python(self): @@ -1304,7 +1305,7 @@ class PyObjectWrapper(PyJs): except Exception as e: message = 'your Python function failed! ' try: - message += e.message + message += str(e) except: pass raise MakeError('Error', message) @@ -1464,9 +1465,11 @@ class PyJsFunction(PyJs): except NotImplementedError: raise except RuntimeError as e: # maximum recursion - raise MakeError( - 'RangeError', e.message if - not isinstance(e, NotImplementedError) else 'Not implemented!') + try: + msg = e.message + except: + msg = repr(e) + raise MakeError('RangeError', msg) def has_instance(self, other): # I am not sure here so instanceof may not work lol. diff --git a/lib/js2py/internals/constructors/jsfunction.py b/lib/js2py/internals/constructors/jsfunction.py index d62731a..738554a 100644 --- a/lib/js2py/internals/constructors/jsfunction.py +++ b/lib/js2py/internals/constructors/jsfunction.py @@ -7,7 +7,7 @@ from ..byte_trans import ByteCodeGenerator, Code def Function(this, args): # convert arguments to python list of strings - a = map(to_string, tuple(args)) + a = list(map(to_string, tuple(args))) _body = u';' _args = () if len(a): diff --git a/lib/js2py/internals/constructors/jsmath.py b/lib/js2py/internals/constructors/jsmath.py index 3eb4461..c6fde67 100644 --- a/lib/js2py/internals/constructors/jsmath.py +++ b/lib/js2py/internals/constructors/jsmath.py @@ -16,7 +16,8 @@ CONSTANTS = { 'SQRT1_2': 0.7071067811865476, 'SQRT2': 1.4142135623730951 } - +def is_infinity(x): + return x - 1e10 == x class MathFunctions: def abs(this, args): @@ -65,22 +66,22 @@ class MathFunctions: def ceil(this, args): x = get_arg(args, 0) a = to_number(x) - if a != a: # it must be a nan - return NaN + if not is_finite(x): + return x return float(math.ceil(a)) def floor(this, args): x = get_arg(args, 0) a = to_number(x) - if a != a: # it must be a nan - return NaN + if not is_finite(x): + return x return float(math.floor(a)) def round(this, args): x = get_arg(args, 0) a = to_number(x) - if a != a: # it must be a nan - return NaN + if not is_finite(x): + return x return float(round(a)) def sin(this, args): diff --git a/lib/js2py/internals/constructors/jsstring.py b/lib/js2py/internals/constructors/jsstring.py index f2b4383..20d92a4 100644 --- a/lib/js2py/internals/constructors/jsstring.py +++ b/lib/js2py/internals/constructors/jsstring.py @@ -1,6 +1,6 @@ from ..conversions import * from ..func_utils import * - +from six import unichr def fromCharCode(this, args): res = u'' diff --git a/lib/js2py/internals/speed.py b/lib/js2py/internals/speed.py index 3ca4079..482eb7e 100644 --- a/lib/js2py/internals/speed.py +++ b/lib/js2py/internals/speed.py @@ -1,7 +1,14 @@ +from __future__ import print_function + from timeit import timeit from collections import namedtuple from array import array -from itertools import izip +try: + #python 2 code + from itertools import izip as zip +except ImportError: + pass + from collections import deque @@ -47,7 +54,7 @@ t = [] Type = None try: - print timeit( + print(timeit( """ t.append(4) @@ -56,7 +63,7 @@ t.pop() """, - "from __main__ import X,Y,namedtuple,array,t,add,Type, izip", - number=1000000) + "from __main__ import X,Y,namedtuple,array,t,add,Type, zip", + number=1000000)) except: raise diff --git a/lib/js2py/legecy_translators/constants.py b/lib/js2py/legecy_translators/constants.py index ea048f1..8ada25a 100644 --- a/lib/js2py/legecy_translators/constants.py +++ b/lib/js2py/legecy_translators/constants.py @@ -1,3 +1,4 @@ +from __future__ import print_function from string import ascii_lowercase, digits ################################## StringName = u'PyJsConstantString%d_' @@ -305,4 +306,4 @@ if __name__ == '__main__': ''') t, d = remove_constants(test) - print t, d + print(t, d) diff --git a/lib/js2py/legecy_translators/exps.py b/lib/js2py/legecy_translators/exps.py index 3c8f793..bcd09b1 100644 --- a/lib/js2py/legecy_translators/exps.py +++ b/lib/js2py/legecy_translators/exps.py @@ -16,6 +16,8 @@ If case of parsing errors it must return a pos of error. NOTES: Strings and other literals are not present so each = means assignment """ +from __future__ import print_function + from utils import * from jsparser import * @@ -80,4 +82,4 @@ def bass_translator(s): if __name__ == '__main__': - print bass_translator('3.ddsd = 40') + print(bass_translator('3.ddsd = 40')) diff --git a/lib/js2py/legecy_translators/flow.py b/lib/js2py/legecy_translators/flow.py index 9e2bb0f..60d99aa 100644 --- a/lib/js2py/legecy_translators/flow.py +++ b/lib/js2py/legecy_translators/flow.py @@ -9,6 +9,8 @@ FOR 123 FOR iter CONTINUE, BREAK, RETURN, LABEL, THROW, TRY, SWITCH """ +from __future__ import print_function + from utils import * from jsparser import * from nodevisitor import exp_translator @@ -477,4 +479,4 @@ def translate_flow(source): if __name__ == '__main__': #print do_dowhile('do {} while(k+f)', 0)[0] #print 'e: "%s"'%do_expression('++(c?g:h); mj', 0)[0] - print translate_flow('a; yimport test')[0] + print(translate_flow('a; yimport test')[0]) diff --git a/lib/js2py/legecy_translators/functions.py b/lib/js2py/legecy_translators/functions.py index 4825eee..ceffc9e 100644 --- a/lib/js2py/legecy_translators/functions.py +++ b/lib/js2py/legecy_translators/functions.py @@ -1,4 +1,6 @@ """This module removes JS functions from source code""" +from __future__ import print_function + from jsparser import * from utils import * @@ -94,5 +96,5 @@ def remove_functions(source, all_inline=False): if __name__ == '__main__': - print remove_functions( - '5+5 function n (functiona ,functionaj) {dsd s, dsdd}') + print(remove_functions( + '5+5 function n (functiona ,functionaj) {dsd s, dsdd}')) diff --git a/lib/js2py/legecy_translators/jsparser.py b/lib/js2py/legecy_translators/jsparser.py index a09537d..7452b07 100644 --- a/lib/js2py/legecy_translators/jsparser.py +++ b/lib/js2py/legecy_translators/jsparser.py @@ -45,6 +45,7 @@ TODO """ +from __future__ import print_function from utils import * @@ -64,7 +65,7 @@ OP_METHODS = { def dbg(source): try: - with open('C:\Users\Piotrek\Desktop\dbg.py', 'w') as f: + with open(r'C:\Users\Piotrek\Desktop\dbg.py', 'w') as f: f.write(source) except: pass @@ -77,13 +78,13 @@ def indent(lines, ind=4): def inject_before_lval(source, lval, code): if source.count(lval) > 1: dbg(source) - print - print lval + print() + print(lval) raise RuntimeError('To many lvals (%s)' % lval) elif not source.count(lval): dbg(source) - print - print lval + print() + print(lval) assert lval not in source raise RuntimeError('No lval found "%s"' % lval) end = source.index(lval) diff --git a/lib/js2py/legecy_translators/nodevisitor.py b/lib/js2py/legecy_translators/nodevisitor.py index 4d50545..5e20654 100644 --- a/lib/js2py/legecy_translators/nodevisitor.py +++ b/lib/js2py/legecy_translators/nodevisitor.py @@ -1,3 +1,5 @@ +from __future__ import print_function + from jsparser import * from utils import * import re @@ -557,6 +559,6 @@ if __name__ == '__main__': #print 'Here', trans('(eee ) . ii [ PyJsMarker ] [ jkj ] ( j , j ) . # jiji (h , ji , i)(non )( )()()()') for e in xrange(3): - print exp_translator('jk = kk.ik++') + print(exp_translator('jk = kk.ik++')) #First line translated with PyJs: PyJsStrictEq(PyJsAdd((Js(100)*Js(50)),Js(30)), Js("5030")), yay! - print exp_translator('delete a.f') + print(exp_translator('delete a.f')) diff --git a/lib/js2py/legecy_translators/objects.py b/lib/js2py/legecy_translators/objects.py index 2abda3e..5cb9012 100644 --- a/lib/js2py/legecy_translators/objects.py +++ b/lib/js2py/legecy_translators/objects.py @@ -1,6 +1,8 @@ """ This module removes all objects/arrays from JS source code and replace them with LVALS. Also it has s function translating removed object/array to python code. Use this module just after removing constants. Later move on to removing functions""" +from __future__ import print_function + OBJECT_LVAL = 'PyJsLvalObject%d_' ARRAY_LVAL = 'PyJsLvalArray%d_' from utils import * @@ -180,7 +182,7 @@ def translate_object(obj, lval, obj_count=1, arr_count=1): try: key, value = spl except: #len(spl)> 2 - print 'Unusual case ' + repr(e) + print('Unusual case ' + repr(e)) key = spl[0] value = ':'.join(spl[1:]) key = key.strip() @@ -293,8 +295,8 @@ if __name__ == '__main__': #print remove_objects(test) #print list(bracket_split(' {}')) - print - print remove_arrays( + print() + print(remove_arrays( 'typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""], [][[5][5]])[1].toLowerCase()])' - ) - print is_object('', ')') + )) + print(is_object('', ')')) diff --git a/lib/js2py/legecy_translators/translator.py b/lib/js2py/legecy_translators/translator.py index 3573f06..c0ad845 100644 --- a/lib/js2py/legecy_translators/translator.py +++ b/lib/js2py/legecy_translators/translator.py @@ -1,3 +1,5 @@ +from __future__ import print_function + from flow import translate_flow from constants import remove_constants, recover_constants from objects import remove_objects, remove_arrays, translate_object, translate_array, set_func_translator @@ -148,4 +150,4 @@ if __name__ == '__main__': #res = translate_js(jq) res = translate_js(t) dbg(SANDBOX % indent(res)) - print 'Done' + print('Done') diff --git a/lib/js2py/node_import.py b/lib/js2py/node_import.py index 605c4b3..2be0985 100644 --- a/lib/js2py/node_import.py +++ b/lib/js2py/node_import.py @@ -1,7 +1,10 @@ __all__ = ['require'] + import subprocess, os, codecs, glob from .evaljs import translate_js, DEFAULT_HEADER +from .translators.friendly_nodes import is_valid_py_name import six + DID_INIT = False DIRNAME = os.path.dirname(os.path.abspath(__file__)) PY_NODE_MODULES_PATH = os.path.join(DIRNAME, 'py_node_modules') @@ -46,15 +49,23 @@ GET_FROM_GLOBALS_FUNC = ''' ''' + def _get_module_py_name(module_name): return module_name.replace('-', '_') + def _get_module_var_name(module_name): - return _get_module_py_name(module_name).rpartition('/')[-1] + cand = _get_module_py_name(module_name).rpartition('/')[-1] + if not is_valid_py_name(cand): + raise ValueError( + "Invalid Python module name %s (generated from %s). Unsupported/invalid npm module specification?" % ( + repr(cand), repr(module_name))) + return cand -def _get_and_translate_npm_module(module_name, include_polyfill=False, update=False): +def _get_and_translate_npm_module(module_name, include_polyfill=False, update=False, maybe_version_str=""): assert isinstance(module_name, str), 'module_name must be a string!' + py_name = _get_module_py_name(module_name) module_filename = '%s.py' % py_name var_name = _get_module_var_name(module_name) @@ -74,6 +85,8 @@ def _get_and_translate_npm_module(module_name, include_polyfill=False, update=Fa f.write(code.encode('utf-8') if six.PY3 else code) pkg_name = module_name.partition('/')[0] + if maybe_version_str: + pkg_name += '@' + maybe_version_str # make sure the module is installed assert subprocess.call( 'cd %s;npm install %s' % (repr(DIRNAME), pkg_name), @@ -117,21 +130,25 @@ def _get_and_translate_npm_module(module_name, include_polyfill=False, update=Fa return py_code -def require(module_name, include_polyfill=False, update=False, context=None): +def require(module_name, include_polyfill=True, update=False, context=None): """ Installs the provided npm module, exports a js bundle via browserify, converts to ECMA 5.1 via babel and finally translates the generated JS bundle to Python via Js2Py. Returns a pure python object that behaves like the installed module. Nice! - :param module_name: Name of the npm module to require. For example 'esprima'. + :param module_name: Name of the npm module to require. For example 'esprima'. Supports specific versions via @ + specification. Eg: 'crypto-js@3.3'. :param include_polyfill: Whether the babel-polyfill should be included as part of the translation. May be needed - for some modules that use unsupported features. + for some modules that use unsupported features of JS6 such as Map or typed arrays. :param update: Whether to force update the translation. Otherwise uses a cached version if exists. :param context: Optional context in which the translated module should be executed in. If provided, the header (js2py imports) will be skipped as it is assumed that the context already has all the necessary imports. :return: The JsObjectWrapper containing the translated module object. Can be used like a standard python object. """ - py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=update) + module_name, maybe_version = (module_name+"@@@").split('@')[:2] + + py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=update, + maybe_version_str=maybe_version) # this is a bit hacky but we need to strip the default header from the generated code... if context is not None: if not py_code.startswith(DEFAULT_HEADER): @@ -141,5 +158,5 @@ def require(module_name, include_polyfill=False, update=False, context=None): assert py_code.startswith(DEFAULT_HEADER), "Unexpected header." py_code = py_code[len(DEFAULT_HEADER):] context = {} if context is None else context - exec (py_code, context) + exec(py_code, context) return context['var'][_get_module_var_name(module_name)].to_py() diff --git a/lib/js2py/test_internals.py b/lib/js2py/test_internals.py deleted file mode 100644 index 12cf4ad..0000000 --- a/lib/js2py/test_internals.py +++ /dev/null @@ -1,9 +0,0 @@ -from internals import byte_trans -from internals import seval -import pyjsparser - -x = r''' -function g() {var h123 = 11; return [function g1() {return h123}, new Function('return h123')]} -g()[1]() -''' -print seval.eval_js_vm(x) diff --git a/lib/js2py/translators/translating_nodes.py b/lib/js2py/translators/translating_nodes.py index 371c8ed..bfce7c1 100644 --- a/lib/js2py/translators/translating_nodes.py +++ b/lib/js2py/translators/translating_nodes.py @@ -108,6 +108,13 @@ def to_key(literal_or_identifier): else: return unicode(k) +def is_iteration_statement(cand): + if not isinstance(cand, dict): + # Multiple statements. + return False + return cand.get("type", "?") in {"ForStatement", "ForInStatement", "WhileStatement", "DoWhileStatement"} + + def trans(ele, standard=False): """Translates esprima syntax tree to python by delegating to appropriate translating node""" @@ -440,8 +447,8 @@ def LabeledStatement(type, label, body): # todo consider using smarter approach! inside = trans(body) defs = '' - if inside.startswith('while ') or inside.startswith( - 'for ') or inside.startswith('#for'): + if is_iteration_statement(body) and (inside.startswith('while ') or inside.startswith( + 'for ') or inside.startswith('#for')): # we have to add contine label as well... # 3 or 1 since #for loop type has more lines before real for. sep = 1 if not inside.startswith('#for') else 3 diff --git a/sickbeard/naming.py b/sickbeard/naming.py index 6f0904e..0d91742 100644 --- a/sickbeard/naming.py +++ b/sickbeard/naming.py @@ -118,6 +118,7 @@ class TVEpisodeSample(tv.TVEpisode): self._release_name = 'Show.Name.S02E03.HDTV.XviD-RLSGROUP' # type: AnyStr self._is_proper = True # type: bool self._version = 2 # type: int + self._epid = season + (100 * episode) # type: int def check_force_season_folders(pattern=None, multi=None, anime_type=None):