예제 #1
0
 def visit_TryExcept(self, node):
     existing_node = self.generic_visit(node)
     for handler in existing_node.handlers:
         handler_name = getattr(handler.name, 'id', handler.name)
         if handler_name is not None:
             handler.body.insert(0, self._create_context_call(
                 'assign',
                 [Str(s=handler_name),
                  Name(id=handler_name, ctx=Load()),
                  Num(n=handler.lineno)]))
     return existing_node
def construct_assign_dict(target, dict_name):
    # construct_assign_dict('a','d')
    # -> a = d['a']
    from ast import Assign, Name, Store, Subscript, Load, Index, Str
    a = target
    d = dict_name
    expr = Assign(targets=[Name(id=a, ctx=Store())],
                  value=Subscript(value=Name(id=d, ctx=Load()),
                                  slice=Index(value=Str(s=a)),
                                  ctx=Load()))
    return expr
예제 #3
0
    def visit_Assign(self, node):
        existing_node = self.generic_visit(node)
        new_nodes = [existing_node]
        existing_node.value = self._create_bare_context_call(
            'assign', [
                Str(s=self._get_assignment_targets(existing_node.targets)),
                existing_node.value,
                Num(n=existing_node.lineno)
            ])

        return new_nodes
예제 #4
0
 def test_get_value(self) -> None:
     """Tests get_value succeeds"""
     val = "foo"
     self.assertEqual(get_value(Str(s=val, constant_value=None, string=None)), val)
     self.assertEqual(
         get_value(Constant(value=val, constant_value=None, string=None)), val
     )
     self.assertIsInstance(get_value(Tuple(expr=None)), Tuple)
     self.assertIsInstance(get_value(Tuple(expr=None)), Tuple)
     self.assertIsNone(get_value(Name(None, None)))
     self.assertEqual(get_value(get_value(ast.parse("-5").body[0])), -5)
예제 #5
0
    def visit_Name(self, node: Name) -> Union[Name, Call]:
        if not isinstance(node.ctx, Load):
            return node

        delegate = Call(func=Name(id='__autoimport__', ctx=Load()),
                        args=[Str(s=node.id)],
                        keywords=[])

        copy_location(delegate, node)
        fix_missing_locations(delegate)
        return delegate
예제 #6
0
 def visit_constant(self, constant: Expr):
     """Proceeds by converting constant value to a numpy array
     and converting it to the appropriate value in the generated
     code (whether it be a Python scalar or a Numpy array)"""
     value = constant.data.asnumpy()
     const_expr = ast.Call(
         ast.Attribute(Name("numpy", Load()), "array", Load()),
         [self.parse_numpy_array(value)],
         [ast.keyword("dtype", Str(constant.checked_type.dtype))],
     )
     return (self.create_call("nd.array", [const_expr]), [])
예제 #7
0
def document(tree, **kw):
    # Simplify it just to show the problem isn't related to the content of the macro
    return tree
    """ This macro takes literal strings and converts them into:
        _help_ID = type_hint+STRING
        where:
        ID is the first target of the last assignment.
        type_hint is the assigned type and default value (only works for a few types)
        STRING is the literal string """
    for n in range(len(tree)):
        s = tree[n]
        if not n:
            prev = s
            continue
        # The whole sentence is a string?
        if (isinstance(s, Expr) and isinstance(s.value, Str) and
           # and the previous is an assign
           isinstance(prev, Assign)):  # noqa: E128
            # Apply it to the first target
            target = prev.targets[0]
            value = prev.value
            # Extract its name
            # variables and attributes are supported
            if isinstance(target, Name):
                name = target.id
                is_attr = False
            elif isinstance(target, Attribute):
                name = target.attr
                is_attr = True
            else:
                continue
            # Remove starting underscore
            if name[0] == '_':
                name = name[1:]
            # Create a _help_ID
            doc_id = '_help_'+name
            # Create the type hint for numbers, strings and booleans
            type_hint = ''
            if isinstance(value, Num):
                type_hint = '[number={}]'.format(value.n)
            elif isinstance(value, Str):
                type_hint = "[string='{}']".format(value.s)
            elif isinstance(value, NameConstant) and isinstance(value.value, bool):
                type_hint = '[boolean={}]'.format(str(value.value).lower())
            # Transform the string into an assign for _help_ID
            if is_attr:
                target = Attribute(value=Name(id='self', ctx=Load()), attr=doc_id, ctx=Store())
            else:
                target = Name(id=doc_id, ctx=Store())
            tree[n] = Assign(targets=[target], value=Str(s=type_hint+s.value.s))
        prev = s
    # Return the modified AST
    return tree
