示例#1
0
def customize_for_version38(self, version):

    # FIXME: pytest doesn't add proper keys in testing. Reinstate after we have fixed pytest.
    # for lhs in 'for forelsestmt forelselaststmt '
    #             'forelselaststmtl tryfinally38'.split():
    #     del TABLE_DIRECT[lhs]

    TABLE_DIRECT.update({
        'async_for_stmt38': ('%|async for %c in %c:\n%+%c%-%-\n\n',
                             (7, 'store'), (0, 'expr'), (8, 'for_block')),
        'async_forelse_stmt38':
        ('%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', (7, 'store'),
         (0, 'expr'), (8, 'for_block'), (-1, 'else_suite')),
        'async_with_stmt38': ('%|async with %c:\n%+%|%c%-', (0, 'expr'), 7),
        'async_with_as_stmt38':
        ('%|async with %c as %c:\n%+%|%c%-', (0, 'expr'), (6, 'store'),
         (7, 'suite_stmts')),
        'except_handler38': ('%c', (2, 'except_stmts')),
        'except_handler38a': ('%c', (-2, 'stmts')),
        'except_ret38a': ('return %c', (4, 'expr')),

        # Note: there is a suite_stmts_opt which seems
        # to be bookkeeping which is not expressed in source code
        'except_ret38': ('%|return %c\n', (1, 'expr')),
        'for38': ('%|for %c in %c:\n%+%c%-\n\n', (2, 'store'), (0, 'expr'),
                  (3, 'for_block')),
        'forelsestmt38': ('%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
                          (2, 'store'), (0, 'expr'), (3, 'for_block'), -2),
        'forelselaststmt38': ('%|for %c in %c:\n%+%c%-%|else:\n%+%c%-',
                              (2, 'store'), (0, 'expr'), (3, 'for_block'), -2),
        'forelselaststmtl38':
        ('%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', (2, 'store'),
         (0, 'expr'), (3, 'for_block'), -2),
        'ifpoplaststmtl':
        ('%|if %c:\n%+%c%-', (0, "testexpr"), (2, "c_stmts")),
        'whilestmt38': ('%|while %c:\n%+%c%-\n\n', (1, 'testexpr'),
                        2),  # "l_stmts" or "pass"
        'whileTruestmt38': ('%|while True:\n%+%c%-\n\n',
                            1),  # "l_stmts" or "pass"
        'try_elsestmtl38':
        ('%|try:\n%+%c%-%c%|else:\n%+%c%-', (1, 'suite_stmts_opt'),
         (3, 'except_handler38'), (5, 'else_suitel')),
        'try_except38': ('%|try:\n%+%c\n%-%|except:\n%|%-%c\n\n',
                         (-2, 'suite_stmts_opt'), (-1, 'except_handler38a')),
        'try_except_ret38':
        ('%|try:\n%+%|return %c%-\n%|except:\n%+%|%c%-\n\n', (1, 'expr'),
         (-1, 'except_ret38a')),
        'tryfinally38rstmt': ('%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
                              (3, 'returns'), 6),
        'tryfinally38stmt': ('%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
                             (1, "suite_stmts_opt"), (6, "suite_stmts_opt")),
        'tryfinally38astmt': ('%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
                              (2, "suite_stmts_opt"), (8, "suite_stmts_opt")),
        "named_expr": (  # AKA "walrus operator"
            "%c := %c", (2, "store"), (0, "expr"))
    })
示例#2
0
def customize_for_version(self, is_pypy, version):
    if is_pypy:
        ########################
        # PyPy changes
        #######################
        TABLE_DIRECT.update({
            'assert_pypy': ('%|assert %c\n', 1),
            'assert2_pypy': ('%|assert %c, %c\n', 1, 4),
            'try_except_pypy': ('%|try:\n%+%c%-%c\n\n', 1, 2),
            'tryfinallystmt_pypy':
            ('%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', 1, 3),
            'assign3_pypy': ('%|%c, %c, %c = %c, %c, %c\n', 5, 4, 3, 0, 1, 2),
            'assign2_pypy': ('%|%c, %c = %c, %c\n', 3, 2, 0, 1),
        })
    else:
        ########################
        # Without PyPy
        #######################
        TABLE_DIRECT.update({
            'assert': ('%|assert %c\n', 0),
            'assert2': ('%|assert %c, %c\n', 0, 3),
            'try_except': ('%|try:\n%+%c%-%c\n\n', 1, 3),
            'assign2': ('%|%c, %c = %c, %c\n', 3, 4, 0, 1),
            'assign3': ('%|%c, %c, %c = %c, %c, %c\n', 5, 6, 7, 0, 1, 2),
        })

    if version >= 3.2:
        TABLE_DIRECT.update({
            'del_deref_stmt': ('%|del %c\n', 0),
            'DELETE_DEREF': ('%{pattr}', 0),
        })
    from decompyle3.semantics.customize3 import customize_for_version3
    customize_for_version3(self, version)

    return
示例#3
0
def customize_for_version(self, is_pypy, version):
    if is_pypy:
        ########################
        # PyPy changes
        #######################
        TABLE_DIRECT.update({
            "assert_pypy": ("%|assert %c\n", 1),
            "assert2_pypy": ("%|assert %c, %c\n", 1, 4),
            "try_except_pypy": ("%|try:\n%+%c%-%c\n\n", 1, 2),
            "tryfinallystmt_pypy":
            ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n", 1, 3),
            "assign3_pypy": ("%|%c, %c, %c = %c, %c, %c\n", 5, 4, 3, 0, 1, 2),
            "assign2_pypy": ("%|%c, %c = %c, %c\n", 3, 2, 0, 1),
        })
    else:
        ########################
        # Without PyPy
        #######################
        TABLE_DIRECT.update({
            # "assert" and "assert_expr" are added via transform rules.
            "assert": ("%|assert %c\n", 0),
            "assert2": ("%|assert %c, %c\n", 0, 3),
            # Created only via transformation
            "assertnot": ("%|assert not %p\n", (0, PRECEDENCE["unary_not"])),
            "assert2not": (
                "%|assert not %p, %c\n",
                (0, PRECEDENCE["unary_not"]),
                3,
            ),
            "assign2": ("%|%c, %c = %c, %c\n", 3, 4, 0, 1),
            "assign3": ("%|%c, %c, %c = %c, %c, %c\n", 5, 6, 7, 0, 1, 2),
            "try_except": ("%|try:\n%+%c%-%c\n\n", 1, 3),
        })

    if version >= 3.2:
        TABLE_DIRECT.update({
            "del_deref_stmt": ("%|del %c\n", 0),
            "DELETE_DEREF": ("%{pattr}", 0),
        })
    from decompyle3.semantics.customize3 import customize_for_version3

    customize_for_version3(self, version)

    return
示例#4
0
def customize_for_version38(self, version):

    # FIXME: pytest doesn't add proper keys in testing. Reinstate after we have fixed pytest.
    # for lhs in 'for forelsestmt forelselaststmt '
    #             'forelselaststmtc tryfinally38'.split():
    #     del TABLE_DIRECT[lhs]

    TABLE_DIRECT.update({
        "async_for_stmt38":  (
            "%|async for %c in %c:\n%+%c%-%-\n\n",
            (7, "store"), (0, "expr"), (8, "for_block") ),

        "async_forelse_stmt38":  (
            "%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
            (7, "store"), (0, "expr"), (8, "for_block"), (-1, "else_suite") ),

        "async_with_stmt38": (
            "%|async with %c:\n%+%|%c%-",
            (0, "expr"), 7),

        "async_with_as_stmt38":  (
            "%|async with %c as %c:\n%+%|%c%-",
            (0, "expr"), (6, "store"),
            (7, "suite_stmts")
        ),
        "c_forelsestmt38":    (
            "%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
            (2, "store"),
            (0, "expr"),
            (3, "for_block"), -1 ),
        "except_cond1a": (
            "%|except %c:\n", (1, "expr"),
            ),
        "except_handler38": (
            "%c", (2, "except_stmts") ),

        "except_handler38a": (
            "%c", (-2, "stmts") ),

        "except_handler38c": (
            "%c%+%c%-",
            (1, "except_cond1a"),
            (2, "except_stmts"),
        ),

        "except_ret38a": (
            "return %c", (4, "expr") ),

        # Note: there is a suite_stmts_opt which seems
        # to be bookkeeping which is not expressed in source code
        "except_ret38":  ( "%|return %c\n", (1, "expr") ),

        "for38":            (
            "%|for %c in %c:\n%+%c%-\n\n",
            (2, "store"),
            (0, "expr"),
            (3, "for_block") ),
        "forelsestmt38":    (
            "%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
            (2, "store"),
            (0, "expr"),
            (3, "for_block"), -1 ),
        "forelselaststmt38": (
            "%|for %c in %c:\n%+%c%-%|else:\n%+%c%-",
            (2, "store"),
            (0, "expr"),
            (3, "for_block"), -2 ),
        "forelselaststmtc38":	(
            "%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
            (2, "store"),
            (0, "expr"),
            (3, "for_block"), -2 ),

        "ifpoplaststmtc": ( "%|if %c:\n%+%c%-",
                            (0, "testexpr"),
                            (2, "c_stmts" ) ),

        "pop_return":  ( "%|return %c\n", (1, "ret_expr") ),
        "popb_return": ( "%|return %c\n", (0, "ret_expr") ),
        "pop_ex_return": (
            "%|return %c\n", (0, "ret_expr")
        ),

        "whilestmt38": ( "%|while %c:\n%+%c%-\n\n",
                         (1, "testexpr"),
                         2 # "c_stmts" or "pass"
        ),
        "whileTruestmt38": ( "%|while True:\n%+%c%-\n\n",
                             1 # "c_stmts" or "pass"
        ),
        "try_elsestmtl38": (
            "%|try:\n%+%c%-%c%|else:\n%+%c%-",
            (1, "suite_stmts_opt"),
            (3, "except_handler38"),
            (5, "else_suitec")
        ),
        "try_except38": (
            "%|try:\n%+%c\n%-%|except:\n%+%c%-\n\n",
            -2,  # "suite_stmts_opt", "suite_stmts"
            -1,  # except-handler38{a,b}
        ),
        "try_except_ret38": (
            "%|try:\n%+%c%-\n%|except:\n%+%|%c%-\n\n",
            (1, "returns"),
            (2, "except_ret38a"),
        ),
        "try_except_ret38a": (
            "%|try:\n%+%c%-%c\n\n",
            (1, "returns"),
            (2, "except_handler38c"),
        ),
        "tryfinally38rstmt": (
            "%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
                   (3, "returns"), 6 ),
        "tryfinally38stmt": (
            "%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
            (1, "suite_stmts_opt"),
            (6, "suite_stmts_opt") ),
        "tryfinally38astmt": (
            "%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
            (2, "suite_stmts_opt"),
            (8, "suite_stmts_opt") ),
        "named_expr": ( # AKA "walrus operator"
            "%c := %p", (2, "store"), (0, "expr", PRECEDENCE["named_expr"]-1)
            )
    })
