"""
Our own implementation of an abstract syntax tree (AST).

The convert function recursively converts a Python AST (from the module `ast`)
to our own AST (of the class `Node`).
"""

import ast
from logging import debug

from typy.builtin import data_types
from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable
from typy import types


class Node:
    def __init__(self, type_map, ast_node):
        self.type_map = type_map
        self._ast_fields = ast_node._fields

    def check(self):
        """Must be overriden in subtype."""
        raise NotYetSupported('check call to', self)

    def iter_fields(self):
        for field in self._ast_fields:
            try:
                yield field, getattr(self, field)
            except AttributeError:
                pass

    def iter_child_nodes(self):
        for _name, field in self.iter_fields():
            if isinstance(field, Node):
                yield field
            elif isinstance(field, list):
                for item in field:
                    if isinstance(item, Node):
                        yield item


class FunctionDef(Node):
    def __init__(self, type_map, ast_node):
        if (ast_node.args.vararg is not None or
                len(ast_node.args.kwonlyargs) > 0 or
                len(ast_node.args.kw_defaults) > 0 or
                ast_node.args.kwarg is not None or
                len(ast_node.args.defaults) > 0):
            raise NotYetSupported('default arguments and keyword arguments')

        super().__init__(type_map, ast_node)
        self.name = ast_node.name
        self.params = [arg.arg for arg in ast_node.args.args]
        self.body = [convert(type_map, stmt) for stmt in ast_node.body]
        self._ast_fields = ('name', 'params', 'body')

    def check(self):
        debug('checking func def %s', self.name)
        function = types.Function(self, self.type_map)
        self.type_map.add_variable(self.name, function)
        return data_types.None_()

    def __repr__(self):
        return 'def ' + self.name + '()'