예제 #8
0
파일: logic.py 프로젝트: NelaMm/arcor2
def program_src(type_defs: TypesDict,
                project: CProject,
                scene: CScene,
                add_logic: bool = True) -> str:

    tree = empty_script_tree(project.id, add_main_loop=add_logic)

    # get object instances from resources object
    main = find_function("main", tree)
    last_assign = find_last_assign(main)
    for obj in scene.objects:
        add_import(tree,
                   "object_types." + humps.depascalize(obj.type),
                   obj.type,
                   try_to_import=False)
        last_assign += 1
        main.body.insert(last_assign,
                         object_instance_from_res(obj.name, obj.id, obj.type))

    # TODO temporary solution - should be (probably) handled by plugin(s)
    from arcor2 import json

    # TODO should we put there even unused parameters?
    for param in project.parameters:
        val = json.loads(param.value)

        aval: Optional[expr] = None

        if isinstance(val, bool):  # subclass of int
            aval = NameConstant(value=val, kind=None)
        elif isinstance(val, (int, float)):
            aval = Num(n=val, kind=None)
        elif isinstance(val, str):
            aval = Str(s=val, kind="")

        if not aval:
            raise Arcor2Exception(
                f"Unsupported project parameter type ({param.type}) or value ({val})."
            )

        last_assign += 1
        main.body.insert(
            last_assign,
            Assign(  # TODO use rather AnnAssign?
                targets=[Name(id=param.name, ctx=Store())],
                value=aval,
                type_comment=None),
        )

    if add_logic:
        add_logic_to_loop(type_defs, tree, scene, project)

    return SCRIPT_HEADER + tree_to_str(tree)
예제 #9
0
        def visit_Name(self, node):
            if node.id in self.local_dict:
                return node
            elif node.id in self.global_dict:
                name_obj = self.global_dict[node.id]

                if isinstance(name_obj, (Basic, type)) or callable(name_obj):
                    return node
            elif node.id in ['True', 'False']:
                return node
            return fix_missing_locations(
                Call(Name('Symbol', Load()), [Str(node.id)], [], None, None))
예제 #10
0
파일: ast.py 프로젝트: curtisma/xonsh
def _getblockattr(name, lineno, col):
    """calls getattr(name, '__xonsh_block__', False)."""
    return xonsh_call(
        "getattr",
        args=[
            Name(id=name, ctx=Load(), lineno=lineno, col_offset=col),
            Str(s="__xonsh_block__", lineno=lineno, col_offset=col),
            NameConstant(value=False, lineno=lineno, col_offset=col),
        ],
        lineno=lineno,
        col=col,
    )
예제 #11
0
파일: ast_parser.py 프로젝트: msgoff/sympy
    def visit_Name(self, node):
        if node.id in self.local_dict:
            return node
        elif node.id in self.global_dict:
            name_obj = self.global_dict[node.id]

            if isinstance(name_obj, (Basic, type)) or callable(name_obj):
                return node
        elif node.id in ["True", "False"]:
            return node
        return fix_missing_locations(
            Call(func=Name("Symbol", Load()), args=[Str(node.id)], keywords=[])
        )
예제 #12
0
    def _trace_assignment(self, target, default_lineno=None):
        lineno = getattr(target, 'lineno', default_lineno)
        # name, value, line number
        if isinstance(target, Name):
            arg_name = target.id
        elif arg and isinstance(target, arg):
            arg_name = target.arg
        else:
            assert_message = 'Target type was {}.'.format(type(target))
            assert isinstance(target, str), assert_message
            arg_name = target

        args = [Str(s=arg_name), Name(id=arg_name, ctx=Load()), Num(n=lineno)]
        return self._create_context_call('assign', args)
