Exemplo n.º 1
0
def customize_for_version37(self, version):
    ########################
    # Python 3.7+ changes
    #######################

    PRECEDENCE['attribute37'] = 2
    TABLE_DIRECT.update({
        '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')),
        'async_for_stmt': ('%|async for %c in %c:\n%+%c%-%-\n\n', (7, 'store'),
                           (1, 'expr'), (17, 'for_block')),
        'async_for_stmt37': ('%|async for %c in %c:\n%+%c%-%-\n\n',
                             (7, 'store'), (1, 'expr'), (16, 'for_block')),
        'attribute37': ('%c.%[1]{pattr}', 0),
        '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_chained1b_37':
        (' %[3]{pattr.replace("-", " ")} %p %p', (0, 19), (-4, 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)),
    })
Exemplo n.º 2
0
def customize_for_version25(self, version):

    ########################
    # Import style for 2.5+
    ########################
    TABLE_DIRECT.update({
        'importmultiple': ('%|import %c%c\n', 2, 3),
        'import_cont': (', %c', 2),
        # With/as is allowed as "from future" thing in 2.5
        # Note: It is safe to put the variables after "as" in parenthesis,
        # and sometimes it is needed.
        'withstmt': ('%|with %c:\n%+%c%-', 0, 3),
        'withasstmt': ('%|with %c as (%c):\n%+%c%-', 0, 2, 3),
    })

    # In 2.5+ "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
    def tryfinallystmt(node):
        if len(node[1][0]) == 1 and node[1][0][0] == 'stmt':
            if node[1][0][0][0] == 'try_except':
                node[1][0][0][0].kind = 'tf_try_except'
            if node[1][0][0][0] == 'tryelsestmt':
                node[1][0][0][0].kind = 'tf_tryelsestmt'
        self.default(node)

    self.n_tryfinallystmt = tryfinallystmt
Exemplo n.º 3
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")),
        'ifstmtl': ('%|if %c:\n%+%c%-', (0, "testexpr"), (1,
                                                          "_ifstmts_jumpl")),
        '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"))
    })
Exemplo n.º 4
0
def customize_for_version26_27(self, version):

    ########################################
    # Python 2.6+
    #    except <condition> as <var>
    # vs. older:
    #    except <condition> , <var>
    #
    # For 2.6 we use the older syntax which
    # matches how we parse this in bytecode
    ########################################
    if version > (2, 6):
        TABLE_DIRECT.update({
            'except_cond2': ('%|except %c as %c:\n', 1, 5),
            # 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)),
        })
    else:
        TABLE_DIRECT.update({
            'testtrue_then': ('not %p', (0, 22)),
        })

    # FIXME: this should be a transformation
    def n_call(node):
        mapping = self._get_mapping(node)
        key = node
        for i in mapping[1:]:
            key = key[i]
            pass
        if key.kind == 'CALL_FUNCTION_1':
            # A function with one argument. If this is a generator,
            # no parenthesis is needed.
            args_node = node[-2]
            if args_node == 'expr':
                n = args_node[0]
                if n == 'generator_exp':
                    node.kind = 'call_generator'
                    pass
                pass

        self.default(node)

    self.n_call = n_call
Exemplo n.º 5
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')),
        '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),
        'whilestmt38':
        ('%|while %c:\n%+%c%-\n\n', (0, 'testexpr'), (1, 'l_stmts')),
        'whileTruestmt38': ('%|while True:\n%+%c%-\n\n', (0, 'l_stmts')),
        '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')),
        'tryfinally38': ('%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
                         (3, 'returns'), 6),
    })
Exemplo n.º 6
0
def customize_for_version26_27(self, version):

    ########################################
    # Python 2.6+
    #    except <condition> as <var>
    # vs. older:
    #    except <condition> , <var>
    #
    # For 2.6 we use the older syntax which
    # matches how we parse this in bytecode
    ########################################
    if version > 2.6:
        TABLE_DIRECT.update({
            'except_cond2': ('%|except %c as %c:\n', 1, 5),
        })
    else:
        TABLE_DIRECT.update({
            'testtrue_then': ('not %p', (0, 22)),
        })
Exemplo n.º 7
0
def customize_for_version37(self, version):
    ########################
    # Python 3.7+ changes
    #######################

    PRECEDENCE['attribute37'] = 2
    PRECEDENCE['if_exp_37a'] = 28
    PRECEDENCE['if_exp_37b'] = 28

    TABLE_DIRECT.update({
        'and_not': ('%c and not %c', (0, 'expr'), (2, 'expr')),
        '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')),
        'async_for_stmt': ('%|async for %c in %c:\n%+%c%-\n\n', (7, 'store'),
                           (1, 'expr'), (17, 'for_block')),
        'async_for_stmt37': ('%|async for %c in %c:\n%+%c%-%-\n\n',
                             (7, 'store'), (1, 'expr'), (16, 'for_block')),
        'attribute37': ('%c.%[1]{pattr}', 0),
        '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)),
        '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)),
        'testfalse_not_or': ("not %c or %c", (0, "expr"), (2, "expr")),
        'testfalse_not_and': ("not (%c)", 0),
    })