示例#5
0
def customize_for_version37(self, version):
    ########################
    # Python 3.7+ changes
    #######################

    PRECEDENCE['attribute37'] = 2
    PRECEDENCE['call_ex'] = 1
    PRECEDENCE['call_ex_kw'] = 1
    PRECEDENCE['call_ex_kw2'] = 1
    PRECEDENCE['call_ex_kw3'] = 1
    PRECEDENCE['call_ex_kw4'] = 1
    PRECEDENCE['call_kw'] = 0
    PRECEDENCE['call_kw36'] = 1
    PRECEDENCE['formatted_value1'] = 100
    PRECEDENCE['if_exp_37a'] = 28
    PRECEDENCE['if_exp_37b'] = 28
    PRECEDENCE['unmap_dict'] = 0

    TABLE_DIRECT.update({
        'async_for_stmt': ('%|async for %c in %c:\n%+%c%-%-\n\n', (7, 'store'),
                           (1, 'expr'), (17, 'for_block')),
        'async_for_stmt36': ('%|async for %c in %c:\n%+%c%-%-\n\n',
                             (9, 'store'), (1, 'expr'), (18, 'for_block')),
        'async_for_stmt37': ('%|async for %c in %c:\n%+%c%-%-\n\n',
                             (7, 'store'), (1, 'expr'), (16, 'for_block')),
        'and_not': ('%c and not %c', (0, 'expr'), (2, 'expr')),
        'async_with_stmt': ('%|async with %c:\n%+%|%c%-', (0, 'expr'), 7),
        'async_with_as_stmt':
        ('%|async with %c as %c:\n%+%|%c%-', (0, 'expr'), (6, 'store'), 7),
        'async_forelse_stmt':
        ('%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', (7, 'store'),
         (1, 'expr'), (17, 'for_block'), (25, 'else_suite')),
        'attribute37': ('%c.%[1]{pattr}', 0),
        'await_expr': ('await %c', 0),
        'await_stmt': ('%|%c\n', 0),
        'call_ex': ('%c(%p)', (0, 'expr'), (1, 100)),
        'compare_chained1a_37':
        (' %[3]{pattr.replace("-", " ")} %p %p', (0, 19), (-4, 19)),
        'compare_chained1_false_37':
        (' %[3]{pattr.replace("-", " ")} %p %p', (0, 19), (-4, 19)),
        'compare_chained2_false_37': (' %[3]{pattr.replace("-", " ")} %p %p',
                                      (0, 19), (-5, 19)),
        'compare_chained1b_37': (' %[3]{pattr.replace("-", " ")} %p %p',
                                 (0, 19), (-4, 19)),
        'compare_chained1c_37': (' %[3]{pattr.replace("-", " ")} %p %p',
                                 (0, 19), (-2, 19)),
        'compare_chained2a_37': ('%[1]{pattr.replace("-", " ")} %p', (0, 19)),
        'compare_chained2b_37': ('%[1]{pattr.replace("-", " ")} %p', (0, 19)),
        'compare_chained2a_false_37': ('%[1]{pattr.replace("-", " ")} %p',
                                       (0, 19)),
        'compare_chained2c_37': ('%[3]{pattr.replace("-", " ")} %p %p',
                                 (0, 19), (6, 19)),
        'except_return': ('%|except:\n%+%c%-', 3),
        'if_exp_37a': ('%p if %p else %p', (1, 'expr', 27), (0, 27),
                       (4, 'expr', 27)),
        'if_exp_37b': ('%p if %p else %p', (2, 'expr', 27), (0, 'expr', 27),
                       (5, 'expr', 27)),
        'try_except36': ('%|try:\n%+%c%-%c\n\n', 1, -2),
        'tryfinally36': ('%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
                         (1, 'returns'), 3),
        'unmap_dict': ('{**%C}', (0, -1, ', **')),
        'unpack_list': ('*%c', (0, 'list')),
    })

    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 async_call(node):
        self.f.write('async ')
        node.kind == 'call'
        p = self.prec
        self.prec = 80
        self.template_engine(('%c(%P)', 0, (1, -4, ', ', 100)), node)
        self.prec = p
        node.kind == 'async_call'
        self.prune()

    self.n_async_call = async_call
    self.n_build_list_unpack = self.n_list

    def n_call(node):
        mapping = self._get_mapping(node)
        table = mapping[0]
        key = node
        for i in mapping[1:]:
            key = key[i]
            pass
        if key.kind.startswith('CALL_FUNCTION_VAR_KW'):
            # Python 3.5 changes the stack position of
            # *args: kwargs come after *args whereas
            # in earlier Pythons, *args is at the end
            # which simplifies things from our
            # perspective.  Python 3.6+ replaces
            # CALL_FUNCTION_VAR_KW with
            # CALL_FUNCTION_EX We will just swap the
            # order to make it look like earlier
            # Python 3.
            entry = table[key.kind]
            kwarg_pos = entry[2][1]
            args_pos = kwarg_pos - 1
            # Put last node[args_pos] after subsequent kwargs
            while node[kwarg_pos] == 'kwarg' and kwarg_pos < len(node):
                # swap node[args_pos] with node[kwargs_pos]
                node[kwarg_pos], node[args_pos] = node[args_pos], node[
                    kwarg_pos]
                args_pos = kwarg_pos
                kwarg_pos += 1
        elif key.kind.startswith('CALL_FUNCTION_VAR'):
            # CALL_FUNCTION_VAR's top element of the stack contains
            # the variable argument list, then comes
            # annotation args, then keyword args.
            # In the most least-top-most stack entry, but position 1
            # in node order, the positional args.
            argc = node[-1].attr
            nargs = argc & 0xFF
            kwargs = (argc >> 8) & 0xFF
            # FIXME: handle annotation args
            if nargs > 0:
                template = ('%c(%C, ', 0, (1, nargs + 1, ', '))
            else:
                template = ('%c(', 0)
            self.template_engine(template, node)

            args_node = node[-2]
            if args_node in ('pos_arg', 'expr'):
                args_node = args_node[0]
            if args_node == 'build_list_unpack':
                template = ('*%P)', (0, len(args_node) - 1, ', *', 100))
                self.template_engine(template, args_node)
            else:
                if len(node) - nargs > 3:
                    template = ('*%c, %C)', nargs + 1, (nargs + kwargs + 1, -1,
                                                        ', '))
                else:
                    template = ('*%c)', nargs + 1)
                self.template_engine(template, node)
            self.prune()

        self.default(node)

    self.n_call = n_call

    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

        if node[0].kind.startswith('kvlist'):
            # Python 3.5+ style key/value list in dict
            kv_node = node[0]
            l = list(kv_node)
            i = 0

            length = len(l)
            # FIXME: Parser-speed improved grammars will have BUILD_MAP
            # at the end. So in the future when everything is
            # complete, we can do an "assert" instead of "if".
            if kv_node[-1].kind.startswith("BUILD_MAP"):
                length -= 1

            # Respect line breaks from source
            while i < length:
                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
        elif node[-1].kind.startswith('BUILD_CONST_KEY_MAP'):
            keys_node = node[-2]
            keys = keys_node.attr
            # from trepan.api import debug; debug()
            assert keys_node == 'LOAD_CONST' and isinstance(keys, tuple)
            for i in range(node[-1].attr):
                self.write(sep)
                self.write(keys[i], '=')
                value = self.traverse(node[i], indent='')
                self.write(value)
                sep = ", "
                if line_number != self.line_number:
                    sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
                    line_number = self.line_number
                    pass
                pass
        else:
            self.write("**")
            try:
                self.default(node)
            except GenericASTTraversalPruningException:
                pass

        self.prec = p
        self.indent_less(INDENT_PER_LEVEL)
        return

    self.call36_dict = call36_dict

    def n_call_kw36(node):
        self.template_engine(("%p(", (0, 100)), node)
        keys = node[-2].attr
        num_kwargs = len(keys)
        num_posargs = len(node) - (num_kwargs + 2)
        n = len(node)
        assert n >= len(keys)+1, \
          'not enough parameters keyword-tuple values'
        sep = ''

        line_number = self.line_number
        for i in range(1, num_posargs):
            self.write(sep)
            self.preorder(node[i])
            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 - 2:
            self.write(sep)
            self.write(keys[j] + '=')
            self.preorder(node[i])
            if line_number != self.line_number:
                sep = ",\n" + self.indent + "  "
            else:
                sep = ", "
            i += 1
            j += 1
        self.write(')')
        self.prune()
        return

    self.n_call_kw36 = n_call_kw36

    def n_function_def(node):
        code_node = node[0][0]
        for n in node[0]:
            if hasattr(n, 'attr') and iscode(n.attr):
                code_node = n
                break
            pass
        pass

        is_code = hasattr(code_node, 'attr') and iscode(code_node.attr)
        if (is_code and
            (code_node.attr.co_flags & COMPILER_FLAG_BIT['COROUTINE'])):
            self.template_engine(('\n\n%|async def %c\n', -2), node)
        else:
            self.template_engine(('\n\n%|def %c\n', -2), node)
        self.prune()

    self.n_function_def = n_function_def

    def unmapexpr(node):
        last_n = node[0][-1]
        for n in node[0]:
            self.preorder(n)
            if n != last_n:
                self.f.write(', **')
                pass
            pass
        self.prune()
        pass

    self.n_unmapexpr = unmapexpr

    # FIXME: start here
    def n_list_unpack(node):
        """
        prettyprint an unpacked list or tuple
        """
        p = self.prec
        self.prec = 100
        lastnode = node.pop()
        lastnodetype = lastnode.kind

        # If this build list is inside a CALL_FUNCTION_VAR,
        # then the first * has already been printed.
        # Until I have a better way to check for CALL_FUNCTION_VAR,
        # will assume that if the text ends in *.
        last_was_star = self.f.getvalue().endswith('*')

        if lastnodetype.startswith('BUILD_LIST'):
            self.write('[')
            endchar = ']'
        elif lastnodetype.startswith('BUILD_TUPLE'):
            # Tuples can appear places that can NOT
            # have parenthesis around them, like array
            # subscripts. We check for that by seeing
            # if a tuple item is some sort of slice.
            no_parens = False
            for n in node:
                if n == 'expr' and n[0].kind.startswith('build_slice'):
                    no_parens = True
                    break
                pass
            if no_parens:
                endchar = ''
            else:
                self.write('(')
                endchar = ')'
                pass

        elif lastnodetype.startswith('BUILD_SET'):
            self.write('{')
            endchar = '}'
        elif lastnodetype.startswith('BUILD_MAP_UNPACK'):
            self.write('{*')
            endchar = '}'
        elif lastnodetype.startswith('ROT_TWO'):
            self.write('(')
            endchar = ')'
        else:
            raise TypeError(
                'Internal Error: n_build_list expects list, tuple, set, or unpack'
            )

        flat_elems = flatten_list(node)

        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 elem[0] == 'tuple':
                assert value[0] == '('
                assert value[-1] == ')'
                value = value[1:-1]
                if value[-1] == ',':
                    # singleton tuple
                    value = value[:-1]
            else:
                value = '*' + value
            if line_number != self.line_number:
                sep += '\n' + self.indent + INDENT_PER_LEVEL[:-1]
            else:
                if sep != '': sep += ' '
            if not last_was_star:
                pass
            else:
                last_was_star = False
            self.write(sep, value)
            sep = ','
        if lastnode.attr == 1 and lastnodetype.startswith('BUILD_TUPLE'):
            self.write(',')
        self.write(endchar)
        self.indent_less(INDENT_PER_LEVEL)

        self.prec = p
        self.prune()
        return

    self.n_tuple_unpack = n_list_unpack

    def build_unpack_tuple_with_call(node):
        n = node[0]
        if n == 'expr':
            n = n[0]
        if n == 'tuple':
            self.call36_tuple(n)
            first = 1
            sep = ', *'
        elif n == 'LOAD_CONST':
            value = self.format_pos_args(n)
            self.f.write(value)
            first = 1
            sep = ', *'
        else:
            first = 0
            sep = '*'

        buwc = node[-1]
        assert buwc.kind.startswith('BUILD_TUPLE_UNPACK_WITH_CALL')
        for n in node[first:-1]:
            self.f.write(sep)
            self.preorder(n)
            sep = ', *'
            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_kw(node):
        """Handle CALL_FUNCTION_EX 1 (have KW) but with
        BUILD_MAP_UNPACK_WITH_CALL"""

        expr = node[1]
        assert expr == 'expr'

        value = self.format_pos_args(expr)
        if value == '':
            fmt = "%c(%p)"
        else:
            fmt = "%%c(%s, %%p)" % value

        self.template_engine(
            (fmt, (0, 'expr'), (2, 'build_map_unpack_with_call', 100)), node)

        self.prune()

    self.n_call_ex_kw = call_ex_kw

    def call_ex_kw2(node):
        """Handle CALL_FUNCTION_EX 2  (have KW) but with
        BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""

        assert node[1] == 'build_tuple_unpack_with_call'
        value = self.format_pos_args(node[1])
        if value == '':
            fmt = "%c(%p)"
        else:
            fmt = "%%c(%s, %%p)" % value

        self.template_engine(
            (fmt, (0, 'expr'), (2, 'build_map_unpack_with_call', 100)), node)

        self.prune()

    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('(')

        value = self.format_pos_args(node[1][0])
        if value == '':
            pass
        else:
            self.write(value)
            self.write(', ')

        self.write('*')
        self.preorder(node[1][1])
        self.write(', ')

        kwargs = node[2]
        if kwargs == 'expr':
            kwargs = kwargs[0]
        if kwargs == 'dict':
            self.call36_dict(kwargs)
        else:
            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 {1 or 2} 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]
        call_function_ex = node[-1]
        assert (call_function_ex == 'CALL_FUNCTION_EX_KW'
                or (self.version >= 3.6
                    and call_function_ex == 'CALL_FUNCTION_EX'))
        # FIXME: decide if the below test be on kwargs == 'dict'
        if (call_function_ex.attr & 1
                and (not isinstance(kwargs, Token) and kwargs != 'attribute')
                and not kwargs[0].kind.startswith('kvlist')):
            self.call36_dict(kwargs)
        else:
            self.write('**')
            self.preorder(kwargs)
        self.write(')')
        self.prune()

    self.n_call_ex_kw4 = call_ex_kw4

    def format_pos_args(node):
        """
        Positional args should format to:
        (*(2, ), ...) -> (2, ...)
        We remove starting and trailing parenthesis and ', ' if
        tuple has only one element.
        """
        value = self.traverse(node, indent='')
        if value.startswith('('):
            assert value.endswith(')')
            value = value[1:-1].rstrip(
                " "
            )  # Remove starting '(' and trailing ')' and additional spaces
            if value == '':
                pass  # args is empty
            else:
                if value.endswith(','):  # if args has only one item
                    value = value[:-1]
        return value

    self.format_pos_args = format_pos_args

    FSTRING_CONVERSION_MAP = {1: '!s', 2: '!r', 3: '!a', 'X': ':X'}

    def n_except_suite_finalize(node):
        if node[1] == 'returns' and self.hide_internal:
            # Process node[1] only.
            # The code after "returns", e.g. node[3], is dead code.
            # Adding it is wrong as it dedents and another
            # exception handler "except_stmt" afterwards.
            # Note it is also possible that the grammar is wrong here.
            # and this should not be "except_stmt".
            self.indent_more()
            self.preorder(node[1])
            self.indent_less()
        else:
            self.default(node)
        self.prune()

    self.n_except_suite_finalize = n_except_suite_finalize

    def n_formatted_value(node):
        if node[0] == 'LOAD_CONST':
            value = node[0].attr
            if isinstance(value, tuple):
                self.write(node[0].attr)
            else:
                self.write(escape_string(node[0].attr))
            self.prune()
        else:
            self.default(node)

    self.n_formatted_value = n_formatted_value

    def n_formatted_value_attr(node):
        f_conversion(node)
        fmt_node = node.data[3]
        if fmt_node == 'expr' and fmt_node[0] == 'LOAD_CONST':
            node.string = escape_format(fmt_node[0].attr)
        else:
            node.string = fmt_node
        self.default(node)

    self.n_formatted_value_attr = n_formatted_value_attr

    def f_conversion(node):
        fmt_node = node.data[1]
        if fmt_node == 'expr' and fmt_node[0] == 'LOAD_CONST':
            data = fmt_node[0].attr
        else:
            data = fmt_node.attr
        node.conversion = FSTRING_CONVERSION_MAP.get(data, '')
        return node.conversion

    def n_formatted_value1(node):
        expr = node[0]
        assert expr == 'expr'
        value = self.traverse(expr, indent='')
        conversion = f_conversion(node)
        f_str = "f%s" % escape_string("{%s%s}" % (value, conversion))
        self.write(f_str)
        self.prune()

    self.n_formatted_value1 = n_formatted_value1

    def n_formatted_value2(node):
        p = self.prec
        self.prec = 100

        expr = node[0]
        assert expr == 'expr'
        value = self.traverse(expr, indent='')
        format_value_attr = node[-1]
        assert format_value_attr == 'FORMAT_VALUE_ATTR'
        attr = format_value_attr.attr
        if attr == 4:
            assert node[1] == 'expr'
            fmt = strip_quotes(self.traverse(node[1], indent=''))
            conversion = ":%s" % fmt
        else:
            conversion = FSTRING_CONVERSION_MAP.get(attr, '')

        f_str = "f%s" % escape_string("{%s%s}" % (value, conversion))
        self.write(f_str)

        self.prec = p
        self.prune()

    self.n_formatted_value2 = n_formatted_value2

    def n_joined_str(node):
        p = self.prec
        self.prec = 100

        result = ''
        for expr in node[:-1]:
            assert expr == 'expr'
            value = self.traverse(expr, indent='')
            if expr[0].kind.startswith('formatted_value'):
                # remove leading 'f'
                assert value.startswith('f')
                value = value[1:]
                pass
            else:
                # {{ and }} in Python source-code format strings mean
                # { and } respectively. But only when *not* part of a
                # formatted value. However in the LOAD_CONST
                # bytecode, the escaping of the braces has been
                # removed. So we need to put back the braces escaping in
                # reconstructing the source.
                assert expr[0] == 'LOAD_CONST'
                value = value.replace("{", "{{").replace("}", "}}")

            # Remove leading quotes
            result += strip_quotes(value)
            pass
        self.write('f%s' % escape_string(result))

        self.prec = p
        self.prune()

    self.n_joined_str = n_joined_str

    # 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 starred(node):
        l = len(node)
        assert l > 0
        pos_args = node[0]
        if pos_args == 'expr':
            pos_args = pos_args[0]
        if pos_args == 'tuple':
            build_tuple = pos_args[0]
            if build_tuple.kind.startswith('BUILD_TUPLE'):
                tuple_len = 0
            else:
                tuple_len = len(node) - 1
            star_start = 1
            template = '%C', (0, -1, ', ')
            self.template_engine(template, pos_args)
            if tuple_len == 0:
                self.write("*()")
                # That's it
                self.prune()
            self.write(', ')
        else:
            star_start = 0
        if l > 1:
            template = ('*%C', (star_start, -1, ', *'))
        else:
            template = ('*%c', (star_start, 'expr'))

        self.template_engine(template, node)
        self.prune()

    self.n_starred = starred

    def return_closure(node):
        # Nothing should be output here
        self.prune()
        return

    self.n_return_closure = return_closure
示例#6
0
def customize_for_version37(self, version):
    ########################
    # Python 3.7+ changes
    #######################

    PRECEDENCE["attribute37"] = 2
    PRECEDENCE["call_ex"] = 1
    PRECEDENCE["call_ex_kw"] = 1
    PRECEDENCE["call_ex_kw2"] = 1
    PRECEDENCE["call_ex_kw3"] = 1
    PRECEDENCE["call_ex_kw4"] = 1
    PRECEDENCE["call_kw"] = 0
    PRECEDENCE["call_kw36"] = 1
    PRECEDENCE["formatted_value1"] = 100
    PRECEDENCE["if_exp_37a"] = 28
    PRECEDENCE["if_exp_37b"] = 28
    PRECEDENCE["unmap_dict"] = 0

    TABLE_DIRECT.update({
        "and_not": ("%c and not %c", (0, "expr_pjif"), (1, "expr")),
        "ann_assign": (
            "%|%[2]{attr}: %c\n",
            0,
        ),
        "ann_assign_init": (
            "%|%[2]{attr}: %c = %c\n",
            0,
            1,
        ),
        "async_for_stmt": (
            "%|async for %c in %c:\n%+%c%-\n\n",
            (7, "store"),
            (1, "expr"),
            (17, "for_block"),
        ),
        "async_for_stmt36": (
            "%|async for %c in %c:\n%+%c%-\n\n",
            (9, "store"),
            (1, "expr"),
            (18, "for_block"),
        ),
        "async_for_stmt37": (
            "%|async for %c in %c:\n%+%c%-\n\n",
            (7, "store"),
            (1, "expr"),
            (16, "for_block"),
        ),
        "async_with_stmt": ("%|async with %c:\n%+%c%-", (0, "expr"), 3),
        "async_with_as_stmt": (
            "%|async with %c as %c:\n%+%c%-",
            (0, "expr"),
            (2, "store"),
            3,
        ),
        "async_forelse_stmt": (
            "%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
            (7, "store"),
            (1, "expr"),
            (17, "for_block"),
            (25, "else_suite"),
        ),
        "attribute37": ("%c.%[1]{pattr}", (0, "expr")),
        "attributes37": (
            "%[0]{pattr} import %c",
            (0, "IMPORT_NAME_ATTR"),
            (1, "IMPORT_FROM"),
        ),

        # nested await expressions like:
        #   return await (await bar())
        # need parenthesis.
        "await_expr": ("await %p", (0, PRECEDENCE["await_expr"] - 1)),
        "await_stmt": ("%|%c\n", 0),
        "call_ex": ("%c(%p)", (0, "expr"), (1, 100)),
        "compare_chained1a_37": (
            ' %[3]{pattr.replace("-", " ")} %p %p',
            (0, 19),
            (-4, 19),
        ),
        "compare_chained1_false_37": (
            ' %[3]{pattr.replace("-", " ")} %p %p',
            (0, 19),
            (-4, 19),
        ),
        "compare_chained2_false_37": (
            ' %[3]{pattr.replace("-", " ")} %p %p',
            (0, 19),
            (-5, 19),
        ),
        "compare_chained1b_false_37": (
            ' %[3]{pattr.replace("-", " ")} %p %p',
            (0, 19),
            (-4, 19),
        ),
        "compare_chained1c_37": (
            ' %[3]{pattr.replace("-", " ")} %p %p',
            (0, 19),
            (-2, 19),
        ),
        "compare_chained2a_37": ('%[1]{pattr.replace("-", " ")} %p', (0, 19)),
        "compare_chained2b_false_37":
        ('%[1]{pattr.replace("-", " ")} %p', (0, 19)),
        "compare_chained2a_false_37":
        ('%[1]{pattr.replace("-", " ")} %p', (0, 19)),
        "compare_chained2c_37": (
            '%[3]{pattr.replace("-", " ")} %p %p',
            (0, 19),
            (6, 19),
        ),
        "c_try_except": ("%|try:\n%+%c%-%c\n\n", 1, (3, "c_except_handler")),
        "if_exp37": ("%p if %c else %c", (1, "expr", 27), 0, 3),
        "except_return": ("%|except:\n%+%c%-", 3),
        "if_exp_37a": (
            "%p if %p else %p",
            (1, "expr", 27),
            (0, 27),
            (4, "expr", 27),
        ),
        "if_exp_37b": (
            "%p if %p else %p",
            (1, "expr_pjif", 27),
            (0, "expr_pjif", 27),
            (3, "expr", 27),
        ),
        "ifstmt_bool": ("%|if %c:\n%+%c%-", (0, "or_and_not"), (1, "stmts")),
        "ifstmtc": ("%|if %c:\n%+%c%-", (0, ("testexpr", "testexprc")),
                    (1, "ifstmts_jumpc")),
        "if_and_elsestmtc":
        ("%|if %c and %c:\n%+%c%-%|else:\n%+%c%-", (0, "expr"), (2, "expr"),
         (4, "c_stmts"), (-2, "else_suitec")),
        "if_and_stmt": ("%|if %c and %c:\n%+%c%-", (0, "expr_pjif"),
                        (1, "expr"), (3, "stmts")),
        "if_or_stmt":
        ("%|if %c or %c:\n%+%c%-", (0, "expr"), (2, "expr"), (5, "stmts")),
        "if_or_elsestmt": ("%|if %c or %c:\n%+%c%-%|else:\n%+%c%-", (0,
                                                                     "expr"),
                           (3, "expr"), (6, "stmts"), (-2, "else_suite")),
        "if_or_not_elsestmt":
        ("%|if %c or not %c:\n%+%c%-%|else:\n%+%c%-", (0, "expr"), (3, "expr"),
         (6, "stmts"), (-2, "else_suite")),
        "import_as37": ("%|import %c as %c\n", 2, -2),
        "import_from37": ("%|from %[2]{pattr} import %c\n", (3,
                                                             "importlist37")),
        "importattr37": ("%c", (0, "IMPORT_NAME_ATTR")),
        "importlist37": ("%C", (0, maxint, ", ")),
        "or_and_not": (
            "%c or %c",
            (0, "expr"),
            (2, "and_not"),
        ),
        "list_if37": (" if %p%c", (0, 27), 1),
        "list_if37_not": (" if not %p%c", (0, 27), 1),
        "testfalse_not_or": ("not %p or %c",
                             (0, "expr", PRECEDENCE["and"] - 1), (2, "expr")),
        "testfalse_not_and": ("not (%c)", 0),
        "testfalsec": ("not %c", (0, "expr")),
        "try_except36": ("%|try:\n%+%c%-%c\n\n", 1, -2),
        "tryfinally36": ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
                         (1, "returns"), 3),
        "tryfinally_return_stmt1": ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
                                    (1, "suite_stmts_opt"), (-1, "returns")),
        "tryfinally_return_stmt2": ("%|try:\n%+%c%-%|finally:\n%+return%-\n\n",
                                    (1, "suite_stmts_opt")),
        "unmap_dict": ("{**%C}", (0, -1, ", **")),
        "unpack_list": ("*%c", (0, "list")),
        "yield_from": ("yield from %c", (0, "expr")),
    })

    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 n_assert_invert(node):
        testtrue = node[0]
        assert testtrue == "testtrue"
        testtrue.kind = "assert"
        self.default(testtrue)

    self.n_assert_invert = n_assert_invert

    def n_async_call(node):
        self.f.write("async ")
        node.kind == "call"
        p = self.prec
        self.prec = 80
        self.template_engine(("%c(%P)", 0, (1, -4, ", ", 100)), node)
        self.prec = p
        node.kind == "async_call"
        self.prune()

    self.n_async_call = n_async_call

    def n_attribute37(node):
        expr = node[0]
        assert expr == "expr"
        if expr[0] == "LOAD_CONST":
            # FIXME: I didn't record which constants parenthesis is
            # necessary. However, I suspect that we could further
            # refine this by looking at operator precedence and
            # eval'ing the constant value (pattr) and comparing with
            # the type of the constant.
            node.kind = "attribute_w_parens"
        self.default(node)

    self.n_attribute37 = n_attribute37

    def n_build_list_unpack(node):
        """
        prettyprint a list or tuple
        """
        p = self.prec
        self.prec = 100
        lastnode = node.pop()
        lastnodetype = lastnode.kind

        # If this build list is inside a CALL_FUNCTION_VAR,
        # then the first * has already been printed.
        # Until I have a better way to check for CALL_FUNCTION_VAR,
        # will assume that if the text ends in *.
        last_was_star = self.f.getvalue().endswith("*")

        if lastnodetype.startswith("BUILD_LIST"):
            self.write("[")
            endchar = "]"

        flat_elems = flatten_list(node)

        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
            use_star = True
            value = self.traverse(elem)
            if value.startswith("("):
                assert value.endswith(")")
                use_star = False
                value = value[1:-1].rstrip(
                    " "
                )  # Remove starting '(' and trailing ')' and additional spaces
                if value == "":
                    pass
                else:
                    if value.endswith(","):  # if args has only one item
                        value = value[:-1]
            if line_number != self.line_number:
                sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
            else:
                if sep != "":
                    sep += " "
            if not last_was_star and use_star:
                sep += "*"
                pass
            else:
                last_was_star = False
            self.write(sep, value)
            sep = ","
        self.write(endchar)
        self.indent_less(INDENT_PER_LEVEL)

        self.prec = p
        self.prune()
        return

    self.n_build_list_unpack = n_build_list_unpack

    def gen_function_parens_adjust(mapping_key, node):
        """If we can avoid the outer parenthesis
        of a generator function, set the node key to
        'call_generator' and the caller will do the default
        action on that. Otherwise we do nothing.
        """
        if mapping_key.kind != "CALL_FUNCTION_1":
            return

        args_node = node[-2]
        if args_node == "pos_arg":
            assert args_node[0] == "expr"
            n = args_node[0][0]
            if n == "generator_exp":
                node.kind = "call_generator"
            pass
        return

    def n_call(node):
        p = self.prec
        self.prec = 100
        mapping = self._get_mapping(node)
        table = mapping[0]
        key = node
        for i in mapping[1:]:
            key = key[i]
            pass
        opname = key.kind
        if opname.startswith("CALL_FUNCTION_VAR_KW"):
            # Python 3.5 changes the stack position of
            # *args: kwargs come after *args whereas
            # in earlier Pythons, *args is at the end
            # which simplifies things from our
            # perspective.  Python 3.6+ replaces
            # CALL_FUNCTION_VAR_KW with
            # CALL_FUNCTION_EX We will just swap the
            # order to make it look like earlier
            # Python 3.
            entry = table[key.kind]
            kwarg_pos = entry[2][1]
            args_pos = kwarg_pos - 1
            # Put last node[args_pos] after subsequent kwargs
            while node[kwarg_pos] == "kwarg" and kwarg_pos < len(node):
                # swap node[args_pos] with node[kwargs_pos]
                node[kwarg_pos], node[args_pos] = node[args_pos], node[
                    kwarg_pos]
                args_pos = kwarg_pos
                kwarg_pos += 1
        elif opname.startswith("CALL_FUNCTION_VAR"):
            # CALL_FUNCTION_VAR's top element of the stack contains
            # the variable argument list, then comes
            # annotation args, then keyword args.
            # In the most least-top-most stack entry, but position 1
            # in node order, the positional args.
            argc = node[-1].attr
            nargs = argc & 0xFF
            kwargs = (argc >> 8) & 0xFF
            # FIXME: handle annotation args
            if nargs > 0:
                template = ("%c(%P, ", 0, (1, nargs + 1, ", ", 100))
            else:
                template = ("%c(", 0)
            self.template_engine(template, node)

            args_node = node[-2]
            if args_node in ("pos_arg", "expr"):
                args_node = args_node[0]
            if args_node == "build_list_unpack":
                template = ("*%P)", (0, len(args_node) - 1, ", *", 100))
                self.template_engine(template, args_node)
            else:
                if len(node) - nargs > 3:
                    template = (
                        "*%c, %P)",
                        nargs + 1,
                        (nargs + kwargs + 1, -1, ", ", 100),
                    )
                else:
                    template = ("*%c)", nargs + 1)
                self.template_engine(template, node)
            self.prec = p
            self.prune()
        elif (opname.startswith("CALL_FUNCTION_1")
              and opname == "CALL_FUNCTION_1"
              or not re.match("\d", opname[-1])):
            self.template_engine(("%c(%c)", (0, "expr"), 1), node)
            self.prec = p
            self.prune()
        else:
            gen_function_parens_adjust(key, node)

        self.prec = p
        self.default(node)

    self.n_call = n_call

    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

        if node[0].kind.startswith("kvlist"):
            # Python 3.5+ style key/value list in dict
            kv_node = node[0]
            l = list(kv_node)
            i = 0

            length = len(l)
            # FIXME: Parser-speed improved grammars will have BUILD_MAP
            # at the end. So in the future when everything is
            # complete, we can do an "assert" instead of "if".
            if kv_node[-1].kind.startswith("BUILD_MAP"):
                length -= 1

            # Respect line breaks from source
            while i < length:
                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
        elif node[-1].kind.startswith("BUILD_CONST_KEY_MAP"):
            keys_node = node[-2]
            keys = keys_node.attr
            # from trepan.api import debug; debug()
            assert keys_node == "LOAD_CONST" and isinstance(keys, tuple)
            for i in range(node[-1].attr):
                self.write(sep)
                self.write(keys[i], "=")
                value = self.traverse(node[i], indent="")
                self.write(value)
                sep = ", "
                if line_number != self.line_number:
                    sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
                    line_number = self.line_number
                    pass
                pass
        else:
            self.write("**")
            try:
                self.default(node)
            except GenericASTTraversalPruningException:
                pass

        self.prec = p
        self.indent_less(INDENT_PER_LEVEL)
        return

    self.call36_dict = call36_dict

    def n_classdef36(node):
        # class definition ('class X(A,B,C):')
        cclass = self.currentclass

        # Pick out various needed bits of information
        # * class_name - the name of the class
        # * subclass_info - the parameters to the class  e.g.
        #      class Foo(bar, baz)
        #               ----------
        # * subclass_code - the code for the subclass body
        subclass_info = None
        if node == "classdefdeco2":
            if isinstance(node[1][1].attr, str):
                class_name = node[1][1].attr
            else:
                class_name = node[1][2].attr
            build_class = node
        else:
            build_class = node[0]
            if build_class == "build_class_kw":
                mkfunc = build_class[1]
                assert mkfunc == "mkfunc"
                subclass_info = build_class
                if hasattr(mkfunc[0], "attr") and iscode(mkfunc[0].attr):
                    subclass_code = mkfunc[0].attr
                else:
                    assert mkfunc[0] == "load_closure"
                    subclass_code = mkfunc[1].attr
                    assert iscode(subclass_code)
            if build_class[1][0] == "load_closure":
                code_node = build_class[1][1]
            else:
                code_node = build_class[1][0]
            class_name = code_node.attr.co_name

        assert "mkfunc" == build_class[1]
        mkfunc = build_class[1]
        if mkfunc[0] in ("kwargs", "no_kwargs"):
            for n in mkfunc:
                if hasattr(n, "attr") and iscode(n.attr):
                    subclass_code = n.attr
                    break
                pass
            if node == "classdefdeco2":
                subclass_info = node
            else:
                subclass_info = node[0]
        elif build_class[1][0] == "load_closure":
            # Python 3 with closures not functions
            load_closure = build_class[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_code = load_closure[-3].attr
            elif hasattr(load_closure[-2], "attr"):
                # Python 3.2 works like this
                subclass_code = load_closure[-2].attr
            else:
                raise "Internal Error n_classdef: cannot find class body"
            if hasattr(build_class[3], "__len__"):
                if not subclass_info:
                    subclass_info = build_class[3]
            elif hasattr(build_class[2], "__len__"):
                subclass_info = build_class[2]
            else:
                raise "Internal Error n_classdef: cannot superclass name"
        elif node == "classdefdeco2":
            subclass_info = node
            subclass_code = build_class[1][0].attr
        elif not subclass_info:
            if mkfunc[0] in ("no_kwargs", "kwargs"):
                subclass_code = mkfunc[1].attr
            else:
                subclass_code = mkfunc[0].attr
            if node == "classdefdeco2":
                subclass_info = node
            else:
                subclass_info = node[0]

        if node == "classdefdeco2":
            self.write("\n")
        else:
            self.write("\n\n")

        self.currentclass = str(class_name)
        self.write(self.indent, "class ", self.currentclass)

        self.print_super_classes3(subclass_info)
        self.println(":")

        # class body
        self.indent_more()
        self.build_class(subclass_code)
        self.indent_less()

        self.currentclass = cclass
        if len(self.param_stack) > 1:
            self.write("\n\n")
        else:
            self.write("\n\n\n")

        self.prune()

    self.n_classdef36 = n_classdef36

    def n_compare_chained(node):
        if node[0] == "compare_chained37":
            self.default(node[0])
        else:
            self.default(node)

    self.n_compare_chained = n_compare_chained

    def n_importlist37(node):
        if len(node) == 1:
            self.default(node)
            return
        n = len(node) - 1
        for i in range(n, -1, -1):
            if node[i] != "ROT_TWO":
                break
        self.template_engine(("%C", (0, i + 1, ", ")), node)
        self.prune()
        return

    self.n_importlist37 = n_importlist37

    def n_call_kw36(node):
        self.template_engine(("%p(", (0, 100)), node)
        keys = node[-2].attr
        num_kwargs = len(keys)
        num_posargs = len(node) - (num_kwargs + 2)
        n = len(node)
        assert n >= len(keys) + 1, "not enough parameters keyword-tuple values"
        sep = ""

        line_number = self.line_number
        for i in range(1, num_posargs):
            self.write(sep)
            self.preorder(node[i])
            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 - 2:
            self.write(sep)
            self.write(keys[j] + "=")
            self.preorder(node[i])
            if line_number != self.line_number:
                sep = ",\n" + self.indent + "  "
            else:
                sep = ", "
            i += 1
            j += 1
        self.write(")")
        self.prune()
        return

    self.n_call_kw36 = n_call_kw36

    def is_async_fn(node):
        code_node = node[0][0]
        for n in node[0]:
            if hasattr(n, "attr") and iscode(n.attr):
                code_node = n
                break
            pass
        pass

        is_code = hasattr(code_node, "attr") and iscode(code_node.attr)
        return is_code and (code_node.attr.co_flags
                            & (COMPILER_FLAG_BIT["COROUTINE"]
                               | COMPILER_FLAG_BIT["ITERABLE_COROUTINE"]
                               | COMPILER_FLAG_BIT["ASYNC_GENERATOR"]))

    def n_function_def(node):
        if is_async_fn(node):
            self.template_engine(("\n\n%|async def %c\n", -2), node)
        else:
            self.default(node)
        self.prune()

    self.n_function_def = n_function_def

    def n_import_from(node):
        relative_path_index = 0
        if node[relative_path_index].pattr > 0:
            node[2].pattr = ("." *
                             node[relative_path_index].pattr) + node[2].pattr
        if isinstance(node[1].pattr, tuple):
            imports = node[1].pattr
            for pattr in imports:
                node[1].pattr = pattr
                self.default(node)
            return
        self.default(node)

    self.n_import_from = n_import_from
    self.n_import_from_star = n_import_from

    def n_mkfuncdeco0(node):
        if is_async_fn(node):
            self.template_engine(("%|async def %c\n", 0), node)
        else:
            self.default(node)
        self.prune()

    self.n_mkfuncdeco0 = n_mkfuncdeco0

    def unmapexpr(node):
        last_n = node[0][-1]
        for n in node[0]:
            self.preorder(n)
            if n != last_n:
                self.f.write(", **")
                pass
            pass
        self.prune()
        pass

    self.n_unmapexpr = unmapexpr

    # FIXME: start here
    def n_list_unpack(node):
        """
        prettyprint an unpacked list or tuple
        """
        p = self.prec
        self.prec = 100
        lastnode = node.pop()
        lastnodetype = lastnode.kind

        # If this build list is inside a CALL_FUNCTION_VAR,
        # then the first * has already been printed.
        # Until I have a better way to check for CALL_FUNCTION_VAR,
        # will assume that if the text ends in *.
        last_was_star = self.f.getvalue().endswith("*")

        if lastnodetype.startswith("BUILD_LIST"):
            self.write("[")
            endchar = "]"
        elif lastnodetype.startswith("BUILD_TUPLE"):
            # Tuples can appear places that can NOT
            # have parenthesis around them, like array
            # subscripts. We check for that by seeing
            # if a tuple item is some sort of slice.
            no_parens = False
            for n in node:
                if n == "expr" and n[0].kind.startswith("build_slice"):
                    no_parens = True
                    break
                pass
            if no_parens:
                endchar = ""
            else:
                self.write("(")
                endchar = ")"
                pass

        elif lastnodetype.startswith("BUILD_SET"):
            self.write("{")
            endchar = "}"
        elif lastnodetype.startswith("BUILD_MAP_UNPACK"):
            self.write("{*")
            endchar = "}"
        elif lastnodetype.startswith("ROT_TWO"):
            self.write("(")
            endchar = ")"
        else:
            raise TypeError(
                "Internal Error: n_build_list expects list, tuple, set, or unpack"
            )

        flat_elems = flatten_list(node)

        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 elem[0] == "tuple":
                assert value[0] == "("
                assert value[-1] == ")"
                value = value[1:-1]
                if value[-1] == ",":
                    # singleton tuple
                    value = value[:-1]
            else:
                value = "*" + value
            if line_number != self.line_number:
                sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
            else:
                if sep != "":
                    sep += " "
            if not last_was_star:
                pass
            else:
                last_was_star = False
            self.write(sep, value)
            sep = ","
        if lastnode.attr == 1 and lastnodetype.startswith("BUILD_TUPLE"):
            self.write(",")
        self.write(endchar)
        self.indent_less(INDENT_PER_LEVEL)

        self.prec = p
        self.prune()
        return

    self.n_tuple_unpack = n_list_unpack

    def build_unpack_tuple_with_call(node):
        n = node[0]
        if n == "expr":
            n = n[0]
        if n == "tuple":
            self.call36_tuple(n)
            first = 1
            sep = ", *"
        elif n in ("LOAD_CONST", "LOAD_STR"):
            value = self.format_pos_args(n)
            self.f.write(value)
            first = 1
            sep = ", *"
        else:
            first = 0
            sep = "*"

        buwc = node[-1]
        assert buwc.kind.startswith("BUILD_TUPLE_UNPACK_WITH_CALL")
        for n in node[first:-1]:
            self.f.write(sep)
            self.preorder(n)
            sep = ", *"
            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_kw(node):
        """Handle CALL_FUNCTION_EX 1 (have KW) but with
        BUILD_MAP_UNPACK_WITH_CALL"""

        expr = node[1]
        assert expr == "expr"

        value = self.format_pos_args(expr)
        if value == "":
            fmt = "%c(%p)"
        else:
            fmt = "%%c(%s, %%p)" % value

        self.template_engine(
            (fmt, (0, "expr"), (2, "build_map_unpack_with_call", 100)), node)

        self.prune()

    self.n_call_ex_kw = call_ex_kw

    def call_ex_kw2(node):
        """Handle CALL_FUNCTION_EX 2  (have KW) but with
        BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""

        assert node[1] == "build_tuple_unpack_with_call"
        value = self.format_pos_args(node[1])
        if value == "":
            fmt = "%c(%p)"
        else:
            fmt = "%%c(%s, %%p)" % value

        self.template_engine(
            (fmt, (0, "expr"), (2, "build_map_unpack_with_call", 100)), node)

        self.prune()

    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("(")

        value = self.format_pos_args(node[1][0])
        if value == "":
            pass
        else:
            self.write(value)
            self.write(", ")

        self.write("*")
        self.preorder(node[1][1])
        self.write(", ")

        kwargs = node[2]
        if kwargs == "expr":
            kwargs = kwargs[0]
        if kwargs == 'expr' and kwargs[0] != "dict":
            self.call36_dict(kwargs)
        else:
            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 {1 or 2} 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]
        call_function_ex = node[-1]
        assert call_function_ex == "CALL_FUNCTION_EX_KW" or (
            self.version >= 3.6 and call_function_ex == "CALL_FUNCTION_EX")
        # FIXME: decide if the below test be on kwargs == 'dict'
        if (call_function_ex.attr & 1
                and (not isinstance(kwargs, Token) and kwargs != "attribute")
                and kwargs != "call_kw36"
                and not kwargs[0].kind.startswith("kvlist")):
            self.call36_dict(kwargs)
        else:
            self.write("**")
            self.preorder(kwargs)
        self.write(")")
        self.prune()

    self.n_call_ex_kw4 = call_ex_kw4

    def format_pos_args(node):
        """
        Positional args should format to:
        (*(2, ), ...) -> (2, ...)
        We remove starting and trailing parenthesis and ', ' if
        tuple has only one element.
        """
        value = self.traverse(node, indent="")
        if value.startswith("("):
            assert value.endswith(")")
            value = value[1:-1].rstrip(
                " "
            )  # Remove starting '(' and trailing ')' and additional spaces
            if value == "":
                pass  # args is empty
            else:
                if value.endswith(","):  # if args has only one item
                    value = value[:-1]
        return value

    self.format_pos_args = format_pos_args

    FSTRING_CONVERSION_MAP = {1: "!s", 2: "!r", 3: "!a", "X": ":X"}

    def n_except_suite_finalize(node):
        if node[1] == "returns" and self.hide_internal:
            # Process node[1] only.
            # The code after "returns", e.g. node[3], is dead code.
            # Adding it is wrong as it dedents and another
            # exception handler "except_stmt" afterwards.
            # Note it is also possible that the grammar is wrong here.
            # and this should not be "except_stmt".
            self.indent_more()
            self.preorder(node[1])
            self.indent_less()
        else:
            self.default(node)
        self.prune()

    self.n_except_suite_finalize = n_except_suite_finalize

    def n_formatted_value(node):
        if node[0] in ("LOAD_STR", "LOAD_CONST"):
            value = node[0].attr
            if isinstance(value, tuple):
                self.write(node[0].attr)
            else:
                self.write(escape_string(node[0].attr))
            self.prune()
        else:
            self.default(node)

    self.n_formatted_value = n_formatted_value

    def n_formatted_value_attr(node):
        f_conversion(node)
        fmt_node = node.data[3]
        if fmt_node == "expr" and fmt_node[0] == "LOAD_STR":
            node.string = escape_format(fmt_node[0].attr)
        else:
            node.string = fmt_node
        self.default(node)

    self.n_formatted_value_attr = n_formatted_value_attr

    def f_conversion(node):
        fmt_node = node.data[1]
        if fmt_node == "expr" and fmt_node[0] == "LOAD_STR":
            data = fmt_node[0].attr
        else:
            data = fmt_node.attr
        node.conversion = FSTRING_CONVERSION_MAP.get(data, "")
        return node.conversion

    def n_formatted_value1(node):
        expr = node[0]
        assert expr == "expr"
        conversion = f_conversion(node)
        if self.in_format_string and self.in_format_string != "formatted_value1":
            value = self.traverse(expr, indent="")
            if value[0] == "{":
                # If we have a set or dictionary comprehension, then we need to add a space
                # so as not to confuse the format string with {{.
                fmt = "{ %s%s }"
            else:
                fmt = "{%s%s}"
            es = escape_string(fmt % (value, conversion))
            f_str = "%s" % es
        else:
            old_in_format_string = self.in_format_string
            self.in_format_string = "formatted_value1"
            value = self.traverse(expr, indent="")
            self.in_format_string = old_in_format_string
            es = escape_string("{%s%s}" % (value, conversion))
            f_str = "f%s" % es

        self.write(f_str)
        self.prune()

    self.n_formatted_value1 = n_formatted_value1

    def n_formatted_value2(node):
        p = self.prec
        self.prec = 100

        expr = node[0]
        assert expr == "expr"
        old_in_format_string = self.in_format_string
        self.in_format_string = "formatted_value2"
        value = self.traverse(expr, indent="")
        format_value_attr = node[-1]
        assert format_value_attr == "FORMAT_VALUE_ATTR"
        attr = format_value_attr.attr
        if attr & 4:
            assert node[1] == "expr"
            fmt = strip_quotes(self.traverse(node[1], indent=""))
            attr_flags = attr & 3
            if attr_flags:
                conversion = "%s:%s" % (FSTRING_CONVERSION_MAP.get(
                    attr_flags, ""), fmt)
            else:
                conversion = ":%s" % fmt
        else:
            conversion = FSTRING_CONVERSION_MAP.get(attr, "")

        self.in_format_string = old_in_format_string
        f_str = "f%s" % escape_string("{%s%s}" % (value, conversion))
        self.write(f_str)

        self.prec = p
        self.prune()

    self.n_formatted_value2 = n_formatted_value2

    def n_joined_str(node):
        p = self.prec
        self.prec = 100

        old_in_format_string = self.in_format_string
        self.in_format_string = "joined_str"
        result = ""
        for expr in node[:-1]:
            assert expr == "expr"
            value = self.traverse(expr, indent="")
            if expr[0].kind.startswith("formatted_value"):
                # remove leading 'f'
                if value.startswith("f"):
                    value = value[1:]
                pass
            else:
                # {{ and }} in Python source-code format strings mean
                # { and } respectively. But only when *not* part of a
                # formatted value. However in the LOAD_STR
                # bytecode, the escaping of the braces has been
                # removed. So we need to put back the braces escaping in
                # reconstructing the source.
                assert expr[0] == "LOAD_STR"
                value = value.replace("{", "{{").replace("}", "}}")

            # Remove leading quotes
            result += strip_quotes(value)
            pass
        self.in_format_string = old_in_format_string
        if self.in_format_string:
            self.write(result)
        else:
            self.write("f%s" % escape_string(result))

        self.prec = p
        self.prune()

    self.n_joined_str = n_joined_str

    # FIXME: The following adjusts I guess a bug in the parser.
    # It might be as simple as renaming grammar symbol "testtrue" to "testtrue_or_false"
    # and then keeping this as is with the name change.
    # Fixing in the parsing by inspection is harder than doing it here.
    def n_testtrue(node):
        compare_chained37 = node[0]
        if (compare_chained37 == "compare_chained37"
                and compare_chained37[1] == "compare_chained1b_37"):
            compare_chained1b_37 = compare_chained37[1]
            if (len(compare_chained1b_37) > 2
                    and compare_chained1b_37[-2] == "JUMP_FORWARD"):
                node.kind = "testfalse"
                pass
            pass
        self.default(node)
        return

    self.n_testtrue = n_testtrue

    # 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 starred(node):
        l = len(node)
        assert l > 0
        pos_args = node[0]
        if pos_args == "expr":
            pos_args = pos_args[0]
        if pos_args == "tuple":
            build_tuple = pos_args[0]
            if build_tuple.kind.startswith("BUILD_TUPLE"):
                tuple_len = 0
            else:
                tuple_len = len(node) - 1
            star_start = 1
            template = "%C", (0, -1, ", ")
            self.template_engine(template, pos_args)
            if tuple_len == 0:
                self.write("*()")
                # That's it
                self.prune()
            self.write(", ")
        else:
            star_start = 0
        if l > 1:
            template = ("*%C", (star_start, -1, ", *"))
        else:
            template = ("*%c", (star_start, "expr"))

        self.template_engine(template, node)
        self.prune()

    self.n_starred = starred

    def return_closure(node):
        # Nothing should be output here
        self.prune()
        return

    self.n_return_closure = return_closure
