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 00000000..c732aa66 Binary files /dev/null and b/test/bytecode_3.2/09_class_closure.pyc differ diff --git a/test/bytecode_3.5/09_class_closure.pyc b/test/bytecode_3.5/09_class_closure.pyc new file mode 100644 index 00000000..21f97a1e Binary files /dev/null and b/test/bytecode_3.5/09_class_closure.pyc differ 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(')')