diff --git a/test/bytecode_3.0_run/04_lambda_star_default.pyc b/test/bytecode_3.0_run/04_lambda_star_default.pyc new file mode 100644 index 00000000..4e366eaa Binary files /dev/null and b/test/bytecode_3.0_run/04_lambda_star_default.pyc differ diff --git a/test/bytecode_3.1_run/04_lambda_star_default.pyc b/test/bytecode_3.1_run/04_lambda_star_default.pyc new file mode 100644 index 00000000..57e564ba Binary files /dev/null and b/test/bytecode_3.1_run/04_lambda_star_default.pyc differ diff --git a/test/bytecode_3.1_run/10_complex.pyc b/test/bytecode_3.1_run/10_complex.pyc new file mode 100644 index 00000000..8374996d Binary files /dev/null and b/test/bytecode_3.1_run/10_complex.pyc differ diff --git a/test/bytecode_3.2_run/04_lambda_star_default.pyc b/test/bytecode_3.2_run/04_lambda_star_default.pyc new file mode 100644 index 00000000..f43ac230 Binary files /dev/null and b/test/bytecode_3.2_run/04_lambda_star_default.pyc differ diff --git a/test/bytecode_3.3_run/04_lambda_star_default.pyc b/test/bytecode_3.3_run/04_lambda_star_default.pyc new file mode 100644 index 00000000..b156da17 Binary files /dev/null and b/test/bytecode_3.3_run/04_lambda_star_default.pyc differ diff --git a/test/bytecode_3.3_run/10_complex.pyc b/test/bytecode_3.3_run/10_complex.pyc new file mode 100644 index 00000000..983188f8 Binary files /dev/null and b/test/bytecode_3.3_run/10_complex.pyc differ diff --git a/test/bytecode_3.5_run/10_complex.pyc b/test/bytecode_3.5_run/10_complex.pyc new file mode 100644 index 00000000..3153b002 Binary files /dev/null and b/test/bytecode_3.5_run/10_complex.pyc differ diff --git a/test/simple_source/bug31/10_complex.py b/test/simple_source/bug31/10_complex.py new file mode 100644 index 00000000..7f1b29b3 --- /dev/null +++ b/test/simple_source/bug31/10_complex.py @@ -0,0 +1,41 @@ +# Greatly simplified from from 3.3 test_complex.py + +# RUNNABLE! +def assertCloseAbs(x, y, eps=1e-09): + """Return true iff floats x and y "are close\"""" + if abs(x) > abs(y): + x, y = y, x + if y == 0: + return abs(x) < eps + if x == 0: + return abs(y) < eps + assert abs((x - y) / y) < eps + +def assertClose(x, y, eps=1e-09): + """Return true iff complexes x and y "are close\"""" + assertCloseAbs(x.real, y.real, eps) + assertCloseAbs(x.imag, y.imag, eps) + +def check_div(x, y): + """Compute complex z=x*y, and check that z/x==y and z/y==x.""" + z = x * y + if x != 0: + q = z / x + assertClose(q, y) + q = z.__truediv__(x) + assertClose(q, y) + if y != 0: + q = z / y + assertClose(q, x) + q = z.__truediv__(y) + assertClose(q, x) + +def test_truediv(): + simple_real = [float(i) for i in range(-5, 6)] + simple_complex = [complex(x, y) for x in simple_real for y in simple_real] + for x in simple_complex: + for y in simple_complex: + check_div(x, y) + +z2 = -1e1000j # Check that we can handle -inf as a complex number +test_truediv() diff --git a/test/simple_source/bug33/04_lambda_star_default.py b/test/simple_source/bug33/04_lambda_star_default.py new file mode 100644 index 00000000..879b8960 --- /dev/null +++ b/test/simple_source/bug33/04_lambda_star_default.py @@ -0,0 +1,18 @@ +# From 3.x test_audiop.py + +# Bug is handling default value after * argument in a lambda. +# That's a mouthful of desciption; I am not sure if the really +# hacky fix to the code is even correct. + +# +# FIXME: try and test with more than one default argument. + +# RUNNABLE +def pack(width, data): + return (width, data) + +packs = {w: (lambda *data, width=w: pack(width, data)) for w in (1, 2, 4)} + +assert packs[1]('a') == (1, ('a',)) +assert packs[2]('b') == (2, ('b',)) +assert packs[4]('c') == (4, ('c',)) diff --git a/test/stdlib/runtests.sh b/test/stdlib/runtests.sh index 532a4549..2cc251a6 100755 --- a/test/stdlib/runtests.sh +++ b/test/stdlib/runtests.sh @@ -142,8 +142,22 @@ case $PYVERSION in SKIP_TESTS[test_base64.py]=1 fi ;; + 3.3) + SKIP_TESTS=( + [test_atexit.py]=1 # + [test_decorators.py]=1 # Control flow wrt "if elif" + ) + if (( batch )) ; then + # Fails in crontab environment? + # Figure out what's up here + SKIP_TESTS[test_exception_variations.py]=1 + SKIP_TESTS[test_quopri.py]=1 + fi + ;; + 3.5) SKIP_TESTS=( + [test_atexit.py]=1 # [test_decorators.py]=1 # Control flow wrt "if elif" ) if (( batch )) ; then diff --git a/uncompyle6/semantics/customize3.py b/uncompyle6/semantics/customize3.py index 599e25d6..f7cefd1a 100644 --- a/uncompyle6/semantics/customize3.py +++ b/uncompyle6/semantics/customize3.py @@ -19,6 +19,7 @@ from uncompyle6.semantics.consts import TABLE_DIRECT from xdis.code import iscode +from uncompyle6.scanner import Code from uncompyle6.semantics.helper import gen_function_parens_adjust from uncompyle6.semantics.make_function import make_function3_annotate from uncompyle6.semantics.customize35 import customize_for_version35 @@ -54,6 +55,108 @@ def customize_for_version3(self, version): assert version >= 3.0 + def listcomp_closure3(node): + """List comprehensions in Python 3 when handled as a closure. + See if we can combine code. + """ + p = self.prec + self.prec = 27 + + code = Code(node[1].attr, self.scanner, self.currentclass) + ast = self.build_ast(code._tokens, code._customize) + self.customize(code._customize) + + # skip over: sstmt, stmt, return, ret_expr + # and other singleton derivations + while len(ast) == 1 or ( + ast in ("sstmt", "return") and ast[-1] in ("RETURN_LAST", "RETURN_VALUE") + ): + self.prec = 100 + ast = ast[0] + + n = ast[1] + + # collections is the name of the expression(s) we are iterating over + collections = [node[-3]] + list_ifs = [] + + if self.version == 3.0 and n != "list_iter": + # FIXME 3.0 is a snowflake here. We need + # special code for this. Not sure if this is totally + # correct. + stores = [ast[3]] + assert ast[4] == "comp_iter" + n = ast[4] + # Find the list comprehension body. It is the inner-most + # node that is not comp_.. . + while n == "comp_iter": + if n[0] == "comp_for": + n = n[0] + stores.append(n[2]) + n = n[3] + elif n[0] in ("comp_if", "comp_if_not"): + n = n[0] + # FIXME: just a guess + if n[0].kind == "expr": + list_ifs.append(n) + else: + list_ifs.append([1]) + n = n[2] + pass + else: + break + pass + + # Skip over n[0] which is something like: _[1] + self.preorder(n[1]) + + else: + assert n == "list_iter" + stores = [] + # Find the list comprehension body. It is the inner-most + # node that is not list_.. . + while n == "list_iter": + n = n[0] # recurse one step + if n == "list_for": + stores.append(n[2]) + n = n[3] + if n[0] == "list_for": + # Dog-paddle down largely singleton reductions + # to find the collection (expr) + c = n[0][0] + if c == "expr": + c = c[0] + # FIXME: grammar is wonky here? Is this really an attribute? + if c == "attribute": + c = c[0] + collections.append(c) + pass + elif n in ("list_if", "list_if_not"): + # FIXME: just a guess + if n[0].kind == "expr": + list_ifs.append(n) + else: + list_ifs.append([1]) + n = n[2] + pass + pass + + assert n == "lc_body", ast + self.preorder(n[0]) + + # FIXME: add indentation around "for"'s and "in"'s + for i, store in enumerate(stores): + self.write(" for ") + self.preorder(store) + self.write(" in ") + self.preorder(collections[i]) + if i < len(list_ifs): + self.preorder(list_ifs[i]) + pass + pass + self.prec = p + self.listcomp_closure3 = listcomp_closure3 + def n_classdef3(node): # class definition ('class X(A,B,C):') cclass = self.currentclass diff --git a/uncompyle6/semantics/customize35.py b/uncompyle6/semantics/customize35.py index cac30af0..7bc48f15 100644 --- a/uncompyle6/semantics/customize35.py +++ b/uncompyle6/semantics/customize35.py @@ -186,8 +186,14 @@ def customize_for_version35(self, version): is_code = True break - if (is_code and - (code_node.attr.co_flags & COMPILER_FLAG_BIT['COROUTINE'])): + if is_code and ( + code_node.attr.co_flags + & ( + COMPILER_FLAG_BIT["COROUTINE"] + | COMPILER_FLAG_BIT["ITERABLE_COROUTINE"] + | COMPILER_FLAG_BIT["ASYNC_GENERATOR"] + ) + ): self.template_engine(('\n\n%|async def %c\n', -2), node) else: diff --git a/uncompyle6/semantics/make_function.py b/uncompyle6/semantics/make_function.py index 7e7dda82..f10f9679 100644 --- a/uncompyle6/semantics/make_function.py +++ b/uncompyle6/semantics/make_function.py @@ -595,9 +595,8 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None): pass if ( - 3.0 <= self.version <= 3.3 + self.version <= 3.3 and len(node) > 2 - and node[lambda_index] != "LOAD_LAMBDA" and (have_kwargs or node[lc_index].kind != "load_closure") ): @@ -608,12 +607,22 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None): default_values_start = 0 if node[0] == "no_kwargs": default_values_start += 1 - # args are after kwargs; kwargs are bundled as one node - if node[default_values_start] == "kwargs": - default_values_start += 1 - defparams = node[ - default_values_start : default_values_start + args_node.attr[0] - ] + + # If in a lambda named args are a sequence of kwarg, not bundled. + # If not in a lambda, named args are after kwargs; kwargs are bundled as one node. + if node[default_values_start] == "kwarg": + assert node[lambda_index] == "LOAD_LAMBDA" + i = default_values_start + defparams = [] + while node[i] == "kwarg": + defparams.append(node[i][1]) + i += 1 + else: + if node[default_values_start] == "kwargs": + default_values_start += 1 + defparams = node[ + default_values_start : default_values_start + args_node.attr[0] + ] else: if self.version < 3.6: defparams = node[: args_node.attr[0]] @@ -718,11 +727,19 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None): paramnames = list(scanner_code.co_varnames[:argc]) if kwonlyargcount > 0: - kwargs = list(scanner_code.co_varnames[argc : argc + kwonlyargcount]) + if self.version <= 3.3 and is_lambda: + kwargs = [] + for i in range(kwonlyargcount): + paramnames.append(scanner_code.co_varnames[argc+i]) + pass + pass + else: + kwargs = list(scanner_code.co_varnames[argc : argc + kwonlyargcount]) - # defaults are for last n parameters, thus reverse - paramnames.reverse() - defparams.reverse() + # defaults are for last n parameters when not in a lambda, thus reverse + if not is_lambda: + paramnames.reverse() + defparams.reverse() try: ast = self.build_ast(scanner_code._tokens, @@ -768,6 +785,9 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None): params.append("*%s: %s" % (star_arg, annotate_dict[star_arg])) else: params.append("*%s" % star_arg) + pass + if is_lambda and self.version <= 3.3: + params.reverse() else: params.append("*%s" % code.co_varnames[argc]) argc += 1 @@ -802,7 +822,7 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None): # created a parameter list and at the very end did a join on that? # Unless careful, We might lose line breaks though. ends_in_comma = False - if kwonlyargcount > 0: + if kwonlyargcount > 0 and not is_lambda: if not (4 & code.co_flags): if argc > 0: self.write(", *, ") @@ -811,7 +831,7 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None): pass ends_in_comma = True else: - if argc > 0: + if argc > 0 and node[0] != "kwarg": self.write(", ") ends_in_comma = True @@ -824,14 +844,18 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None): default = self.traverse(n[1], indent="") idx = kwargs.index(name) kw_args[idx] = "%s=%s" % (name, default) + pass + pass - other_kw = [c == None for c in kw_args] + if kw_nodes != "kwarg": + other_kw = [c == None for c in kw_args] - for i, flag in enumerate(other_kw): - if flag: - kw_args[i] = "%s" % kwargs[i] - self.write(", ".join(kw_args)) - ends_in_comma = False + for i, flag in enumerate(other_kw): + if flag: + kw_args[i] = "%s" % kwargs[i] + self.write(", ".join(kw_args)) + ends_in_comma = False + pass elif self.version >= 3.6: # argc = node[-1].attr # co = node[-3].attr diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index 3a9540bb..691e2f2c 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -630,10 +630,19 @@ class SourceWalker(GenericASTTraversal, object): if isinstance(data, float) and str(data) in frozenset( ["nan", "-nan", "inf", "-inf"] ): - # float values 'nan' and 'inf' are not directly representable in Python at least - # before 3.5 and even there it is via a library constant. - # So we will canonicalize their representation as float('nan') and float('inf') + # float values 'nan' and 'inf' are not directly + # representable in Python before Python 3.5. In Python 3.5 + # it is accessible via a library constant math.inf. So we + # will canonicalize representation of these value as + # float('nan') and float('inf') self.write("float('%s')" % data) + elif isinstance(data, complex) and str(data.imag) in frozenset( + ["nan", "-nan", "inf", "-inf"] + ): + # Likewise, complex values with 'nan' and 'inf' are not + # directly representable in Python. So we will + # canonicalize like we did above. + self.write("complex('%s%sj')" % (data.real, data.imag)) elif isinstance(datatype, int) and data == minint: # convert to hex, since decimal representation # would result in 'LOAD_CONST; UNARY_NEGATIVE' @@ -1327,121 +1336,11 @@ class SourceWalker(GenericASTTraversal, object): pass self.prec = p - def listcomprehension_walk2(self, node): - """List comprehensions the way they are done in Python 2 and - sometimes in Python 3. - They're more other comprehensions, e.g. set comprehensions - See if we can combine code. - """ - p = self.prec - self.prec = 27 - - code = Code(node[1].attr, self.scanner, self.currentclass) - ast = self.build_ast(code._tokens, code._customize) - self.customize(code._customize) - - # skip over: sstmt, stmt, return, ret_expr - # and other singleton derivations - while len(ast) == 1 or ( - ast in ("sstmt", "return") and ast[-1] in ("RETURN_LAST", "RETURN_VALUE") - ): - self.prec = 100 - ast = ast[0] - - n = ast[1] - # collection = node[-3] - collections = [node[-3]] - list_ifs = [] - - if self.version == 3.0 and n != "list_iter": - # FIXME 3.0 is a snowflake here. We need - # special code for this. Not sure if this is totally - # correct. - stores = [ast[3]] - assert ast[4] == "comp_iter" - n = ast[4] - # Find the list comprehension body. It is the inner-most - # node that is not comp_.. . - while n == "comp_iter": - if n[0] == "comp_for": - n = n[0] - stores.append(n[2]) - n = n[3] - elif n[0] in ("comp_if", "comp_if_not"): - n = n[0] - # FIXME: just a guess - if n[0].kind == "expr": - list_ifs.append(n) - else: - list_ifs.append([1]) - n = n[2] - pass - else: - break - pass - - # Skip over n[0] which is something like: _[1] - self.preorder(n[1]) - - else: - assert n == "list_iter" - stores = [] - # Find the list comprehension body. It is the inner-most - # node that is not list_.. . - while n == "list_iter": - n = n[0] # recurse one step - if n == "list_for": - stores.append(n[2]) - n = n[3] - if self.version >= 3.6 and n[0] == "list_for": - # Dog-paddle down largely singleton reductions - # to find the collection (expr) - c = n[0][0] - if c == "expr": - c = c[0] - # FIXME: grammar is wonky here? Is this really an attribute? - if c == "attribute": - c = c[0] - collections.append(c) - pass - elif n in ("list_if", "list_if_not"): - # FIXME: just a guess - if n[0].kind == "expr": - list_ifs.append(n) - else: - list_ifs.append([1]) - n = n[2] - pass - pass - - assert n == "lc_body", ast - self.preorder(n[0]) - - # FIXME: add indentation around "for"'s and "in"'s - if self.version < 3.6: - self.write(" for ") - self.preorder(stores[0]) - self.write(" in ") - self.preorder(collections[0]) - if list_ifs: - self.preorder(list_ifs[0]) - pass - else: - for i, store in enumerate(stores): - self.write(" for ") - self.preorder(store) - self.write(" in ") - self.preorder(collections[i]) - if i < len(list_ifs): - self.preorder(list_ifs[i]) - pass - pass - self.prec = p - def n_listcomp(self, node): self.write("[") if node[0].kind == "load_closure": - self.listcomprehension_walk2(node) + assert self.version >= 3.0 + self.listcomp_closure3(node) else: if node == "listcomp_async": list_iter_index = 5