From 4cc53f2307183d76928531b47787a25c5ee00ec3 Mon Sep 17 00:00:00 2001 From: rocky Date: Wed, 1 May 2019 09:46:56 -0400 Subject: [PATCH 01/11] Python 3.6+ try/else with no trailing END_FINALLY --- uncompyle6/parsers/parse36.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/uncompyle6/parsers/parse36.py b/uncompyle6/parsers/parse36.py index 1c41f1b0..f2d43df1 100644 --- a/uncompyle6/parsers/parse36.py +++ b/uncompyle6/parsers/parse36.py @@ -122,9 +122,12 @@ class Python36Parser(Python35Parser): try_except36 ::= SETUP_EXCEPT returns except_handler36 opt_come_from_except try_except36 ::= SETUP_EXCEPT suite_stmts + try_except36 ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK + except_handler36 opt_come_from_except # 3.6 omits END_FINALLY sometimes except_handler36 ::= COME_FROM_EXCEPT except_stmts + except_handler36 ::= JUMP_FORWARD COME_FROM_EXCEPT except_stmts except_handler ::= jmp_abs COME_FROM_EXCEPT except_stmts stmt ::= tryfinally36 @@ -170,6 +173,7 @@ class Python36Parser(Python35Parser): JUMP_ABSOLUTE END_FINALLY COME_FROM for_block pb_ja else_suite COME_FROM_LOOP + """) self.check_reduce['call_kw'] = 'AST' From ce7015f382f2b6fb80c52697c628264b772f85bb Mon Sep 17 00:00:00 2001 From: rocky Date: Wed, 1 May 2019 11:10:16 -0400 Subject: [PATCH 02/11] Improve 3.x while1 reduction elimination --- uncompyle6/parsers/parse3.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index ae516e2a..23979936 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -1214,7 +1214,7 @@ class Python3Parser(PythonParser): pass elif lhs == 'while1stmt': - # If there is a fall through to the COME_FROM_LOOP. then this is + # If there is a fall through to the COME_FROM_LOOP, then this is # not a while 1. So the instruction before should either be a # JUMP_BACK or the instruction before should not be the target of a # jump. (Well that last clause i not quite right; that target could be @@ -1225,10 +1225,11 @@ class Python3Parser(PythonParser): cfl = last assert tokens[cfl] == 'COME_FROM_LOOP' - if tokens[cfl-1] != 'JUMP_BACK': - cfl_offset = tokens[cfl-1].offset - insn = next(i for i in self.insts if cfl_offset == i.offset) - if insn and insn.is_jump_target: + for i in range(cfl-1, first, -1): + if tokens[i] != 'POP_BLOCK': + break + if tokens[i].kind not in ('JUMP_BACK', 'RETURN_VALUE'): + if not tokens[i].kind.startswith('COME_FROM'): return True # Check that the SETUP_LOOP jumps to the offset after the From 32bc017e2e7c080b49c1d55510acaed7c1487f74 Mon Sep 17 00:00:00 2001 From: rocky Date: Wed, 1 May 2019 14:23:00 -0400 Subject: [PATCH 03/11] Fix wrong node slot in 3.6 for except_handler --- uncompyle6/semantics/customize36.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/uncompyle6/semantics/customize36.py b/uncompyle6/semantics/customize36.py index 8e144df9..4a2e2103 100644 --- a/uncompyle6/semantics/customize36.py +++ b/uncompyle6/semantics/customize36.py @@ -38,20 +38,20 @@ def customize_for_version36(self, version): PRECEDENCE['unmap_dict'] = 0 TABLE_DIRECT.update({ - 'tryfinally36': ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', - (1, 'returns'), 3 ), - 'fstring_expr': ( "{%c%{conversion}}", - (0, 'expr') ), + 'tryfinally36': ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', + (1, 'returns'), 3 ), + 'fstring_expr': ( "{%c%{conversion}}", + (0, 'expr') ), # FIXME: the below assumes the format strings # don't have ''' in them. Fix this properly - 'fstring_single': ( "f'''{%c%{conversion}}'''", 0), + 'fstring_single': ( "f'''{%c%{conversion}}'''", 0), 'formatted_value_attr': ( "f'''{%c%{conversion}}%{string}'''", (0, 'expr')), - 'fstring_multi': ( "f'''%c'''", 0), - 'func_args36': ( "%c(**", 0), - 'try_except36': ( '%|try:\n%+%c%-%c\n\n', 1, 2 ), - 'except_return': ( '%|except:\n%+%c%-', 3 ), - 'unpack_list': ( '*%c', (0, 'list') ), + 'fstring_multi': ( "f'''%c'''", 0), + 'func_args36': ( "%c(**", 0), + 'try_except36': ( '%|try:\n%+%c%-%c\n\n', 1, -2 ), + 'except_return': ( '%|except:\n%+%c%-', 3 ), + 'unpack_list': ( '*%c', (0, 'list') ), 'tryfinally_return_stmt': ( '%|try:\n%+%c%-%|finally:\n%+%|return%-\n\n', 1 ), From 293e7b036766e6a1c3fbde0ac5408b8d664282bd Mon Sep 17 00:00:00 2001 From: rocky Date: Thu, 2 May 2019 06:43:53 -0400 Subject: [PATCH 04/11] store_subscript precedence fix and... Allow format specifier "%p" to indicate a nonterminal name, like "%c" allows. store_subscr -> store_subscript to match Python AST a little closer. --- pytest/test_pysource.py | 14 +- test/bytecode_2.7_run/04_subscript.pyc | Bin 0 -> 332 bytes test/bytecode_3.6_run/04_subscript.pyc | Bin 0 -> 263 bytes test/simple_source/expression/04_subscript.py | 11 ++ uncompyle6/parser.py | 18 +- uncompyle6/semantics/consts.py | 160 +++++++++--------- uncompyle6/semantics/pysource.py | 18 +- 7 files changed, 127 insertions(+), 94 deletions(-) create mode 100644 test/bytecode_2.7_run/04_subscript.pyc create mode 100644 test/bytecode_3.6_run/04_subscript.pyc create mode 100644 test/simple_source/expression/04_subscript.py diff --git a/pytest/test_pysource.py b/pytest/test_pysource.py index 902222c0..bc866671 100644 --- a/pytest/test_pysource.py +++ b/pytest/test_pysource.py @@ -127,11 +127,17 @@ def test_tables(): "Full entry: %s" % (name, k, arg, typ, entry[arg], type(entry[arg]), entry) ) - assert len(tup) == 2 + assert 2 <= len(tup) <= 3 for j, x in enumerate(tup): - assert isinstance(x, int), ( - "%s[%s][%d][%d] type '%s' is '%s should be an int but is %s. Full entry: %s" % - (name, k, arg, j, typ, x, type(x), entry) + if len(tup) == 3 and j == 1: + assert isinstance(x, str), ( + "%s[%s][%d][%d] type '%s' is '%s should be an string but is %s. Full entry: %s" % + (name, k, arg, j, typ, x, type(x), entry) + ) + else: + assert isinstance(x, int), ( + "%s[%s][%d][%d] type '%s' is '%s should be an int but is %s. Full entry: %s" % + (name, k, arg, j, typ, x, type(x), entry) ) pass arg += 1 diff --git a/test/bytecode_2.7_run/04_subscript.pyc b/test/bytecode_2.7_run/04_subscript.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3cd38e7c2ed88a0e40ba6f9424738e4c8d5b4082 GIT binary patch literal 332 zcmYLC!AiqG6r9Z_R*LcN$!kEc9z-aJDCkKjhzB85icMZ+F-_vW7fi2ymuCt_)Hb-fCFW>ZYx@F)A@oX8ui4i mfyvqVg1aRb-nDYtZe(8{U#^?7tLQ59@mL`>sj=q397lfu{6SR! literal 0 HcmV?d00001 diff --git a/test/bytecode_3.6_run/04_subscript.pyc b/test/bytecode_3.6_run/04_subscript.pyc new file mode 100644 index 0000000000000000000000000000000000000000..baaf71a487ced9490fdf2e1a545bab0a2ae886c7 GIT binary patch literal 263 zcmX|(u}T9$5Qb-V_l_eFOTor6n8pZ7DiH)ZGa))qd( zwpMuqvC3%S!2kVI%r~d~e((L`bNUScUy}bZvRz8}R^Y&4jrO;dG # Copyright (c) 2000-2002 by hartmut Goebel # Copyright (c) 1999 John Aycock @@ -589,14 +589,14 @@ class PythonParser(GenericASTBuilder): ## designLists ::= ## Will need to redo semantic actiion - store ::= STORE_FAST - store ::= STORE_NAME - store ::= STORE_GLOBAL - store ::= STORE_DEREF - store ::= expr STORE_ATTR - store ::= store_subscr - store_subscr ::= expr expr STORE_SUBSCR - store ::= unpack + store ::= STORE_FAST + store ::= STORE_NAME + store ::= STORE_GLOBAL + store ::= STORE_DEREF + store ::= expr STORE_ATTR + store ::= store_subscript + store_subscript ::= expr expr STORE_SUBSCR + store ::= unpack ''' diff --git a/uncompyle6/semantics/consts.py b/uncompyle6/semantics/consts.py index 6183e060..719b5789 100644 --- a/uncompyle6/semantics/consts.py +++ b/uncompyle6/semantics/consts.py @@ -27,6 +27,78 @@ else: maxint = sys.maxint +# Operator precidence +# See https://docs.python.org/2/reference/expressions.html +# or https://docs.python.org/3/reference/expressions.html +# for a list. + +# Things at the top of this list below with low-value precidence will +# tend to have parenthesis around them. Things at the bottom +# of the list will tend not to have parenthesis around them. +PRECEDENCE = { + 'list': 0, + 'dict': 0, + 'unary_convert': 0, + 'dict_comp': 0, + 'set_comp': 0, + 'set_comp_expr': 0, + 'list_comp': 0, + 'generator_exp': 0, + + 'attribute': 2, + 'subscript': 2, + 'subscript2': 2, + 'store_subscript': 2, + 'delete_subscr': 2, + 'slice0': 2, + 'slice1': 2, + 'slice2': 2, + 'slice3': 2, + 'buildslice2': 2, + 'buildslice3': 2, + 'call': 2, + + 'BINARY_POWER': 4, + + 'unary_expr': 6, + + 'BINARY_MULTIPLY': 8, + 'BINARY_DIVIDE': 8, + 'BINARY_TRUE_DIVIDE': 8, + 'BINARY_FLOOR_DIVIDE': 8, + 'BINARY_MODULO': 8, + + 'BINARY_ADD': 10, + 'BINARY_SUBTRACT': 10, + + 'BINARY_LSHIFT': 12, + 'BINARY_RSHIFT': 12, + + 'BINARY_AND': 14, + 'BINARY_XOR': 16, + 'BINARY_OR': 18, + + 'compare': 20, + 'unary_not': 22, + 'and': 24, + 'ret_and': 24, + + 'or': 26, + 'ret_or': 26, + + 'conditional': 28, + 'conditional_lamdba': 28, + 'conditional_not_lamdba': 28, + 'conditionalnot': 28, + 'ret_cond': 28, + 'ret_cond_not': 28, + + '_mklambda': 30, + + 'yield': 101, + 'yield_from': 101 +} + LINE_LENGTH = 80 # Some parse trees created below are used for comparing code @@ -150,15 +222,17 @@ TABLE_DIRECT = { 'DELETE_FAST': ( '%|del %{pattr}\n', ), 'DELETE_NAME': ( '%|del %{pattr}\n', ), 'DELETE_GLOBAL': ( '%|del %{pattr}\n', ), - 'delete_subscr': ( '%|del %c[%c]\n', - (0, 'expr'), (1, 'expr') ), - 'subscript': ( '%c[%p]', - (0, 'expr'), - (1, 100) ), - 'subscript2': ( '%c[%c]', - (0, 'expr'), + 'delete_subscr': ( '%|del %p[%c]\n', + (0, 'expr', PRECEDENCE['subscript']), (1, 'expr') ), + 'subscript': ( '%p[%c]', + (0, 'expr', PRECEDENCE['subscript']), (1, 'expr') ), - 'store_subscr': ( '%c[%c]', 0, 1), + 'subscript2': ( '%p[%c]', + (0, 'expr', PRECEDENCE['subscript']), + (1, 'expr') ), + 'store_subscript': ( '%p[%c]', + (0, 'expr', PRECEDENCE['subscript']), + (1, 'expr') ), 'STORE_FAST': ( '%{pattr}', ), 'STORE_NAME': ( '%{pattr}', ), 'STORE_GLOBAL': ( '%{pattr}', ), @@ -337,76 +411,6 @@ MAP = { 'exprlist': MAP_R0, } -# Operator precidence -# See https://docs.python.org/2/reference/expressions.html -# or https://docs.python.org/3/reference/expressions.html -# for a list. - -# Things at the top of this list below with low-value precidence will -# tend to have parenthesis around them. Things at the bottom -# of the list will tend not to have parenthesis around them. -PRECEDENCE = { - 'list': 0, - 'dict': 0, - 'unary_convert': 0, - 'dict_comp': 0, - 'set_comp': 0, - 'set_comp_expr': 0, - 'list_comp': 0, - 'generator_exp': 0, - - 'attribute': 2, - 'subscript': 2, - 'subscript2': 2, - 'slice0': 2, - 'slice1': 2, - 'slice2': 2, - 'slice3': 2, - 'buildslice2': 2, - 'buildslice3': 2, - 'call': 2, - - 'BINARY_POWER': 4, - - 'unary_expr': 6, - - 'BINARY_MULTIPLY': 8, - 'BINARY_DIVIDE': 8, - 'BINARY_TRUE_DIVIDE': 8, - 'BINARY_FLOOR_DIVIDE': 8, - 'BINARY_MODULO': 8, - - 'BINARY_ADD': 10, - 'BINARY_SUBTRACT': 10, - - 'BINARY_LSHIFT': 12, - 'BINARY_RSHIFT': 12, - - 'BINARY_AND': 14, - 'BINARY_XOR': 16, - 'BINARY_OR': 18, - - 'compare': 20, - 'unary_not': 22, - 'and': 24, - 'ret_and': 24, - - 'or': 26, - 'ret_or': 26, - - 'conditional': 28, - 'conditional_lamdba': 28, - 'conditional_not_lamdba': 28, - 'conditionalnot': 28, - 'ret_cond': 28, - 'ret_cond_not': 28, - - '_mklambda': 30, - - 'yield': 101, - 'yield_from': 101 -} - ASSIGN_TUPLE_PARAM = lambda param_name: \ SyntaxTree('expr', [ Token('LOAD_FAST', pattr=param_name) ]) diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index b254b22d..4edeaaca 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -88,7 +88,8 @@ Python. # # %p like %c but sets the operator precedence. # Its argument then is a tuple indicating the node -# index and the precidence value, an integer. +# index and the precedence value, an integer. If 3 items are given, +# the second item is the nonterminal name and the precedence is given last. # # %C evaluate children recursively, with sibling children separated by the # given string. It needs a 3-tuple: a starting node, the maximimum @@ -616,7 +617,7 @@ class SourceWalker(GenericASTTraversal, object): node[-2][0].kind = 'build_tuple2' self.default(node) - n_store_subscr = n_subscript = n_delete_subscr + n_store_subscript = n_subscript = n_delete_subscr # Note: this node is only in Python 2.x # FIXME: figure out how to get this into customization @@ -1873,7 +1874,18 @@ class SourceWalker(GenericASTTraversal, object): arg += 1 elif typ == 'p': p = self.prec - (index, self.prec) = entry[arg] + tup = entry[arg] + assert isinstance(tup, tuple) + if len(tup) == 3: + (index, nonterm_name, self.prec) = tup + assert node[index] == nonterm_name, ( + "at %s[%d], expected '%s' node; got '%s'" % ( + node.kind, arg, nonterm_name, node[index].kind) + ) + else: + assert len(tup) == 2 + (index, self.prec) = entry[arg] + self.preorder(node[index]) self.prec = p arg += 1 From e2c5a79346625922dbfa584edac9dcc0db342745 Mon Sep 17 00:00:00 2001 From: rocky Date: Thu, 2 May 2019 06:46:07 -0400 Subject: [PATCH 05/11] Add pypy3.6 scanner --- uncompyle6/scanners/pypy36.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 uncompyle6/scanners/pypy36.py diff --git a/uncompyle6/scanners/pypy36.py b/uncompyle6/scanners/pypy36.py new file mode 100644 index 00000000..158d8b54 --- /dev/null +++ b/uncompyle6/scanners/pypy36.py @@ -0,0 +1,22 @@ +# Copyright (c) 2019 by Rocky Bernstein +""" +Python PyPy 3.6 decompiler scanner. + +Does some additional massaging of xdis-disassembled instructions to +make things easier for decompilation. +""" + +import uncompyle6.scanners.scanner36 as scan + +# bytecode verification, verify(), uses JUMP_OPS from here +from xdis.opcodes import opcode_35 as opc # is this right? +JUMP_OPs = opc.JUMP_OPS + +# We base this off of 3.5 +class ScannerPyPy36(scan.Scanner36): + def __init__(self, show_asm): + # There are no differences in initialization between + # pypy 3.6 and 3.6 + scan.Scanner36.__init__(self, show_asm, is_pypy=True) + self.version = 3.6 + return From 2813e2212f5cbb2baeb34745d1201a1daf0456c1 Mon Sep 17 00:00:00 2001 From: rocky Date: Thu, 2 May 2019 11:44:20 -0400 Subject: [PATCH 06/11] tidy "not" precedence. --- test/bytecode_2.7_run/04_subscript.pyc | Bin 332 -> 0 bytes test/bytecode_3.6_run/04_subscript.pyc | Bin 263 -> 361 bytes test/simple_source/expression/04_subscript.py | 6 +++++- uncompyle6/semantics/consts.py | 17 +++++++++++------ 4 files changed, 16 insertions(+), 7 deletions(-) delete mode 100644 test/bytecode_2.7_run/04_subscript.pyc diff --git a/test/bytecode_2.7_run/04_subscript.pyc b/test/bytecode_2.7_run/04_subscript.pyc deleted file mode 100644 index 3cd38e7c2ed88a0e40ba6f9424738e4c8d5b4082..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 332 zcmYLC!AiqG6r9Z_R*LcN$!kEc9z-aJDCkKjhzB85icMZ+F-_vW7fi2ymuCt_)Hb-fCFW>ZYx@F)A@oX8ui4i mfyvqVg1aRb-nDYtZe(8{U#^?7tLQ59@mL`>sj=q397lfu{6SR! diff --git a/test/bytecode_3.6_run/04_subscript.pyc b/test/bytecode_3.6_run/04_subscript.pyc index baaf71a487ced9490fdf2e1a545bab0a2ae886c7..6e354680bd9e337cbe6970d5d7d201eb5a58f3e1 100644 GIT binary patch delta 248 zcmZo?ddcKx%*)Fq&38Ix6(a+~V+JI^0%SV?aq$Eok;0I|n8_FgqM3j+V}(;HLn>nm zV;XY`GmvIVV@_joVTje5=qFz9Qo`EAn8K3ERN#}!oWfwkP^cEpkP6f)$&kX5%~V`a z!kWSg;fpaeGd3{-4S>n@faSn^h+GPDFoP!BEtZnhijtQ=Co;Tb1`>X^7#*vG6Qe<* yu?n^ds>KS5whHQw>OiFvJM`H&foh64C+@Wo5dg9{7&#c3fLM%4gi(M=h7kbVgE2z@ delta 148 zcmaFK)XwB*%*)HQ=h&&39}El(kAWBtSb!`CATIU+5-AKRjG2s4AesqCGgg?TGNdx5 zFs3o5Fav3(H0CrW7lv55iGJb=Y9*{qjD>393@OaP44N#rSV~eWN?tMoO?k--B>W~m cG-BfbGKx4ROETK Date: Thu, 2 May 2019 14:01:51 -0400 Subject: [PATCH 07/11] testtrue expr check nuked because of 3.7 --- uncompyle6/semantics/consts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uncompyle6/semantics/consts.py b/uncompyle6/semantics/consts.py index 45774b22..ae6691e1 100644 --- a/uncompyle6/semantics/consts.py +++ b/uncompyle6/semantics/consts.py @@ -336,7 +336,7 @@ TABLE_DIRECT = { 'iflaststmt': ( '%|if %c:\n%+%c%-', 0, 1 ), 'iflaststmtl': ( '%|if %c:\n%+%c%-', 0, 1 ), 'testtrue': ( 'not %p', - (0, 'expr', PRECEDENCE['unary_not']) ), + (0, PRECEDENCE['unary_not']) ), 'ifelsestmt': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ), 'ifelsestmtc': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ), From 0007abf827c080a791d294e74978ee1f3386ace2 Mon Sep 17 00:00:00 2001 From: rocky Date: Fri, 3 May 2019 19:00:25 -0400 Subject: [PATCH 08/11] Fix 3.6+ format string interpolation --- test/simple_source/bug36/01_fstring.py | 9 +++++++++ uncompyle6/semantics/customize36.py | 9 +++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/test/simple_source/bug36/01_fstring.py b/test/simple_source/bug36/01_fstring.py index 1c03b3ae..a15cb516 100644 --- a/test/simple_source/bug36/01_fstring.py +++ b/test/simple_source/bug36/01_fstring.py @@ -30,3 +30,12 @@ chunk2 = 'd' chunk = f'{len(chunk):X}\r\n'.encode('ascii') + chunk \ + b'\r\n' assert chunk == b'3\r\nabc\r\n' + +# From 3.6.8 idlelib/pyshell.py +# Bug was handling ''' +import os +filename = '.' +source = 'foo' +source = (f"__file__ = r'''{os.path.abspath(filename)}'''\n" + + source + "\ndel __file__") +print(source) diff --git a/uncompyle6/semantics/customize36.py b/uncompyle6/semantics/customize36.py index 4a2e2103..24bdf97b 100644 --- a/uncompyle6/semantics/customize36.py +++ b/uncompyle6/semantics/customize36.py @@ -21,6 +21,11 @@ from uncompyle6.semantics.helper import flatten_list from uncompyle6.semantics.consts import ( INDENT_PER_LEVEL, PRECEDENCE, TABLE_DIRECT, TABLE_R) +def escape_format(s): + return s.replace('\r', '\\r').\ + replace('\n', '\\n').\ + replace("'''", '"""') + ####################### # Python 3.6+ Changes # ####################### @@ -347,7 +352,7 @@ def customize_for_version36(self, version): def n_formatted_value(node): if node[0] == 'LOAD_CONST': - self.write(node[0].attr) + self.write(escape_format(node[0].attr)) self.prune() else: self.default(node) @@ -375,7 +380,7 @@ def customize_for_version36(self, version): f_conversion(node) fmt_node = node.data[3] if fmt_node == 'expr' and fmt_node[0] == 'LOAD_CONST': - node.string = fmt_node[0].attr.replace('\r', '\\r').replace('\n', '\\n') + node.string = escape_format(fmt_node[0].attr) else: node.string = fmt_node From 6e753b8743496e217cdd7195b57a924f722d6c2c Mon Sep 17 00:00:00 2001 From: rocky Date: Fri, 3 May 2019 22:38:09 -0400 Subject: [PATCH 09/11] Handle 3.7 format_value tuples --- uncompyle6/semantics/customize36.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/uncompyle6/semantics/customize36.py b/uncompyle6/semantics/customize36.py index 24bdf97b..79d185a2 100644 --- a/uncompyle6/semantics/customize36.py +++ b/uncompyle6/semantics/customize36.py @@ -352,7 +352,11 @@ def customize_for_version36(self, version): def n_formatted_value(node): if node[0] == 'LOAD_CONST': - self.write(escape_format(node[0].attr)) + value = node[0].attr + if isinstance(value, tuple): + self.write(node[0].attr) + else: + self.write(escape_format(node[0].attr)) self.prune() else: self.default(node) From 46ca21596f1a6fc2d9b4478bfcd1af4e53a59500 Mon Sep 17 00:00:00 2001 From: rocky Date: Fri, 3 May 2019 22:47:19 -0400 Subject: [PATCH 10/11] Get ready for release 3.3.2 --- NEWS.md | 15 +++++++++++++++ uncompyle6/version.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 05fccc36..d3b98d04 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,18 @@ +3.3.2 2019-05-03 Better Friday +============================== + +As before, lots of decomplation bugs fixed. The focus has primarily +been on Python 3.6. We can now parse the entire 3.6.8 Python library +and verify that without an error. The same is true for 3.5.8. A number +of the bugs fixed though are not contained to these versions. In fact +some span back as far as 2.x + +But as before, many more remain in the 3.7 and 3.8 range which will +get addressed in future releases + +Pypy 3.6 support was started. Pypy 3.x detection fixed (via xdis) + + 3.3.1 2019-04-19 Good Friday ========================== diff --git a/uncompyle6/version.py b/uncompyle6/version.py index efedabf1..36971cbe 100644 --- a/uncompyle6/version.py +++ b/uncompyle6/version.py @@ -12,4 +12,4 @@ # along with this program. If not, see . # This file is suitable for sourcing inside bash as # well as importing into Python -VERSION='3.3.1' # noqa +VERSION='3.3.2' # noqa From 82a3419eb21af7b8aa7a0ac2827487f097292d71 Mon Sep 17 00:00:00 2001 From: rocky Date: Fri, 3 May 2019 23:11:42 -0400 Subject: [PATCH 11/11] Administrivia: bump testing versions --- admin-tools/pyenv-newer-versions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin-tools/pyenv-newer-versions b/admin-tools/pyenv-newer-versions index a27b2e0a..8155cd0c 100644 --- a/admin-tools/pyenv-newer-versions +++ b/admin-tools/pyenv-newer-versions @@ -5,4 +5,4 @@ if [[ $0 == ${BASH_SOURCE[0]} ]] ; then echo "This script should be *sourced* rather than run directly through bash" exit 1 fi -export PYVERSIONS='3.6.8 3.7.2 2.6.9 3.3.7 2.7.15 3.2.6 3.1.5 3.4.8' +export PYVERSIONS='3.6.8 3.7.3 2.6.9 3.3.7 2.7.16 3.2.6 3.1.5 3.4.8'