예제 #13
0
    def visit_Expr(self, node):
        '''Rewrite foo.append(...) as foo = invoke(foo, 'append', ...).

        This depends on the `invoke` method simply knowing which method invocations ought to be saved, thus this is
        tightly coupled to the pyrsistent API.
        '''
        match = match_ast(self._method_pattern, self.visit(node.value))
        if match is None:
            return cl(Expr(value=self.visit(node.value)), node)
        subject = match['subject']
        args = [subject, Str(s=match['method'])] + match['arguments']
        assign = Assign(targets=[Context.set(Store, subject)],
                        value=self.names.call_global(globals.invoke, args, match['keywords']))
        return self.visit_Assign(cl(assign, node))
예제 #14
0
def _do_wrap_class_register(tree, mod, base_class):
    if isinstance(tree, ClassDef):
        # Create the register call
        name = tree.name
        reg_name = name.lower()
        # BaseOutput.register member:
        attr = Attribute(value=Name(id=base_class, ctx=Load()), attr='register', ctx=Load())
        # Function call to it passing reg_name and name
        do_register = Expr(value=Call(func=attr, args=[Str(s=reg_name), Name(id=name, ctx=Load())], keywords=[]))
        # Create the import
        do_import = ImportFrom(module=mod, names=[alias(name=base_class, asname=None)], level=1)
        return [do_import, tree, do_register]
    # Just in case somebody applies it to anything other than a class
    return tree  # pragma: no cover
예제 #15
0
def log(expr, **kw):
    '''
    Print the passed value, labeling the output with the expression. Example:

        d = { 'a': 1 }
        log[d['a']]

        # prints
        # d['a']: 1
    '''
    label = unparse(expr) + ': '
    return Call(func=Name(id='print', ctx=Load()),
                args=[Str(s=label), expr],
                keywords=[])
예제 #16
0
파일: logging.py 프로젝트: Auzzy/pystick
 def init(vars):
     node = Name(id=Log.log_dict, ctx=Store())
     var_strs = [Str(s=var.name) for var in vars]
     void = Call(func=Name(id='Void', ctx=Load()))
     return [
         Assign(targets=[node],
                value=DictComp(key=Name(id='__key', ctx=Load()),
                               value=Log.__fromkeys(var_strs, void),
                               generators=[
                                   comprehension(target=Name(id='__key',
                                                             ctx=Store()),
                                                 iter=Log.__fromkeys(
                                                     Log.sections))
                               ]))
     ]
예제 #17
0
    def wrapper(self, node):  # pylint: disable=C0111
        if hasattr(node, '_already_process'):
            self.generic_visit(node)
            return node
        call_node = deepcopy(visit_func(self, node).body)
        node._already_process = True
        call_node.args[0] = node if not isinstance(node, Subscript)\
                                   else node.value

        if hasattr(node, 'production_name'):
            call_node.args[1] = node.production_name
            self._hint_name = str(node.production_name.s)
            self._counter = 0
        elif not isinstance(node, (Str, Name)):
            call_node.args[1] = Str(s=self._generate_name())
        else:
            call_node.args[1] = Str(s=self._last_generate_name())

        call_node.args[1]._already_process = True

        call_node = copy_location(call_node, node)
        self.generic_visit(call_node)

        return call_node
예제 #18
0
 def _trace_print_function(self, existing_node):
     values = list(existing_node.args)
     formats = ['%r'] * len(values)
     if existing_node.starargs is not None:
         values.append(existing_node.starargs)
         formats.append('*%r')
     for keyword in existing_node.keywords:
         values.append(keyword.value)
         formats.append('{}=%r'.format(keyword.arg))
     message_format = 'print(' + ', '.join(formats) + ') '
     return self._create_bare_context_call('add_message',
                                           [BinOp(left=Str(message_format),
                                                  op=Mod(),
                                                  right=Tuple(elts=values,
                                                              ctx=Load())),
                                            Num(existing_node.lineno)])
