Ejemplo n.º 1
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
Ejemplo n.º 2
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
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
Ejemplo n.º 4
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
Ejemplo n.º 5
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
Ejemplo n.º 6
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
Ejemplo n.º 7
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
Ejemplo 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_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