diff --git a/test/bytecode_3.6/01_call_function.pyc b/test/bytecode_3.6/01_call_function.pyc index c18dbcbb..82336180 100644 Binary files a/test/bytecode_3.6/01_call_function.pyc and b/test/bytecode_3.6/01_call_function.pyc differ diff --git a/test/simple_source/bug36/01_call_function.py b/test/simple_source/bug36/01_call_function.py index 13870f52..a758e5cb 100644 --- a/test/simple_source/bug36/01_call_function.py +++ b/test/simple_source/bug36/01_call_function.py @@ -2,4 +2,9 @@ # See https://github.com/rocky/python-uncompyle6/issues/58 # CALL_FUNCTION_EX takes 2 to 3 arguments on the stack: the function, the tuple of positional arguments, # and optionally the dict of keyword arguments if bit 0 of oparg is 1. -a(*[]) +from foo import f, dialect, args, kwds, reader + +f(*[]) + +# From Python 3.6 csv.py +x = reader(f, dialect, *args, **kwds) diff --git a/uncompyle6/parsers/parse36.py b/uncompyle6/parsers/parse36.py index be0b6d66..ac4b42c1 100644 --- a/uncompyle6/parsers/parse36.py +++ b/uncompyle6/parsers/parse36.py @@ -187,6 +187,8 @@ class Python36Parser(Python35Parser): self.addRule("""expr ::= call_ex_kw expr ::= call_ex_kw2 expr ::= call_ex_kw3 + expr ::= call_ex_kw4 + call_ex_kw ::= expr expr build_map_unpack_with_call CALL_FUNCTION_EX_KW call_ex_kw2 ::= expr @@ -194,6 +196,10 @@ class Python36Parser(Python35Parser): build_map_unpack_with_call CALL_FUNCTION_EX_KW call_ex_kw3 ::= expr + build_tuple_unpack_with_call + expr + CALL_FUNCTION_EX_KW + call_ex_kw4 ::= expr expr expr CALL_FUNCTION_EX_KW diff --git a/uncompyle6/semantics/customize.py b/uncompyle6/semantics/customize.py index b3c4489f..56ff3eec 100644 --- a/uncompyle6/semantics/customize.py +++ b/uncompyle6/semantics/customize.py @@ -455,6 +455,33 @@ def customize_for_version(self, is_pypy, version): self.n_call_ex_kw2 = call_ex_kw2 def call_ex_kw3(node): + """Handle CALL_FUNCTION_EX 1 (have KW) but without + BUILD_MAP_UNPACK_WITH_CALL""" + self.preorder(node[0]) + self.write('(') + args = node[1][0] + if args == 'expr': + args = args[0] + if args == 'tuple': + if self.call36_tuple(args) > 0: + self.write(', ') + pass + pass + + self.write('*') + self.preorder(node[1][1]) + self.write(', ') + + kwargs = node[2] + if kwargs == 'expr': + kwargs = kwargs[0] + self.write('**') + self.preorder(kwargs) + self.write(')') + self.prune() + self.n_call_ex_kw3 = call_ex_kw3 + + def call_ex_kw4(node): """Handle CALL_FUNCTION_EX 2 (have KW) but without BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL""" self.preorder(node[0]) @@ -478,8 +505,7 @@ def customize_for_version(self, is_pypy, version): self.preorder(kwargs) self.write(')') self.prune() - self.n_call_ex_kw3 = call_ex_kw3 - + self.n_call_ex_kw4 = call_ex_kw4 def call36_tuple(node): """ @@ -665,7 +691,7 @@ def customize_for_version(self, is_pypy, version): self.prune() return self.n_return_closure = return_closure - pass # version > 3.6 - pass # version > 3.4 - pass # version > 3.0 + pass # version >= 3.6 + pass # version >= 3.4 + pass # version >= 3.0 return diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index 4b3c2ca1..bf57a753 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -536,278 +536,8 @@ class SourceWalker(GenericASTTraversal, object): pass self.n_unmapexpr = unmapexpr - if version >= 3.6: - ######################## - # Python 3.6+ Additions - ####################### - - TABLE_DIRECT.update({ - 'fstring_expr': ( "{%c%{conversion}}", 0), - 'fstring_single': ( "f'{%c%{conversion}}'", 0), - 'fstring_multi': ( "f'%c'", 0), - 'func_args36': ( "%c(**", 0), - 'try_except36': ( '%|try:\n%+%c%-%c\n\n', 1, 2 ), - 'unpack_list': ( '*%c', (0, 'list') ), - 'starred': ( '*%c', (0, 'expr') ), - 'call_ex' : ( - '%c(%c)', - (0, 'expr'), 1), - 'call_ex_kw' : ( - '%c(%c)', - (0, 'expr'), 2), - - }) - - TABLE_R.update({ - 'CALL_FUNCTION_EX': ('%c(*%P)', 0, (1, 2, ', ', 100)), - # Not quite right - 'CALL_FUNCTION_EX_KW': ('%c(**%C)', 0, (2, 3, ',')), - }) - - def build_unpack_tuple_with_call(node): - - if node[0] == 'expr': - tup = node[0][0] - else: - tup = node[0] - pass - assert tup == 'tuple' - self.call36_tuple(tup) - - buwc = node[-1] - assert buwc.kind.startswith('BUILD_TUPLE_UNPACK_WITH_CALL') - for n in node[1:-1]: - self.f.write(', *') - self.preorder(n) - pass - self.prune() - return - self.n_build_tuple_unpack_with_call = build_unpack_tuple_with_call - - def build_unpack_map_with_call(node): - n = node[0] - if n == 'expr': - n = n[0] - if n == 'dict': - self.call36_dict(n) - first = 1 - sep = ', **' - else: - first = 0 - sep = '**' - for n in node[first:-1]: - self.f.write(sep) - self.preorder(n) - sep = ', **' - pass - self.prune() - return - self.n_build_map_unpack_with_call = build_unpack_map_with_call - - def call_ex_kw2(node): - """Handle CALL_FUNCTION_EX 2 (have KW) but with - BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL""" - - # This is weird shit. Thanks Python! - self.preorder(node[0]) - self.write('(') - - assert node[1] == 'build_tuple_unpack_with_call' - btuwc = node[1] - tup = btuwc[0] - if tup == 'expr': - tup = tup[0] - assert tup == 'tuple' - self.call36_tuple(tup) - assert node[2] == 'build_map_unpack_with_call' - - self.write(', ') - d = node[2][0] - if d == 'expr': - d = d[0] - assert d == 'dict' - self.call36_dict(d) - - args = btuwc[1] - self.write(', *') - self.preorder(args) - - self.write(', **') - star_star_args = node[2][1] - if star_star_args == 'expr': - star_star_args = star_star_args[0] - self.preorder(star_star_args) - self.write(')') - self.prune() - self.n_call_ex_kw2 = call_ex_kw2 - - def call_ex_kw3(node): - """Handle CALL_FUNCTION_EX 2 (have KW) but without - BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL""" - self.preorder(node[0]) - self.write('(') - args = node[1][0] - if args == 'tuple': - if self.call36_tuple(args) > 0: - self.write(', ') - pass - pass - else: - self.write('*') - self.preorder(args) - self.write(', ') - pass - - kwargs = node[2] - if kwargs == 'expr': - kwargs = kwargs[0] - self.write('**') - self.preorder(kwargs) - self.write(')') - self.prune() - self.n_call_ex_kw3 = call_ex_kw3 - - - def call36_tuple(node): - """ - A tuple used in a call, these are like normal tuples but they - don't have the enclosing parenthesis. - """ - assert node == 'tuple' - # Note: don't iterate over last element which is a - # BUILD_TUPLE... - flat_elems = flatten_list(node[:-1]) - - self.indent_more(INDENT_PER_LEVEL) - sep = '' - - for elem in flat_elems: - if elem in ('ROT_THREE', 'EXTENDED_ARG'): - continue - assert elem == 'expr' - line_number = self.line_number - value = self.traverse(elem) - if line_number != self.line_number: - sep += '\n' + self.indent + INDENT_PER_LEVEL[:-1] - self.write(sep, value) - sep = ', ' - - self.indent_less(INDENT_PER_LEVEL) - return len(flat_elems) - self.call36_tuple = call36_tuple - - def call36_dict(node): - """ - A dict used in a call_ex_kw2, which are a dictionary items expressed - in a call. This should format to: - a=1, b=2 - In other words, no braces, no quotes around keys and ":" becomes - "=". - - We will source-code use line breaks to guide us when to break. - """ - p = self.prec - self.prec = 100 - - self.indent_more(INDENT_PER_LEVEL) - sep = INDENT_PER_LEVEL[:-1] - line_number = self.line_number - - assert node[0].kind.startswith('kvlist') - # Python 3.5+ style key/value list in dict - kv_node = node[0] - l = list(kv_node) - i = 0 - # Respect line breaks from source - while i < len(l): - self.write(sep) - name = self.traverse(l[i], indent='') - # Strip off beginning and trailing quotes in name - name = name[1:-1] - if i > 0: - line_number = self.indent_if_source_nl(line_number, - self.indent + INDENT_PER_LEVEL[:-1]) - line_number = self.line_number - self.write(name, '=') - value = self.traverse(l[i+1], indent=self.indent+(len(name)+2)*' ') - self.write(value) - sep = "," - if line_number != self.line_number: - sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1] - line_number = self.line_number - i += 2 - pass - self.prec = p - self.indent_less(INDENT_PER_LEVEL) - return - self.call36_dict = call36_dict - - - FSTRING_CONVERSION_MAP = {1: '!s', 2: '!r', 3: '!a'} - - def f_conversion(node): - node.conversion = FSTRING_CONVERSION_MAP.get(node.data[1].attr, '') - - def fstring_expr(node): - f_conversion(node) - self.default(node) - self.n_fstring_expr = fstring_expr - - def fstring_single(node): - f_conversion(node) - self.default(node) - self.n_fstring_single = fstring_single - - # def kwargs_only_36(node): - # keys = node[-1].attr - # num_kwargs = len(keys) - # values = node[:num_kwargs] - # for i, (key, value) in enumerate(zip(keys, values)): - # self.write(key + '=') - # self.preorder(value) - # if i < num_kwargs: - # self.write(',') - # self.prune() - # return - # self.n_kwargs_only_36 = kwargs_only_36 - - def kwargs_36(node): - self.write('(') - keys = node[-1].attr - num_kwargs = len(keys) - num_posargs = len(node) - (num_kwargs + 1) - n = len(node) - assert n >= len(keys)+2 - sep = '' - # FIXME: adjust output for line breaks? - for i in range(num_posargs): - self.write(sep) - self.preorder(node[i]) - sep = ', ' - - i = num_posargs - j = 0 - # FIXME: adjust output for line breaks? - while i < n-1: - self.write(sep) - self.write(keys[j] + '=') - self.preorder(node[i]) - i += 1 - j += 1 - self.write(')') - self.prune() - return - self.n_kwargs_36 = kwargs_36 - - - def return_closure(node): - # Nothing should be output here - self.prune() - return - self.n_return_closure = return_closure - pass # version > 3.6 - pass # version > 3.4 - pass # version > 3.0 + pass # version >= 3.4 + pass # version >= 3.0 return f = property(lambda s: s.params['f'],