class ClassDef(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.name = ast_node.name
        self.body = [convert(type_map, stmt) for stmt in ast_node.body]

    def check(self):
        debug('checking class def %s', self.name)

        class_namespace = self.type_map.enter_namespace(self.name)

        for stmt in self.body:
            stmt.check()

        self.type_map.exit_namespace()

        class_ = types.Class(self, self.type_map, class_namespace)
        self.type_map.add_variable(self.name, class_)

        return data_types.None_()

    def __repr__(self):
        return 'def ' + self.name


class Attribute(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.value = convert(type_map, ast_node.value)
        self.attr = ast_node.attr
        self.ctx = ast_node.ctx

    def check(self):
        debug('checking attr %s', self)

        value_type = self.value.check()
        debug('attr %r = %r', self, value_type)

        if isinstance(self.ctx, ast.Load):
            return value_type.get_attribute(self.attr)
        elif isinstance(self.ctx, ast.Store):
            return (value_type, self.attr)
        else:
            # TODO implement for Del, AugLoad, AugStore, Param
            raise NotYetSupported('name context', self.ctx)

    def __repr__(self):
        return repr(self.value) + '.' + self.attr


class Name(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.id = ast_node.id
        self.ctx = ast_node.ctx

    def check(self):
        debug('checking name %s', self.id)

        if isinstance(self.ctx, ast.Load):
            return self.type_map.find(self.id)
        elif isinstance(self.ctx, ast.Store):
            return self
        else:
            # TODO implement for Del, AugLoad, AugStore, Param
            raise NotYetSupported('name context', self.ctx)

    def __repr__(self):
        return self.id


class Call(Node):
    def __init__(self, type_map, ast_node):
        if (len(ast_node.keywords) > 0 or
                ast_node.starargs is not None or
                ast_node.kwargs is not None):
            raise NotYetSupported('keyword arguments and star arguments')

        super().__init__(type_map, ast_node)
        self.func = convert(type_map, ast_node.func)
        self.args = [convert(type_map, expr) for expr in ast_node.args]

    def check(self):
        debug('checking call')
        func = self.func.check()
        args = [arg.check() for arg in self.args]
        return func.check_call(args)

    def __repr__(self):
        return repr(self.func) + \
               '(' + ', '.join(repr(x) for x in self.args) + ')'


class Expr(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.value = convert(type_map, ast_node.value)

    def check(self):
        debug('checking expr')
        self.value.check()
        return data_types.None_()

    def __repr__(self):
        return repr(self.value)


class Return(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.value = convert(type_map, ast_node.value)

    def check(self):
        debug('checking return')
        return self.value.check()

    def __repr__(self):
        return 'return ' + repr(self.value)


class Module(Node, types.Type):
    def __init__(self, type_map, ast_node):
        Node.__init__(self, type_map, ast_node)
        types.Type.__init__(self, type_map)
        self.body = [convert(type_map, stmt) for stmt in ast_node.body]

    def check(self):
        debug('checking module')

        self.module_namespace = self.type_map.enter_namespace('__main__')

        debug('entering %r', self.type_map.current_namespace)

        for stmt in self.body:
            debug('still in %r', self.type_map.current_namespace)
            stmt.check()

        debug('leaving %r', self.type_map.current_namespace)

        self.type_map.exit_namespace()

    def get_attribute(self, name):
        try:
            return self.module_namespace[name]
        except KeyError:
            types.Type.get_attribute(self, name)


class Assign(Node):
    def __init__(self, type_map, ast_node):
        # TODO handle multiple targets
        if len(ast_node.targets) > 1:
            raise NotYetSupported('assignment with multiple targets')

        super().__init__(type_map, ast_node)
        self.target = convert(type_map, ast_node.targets[0])
        self.value = convert(type_map, ast_node.value)
        self._ast_fields = ('target', 'value')

    def check(self):
        debug('checking assign %r', self.target)
        _assign(self.target, self.value, self.type_map)
        return data_types.None_()

    def __repr__(self):
        return repr(self.target) + ' = ' + repr(self.value)


class Pass(Node):
    def check(self):
        debug('checking pass')
        return data_types.None_()

    def __repr__(self):
        return 'pass'


class Not(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.value = convert(type_map, ast_node.value)

    def check(self):
        debug('checking not')
        self.value.check()
        return data_types.Bool()

    def __repr__(self):
        return 'not ' + repr(self.value)


class BoolOp(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.op = ast_node.op
        self.values = [convert(type_map, value) for value in ast_node.values]

    def check(self):
        debug('checking boolop')

        for value in self.values:
            value.check()

        # TODO return intersection van types?
        return data_types.Bool()

    def __repr__(self):
        op_name = ' {} '.format(self.op)
        return '(' + op_name.join(repr(val) for val in self.values) + ')'


class In(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.element = convert(type_map, ast_node.element)
        self.container = convert(type_map, ast_node.container)

    def check(self):
        debug('checking in')

        element = self.element.check()
        container = self.container.check()

        try:
            container.call_magic_method('__contains__', element)
        except NoSuchAttribute:
            if not container.is_iterable():
                raise NotIterable(container)

        return data_types.Bool()

    def __repr__(self):
        return '{!r} in {!r}'.format(self.element, self.container)


class For(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.target = convert(type_map, ast_node.target)
        self.iter = convert(type_map, ast_node.iter)
        self.body = [convert(type_map, stmt) for stmt in ast_node.body]
        self.orelse = [convert(type_map, clause) for clause in ast_node.orelse]

    def check(self):
        debug('checking for')

        iterator = self.iter.check()
        enclosed_type = iterator.get_enclosed_type()
        _assign(self.target, enclosed_type, self.type_map)

        for stmt in self.body:
            stmt.check()
        for stmt in self.orelse:
            stmt.check()

        # TODO return intersection of values of both branches
        return data_types.None_()

    def __repr__(self):
        s = 'for {!r} in {!r}:\n    '.format(self.target, self.iter)
        s += '\n    '.join(repr(stmt) for stmt in self.body)
        if self.orelse:
            s += 'else:\n    '
            s += '\n    '.join(repr(stmt) for stmt in self.orelse)
        return s


class If(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.test = convert(type_map, ast_node.test)
        self.body = [convert(type_map, stmt) for stmt in ast_node.body]
        self.orelse = [convert(type_map, stmt) for stmt in ast_node.orelse]

    def check(self):
        debug('checking if')

        # TODO take isinstance into account (?)
        # TODO real branching?
        self.test.check()
        for stmt in self.body:
            stmt.check()
        for stmt in self.orelse:
            stmt.check()

        # TODO return intersection of values of both branches
        return data_types.None_()

    def __repr__(self):
        s = 'if {!r}:\n    '.format(self.test)
        s += '\n    '.join(repr(stmt) for stmt in self.body)
        if self.orelse:
            s += 'else:\n    '
            s += '\n    '.join(repr(stmt) for stmt in self.orelse)
        return s


class IfExp(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.test = convert(type_map, ast_node.test)
        self.body = convert(type_map, ast_node.body)
        self.orelse = convert(type_map, ast_node.orelse)

    def check(self):
        debug('checking ifexp')

        # TODO take isinstance into account (?)
        self.test.check()
        value1 = self.body.check()
        value2 = self.orelse.check()
        return types.Intersection(value1, value2)

    def __repr__(self):
        template = '{!r} if {!r} else {!r}'
        return template.format(self.test, self.body, self.orelse)


class NameConstant(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.value = ast_node.value

    def check(self):
        debug('checking name constant %r', self.value)
        if self.value is None:
            return data_types.None_()
        elif self.value is True or self.value is False:
            return data_types.Bool()
        else:
            raise NotYetSupported('name constant', self.value)

    def __repr__(self):
        return repr(self.value)


class While(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.test = convert(type_map, ast_node.test)
        self.body = [convert(type_map, stmt) for stmt in ast_node.body]
        self.orelse = [convert(type_map, stmt) for stmt in ast_node.orelse]

    def check(self):
        debug('checking while')

        # TODO take isinstance into account (?)
        # TODO real branching?
        self.test.check()
        for stmt in self.body:
            stmt.check()
        for stmt in self.orelse:
            stmt.check()

        # TODO return intersection of values of both branches
        return data_types.None_()

    def __repr__(self):
        s = 'while {!r}:\n    '.format(self.test)
        s += '\n    '.join(repr(stmt) for stmt in self.body)
        if self.orelse:
            s += 'else:\n    '
            s += '\n    '.join(repr(stmt) for stmt in self.orelse)
        return s


class Break(Node):
    def check(self):
        debug('checking break')
        return data_types.None_()

    def __repr__(self):
        return 'break'


class Continue(Node):
    def check(self):
        debug('checking continue')
        return data_types.None_()

    def __repr__(self):
        return 'continue'


class Num(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.number_type = {
            int: data_types.Int,
            # float: data_types.Float,
            # complex: data_types.Complex,
        }[type(ast_node.n)]

    def check(self):
        debug('checking num')
        return self.number_type()


class Tuple(Node):
    def __init__(self, type_map, ast_node):
        super().__init__(type_map, ast_node)
        self.elts = [convert(type_map, el) for el in ast_node.elts]
        self.ctx = ast_node.ctx

    def check(self):
        debug('checking tuple %r', self)

        if isinstance(self.ctx, ast.Load):
            el_types = (el.check() for el in self.elts)
            return types.Tuple(self.type_map, *el_types)
        elif isinstance(self.ctx, ast.Store):
            return self
        else:
            # TODO implement for Del, AugLoad, AugStore, Param
            raise NotYetSupported('name context', self.ctx)

    def __repr__(self):
        return '(' + ', '.join(repr(el) for el in self.elts) + ')'


def _assign(target, value, type_map):
    value_type = value.check()

    if isinstance(target, Name):
        target_type = target.check()
        type_map.add_variable(target_type.id, value_type)
    elif isinstance(target, Attribute):
        target_type, attr = target.check()
        target_type.set_attribute(attr, value_type)
    else:
        raise NotYetSupported('assignment to', target)


def convert(type_map, node):
    class_name = node.__class__.__name__
    try:
        # Try to convert to a node
        class_ = globals()[class_name]
        return class_(type_map, node)
    except KeyError:
        try:
            # Try to convert to a builtin type
            class_ = getattr(data_types, class_name)
            return class_()
        except AttributeError:
            raise NotYetSupported('node', node)

[((23, 14, 23, 52), 'typy.exceptions.NotYetSupported', 'NotYetSupported', ({(23, 30, 23, 45): '"""check call to"""', (23, 47, 23, 51): 'self'}, {}), "('check call to', self)", False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n'), ((58, 8, 58, 48), 'logging.debug', 'debug', ({(58, 14, 58, 36): '"""checking func def %s"""', (58, 38, 58, 47): 'self.name'}, {}), "('checking func def %s', self.name)", False, 'from logging import debug\n'), ((59, 19, 59, 54), 'typy.types.Function', 'types.Function', ({(59, 34, 59, 38): 'self', (59, 40, 59, 53): 'self.type_map'}, {}), '(self, self.type_map)', False, 'from typy import types\n'), ((61, 15, 61, 33), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((74, 8, 74, 49), 'logging.debug', 'debug', ({(74, 14, 74, 37): '"""checking class def %s"""', (74, 39, 74, 48): 'self.name'}, {}), "('checking class def %s', self.name)", False, 'from logging import debug\n'), ((83, 17, 83, 66), 'typy.types.Class', 'types.Class', ({(83, 29, 83, 33): 'self', (83, 35, 83, 48): 'self.type_map', (83, 50, 83, 65): 'class_namespace'}, {}), '(self, self.type_map, class_namespace)', False, 'from typy import types\n'), ((86, 15, 86, 33), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((100, 8, 100, 39), 'logging.debug', 'debug', ({(100, 14, 100, 32): '"""checking attr %s"""', (100, 34, 100, 38): 'self'}, {}), "('checking attr %s', self)", False, 'from logging import debug\n'), ((103, 8, 103, 47), 'logging.debug', 'debug', ({(103, 14, 103, 28): '"""attr %r = %r"""', (103, 30, 103, 34): 'self', (103, 36, 103, 46): 'value_type'}, {}), "('attr %r = %r', self, value_type)", False, 'from logging import debug\n'), ((124, 8, 124, 42), 'logging.debug', 'debug', ({(124, 14, 124, 32): '"""checking name %s"""', (124, 34, 124, 41): 'self.id'}, {}), "('checking name %s', self.id)", False, 'from logging import debug\n'), ((150, 8, 150, 30), 'logging.debug', 'debug', ({(150, 14, 150, 29): '"""checking call"""'}, {}), "('checking call')", False, 'from logging import debug\n'), ((166, 8, 166, 30), 'logging.debug', 'debug', ({(166, 14, 166, 29): '"""checking expr"""'}, {}), "('checking expr')", False, 'from logging import debug\n'), ((168, 15, 168, 33), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((180, 8, 180, 32), 'logging.debug', 'debug', ({(180, 14, 180, 31): '"""checking return"""'}, {}), "('checking return')", False, 'from logging import debug\n'), ((190, 8, 190, 43), 'typy.types.Type.__init__', 'types.Type.__init__', ({(190, 28, 190, 32): 'self', (190, 34, 190, 42): 'type_map'}, {}), '(self, type_map)', False, 'from typy import types\n'), ((194, 8, 194, 32), 'logging.debug', 'debug', ({(194, 14, 194, 31): '"""checking module"""'}, {}), "('checking module')", False, 'from logging import debug\n'), ((198, 8, 198, 61), 'logging.debug', 'debug', ({(198, 14, 198, 27): '"""entering %r"""', (198, 29, 198, 60): 'self.type_map.current_namespace'}, {}), "('entering %r', self.type_map.current_namespace)", False, 'from logging import debug\n'), ((204, 8, 204, 60), 'logging.debug', 'debug', ({(204, 14, 204, 26): '"""leaving %r"""', (204, 28, 204, 59): 'self.type_map.current_namespace'}, {}), "('leaving %r', self.type_map.current_namespace)", False, 'from logging import debug\n'), ((227, 8, 227, 48), 'logging.debug', 'debug', ({(227, 14, 227, 34): '"""checking assign %r"""', (227, 36, 227, 47): 'self.target'}, {}), "('checking assign %r', self.target)", False, 'from logging import debug\n'), ((229, 15, 229, 33), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((237, 8, 237, 30), 'logging.debug', 'debug', ({(237, 14, 237, 29): '"""checking pass"""'}, {}), "('checking pass')", False, 'from logging import debug\n'), ((238, 15, 238, 33), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((250, 8, 250, 29), 'logging.debug', 'debug', ({(250, 14, 250, 28): '"""checking not"""'}, {}), "('checking not')", False, 'from logging import debug\n'), ((252, 15, 252, 32), 'typy.builtin.data_types.Bool', 'data_types.Bool', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((265, 8, 265, 32), 'logging.debug', 'debug', ({(265, 14, 265, 31): '"""checking boolop"""'}, {}), "('checking boolop')", False, 'from logging import debug\n'), ((271, 15, 271, 32), 'typy.builtin.data_types.Bool', 'data_types.Bool', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((285, 8, 285, 28), 'logging.debug', 'debug', ({(285, 14, 285, 27): '"""checking in"""'}, {}), "('checking in')", False, 'from logging import debug\n'), ((296, 15, 296, 32), 'typy.builtin.data_types.Bool', 'data_types.Bool', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((311, 8, 311, 29), 'logging.debug', 'debug', ({(311, 14, 311, 28): '"""checking for"""'}, {}), "('checking for')", False, 'from logging import debug\n'), ((323, 15, 323, 33), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((342, 8, 342, 28), 'logging.debug', 'debug', ({(342, 14, 342, 27): '"""checking if"""'}, {}), "('checking if')", False, 'from logging import debug\n'), ((353, 15, 353, 33), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((372, 8, 372, 31), 'logging.debug', 'debug', ({(372, 14, 372, 30): '"""checking ifexp"""'}, {}), "('checking ifexp')", False, 'from logging import debug\n'), ((378, 15, 378, 49), 'typy.types.Intersection', 'types.Intersection', ({(378, 34, 378, 40): 'value1', (378, 42, 378, 48): 'value2'}, {}), '(value1, value2)', False, 'from typy import types\n'), ((391, 8, 391, 54), 'logging.debug', 'debug', ({(391, 14, 391, 41): '"""checking name constant %r"""', (391, 43, 391, 53): 'self.value'}, {}), "('checking name constant %r', self.value)", False, 'from logging import debug\n'), ((411, 8, 411, 31), 'logging.debug', 'debug', ({(411, 14, 411, 30): '"""checking while"""'}, {}), "('checking while')", False, 'from logging import debug\n'), ((422, 15, 422, 33), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((435, 8, 435, 31), 'logging.debug', 'debug', ({(435, 14, 435, 30): '"""checking break"""'}, {}), "('checking break')", False, 'from logging import debug\n'), ((436, 15, 436, 33), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((444, 8, 444, 34), 'logging.debug', 'debug', ({(444, 14, 444, 33): '"""checking continue"""'}, {}), "('checking continue')", False, 'from logging import debug\n'), ((445, 15, 445, 33), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((461, 8, 461, 29), 'logging.debug', 'debug', ({(461, 14, 461, 28): '"""checking num"""'}, {}), "('checking num')", False, 'from logging import debug\n'), ((472, 8, 472, 40), 'logging.debug', 'debug', ({(472, 14, 472, 33): '"""checking tuple %r"""', (472, 35, 472, 39): 'self'}, {}), "('checking tuple %r', self)", False, 'from logging import debug\n'), ((49, 18, 49, 76), 'typy.exceptions.NotYetSupported', 'NotYetSupported', ({(49, 34, 49, 75): '"""default arguments and keyword arguments"""'}, {}), "('default arguments and keyword arguments')", False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n'), ((143, 18, 143, 73), 'typy.exceptions.NotYetSupported', 'NotYetSupported', ({(143, 34, 143, 72): '"""keyword arguments and star arguments"""'}, {}), "('keyword arguments and star arguments')", False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n'), ((201, 12, 201, 65), 'logging.debug', 'debug', ({(201, 18, 201, 31): '"""still in %r"""', (201, 33, 201, 64): 'self.type_map.current_namespace'}, {}), "('still in %r', self.type_map.current_namespace)", False, 'from logging import debug\n'), ((219, 18, 219, 69), 'typy.exceptions.NotYetSupported', 'NotYetSupported', ({(219, 34, 219, 68): '"""assignment with multiple targets"""'}, {}), "('assignment with multiple targets')", False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n'), ((393, 19, 393, 37), 'typy.builtin.data_types.None_', 'data_types.None_', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((476, 19, 476, 56), 'typy.types.Tuple', 'types.Tuple', ({(476, 31, 476, 44): 'self.type_map', (476, 46, 476, 55): '*el_types'}, {}), '(self.type_map, *el_types)', False, 'from typy import types\n'), ((497, 14, 497, 54), 'typy.exceptions.NotYetSupported', 'NotYetSupported', ({(497, 30, 497, 45): '"""assignment to"""', (497, 47, 497, 53): 'target'}, {}), "('assignment to', target)", False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n'), ((111, 18, 111, 59), 'typy.exceptions.NotYetSupported', 'NotYetSupported', ({(111, 34, 111, 48): '"""name context"""', (111, 50, 111, 58): 'self.ctx'}, {}), "('name context', self.ctx)", False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n'), ((132, 18, 132, 59), 'typy.exceptions.NotYetSupported', 'NotYetSupported', ({(132, 34, 132, 48): '"""name context"""', (132, 50, 132, 58): 'self.ctx'}, {}), "('name context', self.ctx)", False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n'), ((212, 12, 212, 48), 'typy.types.Type.get_attribute', 'types.Type.get_attribute', ({(212, 37, 212, 41): 'self', (212, 43, 212, 47): 'name'}, {}), '(self, name)', False, 'from typy import types\n'), ((395, 19, 395, 36), 'typy.builtin.data_types.Bool', 'data_types.Bool', ({}, {}), '()', False, 'from typy.builtin import data_types\n'), ((397, 18, 397, 62), 'typy.exceptions.NotYetSupported', 'NotYetSupported', ({(397, 34, 397, 49): '"""name constant"""', (397, 51, 397, 61): 'self.value'}, {}), "('name constant', self.value)", False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n'), ((481, 18, 481, 59), 'typy.exceptions.NotYetSupported', 'NotYetSupported', ({(481, 34, 481, 48): '"""name context"""', (481, 50, 481, 58): 'self.ctx'}, {}), "('name context', self.ctx)", False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n'), ((294, 22, 294, 44), 'typy.exceptions.NotIterable', 'NotIterable', ({(294, 34, 294, 43): 'container'}, {}), '(container)', False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n'), ((512, 18, 512, 47), 'typy.exceptions.NotYetSupported', 'NotYetSupported', ({(512, 34, 512, 40): '"""node"""', (512, 42, 512, 46): 'node'}, {}), "('node', node)", False, 'from typy.exceptions import NotYetSupported, NoSuchAttribute, NotIterable\n')]