예제 #19
0
        def _generate_assignment((arg_name, arg_resource_handle)):
            # type: (Tuple[basestring, int]) -> If
            """
            We have a function that looks like:
            def do_something(param, model_=INJECTED):
                <...>

            We insert into its beginning a statement like
                ___INJECT_CONTEXT_INTERNAL_RESOURCES = ___INJECT_CONTEXT_INTERNAL.resources
                if model_ is INJECTED:
                    model_ = ___INJECT_CONTEXT_INTERNAL_RESOURCES[3]
                    if model is ___INJECT_CONTEXT_INTERNAL:    # means that no resource is available
                        ___INJECT_CONTEXT_INTERNAL_RESOURCES.flag_missing('model_')

            Code outside of this function sets a global variable _INJECTED__model to point at the right thing.
            """
            target_attribute = Subscript(
                value=Name(id=INTERNAL_RESOURCES_NAME, ctx=Load()),
                slice=Index(value=Num(n=arg_resource_handle)),
                ctx=Load())

            consequence = [
                Assign(targets=[Name(id=arg_name, ctx=Store())],
                       value=target_attribute),
                If(test=Compare(
                    left=Name(id=arg_name, ctx=Load()),
                    ops=[Is()],
                    comparators=[Name(id=INTERNAL_CONTEXT_NAME, ctx=Load())]),
                   body=[
                       Expr(value=Call(func=Attribute(value=Name(
                           id=INTERNAL_CONTEXT_NAME, ctx=Load()),
                                                      attr='flag_missing',
                                                      ctx=Load()),
                                       keywords=[],
                                       starargs=None,
                                       kwargs=None,
                                       args=[Str(s=arg_name)]))
                   ],
                   orelse=[])
            ]  # type: List[Union[Assign, If]

            return If(test=Compare(
                left=Name(id=arg_name, ctx=Load()),
                ops=[Is()],
                comparators=[default_nodes_mapping[arg_name]]),
                      body=consequence,
                      orelse=[])
예제 #20
0
 def visit_Delete(self, node):
     existing_node = self.generic_visit(node)
     for target in existing_node.targets:
         attribute_names = self._get_attribute_names(target)
         if attribute_names:
             target_name = '.'.join(attribute_names[:-1])
         else:
             target_value = getattr(target, 'value', None)
             attribute_names = self._get_attribute_names(target_value)
             if attribute_names:
                 target_name = '.'.join(attribute_names)
             else:
                 target_name = getattr(target_value, 'id', None)
         if target_name is not None:
             args = [Str(s=target_name), target.value, Num(n=target.lineno)]
             target.value = self._create_bare_context_call(
                 'record_delete', args)
     return existing_node
    def compile_importnode(self, srcnode, parent):

        assign = Assign(targets=[Name(id=str(srcnode.alias), ctx=Store())],
                        value=Call(func=Attribute(value=Name(id='__piglet_rt',
                                                             ctx=Load()),
                                                  attr='load',
                                                  ctx=Load()),
                                   args=[Name(id='__piglet_template', ctx=Load()),
                                         Str(s=srcnode.href)],
                                   starargs=None,
                                   kwargs=None,
                                   keywords=[]))
        assign = self.annotate_runtime_errors(assign, srcnode)

        container = self.get_func(parent)
        if container.name == '__piglet_root__':
            self.module.body.insert(self.module.body.index(container), assign)
        else:
            container.body.append(assign)
예제 #22
0
    def visit_Call(self, node):
        existing_node = self.generic_visit(node)
        func_node = existing_node.func

        if self._is_untraceable_attribute(func_node):
            return existing_node
        if isinstance(func_node, Name) and func_node.id == 'print':
            return self._trace_print_function(existing_node)

        comparisons = []  # [(name, node)]
        names = self._get_attribute_names(func_node)
        if names is not None:
            comparisons.append(
                ('.'.join(names[:-1]), existing_node.func.value))

        for arg_node in existing_node.args:
            if isinstance(arg_node, Name):
                comparisons.append((arg_node.id, arg_node))

        if not comparisons:
            return existing_node
        args = [
            List(elts=[], ctx=Load()),
            List(elts=[], ctx=Load()), existing_node,
            List(elts=[], ctx=Load()),
            Num(n=existing_node.lineno)
        ]
        for name, node in comparisons:
            args[0].elts.append(Str(s=name))  # name
            args[1].elts.append(  # repr() before
                Call(func=Name(id='repr', ctx=Load()),
                     args=[node],
                     keywords=[],
                     starargs=None,
                     kwargs=None))
            args[3].elts.append(  # repr() after
                Call(func=Name(id='repr', ctx=Load()),
                     args=[node],
                     keywords=[],
                     starargs=None,
                     kwargs=None))
        new_node = self._create_bare_context_call('record_call', args)
        return new_node
    def compile_filternode(self, srcnode, parent):
        func = parse_and_strip(srcnode.function)[0].value

        with self.collect_output(parent) as ACC:
            for node in srcnode.children:
                self._compile(node, parent)

        joined = Call(func=Attribute(value=Str(s=''), attr='join', content=Load()),
                      args=[LoadName(ACC)],
                      starargs=None,
                      kwargs=None,
                      keywords=[])
        parent.body.append(Expr(value=Yield(
            Call(func=func,
                 args=[joined],
                 starargs=None,
                 kwargs=None,
                 keywords=[])
        )))
