From 9462e33f485c038083d46ead483fb926aa8339f5 Mon Sep 17 00:00:00 2001 From: rocky Date: Tue, 17 May 2016 04:00:54 -0400 Subject: [PATCH] Fix Python 3.x bugs * class definitions made via closures * Add "make check-short" to top-level * parse3.py: Python 3.3 uses STORE_LOGALS --- Makefile | 4 ++ test/bytecode_3.2/09_class_closure.pyc | Bin 0 -> 722 bytes test/bytecode_3.5/09_class_closure.pyc | Bin 0 -> 572 bytes test/simple_source/def/09_class_closure.py | 7 +++ uncompyle6/parser.py | 5 ++ uncompyle6/parsers/parse3.py | 14 ++++- uncompyle6/semantics/fragments.py | 22 ++++++- uncompyle6/semantics/pysource.py | 63 ++++++++++++++------- 8 files changed, 94 insertions(+), 21 deletions(-) create mode 100644 test/bytecode_3.2/09_class_closure.pyc create mode 100644 test/bytecode_3.5/09_class_closure.pyc create mode 100644 test/simple_source/def/09_class_closure.py diff --git a/Makefile b/Makefile index f4af0893..01ae79ca 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,10 @@ check: @PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2`; \ $(MAKE) check-$$PYTHON_VERSION +# Run all quick tests +check-short: pytest + $(MAKE) -C test check-short + #: Tests for Python 2.7, 3.3 and 3.4 check-2.7 check-3.3 check-3.4: pytest $(MAKE) -C test $@ diff --git a/test/bytecode_3.2/09_class_closure.pyc b/test/bytecode_3.2/09_class_closure.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c732aa66d1cc9a24884be99cc2ec0ba968d910be GIT binary patch literal 722 zcmbtSu};G<5IrY=P(&vt21cY#lof;!1Bd}B3j#mzI5>*qAyS!&fS==^xMxQt zuvJ*P7u)yl-rYH7Z<1Yoo!u)MEy4RsHaB4?iQb6jyb*LaP54H^kA=e)Tgv7R8E!s^ zDgov@^AqZbRw=y-S|S#-d>xD=NB|4LOQLJdN`i(gge>!9Bn&PVzR0%E<^Z2W9KK)_ zz*WL>5Xb|1aP7k)RPRx-)28ywK$^o8Ip*Q_2@DB~rLhq{u_NemDAxn!P>?huW5&lgd$CEFij#RfTa<{lZT51b|=&1)0JmivkRZ8(7cqsJZLFi?P$ri1Nsk_OcrJfY^ulQrU zc-*W1!jo?{P(N_Uyd5U*&6`bjyWRHb+wsLa5q%)n;h{P~nk_(zN6;hD1cgWEp`Qq} zIhYq9eWcj~oIMjw1@pmta{Q9$!KZscE5X`v0@^xQ$YJ&Y3KkFoJ^C|e;#2BF9xQi~ zd@jpE-l#=1$ciM+%rF`yya@-NJaijr7+#20L^GgZeBcX+aVV@`T+Twz21aF5+bGH_ zrO|EV3mCl8=YhG-=b4hGsC1(Gshai=k7Sa?#sCXbX%)>EwkhSUlCr^eZU?dK{kvgQ z6USj-TPWikD%+MaFVYG&(src0tKzJFqc<>6^VCpi{=%--O;lElxZ&J7LT@^bZQ?d6 qDMPPjbN83rDcj$hxj)8pNc4Q&#ffj?&f1Bse|WYA^`1kgW7!wU7;%{Z literal 0 HcmV?d00001 diff --git a/test/simple_source/def/09_class_closure.py b/test/simple_source/def/09_class_closure.py new file mode 100644 index 00000000..800686aa --- /dev/null +++ b/test/simple_source/def/09_class_closure.py @@ -0,0 +1,7 @@ +# Bug from 3.4 functools.py +# class is made with a closure not a makefunc +def cmp_to_key(mycmp): + class K(object): + def __ne__(self, other): + return mycmp(self.obj, other.obj) + return K diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py index a8285d7a..c31fb6e6 100644 --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -471,6 +471,11 @@ def get_python_parser(version, debug_parser, compile_mode='exec'): p = parse3.Python32Parser(debug_parser) else: p = parse3.Python32ParserSingle(debug_parser) + elif version == 3.3: + if compile_mode == 'exec': + p = parse3.Python33Parser(debug_parser) + else: + p = parse3.Python33ParserSingle(debug_parser) elif version == 3.4: if compile_mode == 'exec': p = parse3.Python34Parser(debug_parser) diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index 8912400e..3e45b6ab 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -398,6 +398,8 @@ class Python3Parser(PythonParser): for i in range(i+1, len(tokens)): if tokens[i].type.startswith('MAKE_FUNCTION'): break + elif tokens[i].type.startswith('MAKE_CLOSURE'): + break pass assert i < len(tokens), "build_class needs to find MAKE_FUNCTION" assert tokens[i+1].type == 'LOAD_CONST', \ @@ -592,7 +594,14 @@ class Python3Parser(PythonParser): class Python32Parser(Python3Parser): def p_32(self, args): """ - # Store locals is only used in Python 3.2 + # Store locals is only in Python 3.2 and 3.3 + designator ::= STORE_LOCALS + """ + +class Python33Parser(Python3Parser): + def p_33(self, args): + """ + # Store locals is only in Python 3.2 and 3.3 designator ::= STORE_LOCALS """ @@ -628,6 +637,9 @@ class Python32ParserSingle(Python32Parser, PythonParserSingle): pass +class Python33ParserSingle(Python33Parser, PythonParserSingle): + pass + class Python34ParserSingle(Python34Parser, PythonParserSingle): pass diff --git a/uncompyle6/semantics/fragments.py b/uncompyle6/semantics/fragments.py index 9887912e..fb8c046a 100644 --- a/uncompyle6/semantics/fragments.py +++ b/uncompyle6/semantics/fragments.py @@ -666,9 +666,29 @@ class FragmentsWalker(pysource.SourceWalker, object): buildclass = node[0] if buildclass[1][0] == 'kwargs': subclass = buildclass[1][1].attr + subclass_info = node[0] + elif buildclass[1][0] == 'load_closure': + # Python 3 with closures not functions + load_closure = buildclass[1] + if hasattr(load_closure[-3], 'attr'): + # Python 3.3 classes with closures work like this. + # Note have to test before 3.2 case because + # index -2 also has an attr. + subclass = load_closure[-3].attr + elif hasattr(load_closure[-2], 'attr'): + # Python 3.2 works like this + subclass = load_closure[-2].attr + else: + raise 'Internal Error n_classdef: cannot find class body' + if hasattr(buildclass[3], '__len__'): + subclass_info = buildclass[3] + elif hasattr(buildclass[2], '__len__'): + subclass_info = buildclass[2] + else: + raise 'Internal Error n_classdef: cannot superclass name' else: subclass = buildclass[1][0].attr - subclass_info = node[0] + subclass_info = node[0] else: buildclass = node[0] build_list = buildclass[1][0] diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index 4d6bec23..83424243 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -1141,7 +1141,6 @@ class SourceWalker(GenericASTTraversal, object): def n_classdef(self, node): # class definition ('class X(A,B,C):') - cclass = self.currentclass if self.version > 3.0: @@ -1149,9 +1148,29 @@ class SourceWalker(GenericASTTraversal, object): buildclass = node[0] if buildclass[1][0] == 'kwargs': subclass = buildclass[1][1].attr + subclass_info = node[0] + elif buildclass[1][0] == 'load_closure': + # Python 3 with closures not functions + load_closure = buildclass[1] + if hasattr(load_closure[-3], 'attr'): + # Python 3.3 classes with closures work like this. + # Note have to test before 3.2 case because + # index -2 also has an attr. + subclass = load_closure[-3].attr + elif hasattr(load_closure[-2], 'attr'): + # Python 3.2 works like this + subclass = load_closure[-2].attr + else: + raise 'Internal Error n_classdef: cannot find class body' + if hasattr(buildclass[3], '__len__'): + subclass_info = buildclass[3] + elif hasattr(buildclass[2], '__len__'): + subclass_info = buildclass[2] + else: + raise 'Internal Error n_classdef: cannot superclass name' else: subclass = buildclass[1][0].attr - subclass_info = node[0] + subclass_info = node[0] else: buildclass = node if (node == 'classdefdeco2') else node[0] build_list = buildclass[1][0] @@ -1208,26 +1227,32 @@ class SourceWalker(GenericASTTraversal, object): self.write(')') def print_super_classes3(self, node): - n = len(node)-1 - assert node[n].type.startswith('CALL_FUNCTION') + if node.type != 'expr': + assert node[n].type.startswith('CALL_FUNCTION') + for i in range(n-2, 0, -1): + if not node[i].type in ['expr', 'LOAD_CLASSNAME']: + break + pass - for i in range(n-2, 0, -1): - if not node[i].type in ['expr', 'LOAD_CLASSNAME']: - break + if i == n-2: + return + line_separator = ', ' + sep = '' + self.write('(') + i += 2 + while i < n: + value = self.traverse(node[i]) + i += 1 + self.write(sep, value) + sep = line_separator + pass + pass + else: + self.write('(') + value = self.traverse(node[0]) + self.write(value) pass - - if i == n-2: - return - self.write('(') - line_separator = ', ' - sep = '' - i += 2 - while i < n: - value = self.traverse(node[i]) - i += 1 - self.write(sep, value) - sep = line_separator self.write(')')