diff --git a/test/bytecode_3.6_run/10_argparse.pyc b/test/bytecode_3.6_run/10_argparse.pyc new file mode 100644 index 00000000..891345a8 Binary files /dev/null and b/test/bytecode_3.6_run/10_argparse.pyc differ diff --git a/test/simple_source/bug36/10_argparse.py b/test/simple_source/bug36/10_argparse.py new file mode 100644 index 00000000..14685606 --- /dev/null +++ b/test/simple_source/bug36/10_argparse.py @@ -0,0 +1,37 @@ +# From 3.6.4 pickletools.py +# Bug in 3.6 CALL_FUNCTION_KW in an not exponentially + +import argparse +parser = argparse.ArgumentParser( + description='disassemble one or more pickle files') +parser.add_argument( + 'pickle_file', type=argparse.FileType('br'), + nargs='*', help='the pickle file') +parser.add_argument( + '-m', '--memo', action='store_true', + help='preserve memo between disassemblies') +parser.add_argument( + '-l', '--indentlevel', default=4, type=int, + help='the number of blanks by which to indent a new MARK level') +parser.add_argument( + '-a', '--annotate', action='store_true', + help='annotate each line with a short opcode description') +parser.add_argument( + '-p', '--preamble', default="==> {name} <==", + help='if more than one pickle file is specified, print this before' + ' each disassembly') +parser.add_argument( + '-t', '--test', action='store_true', + help='run self-test suite') +parser.add_argument( + '-v', action='store_true', + help='run verbosely; only affects self-test run') +args = parser.parse_args() + +assert args.annotate == False +assert args.indentlevel == 4 +assert args.memo == False +assert args.pickle_file == [] +assert args.preamble == '==> {name} <==' +assert args.test == False +assert args.v == False diff --git a/uncompyle6/parsers/parse36.py b/uncompyle6/parsers/parse36.py index 254fd929..f3925400 100644 --- a/uncompyle6/parsers/parse36.py +++ b/uncompyle6/parsers/parse36.py @@ -205,11 +205,9 @@ class Python36Parser(Python35Parser): self.add_unique_rule('expr ::= async_call', token.kind, uniq_param, customize) if opname.startswith('CALL_FUNCTION_KW'): - self.addRule("expr ::= call_kw", nop_func) + self.addRule("expr ::= call_kw36", nop_func) values = 'expr ' * token.attr - rule = 'call_kw ::= expr kwargs_36 {token.kind}'.format(**locals()) - self.addRule(rule, nop_func) - rule = 'kwargs_36 ::= {values} LOAD_CONST'.format(**locals()) + rule = "call_kw36 ::= expr {values} LOAD_CONST {opname}".format(**locals()) self.add_unique_rule(rule, token.kind, token.attr, customize) elif opname == 'CALL_FUNCTION_EX_KW': self.addRule("""expr ::= call_ex_kw diff --git a/uncompyle6/semantics/customize.py b/uncompyle6/semantics/customize.py index d85a24f1..a0d34f48 100644 --- a/uncompyle6/semantics/customize.py +++ b/uncompyle6/semantics/customize.py @@ -407,8 +407,9 @@ def customize_for_version(self, is_pypy, version): # Value 100 is important; it is exactly # module/function precidence. - PRECEDENCE['call_kw'] = 100 - PRECEDENCE['call_ex'] = 100 + PRECEDENCE['call_kw'] = 100 + PRECEDENCE['call_kw36'] = 100 + PRECEDENCE['call_ex'] = 100 TABLE_DIRECT.update({ 'tryfinally36': ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', @@ -731,40 +732,43 @@ def customize_for_version(self, is_pypy, version): # return # self.n_kwargs_only_36 = kwargs_only_36 - def kwargs_36(node): - self.write('(') - keys = node[-1].attr + def n_call_kw36(node): + self.template_engine(("%c(", 0), node) + keys = node[-2].attr num_kwargs = len(keys) - num_posargs = len(node) - (num_kwargs + 1) + num_posargs = len(node) - (num_kwargs + 2) n = len(node) assert n >= len(keys)+1, \ 'not enough parameters keyword-tuple values' - # try: - # assert n >= len(keys)+1, \ - # 'not enough parameters keyword-tuple values' - # except: - # from trepan.api import debug; debug() sep = '' - # FIXME: adjust output for line breaks? - for i in range(num_posargs): + + line_number = self.line_number + for i in range(1, num_posargs): self.write(sep) self.preorder(node[i]) - sep = ', ' + if line_number != self.line_number: + sep = ",\n" + self.indent + " " + else: + sep = ", " + line_number = self.line_number i = num_posargs j = 0 # FIXME: adjust output for line breaks? - while i < n-1: + while i < n-2: self.write(sep) self.write(keys[j] + '=') self.preorder(node[i]) - sep=', ' + if line_number != self.line_number: + sep = ",\n" + self.indent + " " + else: + sep = ", " i += 1 j += 1 self.write(')') self.prune() return - self.n_kwargs_36 = kwargs_36 + self.n_call_kw36 = n_call_kw36 def starred(node): l = len(node)