Exemplo n.º 8
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.0:
        TABLE_R.update({
            'STORE_SLICE+0': ('%c[:]', 0),
            'STORE_SLICE+1': ('%c[%p:]', 0, (1, -1)),
            'STORE_SLICE+2': ('%c[:%p]', 0, (1, -1)),
            'STORE_SLICE+3': ('%c[%p:%p]', 0, (1, -1), (2, -1)),
            'DELETE_SLICE+0': ('%|del %c[:]\n', 0),
            'DELETE_SLICE+1': ('%|del %c[%c:]\n', 0, 1),
            'DELETE_SLICE+2': ('%|del %c[:%c]\n', 0, 1),
            'DELETE_SLICE+3': ('%|del %c[%c:%c]\n', 0, 1, 2),
        })
        TABLE_DIRECT.update({
            'raise_stmt2': ('%|raise %c, %c\n', 0, 1),
        })

        # exec as a built-in statement is only in Python 2.x
        def n_exec_stmt(self, node):
            """
            exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
            exec_stmt ::= expr exprlist EXEC_STMT
            """
            self.write(self.indent, 'exec ')
            self.preorder(node[0])
            if not node[1][0].isNone():
                sep = ' in '
                for subnode in node[1]:
                    self.write(sep)
                    sep = ", "
                    self.preorder(subnode)
            self.println()
            self.prune()  # stop recursing

        self.n_exec_smt = n_exec_stmt

    else:
        TABLE_DIRECT.update({
            # Gotta love Python for its futzing around with syntax like this
            'raise_stmt2': ('%|raise %c from %c\n', 0, 1),
        })

        if version >= 3.2:
            TABLE_DIRECT.update({
                'del_deref_stmt': ('%|del %c\n', 0),
                'DELETE_DEREF': ('%{pattr}', 0),
            })

    if version <= 2.4:
        TABLE_DIRECT.update({
            'importmultiple': ('%|import %c%c\n', 2, 3),
            'import_cont': (', %c', 2),
            'tryfinallystmt': ('%|try:\n%+%c%-%|finally:\n%+%c%-',
                               (1, 'suite_stmts_opt'), (5, 'suite_stmts_opt'))
        })
        if version == 2.3:
            TABLE_DIRECT.update({'if1_stmt': ('%|if 1\n%+%c%-', 5)})

        global NAME_MODULE
        NAME_MODULE = AST('stmt', [
            AST('assign', [
                AST('expr', [
                    Token('LOAD_GLOBAL',
                          pattr='__name__',
                          offset=0,
                          has_arg=True)
                ]),
                AST('store', [
                    Token('STORE_NAME',
                          pattr='__module__',
                          offset=3,
                          has_arg=True)
                ])
            ])
        ])
        pass
        if version <= 2.3:
            if version <= 2.1:
                TABLE_DIRECT.update({
                    'importmultiple': ('%c', 2),
                    # FIXME: not quite right. We have indiividual imports
                    # when there is in fact one: "import a, b, ..."
                    'imports_cont': ('%C%,', (1, 100, '\n')),
                })
                pass
            pass
        pass
    elif version >= 2.5:
        ########################
        # Import style for 2.5+
        ########################
        TABLE_DIRECT.update({
            'importmultiple': ('%|import %c%c\n', 2, 3),
            'import_cont': (', %c', 2),
            # With/as is allowed as "from future" thing in 2.5
            # Note: It is safe to put the variables after "as" in parenthesis,
            # and sometimes it is needed.
            'withstmt': ('%|with %c:\n%+%c%-', 0, 3),
            'withasstmt': ('%|with %c as (%c):\n%+%c%-', 0, 2, 3),
        })

        # In 2.5+ "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
        def tryfinallystmt(node):
            if len(node[1][0]) == 1 and node[1][0][0] == 'stmt':
                if node[1][0][0][0] == 'try_except':
                    node[1][0][0][0].kind = 'tf_try_except'
                if node[1][0][0][0] == 'tryelsestmt':
                    node[1][0][0][0].kind = 'tf_tryelsestmt'
            self.default(node)

        self.n_tryfinallystmt = tryfinallystmt

    ########################################
    # Python 2.6+
    #    except <condition> as <var>
    # vs. older:
    #    except <condition> , <var>
    #
    # For 2.6 we use the older syntax which
    # matches how we parse this in bytecode
    ########################################
    if version > 2.6:
        TABLE_DIRECT.update({
            'except_cond2': ('%|except %c as %c:\n', 1, 5),
        })
    else:
        TABLE_DIRECT.update({
            'except_cond3': ('%|except %c, %c:\n', 1, 6),
            'testtrue_then': ('not %p', (0, 22)),
        })

    if 2.4 <= version <= 2.6:
        TABLE_DIRECT.update({
            'comp_for': (' for %c in %c', 3, 1),
        })
    else:
        TABLE_DIRECT.update({
            'comp_for': (' for %c in %c%c', 2, 0, 3),
        })

    if version >= 3.0:
        TABLE_DIRECT.update({
            'function_def_annotate': ('\n\n%|def %c%c\n', -1, 0),
            'store_locals':
            ('%|# inspect.currentframe().f_locals = __locals__\n', ),
        })

        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,
                                    codeNode=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

        if version >= 3.4:
            ########################
            # Python 3.4+ Additions
            #######################
            TABLE_DIRECT.update({
                'LOAD_CLASSDEREF': ('%{pattr}', ),
            })
            ########################
            # Python 3.5+ Additions
            #######################
            if version >= 3.5:
                TABLE_DIRECT.update({
                    'await_expr': ('await %c', 0),
                    'await_stmt': ('%|%c\n', 0),
                    'async_for_stmt':
                    ('%|async for %c in %c:\n%+%c%-\n\n', 9, 1, 25),
                    'async_forelse_stmt':
                    ('%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 9, 1,
                     25, 28),
                    'async_with_stmt': ('%|async with %c:\n%+%c%-', 0, 7),
                    'async_with_as_stmt':
                    ('%|async with %c as %c:\n%+%c%-', 0, 6, 7),
                    'unmap_dict': ('{**%C}', (0, -1, ', **')),
                    # 'unmapexpr':	       ( '{**%c}', 0), # done by n_unmapexpr
                })

                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

                if version == 3.5:

                    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'):
                            nargs = node[-1].attr & 0xFF
                            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 == 'pos_arg':
                                args_node = args_node[0]
                            if args_node == '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:
                                template = ('*%c)', -2)
                                self.template_engine(template, node)
                            self.prune()

                        self.default(node)

                    self.n_call = n_call

                def n_function_def(node):
                    if self.version == 3.6:
                        code_node = node[0][0]
                    else:
                        code_node = node[0][1]

                    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

            if version >= 3.6:
                ########################
                # Python 3.6+ Additions
                #######################

                # Value 100 is important; it is exactly
                # module/function precidence.
                PRECEDENCE['call_kw'] = 100

                TABLE_DIRECT.update({
                    'tryfinally36': ('%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
                                     (1, 'returns'), 3),
                    'fstring_expr': ("{%c%{conversion}}", 0),
                    # FIXME: the below assumes the format strings
                    # don't have ''' in them. Fix this properly
                    'fstring_single': ("f'''{%c%{conversion}}'''", 0),
                    'fstring_multi': ("f'''%c'''", 0),
                    'func_args36': ("%c(**", 0),
                    'try_except36': ('%|try:\n%+%c%-%c\n\n', 1, 2),
                    'unpack_list': ('*%c', (0, 'list')),
                    'call_ex': ('%c(%c)', (0, 'expr'), 1),
                    'call_ex_kw': ('%c(%c)', (0, 'expr'), 2),
                })

                TABLE_R.update({
                    'CALL_FUNCTION_EX': ('%c(*%P)', 0, (1, 2, ', ', 100)),
                    # Not quite right
                    'CALL_FUNCTION_EX_KW': ('%c(**%C)', 0, (2, 3, ',')),
                })

                def build_unpack_tuple_with_call(node):

                    if node[0] == 'expr':
                        tup = node[0][0]
                    else:
                        tup = node[0]
                        pass
                    assert tup == 'tuple'
                    self.call36_tuple(tup)

                    buwc = node[-1]
                    assert buwc.kind.startswith('BUILD_TUPLE_UNPACK_WITH_CALL')
                    for n in node[1:-1]:
                        self.f.write(', *')
                        self.preorder(n)
                        pass
                    self.prune()
                    return

                self.n_build_tuple_unpack_with_call = build_unpack_tuple_with_call

                def build_unpack_map_with_call(node):
                    n = node[0]
                    if n == 'expr':
                        n = n[0]
                    if n == 'dict':
                        self.call36_dict(n)
                        first = 1
                        sep = ', **'
                    else:
                        first = 0
                        sep = '**'
                    for n in node[first:-1]:
                        self.f.write(sep)
                        self.preorder(n)
                        sep = ', **'
                        pass
                    self.prune()
                    return

                self.n_build_map_unpack_with_call = build_unpack_map_with_call

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

                    # This is weird shit. Thanks Python!
                    self.preorder(node[0])
                    self.write('(')

                    assert node[1] == 'build_tuple_unpack_with_call'
                    btuwc = node[1]
                    tup = btuwc[0]
                    if tup == 'expr':
                        tup = tup[0]
                    assert tup == 'tuple'
                    self.call36_tuple(tup)
                    assert node[2] == 'build_map_unpack_with_call'

                    self.write(', ')
                    d = node[2][0]
                    if d == 'expr':
                        d = d[0]
                    assert d == 'dict'
                    self.call36_dict(d)

                    args = btuwc[1]
                    self.write(', *')
                    self.preorder(args)

                    self.write(', **')
                    star_star_args = node[2][1]
                    if star_star_args == 'expr':
                        star_star_args = star_star_args[0]
                    self.preorder(star_star_args)
                    self.write(')')
                    self.prune()

                self.n_call_ex_kw2 = call_ex_kw2

                def call_ex_kw3(node):
                    """Handle CALL_FUNCTION_EX 1 (have KW) but without
                    BUILD_MAP_UNPACK_WITH_CALL"""
                    self.preorder(node[0])
                    self.write('(')
                    args = node[1][0]
                    if args == 'expr':
                        args = args[0]
                    if args == 'tuple':
                        if self.call36_tuple(args) > 0:
                            self.write(', ')
                            pass
                        pass

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

                    kwargs = node[2]
                    if kwargs == 'expr':
                        kwargs = kwargs[0]
                    self.write('**')
                    self.preorder(kwargs)
                    self.write(')')
                    self.prune()

                self.n_call_ex_kw3 = call_ex_kw3

                def call_ex_kw4(node):
                    """Handle CALL_FUNCTION_EX 2  (have KW) but without
                    BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""
                    self.preorder(node[0])
                    self.write('(')
                    args = node[1][0]
                    if args == 'tuple':
                        if self.call36_tuple(args) > 0:
                            self.write(', ')
                            pass
                        pass
                    else:
                        self.write('*')
                        self.preorder(args)
                        self.write(', ')
                        pass

                    kwargs = node[2]
                    if kwargs == 'expr':
                        kwargs = kwargs[0]
                    self.write('**')
                    self.preorder(kwargs)
                    self.write(')')
                    self.prune()

                self.n_call_ex_kw4 = call_ex_kw4

                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:
                        assert False, "Don't known to to untangle dictionary"

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

                self.call36_dict = call36_dict

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

                def 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':
                        self.write(node[0].attr)
                        self.prune()
                    else:
                        self.default(node)

                self.n_formatted_value = n_formatted_value

                def f_conversion(node):
                    node.conversion = FSTRING_CONVERSION_MAP.get(
                        node.data[1].attr, '')

                def fstring_expr(node):
                    f_conversion(node)
                    self.default(node)

                self.n_fstring_expr = fstring_expr

                def fstring_single(node):
                    f_conversion(node)
                    self.default(node)

                self.n_fstring_single = fstring_single

                # def kwargs_only_36(node):
                #     keys = node[-1].attr
                #     num_kwargs = len(keys)
                #     values = node[:num_kwargs]
                #     for i, (key, value) in enumerate(zip(keys, values)):
                #         self.write(key + '=')
                #         self.preorder(value)
                #         if i < num_kwargs:
                #             self.write(',')
                #     self.prune()
                #     return
                # self.n_kwargs_only_36 = kwargs_only_36

                def kwargs_36(node):
                    self.write('(')
                    keys = node[-1].attr
                    num_kwargs = len(keys)
                    num_posargs = len(node) - (num_kwargs + 1)
                    n = len(node)
                    assert n >= len(keys)+1, \
                      'not enough parameters keyword-tuple values'
                    # try:
                    #     assert n >= len(keys)+1, \
                    #         'not enough parameters keyword-tuple values'
                    # except:
                    #     from trepan.api import debug; debug()
                    sep = ''
                    # FIXME: adjust output for line breaks?
                    for i in range(num_posargs):
                        self.write(sep)
                        self.preorder(node[i])
                        sep = ', '

                    i = num_posargs
                    j = 0
                    # FIXME: adjust output for line breaks?
                    while i < n - 1:
                        self.write(sep)
                        self.write(keys[j] + '=')
                        self.preorder(node[i])
                        sep = ', '
                        i += 1
                        j += 1
                    self.write(')')
                    self.prune()
                    return

                self.n_kwargs_36 = kwargs_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':
                        star_start = 1
                        template = '%C', (0, -1, ', ')
                        self.template_engine(template, pos_args)
                        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
                pass  # version >= 3.6
            pass  # version >= 3.4
        pass  # version >= 3.0
    return
Exemplo n.º 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),

        # 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),
        'kwarg': ('%[0]{attr}=%c', 1),
        '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.0

    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 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

    if version == 3.0:
        # In Python 3.0 there is code to move from _[dd] into
        # the iteration variable. These rules we can ignore
        # since we pick up the iteration variable some other way and
        # we definitely don't include in the source  _[dd].
        def n_comp_iter(node):
            if node[0] == 'expr':
                n = node[0][0]
                if (n == 'LOAD_FAST' and n.pattr[0:2] == '_['):
                    self.prune()
                    pass
                pass
            # Not this special case, procede as normal...
            self.default(node)

        self.n_comp_iter = n_comp_iter

    if version >= 3.3:

        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

    if 3.2 <= version <= 3.4:

        def n_call(node):

            mapping = self._get_mapping(node)
            key = node
            for i in mapping[1:]:
                key = key[i]
                pass
            if key.kind.startswith('CALL_FUNCTION_VAR_KW'):
                # We may want to fill this in...
                # But it is distinct from CALL_FUNCTION_VAR below
                pass
            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 kwargs != 0:
                    # kwargs == 0 is handled by the table entry
                    # Should probably handle it here though.
                    if nargs == 0:
                        template = ('%c(*%c, %C)', 0, -2, (1, kwargs + 1,
                                                           ', '))
                    else:
                        template = ('%c(%C, *%c, %C)', 0, (1, nargs + 1, ', '),
                                    -2, (-2 - kwargs, -2, ', '))
                    self.template_engine(template, node)
                    self.prune()
            else:
                gen_function_parens_adjust(key, node)
            self.default(node)

        self.n_call = n_call
    elif version < 3.2:

        def n_call(node):
            mapping = self._get_mapping(node)
            key = node
            for i in mapping[1:]:
                key = key[i]
                pass
            gen_function_parens_adjust(key, node)
            self.default(node)

        self.n_call = n_call

    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')),
    })
    if version >= 3.4:
        #######################
        # Python 3.4+ Changes #
        #######################
        TABLE_DIRECT.update({
            'LOAD_CLASSDEREF': ('%{pattr}', ),
        })
        if version >= 3.5:
            customize_for_version35(self, version)
            if version >= 3.6:
                customize_for_version36(self, version)
                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
                pass  # 3.6
            pass  # 3.5
        pass  # 3.4
    return
Exemplo n.º 10
0
def customize_for_version3(self, version):
    TABLE_DIRECT.update({
        'function_def_annotate': ( '\n\n%|def %c%c\n', -1, 0),
        'store_locals': ( '%|# inspect.currentframe().f_locals = __locals__\n', ),
        })

    assert version >= 3.0

    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

    if version == 3.0:
        # In Python 3.0 there is code to move from _[dd] into
        # the iteration variable. These rules we can ignore
        # since we pick up the iteration variable some other way and
        # we definitely don't include in the source  _[dd].
        def n_comp_iter(node):
            if node[0] == 'expr':
                n = node[0][0]
                if (n == 'LOAD_FAST' and
                    n.pattr[0:2] == '_['):
                    self.prune()
                    pass
                pass
            # Not this special case, procede as normal...
            self.default(node)
        self.n_comp_iter = n_comp_iter

    if version >= 3.3:
        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

    if 3.2 <= version <= 3.4:
        def n_call(node):
            mapping = self._get_mapping(node)
            key = node
            for i in mapping[1:]:
                key = key[i]
                pass
            if key.kind.startswith('CALL_FUNCTION_VAR_KW'):
                # We may want to fill this in...
                # But it is distinct from CALL_FUNCTION_VAR below
                pass
            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 kwargs != 0:
                    # kwargs == 0 is handled by the table entry
                    # Should probably handle it here though.
                    if nargs == 0:
                        template = ('%c(*%c, %C)',
                                    0, -2, (1, kwargs+1, ', '))
                    else:
                        template = ('%c(%C, *%c, %C)',
                                    0, (1, nargs+1, ', '),
                                    -2, (-2-kwargs, -2, ', '))
                    self.template_engine(template, node)
                    self.prune()

            self.default(node)
        self.n_call = n_call


    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

    if version >= 3.4:
        ########################
        # Python 3.4+ Additions
        #######################
        TABLE_DIRECT.update({
            'LOAD_CLASSDEREF':	( '%{pattr}', ),
            })

        ########################
        # Python 3.5+ Additions
        #######################
        if version >= 3.5:
            TABLE_DIRECT.update({
                'await_expr':	       ( 'await %c', 0),
                'await_stmt':	       ( '%|%c\n', 0),
                'async_for_stmt':      (
                    '%|async for %c in %c:\n%+%c%-\n\n', 9, 1, 25 ),
                'async_forelse_stmt':  (
                    '%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
                    9, 1, 25, (27, 'else_suite') ),
                'async_with_stmt':     (
                    '%|async with %c:\n%+%c%-', 0, 7),
                'async_with_as_stmt':  (
                    '%|async with %c as %c:\n%+%c%-', 0, 6, 7),
                'unmap_dict':	       ( '{**%C}', (0, -1, ', **') ),
                # 'unmapexpr':	       ( '{**%c}', 0), # done by n_unmapexpr

            })

            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

            if version == 3.5:
                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 n_function_def(node):
                if self.version >= 3.6:
                    code_node = node[0][0]
                else:
                    code_node = node[0][1]

                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


        if version >= 3.6:
            ########################
            # Python 3.6+ Additions
            #######################

            # Value 100 is important; it is exactly
            # module/function precidence.
            PRECEDENCE['call_kw']     = 100
            PRECEDENCE['call_kw36']   = 100
            PRECEDENCE['call_ex']     = 100
            PRECEDENCE['call_ex_kw']  = 100
            PRECEDENCE['call_ex_kw2'] = 100
            PRECEDENCE['call_ex_kw3'] = 100
            PRECEDENCE['call_ex_kw4'] = 100
            PRECEDENCE['unmap_dict']  = 0

            TABLE_DIRECT.update({
                'tryfinally36':  ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
                                   (1, 'returns'), 3 ),
                'fstring_expr':   ( "{%c%{conversion}}", 0),
                # FIXME: the below assumes the format strings
                # don't have ''' in them. Fix this properly
                'fstring_single': ( "f'''{%c%{conversion}}'''", 0),
                'fstring_multi':  ( "f'''%c'''", 0),
                'func_args36':    ( "%c(**", 0),
                'try_except36':   ( '%|try:\n%+%c%-%c\n\n', 1, 2 ),
                'except_return':  ( '%|except:\n%+%c%-', 3 ),
                'unpack_list':    ( '*%c', (0, 'list') ),
                'tryfinally_return_stmt':
                      ( '%|try:\n%+%c%-%|finally:\n%+%|return%-\n\n', 1 ),

                'call_ex' : (
                    '%c(%p)',
                    (0, 'expr'), (1, 100)),
                'call_ex_kw' : (
                    '%c(%p)',
                    (0, 'expr'), (2, 100)),

            })

            TABLE_R.update({
                'CALL_FUNCTION_EX': ('%c(*%P)', 0, (1, 2, ', ', 100)),
                # Not quite right
                'CALL_FUNCTION_EX_KW': ('%c(**%C)', 0, (2, 3, ',')),
                })

            def build_unpack_tuple_with_call(node):

                if node[0] == 'expr':
                    tup = node[0][0]
                else:
                    tup = node[0]
                    pass
                assert tup == 'tuple'
                self.call36_tuple(tup)

                buwc = node[-1]
                assert buwc.kind.startswith('BUILD_TUPLE_UNPACK_WITH_CALL')
                for n in node[1:-1]:
                    self.f.write(', *')
                    self.preorder(n)
                    pass
                self.prune()
                return
            self.n_build_tuple_unpack_with_call = build_unpack_tuple_with_call

            def build_unpack_map_with_call(node):
                n = node[0]
                if n == 'expr':
                    n = n[0]
                if n == 'dict':
                    self.call36_dict(n)
                    first = 1
                    sep = ', **'
                else:
                    first = 0
                    sep = '**'
                for n in node[first:-1]:
                    self.f.write(sep)
                    self.preorder(n)
                    sep = ', **'
                    pass
                self.prune()
                return
            self.n_build_map_unpack_with_call = build_unpack_map_with_call

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

                # This is weird shit. Thanks Python!
                self.preorder(node[0])
                self.write('(')

                assert node[1] == 'build_tuple_unpack_with_call'
                btuwc = node[1]
                tup = btuwc[0]
                if tup == 'expr':
                    tup = tup[0]
                assert tup == 'tuple'
                self.call36_tuple(tup)
                assert node[2] == 'build_map_unpack_with_call'

                self.write(', ')
                d = node[2][0]
                if d == 'expr':
                    d = d[0]
                assert d == 'dict'
                self.call36_dict(d)

                args = btuwc[1]
                self.write(', *')
                self.preorder(args)

                self.write(', **')
                star_star_args = node[2][1]
                if star_star_args == 'expr':
                    star_star_args = star_star_args[0]
                self.preorder(star_star_args)
                self.write(')')
                self.prune()
            self.n_call_ex_kw2 = call_ex_kw2

            def call_ex_kw3(node):
                """Handle CALL_FUNCTION_EX 1 (have KW) but without
                BUILD_MAP_UNPACK_WITH_CALL"""
                self.preorder(node[0])
                self.write('(')
                args = node[1][0]
                if args == 'expr':
                    args = args[0]
                if args == 'tuple':
                    if self.call36_tuple(args) > 0:
                        self.write(', ')
                        pass
                    pass

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

                kwargs = node[2]
                if kwargs == 'expr':
                    kwargs = kwargs[0]
                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 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


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

            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':
                    self.write(node[0].attr)
                    self.prune()
                else:
                    self.default(node)
            self.n_formatted_value = n_formatted_value

            def f_conversion(node):
                node.conversion = FSTRING_CONVERSION_MAP.get(node.data[1].attr, '')

            def fstring_expr(node):
                f_conversion(node)
                self.default(node)
            self.n_fstring_expr = fstring_expr

            def fstring_single(node):
                f_conversion(node)
                self.default(node)
            self.n_fstring_single = fstring_single

            # def kwargs_only_36(node):
            #     keys = node[-1].attr
            #     num_kwargs = len(keys)
            #     values = node[:num_kwargs]
            #     for i, (key, value) in enumerate(zip(keys, values)):
            #         self.write(key + '=')
            #         self.preorder(value)
            #         if i < num_kwargs:
            #             self.write(',')
            #     self.prune()
            #     return
            # self.n_kwargs_only_36 = kwargs_only_36

            def n_call_kw36(node):
                self.template_engine(("%c(", 0), 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 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

            if version >= 3.7:
                PRECEDENCE['attribute37'] = 2
                TABLE_DIRECT.update({
                    'attribute37':  ( '%c.%[1]{pattr}', 0 ),
                    'async_forelse_stmt':  (
                        '%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
                        7, 1, 17, (25, 'else_suite') ),
                    'async_for_stmt':  (
                        '%|async for %c in %c:\n%+%c%-%-\n\n',
                        7, 1, 17),
                    })
                pass
            pass # version >= 3.6
        pass # version >= 3.4
    return
def customize_for_version36(self, version):
    PRECEDENCE['call_kw'] = 0
    PRECEDENCE['call_kw36'] = 1
    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['unmap_dict'] = 0
    PRECEDENCE['formatted_value1'] = 100

    TABLE_DIRECT.update({
        'tryfinally36':
        ('%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', (1, 'returns'), 3),
        'func_args36': ("%c(**", 0),
        'try_except36': ('%|try:\n%+%c%-%c\n\n', 1, -2),
        'except_return': ('%|except:\n%+%c%-', 3),
        'unpack_list': ('*%c', (0, 'list')),
        'tryfinally_return_stmt':
        ('%|try:\n%+%c%-%|finally:\n%+%|return%-\n\n', 1),
        'async_for_stmt36': ('%|async for %c in %c:\n%+%c%-%-\n\n',
                             (9, 'store'), (1, 'expr'), (18, 'for_block')),
        'call_ex': ('%c(%p)', (0, 'expr'), (1, 100)),
    })

    TABLE_R.update({
        'CALL_FUNCTION_EX': ('%c(*%P)', 0, (1, 2, ', ', 100)),
        # Not quite right
        'CALL_FUNCTION_EX_KW': ('%c(**%C)', 0, (2, 3, ',')),
    })

    def build_unpack_tuple_with_call(node):
        n = node[0]
        if n == 'expr':
            n = n[0]
        if n == 'tuple':
            self.call36_tuple(n)
            first = 1
            sep = ', *'
        elif n == '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 == '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

    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

    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'
        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_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.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
Exemplo n.º 12
0
def customize_for_version36(self, version):
    PRECEDENCE["call_kw"] = 0
    PRECEDENCE["call_kw36"] = 1
    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["unmap_dict"] = 0
    PRECEDENCE["formatted_value1"] = 100

    TABLE_DIRECT.update({
        "ann_assign_init_value": (
            "%|%c = %p\n",
            (-1, "store_annotation"),
            (0, "expr", 200),
        ),
        "ann_assign_no_init": ("%|%c\n", (0, "store_annotation")),
        "async_for_stmt36": (
            "%|async for %c in %c:\n%+%c%-\n\n",
            (9, "store"),
            (1, "expr"),
            (18, "for_block"),
        ),
        "call_ex": ("%c(%p)", (0, "expr"), (1, 100)),
        "except_return": ("%|except:\n%+%c%-", 3),
        "func_args36": ("%c(**", 0),
        # This comes from 3.7. Eventually we will rebase from 3.7
        # and then this can go away
        "if_exp37": ("%p if %c else %c", (1, "expr", 27), 0, 3),
        "ifstmtl":
        ("%|if %c:\n%+%c%-", (0, "testexpr"), (1, "_ifstmts_jumpl")),
        "try_except36": ("%|try:\n%+%c%-%c\n\n", 1, -2),
        "tryfinally36":
        ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n", (1, "returns"), 3),
        "tryfinally_return_stmt":
        ("%|try:\n%+%c%-%|finally:\n%+%|return%-\n\n", 1),
        "unpack_list": ("*%c", (0, "list")),
        "store_annotation": ("%[1]{pattr}: %c", 0),
    })

    TABLE_R.update({
        "CALL_FUNCTION_EX": ("%c(*%P)", 0, (1, 2, ", ", 100)),
        # Not quite right
        "CALL_FUNCTION_EX_KW": ("%c(**%C)", 0, (2, 3, ",")),
    })

    def build_unpack_tuple_with_call(node):
        n = node[0]
        if n == "expr":
            n = n[0]
        if n == "tuple":
            self.call36_tuple(n)
            first = 1
            sep = ", *"
        elif n == "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 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 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" and kwargs[0] != "dict":
            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 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

    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

    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" or expr[0] == "LOAD_CONST"
                        and isinstance(expr[0].attr, unicode))
                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

    # 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
Exemplo n.º 13
0
def customize_for_version35(self, version):
    TABLE_DIRECT.update(
        {
            # nested await expressions like:
            #   return await (await bar())
            # need parenthesis.
            "await_expr": ("await %p", (0, PRECEDENCE["await_expr"]-1)),

            "await_stmt": ("%|%c\n", 0),
            "async_for_stmt": ("%|async for %c in %c:\n%+%|%c%-\n\n", 9, 1, 25),
            "async_forelse_stmt": (
                "%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
                9,
                1,
                25,
                (27, "else_suite"),
            ),
            "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,
            ),
            "unmap_dict": ("{**%C}", (0, -1, ", **")),
            # "unmapexpr":	       ( "{**%c}", 0), # done by n_unmapexpr
        }
    )

    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

    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 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
        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(%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()
        else:
            gen_function_parens_adjust(key, node)

        self.prec = 100
        self.default(node)

    self.n_call = n_call

    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 co_flags_is_async(code_node.attr.co_flags)

    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_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
Exemplo n.º 14
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.0:
        TABLE_DIRECT.update({
            # Gotta love Python for its futzing around with syntax like this
            'raise_stmt2':	 ( '%|raise %c from %c\n', 0, 1),
        })

        if version >= 3.2:
            TABLE_DIRECT.update({
            'del_deref_stmt': ( '%|del %c\n', 0),
            'DELETE_DEREF': ( '%{pattr}', 0 ),
            })
        from uncompyle6.semantics.customize3 import customize_for_version3
        customize_for_version3(self, version)
    else:  # < 3.0
        if 2.4 <= version <= 2.6:
            TABLE_DIRECT.update({
                'comp_for':	( ' for %c in %c', 3, 1 ),
            })
        else:
            TABLE_DIRECT.update({
                'comp_for':	( ' for %c in %c%c', 2, 0, 3 ),
            })

        if version >= 2.5:
            from uncompyle6.semantics.customize25 import customize_for_version25
            customize_for_version25(self, version)

            if version >= 2.6:
                from uncompyle6.semantics.customize26_27 import customize_for_version26_27
                customize_for_version26_27(self, version)
                pass
        else:  # < 2.5
            global NAME_MODULE
            NAME_MODULE = SyntaxTree('stmt',
                              [ SyntaxTree('assign',
                                    [ SyntaxTree('expr',
                                          [Token('LOAD_GLOBAL', pattr='__name__',
                                                 offset=0, has_arg=True)]),
                                      SyntaxTree('store',
                                          [ Token('STORE_NAME', pattr='__module__',
                                                  offset=3, has_arg=True)])
                                    ])])
            TABLE_DIRECT.update({
                'importmultiple': ( '%|import %c%c\n', 2, 3),
                'import_cont'   : ( ', %c', 2),
                'tryfinallystmt': ( '%|try:\n%+%c%-%|finally:\n%+%c%-',
                                    (1, 'suite_stmts_opt') ,
                                    (5, 'suite_stmts_opt') )
                })
            if version == 2.4:
                def n_iftrue_stmt24(node):
                    self.template_engine(('%c', 0), node)
                    self.default(node)
                    self.prune()
                self.n_iftrue_stmt24 = n_iftrue_stmt24
            else:  # version <= 2.3:
                TABLE_DIRECT.update({
                    'if1_stmt':	( '%|if 1\n%+%c%-', 5 )
                })
                if version <= 2.1:
                    TABLE_DIRECT.update({
                        'importmultiple': ( '%c', 2 ),
                        # FIXME: not quite right. We have indiividual imports
                        # when there is in fact one: "import a, b, ..."
                        'imports_cont': ( '%C%,', (1, 100, '\n') ),
                    })
                    pass
                pass
            pass # < 2.5

        # < 3.0 continues

        TABLE_R.update({
            'STORE_SLICE+0':	( '%c[:]', 0 ),
            'STORE_SLICE+1':	( '%c[%p:]', 0, (1, -1) ),
            'STORE_SLICE+2':	( '%c[:%p]', 0, (1, -1) ),
            'STORE_SLICE+3':	( '%c[%p:%p]', 0, (1, -1), (2, -1) ),
            'DELETE_SLICE+0':	( '%|del %c[:]\n', 0 ),
            'DELETE_SLICE+1':	( '%|del %c[%c:]\n', 0, 1 ),
            'DELETE_SLICE+2':	( '%|del %c[:%c]\n', 0, 1 ),
            'DELETE_SLICE+3':	( '%|del %c[%c:%c]\n', 0, 1, 2 ),
        })
        TABLE_DIRECT.update({
            'raise_stmt2':	( '%|raise %c, %c\n', 0, 1),
        })

        # exec as a built-in statement is only in Python 2.x
        def n_exec_stmt(node):
            """
            exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
            exec_stmt ::= expr exprlist EXEC_STMT
            """
            self.write(self.indent, 'exec ')
            self.preorder(node[0])
            if not node[1][0].isNone():
                sep = ' in '
                for subnode in node[1]:
                    self.write(sep); sep = ", "
                    self.preorder(subnode)
            self.println()
            self.prune() # stop recursing
        self.n_exec_smt = n_exec_stmt

        pass # < 3.0

    return
Exemplo n.º 15
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",
                             (2, "store"), (0, "expr"), (3, "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")),
        "call_stmt": ("%|%c\n", 0),
        "except_cond_as": (
            "%|except %c as %c:\n",
            (1, "expr"),
            (-2, "STORE_FAST"),
        ),
        'except_handler38': ('%c', (2, 'except_stmts')),
        'except_handler38a': ('%c', (-2, 'stmts')),
        "except_handler_as": (
            "%c%+\n%+%c%-",
            (1, "except_cond_as"),
            (2, "tryfinallystmt"),
        ),
        '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),
        '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")),
        'ifstmtl': ('%|if %c:\n%+%c%-', (0, "testexpr"), (1,
                                                          "_ifstmts_jumpl")),
        '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_as": (
            "%|try:\n%+%c%-\n%|%-%c\n\n",
            (-4,
             "suite_stmts"),  # Go from the end because of POP_BLOCK variation
            (-3, "except_handler_as"),
        ),
        "try_except_ret38": (
            "%|try:\n%+%c%-\n%|except:\n%+%|%c%-\n\n",
            (1, "returns"),
            (2, "except_ret38a"),
        ),
        'tryfinally38rstmt': (
            '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
            (0, "sf_pb_call_returns"),
            (-1, ("ss_end_finally", "suite_stmts")),
        ),
        "tryfinally38rstmt2": ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
                               (4, "returns"), -2, "ss_end_finally"),
        "tryfinally38rstmt3":
        ("%|try:\n%+%|return %c%-\n%|finally:\n%+%c%-\n\n", (1, "expr"),
         (-1, "ss_end_finally")),
        '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))
    })
Exemplo n.º 16
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"), (2, "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_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),
        ),
        '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",
            (2, "expr", 27),
            (0, "expr", 27),
            (5, "expr", 27),
        ),
        "ifstmtl":
        ("%|if %c:\n%+%c%-", (0, "testexpr"), (1, "_ifstmts_jumpl")),
        '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, ", ")),
        "list_afor": (
            " async for %[1]{%c} in %c%[1]{%c}",
            (1, "store"),
            (0, "get_aiter"),
            (3, "list_iter"),
        ),
        "list_afor": (
            " async for %[1]{%c} in %c%[1]{%c}",
            (1, "store"),
            (0, "get_aiter"),
            (3, "list_iter"),
        ),
        "list_if37": (" if %p%c", (0, 27), 1),
        "list_if37_not": (" if not %p%c", (0, 27), 1),
        "testfalse_not_or": ("not %c or %c", (0, "expr"), (2, "expr")),
        "testfalse_not_and": ("not (%c)", 0),
        "testfalsel": ("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),
        "unmap_dict": ("{**%C}", (0, -1, ", **")),
        "unpack_list": ("*%c", (0, "list")),
        "yield_from": ("yield from %c", (0, "expr")),
    })

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

    self.n_assert_invert = n_assert_invert

    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_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(r"\d", opname[-1])):
            self.template_engine(
                ("%c(%p)", (0, "expr"), (1, PRECEDENCE["yield"] - 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 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_list_comp_async(node):
        self.write("[")
        if node[0].kind == "load_closure":
            self.listcomp_closure3(node)
        else:
            self.comprehension_walk_newer(node, iter_index=3, code_index=0)
        self.write("]")
        self.prune()

    self.n_list_comp_async = n_list_comp_async
Exemplo n.º 17
0
def customize_for_version(self, is_pypy, version):
    if is_pypy:
        ########################
        # PyPy changes
        #######################
        TABLE_DIRECT.update({
            'assert_pypy': ('%|assert %c\n', (1, 'assert_expr')),
            # This is as a result of an if transoration
            'assert0_pypy': ('%|assert %c\n', (0, 'assert_expr')),
            'assert_not_pypy': ('%|assert not %c\n', (1, 'assert_exp')),
            'assert2_not_pypy':
            ('%|assert not %c, %c\n', (1, 'assert_exp'), (4, 'expr')),
            'assert2_pypy':
            ('%|assert %c, %c\n', (1, 'assert_expr'), (4, 'expr')),
            '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.0:
        if version >= 3.2:
            TABLE_DIRECT.update({
                "del_deref_stmt": ("%|del %c\n", 0),
                "DELETE_DEREF": ("%{pattr}", 0)
            })
        from uncompyle6.semantics.customize3 import customize_for_version3

        customize_for_version3(self, version)
    else:  # < 3.0
        TABLE_DIRECT.update({
            "except_cond3": ("%|except %c, %c:\n", (1, "expr"), (-2, "store"))
        })
        if version <= 2.6:
            TABLE_DIRECT["testtrue_then"] = TABLE_DIRECT["testtrue"]

        if 2.4 <= version <= 2.6:
            TABLE_DIRECT.update({"comp_for": (" for %c in %c", 3, 1)})
        else:
            TABLE_DIRECT.update({"comp_for": (" for %c in %c%c", 2, 0, 3)})

        if version >= 2.5:
            from uncompyle6.semantics.customize25 import customize_for_version25

            customize_for_version25(self, version)

            if version >= 2.6:
                from uncompyle6.semantics.customize26_27 import (
                    customize_for_version26_27, )

                customize_for_version26_27(self, version)
                pass
        else:  # < 2.5
            global NAME_MODULE
            NAME_MODULE = SyntaxTree(
                "stmt",
                [
                    SyntaxTree(
                        "assign",
                        [
                            SyntaxTree(
                                "expr",
                                [
                                    Token(
                                        "LOAD_GLOBAL",
                                        pattr="__name__",
                                        offset=0,
                                        has_arg=True,
                                    )
                                ],
                            ),
                            SyntaxTree(
                                "store",
                                [
                                    Token(
                                        "STORE_NAME",
                                        pattr="__module__",
                                        offset=3,
                                        has_arg=True,
                                    )
                                ],
                            ),
                        ],
                    )
                ],
            )
            TABLE_DIRECT.update({
                "importmultiple": ("%|import %c%c\n", 2, 3),
                "import_cont": (", %c", 2),
                "tryfinallystmt": (
                    "%|try:\n%+%c%-%|finally:\n%+%c%-",
                    (1, "suite_stmts_opt"),
                    (5, "suite_stmts_opt"),
                ),
            })
            if version == 2.4:

                def n_iftrue_stmt24(node):
                    self.template_engine(("%c", 0), node)
                    self.default(node)
                    self.prune()

                self.n_iftrue_stmt24 = n_iftrue_stmt24
            else:  # version <= 2.3:
                TABLE_DIRECT.update({"if1_stmt": ("%|if 1\n%+%c%-", 5)})
                if version <= 2.1:
                    TABLE_DIRECT.update({
                        "importmultiple": ("%c", 2),
                        # FIXME: not quite right. We have indiividual imports
                        # when there is in fact one: "import a, b, ..."
                        "imports_cont": ("%C%,", (1, 100, "\n")),
                    })
                    pass
                pass
            pass  # < 2.5

        # < 3.0 continues

        TABLE_R.update({
            "STORE_SLICE+0": ("%c[:]", 0),
            "STORE_SLICE+1": ("%c[%p:]", 0, (1, -1)),
            "STORE_SLICE+2": ("%c[:%p]", 0, (1, -1)),
            "STORE_SLICE+3": ("%c[%p:%p]", 0, (1, -1), (2, -1)),
            "DELETE_SLICE+0": ("%|del %c[:]\n", 0),
            "DELETE_SLICE+1": ("%|del %c[%c:]\n", 0, 1),
            "DELETE_SLICE+2": ("%|del %c[:%c]\n", 0, 1),
            "DELETE_SLICE+3": ("%|del %c[%c:%c]\n", 0, 1, 2),
        })
        TABLE_DIRECT.update({"raise_stmt2": ("%|raise %c, %c\n", 0, 1)})

        # exec as a built-in statement is only in Python 2.x
        def n_exec_stmt(node):
            """
            exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
            exec_stmt ::= expr exprlist EXEC_STMT
            """
            self.write(self.indent, "exec ")
            self.preorder(node[0])
            if not node[1][0].isNone():
                sep = " in "
                for subnode in node[1]:
                    self.write(sep)
                    sep = ", "
                    self.preorder(subnode)
            self.println()
            self.prune()  # stop recursing

        self.n_exec_smt = n_exec_stmt

        pass  # < 3.0

    return
Exemplo n.º 18
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.0:
        if version == 2.4:
            def n_iftrue_stmt24(node):
                self.template_engine(('%|%c', 0), node)
                self.default(node)
                self.prune()
            self.n_iftrue_stmt24 = n_iftrue_stmt24

        TABLE_R.update({
            'STORE_SLICE+0':	( '%c[:]', 0 ),
            'STORE_SLICE+1':	( '%c[%p:]', 0, (1, -1) ),
            'STORE_SLICE+2':	( '%c[:%p]', 0, (1, -1) ),
            'STORE_SLICE+3':	( '%c[%p:%p]', 0, (1, -1), (2, -1) ),
            'DELETE_SLICE+0':	( '%|del %c[:]\n', 0 ),
            'DELETE_SLICE+1':	( '%|del %c[%c:]\n', 0, 1 ),
            'DELETE_SLICE+2':	( '%|del %c[:%c]\n', 0, 1 ),
            'DELETE_SLICE+3':	( '%|del %c[%c:%c]\n', 0, 1, 2 ),
        })
        TABLE_DIRECT.update({
            'raise_stmt2':	( '%|raise %c, %c\n', 0, 1),
        })

        # exec as a built-in statement is only in Python 2.x
        def n_exec_stmt(node):
            """
            exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
            exec_stmt ::= expr exprlist EXEC_STMT
            """
            self.write(self.indent, 'exec ')
            self.preorder(node[0])
            if not node[1][0].isNone():
                sep = ' in '
                for subnode in node[1]:
                    self.write(sep); sep = ", "
                    self.preorder(subnode)
            self.println()
            self.prune() # stop recursing
        self.n_exec_smt = n_exec_stmt

    else:
        TABLE_DIRECT.update({
            # Gotta love Python for its futzing around with syntax like this
            'raise_stmt2':	 ( '%|raise %c from %c\n', 0, 1),
        })

        if version >= 3.2:
            TABLE_DIRECT.update({
            'del_deref_stmt': ( '%|del %c\n', 0),
            'DELETE_DEREF': ( '%{pattr}', 0 ),
            })

    if version <= 2.4:
        TABLE_DIRECT.update({
            'importmultiple': ( '%|import %c%c\n', 2, 3),
            'import_cont'   : ( ', %c', 2),
            'tryfinallystmt': ( '%|try:\n%+%c%-%|finally:\n%+%c%-',
                                (1, 'suite_stmts_opt') ,
                                (5, 'suite_stmts_opt') )
            })
        if version == 2.3:
            TABLE_DIRECT.update({
                'if1_stmt':	( '%|if 1\n%+%c%-', 5 )
            })

        global NAME_MODULE
        NAME_MODULE = SyntaxTree('stmt',
                          [ SyntaxTree('assign',
                                [ SyntaxTree('expr',
                                      [Token('LOAD_GLOBAL', pattr='__name__',
                                             offset=0, has_arg=True)]),
                                  SyntaxTree('store',
                                      [ Token('STORE_NAME', pattr='__module__',
                                              offset=3, has_arg=True)])
                                ])])
        pass
        if version <= 2.3:
            if version <= 2.1:
                TABLE_DIRECT.update({
                    'importmultiple': ( '%c', 2 ),
                    # FIXME: not quite right. We have indiividual imports
                    # when there is in fact one: "import a, b, ..."
                    'imports_cont': ( '%C%,', (1, 100, '\n') ),
                })
                pass
            pass
        pass
    elif version >= 2.5:
        ########################
        # Import style for 2.5+
        ########################
        TABLE_DIRECT.update({
            'importmultiple': ( '%|import %c%c\n', 2, 3 ),
            'import_cont'   : ( ', %c', 2 ),
            # With/as is allowed as "from future" thing in 2.5
            # Note: It is safe to put the variables after "as" in parenthesis,
            # and sometimes it is needed.
            'withstmt':     ( '%|with %c:\n%+%c%-', 0, 3),
            'withasstmt':   ( '%|with %c as (%c):\n%+%c%-', 0, 2, 3),
        })

        # In 2.5+ "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
        def tryfinallystmt(node):
            if len(node[1][0]) == 1 and node[1][0][0] == 'stmt':
                if node[1][0][0][0] == 'try_except':
                    node[1][0][0][0].kind = 'tf_try_except'
                if node[1][0][0][0] == 'tryelsestmt':
                    node[1][0][0][0].kind = 'tf_tryelsestmt'
            self.default(node)
        self.n_tryfinallystmt = tryfinallystmt

    ########################################
    # Python 2.6+
    #    except <condition> as <var>
    # vs. older:
    #    except <condition> , <var>
    #
    # For 2.6 we use the older syntax which
    # matches how we parse this in bytecode
    ########################################
    if version > 2.6:
        TABLE_DIRECT.update({
            'except_cond2':	( '%|except %c as %c:\n', 1, 5 ),
        })
    else:
        TABLE_DIRECT.update({
            'except_cond3':	 ( '%|except %c, %c:\n', 1, 6 ),
            'testtrue_then': ( 'not %p', (0, 22) ),

        })

    if 2.4 <= version <= 2.6:
        TABLE_DIRECT.update({
            'comp_for':	( ' for %c in %c', 3, 1 ),
        })
    else:
        TABLE_DIRECT.update({
            'comp_for':	( ' for %c in %c%c', 2, 0, 3 ),
        })

    if  version >= 3.0:
        from uncompyle6.semantics.customize3 import customize_for_version3
        customize_for_version3(self, version)
    return
Exemplo n.º 19
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),
        "kwarg": ("%[0]{attr}=%c", 1),
        "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.0

    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 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

    if version == 3.0:
        # In Python 3.0 there is code to move from _[dd] into
        # the iteration variable. These rules we can ignore
        # since we pick up the iteration variable some other way and
        # we definitely don't include in the source  _[dd].
        TABLE_DIRECT.update({
            "ifstmt30": ("%|if %c:\n%+%c%-", (0, "testfalse_then"),
                         (1, "_ifstmts_jump30")),
            "ifnotstmt30": ("%|if not %c:\n%+%c%-", (0, "testtrue_then"),
                            (1, "_ifstmts_jump30")),
            "try_except30": ("%|try:\n%+%c%-%c\n\n", (1, "suite_stmts_opt"),
                             (4, "except_handler")),
        })

        def n_comp_iter(node):
            if node[0] == "expr":
                n = node[0][0]
                if n == "LOAD_FAST" and n.pattr[0:2] == "_[":
                    self.prune()
                    pass
                pass
            # Not this special case, proceed as normal...
            self.default(node)

        self.n_comp_iter = n_comp_iter

    elif version == 3.3:
        # FIXME: perhaps this can be folded into the 3.4+ case?
        def n_yield_from(node):
            assert node[0] == "expr"
            if node[0][0] == "get_iter":
                # Skip over yield_from.expr.get_iter which adds an
                # extra iter(). Maybe we can do in tranformation phase instead?
                template = ("yield from %c", (0, "expr"))
                self.template_engine(template, node[0][0])
            else:
                template = ("yield from %c", (0, "attribute"))
                self.template_engine(template, node[0][0][0])
            self.prune()

        self.n_yield_from = n_yield_from

    if 3.2 <= version <= 3.4:

        def n_call(node):

            mapping = self._get_mapping(node)
            key = node
            for i in mapping[1:]:
                key = key[i]
                pass
            if key.kind.startswith("CALL_FUNCTION_VAR_KW"):
                # We may want to fill this in...
                # But it is distinct from CALL_FUNCTION_VAR below
                pass
            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 kwargs != 0:
                    # kwargs == 0 is handled by the table entry
                    # Should probably handle it here though.
                    if nargs == 0:
                        template = ("%c(*%c, %C)", 0, -2, (1, kwargs + 1,
                                                           ", "))
                    else:
                        template = (
                            "%c(%C, *%c, %C)",
                            0,
                            (1, nargs + 1, ", "),
                            -2,
                            (-2 - kwargs, -2, ", "),
                        )
                    self.template_engine(template, node)
                    self.prune()
            else:
                gen_function_parens_adjust(key, node)
            self.default(node)

        self.n_call = n_call
    elif version < 3.2:

        def n_call(node):
            mapping = self._get_mapping(node)
            key = node
            for i in mapping[1:]:
                key = key[i]
                pass
            gen_function_parens_adjust(key, node)
            self.default(node)

        self.n_call = n_call

    def n_mkfunc_annotate(node):

        # Handling EXTENDED_ARG before MAKE_FUNCTION ...
        i = -1 if node[-2] == "EXTENDED_ARG" else 0

        if self.version <= 3.2:
            code = node[-2 + i]
        elif self.version >= 3.3 or node[-2] == "kwargs":
            # LOAD_CONST code object ..
            # LOAD_CONST        'x0'  if >= 3.3
            # EXTENDED_ARG
            # MAKE_FUNCTION ..
            code = node[-3 + i]
        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}", ),
    })
    if version >= 3.4:
        #######################
        # Python 3.4+ Changes #
        #######################
        TABLE_DIRECT.update({
            "LOAD_CLASSDEREF": ("%{pattr}", ),
            "yield_from": ("yield from %c", (0, "expr")),
        })
        if version >= 3.5:
            customize_for_version35(self, version)
            if version >= 3.6:
                customize_for_version36(self, version)
                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
                pass  # 3.6
            pass  # 3.5
        pass  # 3.4
    return
Exemplo n.º 20
0
def customize_for_version36(self, version):
    # Value 100 is important; it is exactly
    # module/function precidence.
    PRECEDENCE['call_kw']     = 100
    PRECEDENCE['call_kw36']   = 100
    PRECEDENCE['call_ex']     = 100
    PRECEDENCE['call_ex_kw']  = 100
    PRECEDENCE['call_ex_kw2'] = 100
    PRECEDENCE['call_ex_kw3'] = 100
    PRECEDENCE['call_ex_kw4'] = 100
    PRECEDENCE['unmap_dict']  = 0

    TABLE_DIRECT.update({
        'tryfinally36':  ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
                           (1, 'returns'), 3 ),
        'fstring_expr':   ( "{%c%{conversion}}", 0),
        # FIXME: the below assumes the format strings
        # don't have ''' in them. Fix this properly
        'fstring_single': ( "f'''{%c%{conversion}}'''", 0),
        'fstring_multi':  ( "f'''%c'''", 0),
        'func_args36':    ( "%c(**", 0),
        'try_except36':   ( '%|try:\n%+%c%-%c\n\n', 1, 2 ),
        'except_return':  ( '%|except:\n%+%c%-', 3 ),
        'unpack_list':    ( '*%c', (0, 'list') ),
        'tryfinally_return_stmt':
              ( '%|try:\n%+%c%-%|finally:\n%+%|return%-\n\n', 1 ),

        'async_for_stmt36':  (
            '%|async for %c in %c:\n%+%c%-%-\n\n',
            (9, 'store'), (1, 'expr'), (18, 'for_block') ),

        'call_ex' : (
            '%c(%p)',
            (0, 'expr'), (1, 100)),
        'call_ex_kw' : (
            '%c(%p)',
            (0, 'expr'), (2, 100)),

    })

    TABLE_R.update({
        'CALL_FUNCTION_EX': ('%c(*%P)', 0, (1, 2, ', ', 100)),
        # Not quite right
        'CALL_FUNCTION_EX_KW': ('%c(**%C)', 0, (2, 3, ',')),
        })

    def build_unpack_tuple_with_call(node):

        if node[0] == 'expr':
            tup = node[0][0]
        else:
            tup = node[0]
            pass
        assert tup == 'tuple'
        self.call36_tuple(tup)

        buwc = node[-1]
        assert buwc.kind.startswith('BUILD_TUPLE_UNPACK_WITH_CALL')
        for n in node[1:-1]:
            self.f.write(', *')
            self.preorder(n)
            pass
        self.prune()
        return
    self.n_build_tuple_unpack_with_call = build_unpack_tuple_with_call

    def build_unpack_map_with_call(node):
        n = node[0]
        if n == 'expr':
            n = n[0]
        if n == 'dict':
            self.call36_dict(n)
            first = 1
            sep = ', **'
        else:
            first = 0
            sep = '**'
        for n in node[first:-1]:
            self.f.write(sep)
            self.preorder(n)
            sep = ', **'
            pass
        self.prune()
        return
    self.n_build_map_unpack_with_call = build_unpack_map_with_call

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

        # This is weird shit. Thanks Python!
        self.preorder(node[0])
        self.write('(')

        assert node[1] == 'build_tuple_unpack_with_call'
        btuwc = node[1]
        tup = btuwc[0]
        if tup == 'expr':
            tup = tup[0]
        assert tup == 'tuple'
        self.call36_tuple(tup)
        assert node[2] == 'build_map_unpack_with_call'

        self.write(', ')
        d = node[2][0]
        if d == 'expr':
            d = d[0]
        assert d == 'dict'
        self.call36_dict(d)

        args = btuwc[1]
        self.write(', *')
        self.preorder(args)

        self.write(', **')
        star_star_args = node[2][1]
        if star_star_args == 'expr':
            star_star_args = star_star_args[0]
        self.preorder(star_star_args)
        self.write(')')
        self.prune()
    self.n_call_ex_kw2 = call_ex_kw2

    def call_ex_kw3(node):
        """Handle CALL_FUNCTION_EX 1 (have KW) but without
        BUILD_MAP_UNPACK_WITH_CALL"""
        self.preorder(node[0])
        self.write('(')
        args = node[1][0]
        if args == 'expr':
            args = args[0]
        if args == 'tuple':
            if self.call36_tuple(args) > 0:
                self.write(', ')
                pass
            pass

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

        kwargs = node[2]
        if kwargs == 'expr':
            kwargs = kwargs[0]
        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 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


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

    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':
            self.write(node[0].attr)
            self.prune()
        else:
            self.default(node)
    self.n_formatted_value = n_formatted_value

    def f_conversion(node):
        node.conversion = FSTRING_CONVERSION_MAP.get(node.data[1].attr, '')

    def fstring_expr(node):
        f_conversion(node)
        self.default(node)
    self.n_fstring_expr = fstring_expr

    def fstring_single(node):
        f_conversion(node)
        self.default(node)
    self.n_fstring_single = fstring_single

    # def kwargs_only_36(node):
    #     keys = node[-1].attr
    #     num_kwargs = len(keys)
    #     values = node[:num_kwargs]
    #     for i, (key, value) in enumerate(zip(keys, values)):
    #         self.write(key + '=')
    #         self.preorder(value)
    #         if i < num_kwargs:
    #             self.write(',')
    #     self.prune()
    #     return
    # self.n_kwargs_only_36 = kwargs_only_36

    def n_call_kw36(node):
        self.template_engine(("%c(", 0), 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 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
Exemplo n.º 21
0
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),
        "kwarg": ("%[0]{attr}=%c", 1),
        "raise_stmt2": ("%|raise %c from %c\n", 0, 1),
        "tf_tryelsestmtl3": ('%c%-%c%|else:\n%+%c', 1, 3, 5),
        "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.0

    # 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 == "tryelsestmtl3":
                    try_something.kind = 'tf_tryelsestmtl3'
                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 = 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
                elif n == "list_if37":
                    list_ifs.append(n)
                    n = n[-1]
                    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):
        """Handle "classdef" nonterminal for 3.0 >= version 3.0 <= 3.5
        """

        assert 3.0 <= self.version <= 3.5

        # 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.3:
                class_name = node[2][0].attr
            else:
                class_name = node[1][2].attr
            build_class = node
        else:
            build_class = node[0]
            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 = find_code_node(load_closure, -3).attr
            elif hasattr(load_closure[-2], "attr"):
                # Python 3.2 works like this
                subclass_code = find_code_node(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 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

    if version == 3.0:
        # In Python 3.0 there is code to move from _[dd] into
        # the iteration variable. These rules we can ignore
        # since we pick up the iteration variable some other way and
        # we definitely don't include in the source  _[dd].
        TABLE_DIRECT.update({
            "ifstmt30": ("%|if %c:\n%+%c%-", (0, "testfalse_then"),
                         (1, "_ifstmts_jump30")),
            "ifnotstmt30": ("%|if not %c:\n%+%c%-", (0, "testtrue_then"),
                            (1, "_ifstmts_jump30")),
            "try_except30": ("%|try:\n%+%c%-%c\n\n", (1, "suite_stmts_opt"),
                             (4, "except_handler")),
        })

        def n_comp_iter(node):
            if node[0] == "expr":
                n = node[0][0]
                if n == "LOAD_FAST" and n.pattr[0:2] == "_[":
                    self.prune()
                    pass
                pass
            # Not this special case, proceed as normal...
            self.default(node)

        self.n_comp_iter = n_comp_iter

    elif version == 3.3:
        # FIXME: perhaps this can be folded into the 3.4+ case?
        def n_yield_from(node):
            assert node[0] == "expr"
            if node[0][0] == "get_iter":
                # Skip over yield_from.expr.get_iter which adds an
                # extra iter(). Maybe we can do in tranformation phase instead?
                template = ("yield from %c", (0, "expr"))
                self.template_engine(template, node[0][0])
            else:
                template = ("yield from %c", (0, "attribute"))
                self.template_engine(template, node[0][0][0])
            self.prune()

        self.n_yield_from = n_yield_from

    if 3.2 <= version <= 3.4:

        def n_call(node):

            mapping = self._get_mapping(node)
            key = node
            for i in mapping[1:]:
                key = key[i]
                pass
            if key.kind.startswith("CALL_FUNCTION_VAR_KW"):
                # We may want to fill this in...
                # But it is distinct from CALL_FUNCTION_VAR below
                pass
            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 kwargs != 0:
                    # kwargs == 0 is handled by the table entry
                    # Should probably handle it here though.
                    if nargs == 0:
                        template = ("%c(*%c, %C)", 0, -2, (1, kwargs + 1,
                                                           ", "))
                    else:
                        template = (
                            "%c(%C, *%c, %C)",
                            0,
                            (1, nargs + 1, ", "),
                            -2,
                            (-2 - kwargs, -2, ", "),
                        )
                    self.template_engine(template, node)
                    self.prune()
            else:
                gen_function_parens_adjust(key, node)
            self.default(node)

        self.n_call = n_call
    elif version < 3.2:

        def n_call(node):
            mapping = self._get_mapping(node)
            key = node
            for i in mapping[1:]:
                key = key[i]
                pass
            gen_function_parens_adjust(key, node)
            self.default(node)

        self.n_call = n_call

    def n_mkfunc_annotate(node):

        # Handling EXTENDED_ARG before MAKE_FUNCTION ...
        i = -1 if node[-2] == "EXTENDED_ARG" else 0

        if self.version <= 3.2:
            code = node[-2 + i]
        elif self.version >= 3.3 or node[-2] == "kwargs":
            # LOAD_CONST code object ..
            # LOAD_CONST        'x0'  if >= 3.3
            # EXTENDED_ARG
            # MAKE_FUNCTION ..
            code = node[-3 + i]
        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_else" or "except_handler"
            (5, "else_suitel"),
        ),
        "LOAD_CLASSDEREF": ("%{pattr}", ),
    })
    if version >= 3.4:
        #######################
        # Python 3.4+ Changes #
        #######################
        TABLE_DIRECT.update({
            "LOAD_CLASSDEREF": ("%{pattr}", ),
            "yield_from": ("yield from %c", (0, "expr")),
        })
        if version >= 3.5:
            customize_for_version35(self, version)
            if version >= 3.6:
                customize_for_version36(self, version)
                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
                pass  # 3.6
            pass  # 3.5
        pass  # 3.4
    return
Exemplo n.º 22
0
def customize_for_version35(self, version):
    TABLE_DIRECT.update({
        'await_expr': ('await %c', 0),
        'await_stmt': ('%|%c\n', 0),
        'async_for_stmt': ('%|async for %c in %c:\n%+%|%c%-\n\n', 9, 1, 25),
        'async_forelse_stmt':
        ('%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 9, 1, 25,
         (27, 'else_suite')),
        '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),
        'unmap_dict': ('{**%C}', (0, -1, ', **')),
        # 'unmapexpr':	       ( '{**%c}', 0), # done by n_unmapexpr
    })

    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 n_function_def(node):
        if self.version >= 3.6:
            code_node = node[0][0]
        else:
            code_node = node[0][1]

        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