def customize_for_version3(self, version):
    TABLE_DIRECT.update({
        "comp_for": (" for %c in %c", (2, "store"), (0, "expr")),
        "if_exp_not": (
            "%c if not %c else %c",
            (2, "expr"),
            (0, "expr"),
            (4, "expr"),
        ),
        "except_cond2": ("%|except %c as %c:\n", (1, "expr"), (5, "store")),
        "function_def_annotate": ("\n\n%|def %c%c\n", -1, 0),
        # When a generator is a single parameter of a function,
        # it doesn't need the surrounding parenethesis.
        "call_generator": ("%c%P", 0, (1, -1, ", ", 100)),
        "importmultiple": ("%|import %c%c\n", 2, 3),
        "import_cont": (", %c", 2),
        "raise_stmt2": ("%|raise %c from %c\n", 0, 1),
        "tf_tryelsestmtc3": ("%c%-%c%|else:\n%+%c", 1, 3, 5),
        "store_locals":
        ("%|# inspect.currentframe().f_locals = __locals__\n", ),
        "with": ("%|with %c:\n%+%c%-", 0, 3),
        "withasstmt": ("%|with %c as %c:\n%+%c%-", 0, 2, 3),
    })

    assert version >= 3.7

    # In 2.5+ and 3.0+ "except" handlers and the "finally" can appear in one
    # "try" statement. So the below has the effect of combining the
    # "tryfinally" with statement with the "try_except" statement.
    # FIXME: something doesn't smell right, since the semantics
    # are different. See test_fileio.py for an example that shows this.
    def tryfinallystmt(node):
        suite_stmts = node[1][0]
        if len(suite_stmts) == 1 and suite_stmts[0] == "stmt":
            stmt = suite_stmts[0]
            try_something = stmt[0]
            if try_something == "try_except":
                try_something.kind = "tf_try_except"
            if try_something.kind.startswith("tryelsestmt"):
                if try_something == "c_tryelsestmt":
                    try_something.kind = "tf_tryelsestmtc3"
                else:
                    try_something.kind = "tf_tryelsestmt"
        self.default(node)

    self.n_tryfinallystmt = tryfinallystmt

    def listcomp_closure3(node):
        """List comprehensions in Python 3 when handled as a closure.
        See if we can combine code.
        """
        p = self.prec
        self.prec = 27

        code_obj = node[1].attr
        assert iscode(code_obj)
        code = Code(code_obj, self.scanner, self.currentclass)
        ast = self.build_ast(code._tokens, code._customize)
        self.customize(code._customize)

        # skip over: sstmt, stmt, return, ret_expr
        # and other singleton derivations
        while len(ast) == 1 or (ast in ("sstmt", "return") and ast[-1]
                                in ("RETURN_LAST", "RETURN_VALUE")):
            self.prec = 100
            ast = ast[0]

        n = ast[1]

        # collections is the name of the expression(s) we are iterating over
        collections = [node[-3]]
        list_ifs = []

        assert n == "list_iter"
        stores = []
        # Find the list comprehension body. It is the inner-most
        # node that is not list_.. .
        while n == "list_iter":

            # recurse one step
            n = n[0]

            if n == "list_for":
                stores.append(n[2])
                n = n[3]
                if n[0] == "list_for":
                    # Dog-paddle down largely singleton reductions
                    # to find the collection (expr)
                    c = n[0][0]
                    if c == "expr":
                        c = c[0]
                    # FIXME: grammar is wonky here? Is this really an attribute?
                    if c == "attribute":
                        c = c[0]
                    collections.append(c)
                    pass
            elif n in ("list_if", "list_if_not", "list_if_or_not"):
                if n[0].kind == "expr":
                    list_ifs.append(n)
                else:
                    list_ifs.append([1])
                n = n[-2] if n[-1] == "come_from_opt" else n[-1]
                pass
            elif n == "list_if37":
                list_ifs.append(n)
                n = n[-1]
                pass
            elif n == "list_afor":
                collections.append(n[0][0])
                n = n[1]
                stores.append(n[1][0])
                n = n[3]
            pass

        assert n == "lc_body", ast
        self.preorder(n[0])

        # FIXME: add indentation around "for"'s and "in"'s
        n_colls = len(collections)
        for i, store in enumerate(stores):
            if i >= n_colls:
                break
            if collections[i] == "LOAD_DEREF" and co_flags_is_async(
                    code_obj.co_flags):
                self.write(" async")
                pass
            self.write(" for ")
            self.preorder(store)
            self.write(" in ")
            self.preorder(collections[i])
            if i < len(list_ifs):
                self.preorder(list_ifs[i])
                pass
            pass
        self.prec = p

    self.listcomp_closure3 = listcomp_closure3

    TABLE_DIRECT.update({
        "c_tryelsestmt": (
            "%|try:\n%+%c%-%c%|else:\n%+%c%-",
            (1, "c_suite_stmts"),
            (3, "c_except_handler"),
            (5, "else_suitec"),
        ),
        "LOAD_CLASSDEREF": ("%{pattr}", ),
    })

    TABLE_DIRECT.update({"LOAD_CLASSDEREF": ("%{pattr}", )})

    if version >= 3.7:
        customize_for_version37(self, version)
        if version >= 3.8:
            customize_for_version38(self, version)
            pass  # version >= 3.8
        pass  # 3.7
    return