예제 #24
0
    def visit_Assign(self, node):
        existing_node = self.generic_visit(node)
        try:
            targets = existing_node.targets
        except AttributeError:
            targets = [existing_node.target]
        if any(map(self._is_untraceable_attribute, targets)):
            return existing_node
        line_numbers = set()
        find_line_numbers(existing_node, line_numbers)
        first_line_number = min(line_numbers)
        last_line_number = max(line_numbers)
        new_nodes = []
        format_string = self._wrap_assignment_targets(
            targets)
        if (len(targets) == 1 and
                isinstance(targets[0], Tuple)):
            existing_node.value = Call(func=Name(id='tuple', ctx=Load()),
                                       args=[existing_node.value],
                                       keywords=[],
                                       starargs=None,
                                       kwargs=None)
        existing_node.value = self._create_bare_context_call(
            'set_assignment_value',
            [existing_node.value])
        new_nodes.append(self._create_context_call('start_assignment'))
        try_body = [existing_node]
        if format_string is not None:
            try_body.append(self._create_context_call(
                'report_assignment',
                [Str(s=format_string), Num(n=existing_node.lineno)]))
        end_assignment = self._create_context_call('end_assignment')
        finally_body = [end_assignment]
        new_nodes.append(TryFinally(body=try_body,
                                    finalbody=finally_body,
                                    handlers=[],
                                    orelse=[],
                                    lineno=first_line_number))
        self._set_statement_line_numbers(try_body, first_line_number)
        self._set_statement_line_numbers(finally_body, last_line_number)

        return new_nodes
예제 #25
0
    def visit_Assign(self, node):
        '''Interprets an assignament as a definition of a new nonterminal symbol.
         
         Only can be assigned rules, optionals, choices, nonterminals,
         terminals and repeated expressions.
         Valid:
            A = B, C       meaning  A := B follow by C
            A = [B, C]     meaning  A := B follow by C or the empty string
            A = {B | C}    meaning  A := B or C
            A = B          meaning  A := B (other nonterminal symbol)
            A = c          meaning  A := c (a terminal symbol)a
            A = B[...]     meaning  A := B one or more times

         It is not allowed multiples assignaments like A, B = C
         and in all the cases the name of the symbol (A in the case A=...)
         must be a valid name.
         '''
        if len(node.targets) != 1 or not isinstance(node.targets[0], Name):
            raise NotationASTTransformer.ParserASTError(
                node, "Only one assign 'A = ...' is allowed and 'A' must be a "
                "variable with a valid name.")

        # We allow 'Dict' to handle better error messages.
        if not isinstance(node.value,
                          (Tuple, List, Set, Name, Str, Subscript, Dict)):
            raise NotationASTTransformer.ParserASTError(
                node,
                "The value to assign is invalid. The 'rule', 'optional', "
                "'choices', 'repeated', a variable name and a literal string "
                "are allowes.")

        if isinstance(node.value, (Name, Str)):
            new_node = deepcopy(NotationASTTransformer.__parsed_tuple.body)
            new_node.elts[0] = node.value
            node.value = new_node

        node.value.production_name = Str(node.targets[0].id)
        node.value.production_name._already_process = True
        node.targets[0]._already_process = True
        self.generic_visit(node)

        return node
예제 #26
0
    def _fix_module_ds(self, module):
        """
        Starting in python 3.7 the AST for mule have changed, and if
        the first expressions encountered is a string it is attached to the
        `docstring` attribute of the `Module` ast node.

        This breaks IPython, as if this string is the only expression, IPython
        will not return it as the result of the current cell.
        """
        from ast import Str, Expr, Module, fix_missing_locations
        docstring = getattr(module, 'docstring', None)
        if not docstring:
            return module
        new_body = [
            Expr(Str(docstring, lineno=1, col_offset=0),
                 lineno=1,
                 col_offset=0)
        ]
        new_body.extend(module.body)
        return fix_missing_locations(Module(new_body))
