generics.py 3.08 KB
Newer Older
Stelios Karozis's avatar
Stelios Karozis committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
"""
This module is about generics, like the `int` in `List[int]`. It's not about
the Generic class.
"""

from jedi import debug
from jedi.cache import memoize_method
from jedi.inference.utils import to_tuple
from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference.value.iterable import SequenceLiteralValue
from jedi.inference.helpers import is_string


def _resolve_forward_references(context, value_set):
    for value in value_set:
        if is_string(value):
            from jedi.inference.gradual.annotation import _get_forward_reference_node
            node = _get_forward_reference_node(context, value.get_safe_value())
            if node is not None:
                for c in context.infer_node(node):
                    yield c
        else:
            yield value


class _AbstractGenericManager(object):
    def get_index_and_execute(self, index):
        try:
            return self[index].execute_annotation()
        except IndexError:
            debug.warning('No param #%s found for annotation %s', index, self)
            return NO_VALUES

    def get_type_hint(self):
        return '[%s]' % ', '.join(t.get_type_hint(add_class_info=False) for t in self.to_tuple())


class LazyGenericManager(_AbstractGenericManager):
    def __init__(self, context_of_index, index_value):
        self._context_of_index = context_of_index
        self._index_value = index_value

    @memoize_method
    def __getitem__(self, index):
        return self._tuple()[index]()

    def __len__(self):
        return len(self._tuple())

    @memoize_method
    @to_tuple
    def _tuple(self):
        def lambda_scoping_in_for_loop_sucks(lazy_value):
            return lambda: ValueSet(_resolve_forward_references(
                self._context_of_index,
                lazy_value.infer()
            ))

        if isinstance(self._index_value, SequenceLiteralValue):
            for lazy_value in self._index_value.py__iter__(contextualized_node=None):
                yield lambda_scoping_in_for_loop_sucks(lazy_value)
        else:
            yield lambda: ValueSet(_resolve_forward_references(
                self._context_of_index,
                ValueSet([self._index_value])
            ))

    @to_tuple
    def to_tuple(self):
        for callable_ in self._tuple():
            yield callable_()

    def is_homogenous_tuple(self):
        if isinstance(self._index_value, SequenceLiteralValue):
            entries = self._index_value.get_tree_entries()
            if len(entries) == 2 and entries[1] == '...':
                return True
        return False

    def __repr__(self):
        return '<LazyG>[%s]' % (', '.join(repr(x) for x in self.to_tuple()))


class TupleGenericManager(_AbstractGenericManager):
    def __init__(self, tup):
        self._tuple = tup

    def __getitem__(self, index):
        return self._tuple[index]

    def __len__(self):
        return len(self._tuple)

    def to_tuple(self):
        return self._tuple

    def is_homogenous_tuple(self):
        return False

    def __repr__(self):
        return '<TupG>[%s]' % (', '.join(repr(x) for x in self.to_tuple()))