示例#8
0
def customize_for_version3(self, version):
    TABLE_DIRECT.update({
        "comp_for": (" for %c in %c", (2, "store"), (0, "expr")),
        "conditionalnot": (
            "%c if not %c else %c",
            (2, "expr"),
            (0, "expr"),
            (4, "expr"),
        ),
        "except_cond2": ("%|except %c as %c:\n", (1, "expr"), (5, "store")),
        "function_def_annotate": ("\n\n%|def %c%c\n", -1, 0),
        # When a generator is a single parameter of a function,
        # it doesn't need the surrounding parenethesis.
        "call_generator": ("%c%P", 0, (1, -1, ", ", 100)),
        "importmultiple": ("%|import %c%c\n", 2, 3),
        "import_cont": (", %c", 2),
        "raise_stmt2": ("%|raise %c from %c\n", 0, 1),
        "store_locals":
        ("%|# inspect.currentframe().f_locals = __locals__\n", ),
        "withstmt": ("%|with %c:\n%+%c%-", 0, 3),
        "withasstmt": ("%|with %c as %c:\n%+%c%-", 0, 2, 3),
    })

    assert version >= 3.7

    def listcomp_closure3(node):
        """List comprehensions in Python 3 when handled as a closure.
        See if we can combine code.
        """
        p = self.prec
        self.prec = 27

        code = Code(node[1].attr, self.scanner, self.currentclass)
        ast = self.build_ast(code._tokens, code._customize)
        self.customize(code._customize)

        # skip over: sstmt, stmt, return, ret_expr
        # and other singleton derivations
        while len(ast) == 1 or (ast in ("sstmt", "return") and ast[-1]
                                in ("RETURN_LAST", "RETURN_VALUE")):
            self.prec = 100
            ast = ast[0]

        n = ast[1]

        # collections is the name of the expression(s) we are iterating over
        collections = [node[-3]]
        list_ifs = []

        if self.version == 3.0 and n != "list_iter":
            # FIXME 3.0 is a snowflake here. We need
            # special code for this. Not sure if this is totally
            # correct.
            stores = [ast[3]]
            assert ast[4] == "comp_iter"
            n = ast[4]
            # Find the list comprehension body. It is the inner-most
            # node that is not comp_.. .
            while n == "comp_iter":
                if n[0] == "comp_for":
                    n = n[0]
                    stores.append(n[2])
                    n = n[3]
                elif n[0] in ("comp_if", "comp_if_not"):
                    n = n[0]
                    # FIXME: just a guess
                    if n[0].kind == "expr":
                        list_ifs.append(n)
                    else:
                        list_ifs.append([1])
                    n = n[2]
                    pass
                else:
                    break
                pass

            # Skip over n[0] which is something like: _[1]
            self.preorder(n[1])

        else:
            assert n == "list_iter"
            stores = []
            # Find the list comprehension body. It is the inner-most
            # node that is not list_.. .
            while n == "list_iter":
                n = n[0]  # recurse one step
                if n == "list_for":
                    stores.append(n[2])
                    n = n[3]
                    if n[0] == "list_for":
                        # Dog-paddle down largely singleton reductions
                        # to find the collection (expr)
                        c = n[0][0]
                        if c == "expr":
                            c = c[0]
                        # FIXME: grammar is wonky here? Is this really an attribute?
                        if c == "attribute":
                            c = c[0]
                        collections.append(c)
                        pass
                elif n in ("list_if", "list_if_not"):
                    # FIXME: just a guess
                    if n[0].kind == "expr":
                        list_ifs.append(n)
                    else:
                        list_ifs.append([1])
                    n = n[2]
                    pass
                pass

            assert n == "lc_body", ast
            self.preorder(n[0])

        # FIXME: add indentation around "for"'s and "in"'s
        for i, store in enumerate(stores):
            self.write(" for ")
            self.preorder(store)
            self.write(" in ")
            self.preorder(collections[i])
            if i < len(list_ifs):
                self.preorder(list_ifs[i])
                pass
            pass
        self.prec = p

    self.listcomp_closure3 = listcomp_closure3

    def n_classdef3(node):
        # class definition ('class X(A,B,C):')
        cclass = self.currentclass

        # Pick out various needed bits of information
        # * class_name - the name of the class
        # * subclass_info - the parameters to the class  e.g.
        #      class Foo(bar, baz)
        #               ----------
        # * subclass_code - the code for the subclass body
        subclass_info = None
        if node == "classdefdeco2":
            if self.version >= 3.6:
                class_name = node[1][1].attr
            elif self.version <= 3.3:
                class_name = node[2][0].attr
            else:
                class_name = node[1][2].attr
            build_class = node
        else:
            build_class = node[0]
            if self.version >= 3.6:
                if build_class == "build_class_kw":
                    mkfunc = build_class[1]
                    assert mkfunc == "mkfunc"
                    subclass_info = build_class
                    if hasattr(mkfunc[0], "attr") and iscode(mkfunc[0].attr):
                        subclass_code = mkfunc[0].attr
                    else:
                        assert mkfunc[0] == "load_closure"
                        subclass_code = mkfunc[1].attr
                        assert iscode(subclass_code)
                if build_class[1][0] == "load_closure":
                    code_node = build_class[1][1]
                else:
                    code_node = build_class[1][0]
                class_name = code_node.attr.co_name
            else:
                class_name = node[1][0].attr
                build_class = node[0]

        assert "mkfunc" == build_class[1]
        mkfunc = build_class[1]
        if mkfunc[0] in ("kwargs", "no_kwargs"):
            if 3.0 <= self.version <= 3.2:
                for n in mkfunc:
                    if hasattr(n, "attr") and iscode(n.attr):
                        subclass_code = n.attr
                        break
                    elif n == "expr":
                        subclass_code = n[0].attr
                    pass
                pass
            else:
                for n in mkfunc:
                    if hasattr(n, "attr") and iscode(n.attr):
                        subclass_code = n.attr
                        break
                    pass
                pass
            if node == "classdefdeco2":
                subclass_info = node
            else:
                subclass_info = node[0]
        elif build_class[1][0] == "load_closure":
            # Python 3 with closures not functions
            load_closure = build_class[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_code = load_closure[-3].attr
            elif hasattr(load_closure[-2], "attr"):
                # Python 3.2 works like this
                subclass_code = load_closure[-2].attr
            else:
                raise "Internal Error n_classdef: cannot find class body"
            if hasattr(build_class[3], "__len__"):
                if not subclass_info:
                    subclass_info = build_class[3]
            elif hasattr(build_class[2], "__len__"):
                subclass_info = build_class[2]
            else:
                raise "Internal Error n_classdef: cannot superclass name"
        elif node == "classdefdeco2":
            subclass_info = node
            subclass_code = build_class[1][0].attr
        elif not subclass_info:
            if mkfunc[0] in ("no_kwargs", "kwargs"):
                subclass_code = mkfunc[1].attr
            else:
                subclass_code = mkfunc[0].attr
            if node == "classdefdeco2":
                subclass_info = node
            else:
                subclass_info = node[0]

        if node == "classdefdeco2":
            self.write("\n")
        else:
            self.write("\n\n")

        self.currentclass = str(class_name)
        self.write(self.indent, "class ", self.currentclass)

        self.print_super_classes3(subclass_info)
        self.println(":")

        # class body
        self.indent_more()
        self.build_class(subclass_code)
        self.indent_less()

        self.currentclass = cclass
        if len(self.param_stack) > 1:
            self.write("\n\n")
        else:
            self.write("\n\n\n")

        self.prune()

    self.n_classdef3 = n_classdef3

    TABLE_DIRECT.update({
        "tryelsestmtl3": (
            "%|try:\n%+%c%-%c%|else:\n%+%c%-",
            (1, "suite_stmts_opt"),
            (3, "except_handler"),
            (5, "else_suitel"),
        ),
        "LOAD_CLASSDEREF": ("%{pattr}", ),
    })

    TABLE_DIRECT.update({"LOAD_CLASSDEREF": ("%{pattr}", )})

    if version >= 3.7:
        customize_for_version37(self, version)
        if version >= 3.8:
            customize_for_version38(self, version)
            pass  # version >= 3.8
        pass  # 3.7
    return
示例#9
0
def customize_for_version3(self, version):
    TABLE_DIRECT.update({
        'comp_for': (' for %c in %c', (2, 'store'), (0, 'expr')),
        'conditionalnot':
        ('%c if not %c else %c', (2, 'expr'), (0, 'expr'), (4, 'expr')),
        'except_cond2': ('%|except %c as %c:\n', 1, 5),
        'function_def_annotate': ('\n\n%|def %c%c\n', -1, 0),
        'importmultiple': ('%|import %c%c\n', 2, 3),
        'import_cont': (', %c', 2),
        'store_locals':
        ('%|# inspect.currentframe().f_locals = __locals__\n', ),
        'withstmt': ('%|with %c:\n%+%c%-', 0, 3),
        'withasstmt': ('%|with %c as (%c):\n%+%c%-', 0, 2, 3),
    })

    assert version >= 3.7

    def n_classdef3(node):
        # class definition ('class X(A,B,C):')
        cclass = self.currentclass

        # Pick out various needed bits of information
        # * class_name - the name of the class
        # * subclass_info - the parameters to the class  e.g.
        #      class Foo(bar, baz)
        #               ----------
        # * subclass_code - the code for the subclass body
        subclass_info = None
        if node == 'classdefdeco2':
            if self.version >= 3.6:
                class_name = node[1][1].pattr
            elif self.version <= 3.3:
                class_name = node[2][0].pattr
            else:
                class_name = node[1][2].pattr
            build_class = node
        else:
            build_class = node[0]
            if self.version >= 3.6:
                if build_class == 'build_class_kw':
                    mkfunc = build_class[1]
                    assert mkfunc == 'mkfunc'
                    subclass_info = build_class
                    if hasattr(mkfunc[0], 'attr') and iscode(mkfunc[0].attr):
                        subclass_code = mkfunc[0].attr
                    else:
                        assert mkfunc[0] == 'load_closure'
                        subclass_code = mkfunc[1].attr
                        assert iscode(subclass_code)
                if build_class[1][0] == 'load_closure':
                    code_node = build_class[1][1]
                else:
                    code_node = build_class[1][0]
                class_name = code_node.attr.co_name
            else:
                class_name = node[1][0].pattr
                build_class = node[0]

        assert 'mkfunc' == build_class[1]
        mkfunc = build_class[1]
        if mkfunc[0] in ('kwargs', 'no_kwargs'):
            if 3.0 <= self.version <= 3.2:
                for n in mkfunc:
                    if hasattr(n, 'attr') and iscode(n.attr):
                        subclass_code = n.attr
                        break
                    elif n == 'expr':
                        subclass_code = n[0].attr
                    pass
                pass
            else:
                for n in mkfunc:
                    if hasattr(n, 'attr') and iscode(n.attr):
                        subclass_code = n.attr
                        break
                    pass
                pass
            if node == 'classdefdeco2':
                subclass_info = node
            else:
                subclass_info = node[0]
        elif build_class[1][0] == 'load_closure':
            # Python 3 with closures not functions
            load_closure = build_class[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_code = load_closure[-3].attr
            elif hasattr(load_closure[-2], 'attr'):
                # Python 3.2 works like this
                subclass_code = load_closure[-2].attr
            else:
                raise 'Internal Error n_classdef: cannot find class body'
            if hasattr(build_class[3], '__len__'):
                if not subclass_info:
                    subclass_info = build_class[3]
            elif hasattr(build_class[2], '__len__'):
                subclass_info = build_class[2]
            else:
                raise 'Internal Error n_classdef: cannot superclass name'
        elif self.version >= 3.6 and node == 'classdefdeco2':
            subclass_info = node
            subclass_code = build_class[1][0].attr
        elif not subclass_info:
            if mkfunc[0] in ('no_kwargs', 'kwargs'):
                subclass_code = mkfunc[1].attr
            else:
                subclass_code = mkfunc[0].attr
            if node == 'classdefdeco2':
                subclass_info = node
            else:
                subclass_info = node[0]

        if (node == 'classdefdeco2'):
            self.write('\n')
        else:
            self.write('\n\n')

        self.currentclass = str(class_name)
        self.write(self.indent, 'class ', self.currentclass)

        self.print_super_classes3(subclass_info)
        self.println(':')

        # class body
        self.indent_more()
        self.build_class(subclass_code)
        self.indent_less()

        self.currentclass = cclass
        if len(self.param_stack) > 1:
            self.write('\n\n')
        else:
            self.write('\n\n\n')

        self.prune()

    self.n_classdef3 = n_classdef3

    def n_yield_from(node):
        self.write('yield from')
        self.write(' ')
        if 3.3 <= self.version <= 3.4:
            self.preorder(node[0][0][0][0])
        elif self.version >= 3.5:
            self.preorder(node[0])
        else:
            assert False, "dunno about this python version"
        self.prune()  # stop recursing

    self.n_yield_from = n_yield_from

    def n_mkfunc_annotate(node):

        if self.version >= 3.3 or node[-2] == 'kwargs':
            # LOAD_CONST code object ..
            # LOAD_CONST        'x0'  if >= 3.3
            # EXTENDED_ARG
            # MAKE_FUNCTION ..
            code = node[-4]
        elif node[-3] == 'expr':
            code = node[-3][0]
        else:
            # LOAD_CONST code object ..
            # MAKE_FUNCTION ..
            code = node[-3]

        self.indent_more()
        for annotate_last in range(len(node) - 1, -1, -1):
            if node[annotate_last] == 'annotate_tuple':
                break

        # FIXME: the real situation is that when derived from
        # function_def_annotate we the name has been filled in.
        # But when derived from funcdefdeco it hasn't Would like a better
        # way to distinquish.
        if self.f.getvalue()[-4:] == 'def ':
            self.write(code.attr.co_name)

        # FIXME: handle and pass full annotate args
        make_function3_annotate(self,
                                node,
                                is_lambda=False,
                                code_node=code,
                                annotate_last=annotate_last)

        if len(self.param_stack) > 1:
            self.write('\n\n')
        else:
            self.write('\n\n\n')
        self.indent_less()
        self.prune()  # stop recursing

    self.n_mkfunc_annotate = n_mkfunc_annotate

    TABLE_DIRECT.update({
        'tryelsestmtl3':
        ('%|try:\n%+%c%-%c%|else:\n%+%c%-', (1, 'suite_stmts_opt'),
         (3, 'except_handler'), (5, 'else_suitel')),
        'LOAD_CLASSDEREF': ('%{pattr}', ),
    })

    TABLE_DIRECT.update({
        'LOAD_CLASSDEREF': ('%{pattr}', ),
    })

    if version >= 3.7:
        customize_for_version37(self, version)
        if version >= 3.8:
            customize_for_version38(self, version)
            pass  # version >= 3.8
        pass  # 3.7
    return
示例#10
0
def customize_for_version3(self, version):
    TABLE_DIRECT.update({
        "comp_for": (" for %c in %c", (2, "store"), (0, "expr")),
        "conditionalnot": (
            "%c if not %c else %c",
            (2, "expr"),
            (0, "expr"),
            (4, "expr"),
        ),
        "except_cond2": ("%|except %c as %c:\n", (1, "expr"), (5, "store")),
        "function_def_annotate": ("\n\n%|def %c%c\n", -1, 0),
        # When a generator is a single parameter of a function,
        # it doesn't need the surrounding parenethesis.
        "call_generator": ("%c%P", 0, (1, -1, ", ", 100)),
        "importmultiple": ("%|import %c%c\n", 2, 3),
        "import_cont": (", %c", 2),
        "raise_stmt2": ("%|raise %c from %c\n", 0, 1),
        "store_locals":
        ("%|# inspect.currentframe().f_locals = __locals__\n", ),
        "withstmt": ("%|with %c:\n%+%c%-", 0, 3),
        "withasstmt": ("%|with %c as (%c):\n%+%c%-", 0, 2, 3),
    })

    assert version >= 3.7

    def n_classdef3(node):
        # class definition ('class X(A,B,C):')
        cclass = self.currentclass

        # Pick out various needed bits of information
        # * class_name - the name of the class
        # * subclass_info - the parameters to the class  e.g.
        #      class Foo(bar, baz)
        #               ----------
        # * subclass_code - the code for the subclass body
        subclass_info = None
        if node == "classdefdeco2":
            if self.version >= 3.6:
                class_name = node[1][1].attr
            elif self.version <= 3.3:
                class_name = node[2][0].attr
            else:
                class_name = node[1][2].attr
            build_class = node
        else:
            build_class = node[0]
            if self.version >= 3.6:
                if build_class == "build_class_kw":
                    mkfunc = build_class[1]
                    assert mkfunc == "mkfunc"
                    subclass_info = build_class
                    if hasattr(mkfunc[0], "attr") and iscode(mkfunc[0].attr):
                        subclass_code = mkfunc[0].attr
                    else:
                        assert mkfunc[0] == "load_closure"
                        subclass_code = mkfunc[1].attr
                        assert iscode(subclass_code)
                if build_class[1][0] == "load_closure":
                    code_node = build_class[1][1]
                else:
                    code_node = build_class[1][0]
                class_name = code_node.attr.co_name
            else:
                class_name = node[1][0].attr
                build_class = node[0]

        assert "mkfunc" == build_class[1]
        mkfunc = build_class[1]
        if mkfunc[0] in ("kwargs", "no_kwargs"):
            if 3.0 <= self.version <= 3.2:
                for n in mkfunc:
                    if hasattr(n, "attr") and iscode(n.attr):
                        subclass_code = n.attr
                        break
                    elif n == "expr":
                        subclass_code = n[0].attr
                    pass
                pass
            else:
                for n in mkfunc:
                    if hasattr(n, "attr") and iscode(n.attr):
                        subclass_code = n.attr
                        break
                    pass
                pass
            if node == "classdefdeco2":
                subclass_info = node
            else:
                subclass_info = node[0]
        elif build_class[1][0] == "load_closure":
            # Python 3 with closures not functions
            load_closure = build_class[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_code = load_closure[-3].attr
            elif hasattr(load_closure[-2], "attr"):
                # Python 3.2 works like this
                subclass_code = load_closure[-2].attr
            else:
                raise "Internal Error n_classdef: cannot find class body"
            if hasattr(build_class[3], "__len__"):
                if not subclass_info:
                    subclass_info = build_class[3]
            elif hasattr(build_class[2], "__len__"):
                subclass_info = build_class[2]
            else:
                raise "Internal Error n_classdef: cannot superclass name"
        elif node == "classdefdeco2":
            subclass_info = node
            subclass_code = build_class[1][0].attr
        elif not subclass_info:
            if mkfunc[0] in ("no_kwargs", "kwargs"):
                subclass_code = mkfunc[1].attr
            else:
                subclass_code = mkfunc[0].attr
            if node == "classdefdeco2":
                subclass_info = node
            else:
                subclass_info = node[0]

        if node == "classdefdeco2":
            self.write("\n")
        else:
            self.write("\n\n")

        self.currentclass = str(class_name)
        self.write(self.indent, "class ", self.currentclass)

        self.print_super_classes3(subclass_info)
        self.println(":")

        # class body
        self.indent_more()
        self.build_class(subclass_code)
        self.indent_less()

        self.currentclass = cclass
        if len(self.param_stack) > 1:
            self.write("\n\n")
        else:
            self.write("\n\n\n")

        self.prune()

    self.n_classdef3 = n_classdef3

    TABLE_DIRECT.update({
        "tryelsestmtl3": (
            "%|try:\n%+%c%-%c%|else:\n%+%c%-",
            (1, "suite_stmts_opt"),
            (3, "except_handler"),
            (5, "else_suitel"),
        ),
        "LOAD_CLASSDEREF": ("%{pattr}", ),
    })

    TABLE_DIRECT.update({"LOAD_CLASSDEREF": ("%{pattr}", )})

    if version >= 3.7:
        customize_for_version37(self, version)
        if version >= 3.8:
            customize_for_version38(self, version)
            pass  # version >= 3.8
        pass  # 3.7
    return