예제 #27
0
    def visit_AugAssign(self, node):
        read_target = deepcopy(node.target)
        existing_node = self.generic_visit(node)
        line_numbers = set()
        self._find_line_numbers(existing_node, line_numbers)
        first_line_number = min(line_numbers)
        last_line_number = max(line_numbers)
        new_nodes = []
        try_body = [existing_node]
        new_nodes.append(self._create_context_call('start_assignment'))
        format_string = self._wrap_assignment_target(existing_node.target)
        if format_string is not None:
            if ':' in format_string:
                existing_node.value = self._create_bare_context_call(
                    'set_assignment_value', [existing_node.value])
                operator_char = OPERATOR_CHARS.get(type(existing_node.op), '?')
                format_string += ' {}= {{}} '.format(operator_char)
            else:
                self._wrap_assignment_target(read_target, index_to_get=-1)
                read_target.ctx = Load()
                set_assignment_value = self._create_context_call(
                    'set_assignment_value', [read_target])
                try_body.append(set_assignment_value)
                format_string += ' = {}'
            try_body.append(
                self._create_context_call(
                    'report_assignment',
                    [Str(s=format_string),
                     Num(n=existing_node.lineno)]))
        end_assignment = self._create_context_call('end_assignment')
        finally_body = [end_assignment]
        new_nodes.append(
            TryFinally(body=try_body,
                       finalbody=finally_body,
                       handlers=[],
                       orelse=[],
                       lineno=first_line_number))
        self._set_statement_line_numbers(try_body, first_line_number)
        self._set_statement_line_numbers(finally_body, last_line_number)

        return new_nodes
예제 #28
0
def object_instance_from_res(object_name: str, object_id: str,
                             cls_name: str) -> AnnAssign:

    try:
        is_valid_identifier(object_name)
    except Arcor2Exception as e:
        raise Arcor2Exception(f"Object name {object_name} invalid. {str(e)}")

    try:
        is_valid_type(cls_name)
    except Arcor2Exception as e:
        raise Arcor2Exception(f"Class name {cls_name} invalid. {str(e)}")

    return AnnAssign(
        target=Name(id=object_name, ctx=Store()),
        annotation=Name(id=cls_name, ctx=Load()),
        value=Subscript(value=get_name_attr("res", "objects"),
                        slice=Index(value=Str(s=object_id, kind="")),
                        ctx=Load()),
        simple=1,
    )
예제 #29
0
    def handle_value(self, value):
        if isinstance(value.value, Enum):
            return Attribute(
                value=Name(id=value.value.__class__.__name__, **self.file),
                attr=value.value.name,
                **self.file,
            )

        if isinstance(value.value, str):
            return Str(s=value.value, **self.file)

        if value.value is None:
            return Constant(value=None, **self.file)

        if isinstance(value.value, int):
            return Num(n=value.value, **self.file)

        if isinstance(value.value, bool):
            return Name(id=str(value.value), **self.file)

        raise ValueError("Unhandled Value")
예제 #30
0
def _do_wrap_class_register(tree, mod, base_class):
    if isinstance(tree, ClassDef):
        # Create the register call
        name = tree.name
        l_start = tree.lineno
        # Python 3.8:
        # l_end = tree.end_lineno
        # Python 3.7, this is good enough for our needs:
        l_end = l_start + 1
        reg_name = name.lower()
        # BaseOutput.register member:
        attr = Attribute(value=Name(id=base_class, ctx=Load()),
                         attr='register',
                         ctx=Load())
        # Function call to it passing reg_name and name
        # Put it in the last line.
        do_register = Expr(value=Call(
            func=attr,
            args=[Str(s=reg_name), Name(id=name, ctx=Load())],
            keywords=[]),
                           lineno=l_end,
                           col_offset=0,
                           end_lineno=l_end,
                           end_col_offset=50)

        # Create the import
        # Put it in the decorator line.
        do_import = ImportFrom(module=mod,
                               names=[alias(name=base_class, asname=None)],
                               level=1,
                               lineno=l_start - 1,
                               col_offset=0,
                               end_lineno=l_start - 1,
                               end_col_offset=50)

        return [do_import, tree, do_register]
    # Just in case somebody applies it to anything other than a class
    return tree  # pragma: no cover