diff --git a/jedi/evaluate/context/iterable.py b/jedi/evaluate/context/iterable.py index fb6aa668..07ba8fce 100644 --- a/jedi/evaluate/context/iterable.py +++ b/jedi/evaluate/context/iterable.py @@ -273,6 +273,7 @@ class GeneratorComprehension(ComprehensionMixin, GeneratorBase): class SequenceLiteralContext(Sequence): + _TUPLE_LIKE = 'testlist_star_expr', 'testlist', 'subscriptlist' mapping = {'(': u'tuple', '[': u'list', '{': u'set'} @@ -282,7 +283,7 @@ class SequenceLiteralContext(Sequence): self.atom = atom self._defining_context = defining_context - if self.atom.type in ('testlist_star_expr', 'testlist'): + if self.atom.type in self._TUPLE_LIKE: self.array_type = u'tuple' else: self.array_type = SequenceLiteralContext.mapping[atom.children[0]] @@ -336,7 +337,7 @@ class SequenceLiteralContext(Sequence): def _items(self): c = self.atom.children - if self.atom.type in ('testlist_star_expr', 'testlist'): + if self.atom.type in self._TUPLE_LIKE: return c[::2] array_node = c[1] diff --git a/jedi/evaluate/context/typing.py b/jedi/evaluate/context/typing.py index 09770f5a..482fba80 100644 --- a/jedi/evaluate/context/typing.py +++ b/jedi/evaluate/context/typing.py @@ -3,6 +3,7 @@ We need to somehow work with the typing objects. Since the typing objects are pretty bare we need to add all the Jedi customizations to make them work as contexts. """ +from parso.python import tree from jedi import debug from jedi.evaluate.compiled import builtin_from_name @@ -13,10 +14,30 @@ _PROXY_TYPES = 'Optional Union Callable Type ClassVar Tuple Generic Protocol'.sp _TYPE_ALIAS_TYPES = 'List Dict DefaultDict Set FrozenSet Counter Deque ChainMap'.split() -def check(context, name): +class _TypingBase(object): + def __init__(self, name, typing_context): + self._name = name + self._context = typing_context + def __getattr__(self, name): + return getattr(self._context, name) + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, self._context) + + +class TypingModuleWrapper(_TypingBase): + def py__getattribute__(self, name_or_str, *args, **kwargs): + result = self._context.py__getattribute__(name_or_str) + if kwargs.get('is_goto'): + return result + name = name_or_str.value if isinstance(name_or_str, tree.Name) else name_or_str + return ContextSet.from_iterable(_remap(c, name) for c in result) + + +def _remap(context, name): if name in _PROXY_TYPES: - return TypingProxy(context) + return TypingProxy(name, context) elif name in _TYPE_ALIAS_TYPES: # TODO raise NotImplementedError @@ -45,31 +66,22 @@ def check(context, name): return context -class _TypingBase(object): - def __init__(self, typing_context): - self._class_context = typing_context +class TypingProxy(_TypingBase): + py__simple_getitem__ = None - def __getattr__(self, name): - return getattr(self._class_context, name) - - -class TypingProxy(object): def py__getitem__(self, index_context, contextualized_node): - return TypingProxyWithIndex(self._class_context, index_context) - - def __repr__(self): - return '%s(%s)' % (self.__class__.__name__, self._class_context) + return ContextSet(TypingProxyWithIndex(self._name, self._context, index_context)) class _WithIndexBase(_TypingBase): - def __init__(self, class_context, index_context): - super(_WithIndexBase, self).__init__(class_context) + def __init__(self, name, class_context, index_context): + super(_WithIndexBase, self).__init__(name, class_context) self._index_context = index_context def __repr__(self): return '%s(%s, %s)' % ( self.__class__.__name__, - self._class_context, + self._context, self._index_context ) @@ -80,12 +92,8 @@ class _WithIndexBase(_TypingBase): class TypingProxyWithIndex(_WithIndexBase): - def __init__(self, typing_context, index_context): - self._class_context = typing_context - self._index_context = index_context - def execute_annotation(self): - name = self._class_context.py__name__() + name = self._name if name == 'Union': # This is kind of a special case, because we have Unions (in Jedi # ContextSets). @@ -93,17 +101,17 @@ class TypingProxyWithIndex(_WithIndexBase): elif name == 'Optional': # Optional is basically just saying it's either None or the actual # type. - return ContextSet(self._class_context) \ + return ContextSet(self._context) \ | ContextSet(builtin_from_name(self.evaluator, u'None')) elif name == 'Type': # The type is actually already given in the index_context return ContextSet(self._index_context) elif name == 'ClassVar': # For now don't do anything here, ClassVars are always used. - return self._class_context.execute_annotation() + return self._context.execute_annotation() cls = globals()[name] - return cls(self._class_context, self._index_context) + return ContextSet(cls(name, self._context, self._index_context)) def _iter_over_arguments(maybe_tuple_context): @@ -146,14 +154,14 @@ class Tuple(_ContainerBase): return self._get_getitem_contexts(0) else: if isinstance(index, int): - return self._get_getitem_contexts(index) + return self._get_getitem_contexts(index).execute_annotation() - debug.dbg('The getitem is') + debug.dbg('The getitem type on Tuple was %s' % index) return NO_CONTEXTS def py__getitem__(self): if self._is_homogenous(): - return self._get_getitem_contexts(0) + return self._get_getitem_contexts(0).execute_annotation() return self._execute_annotations_for_all_indexes() diff --git a/jedi/evaluate/docstrings.py b/jedi/evaluate/docstrings.py index f2676355..032f9d3c 100644 --- a/jedi/evaluate/docstrings.py +++ b/jedi/evaluate/docstrings.py @@ -21,9 +21,9 @@ from textwrap import dedent from parso import parse, ParserSyntaxError from jedi._compatibility import u +from jedi import debug from jedi.evaluate.utils import indent_block from jedi.evaluate.cache import evaluator_method_cache -from jedi.evaluate.helpers import execute_evaluated from jedi.evaluate.base_context import iterator_to_context_set, ContextSet, \ NO_CONTEXTS from jedi.evaluate.lazy_context import LazyKnownContexts @@ -203,6 +203,7 @@ def _evaluate_for_statement_string(module_context, string): # Take the default grammar here, if we load the Python 2.7 grammar here, it # will be impossible to use `...` (Ellipsis) as a token. Docstring types # don't need to conform with the current grammar. + debug.dbg('Parse docstring code %s', string, color='BLUE') grammar = module_context.evaluator.latest_grammar try: module = grammar.parse(code.format(indent_block(string)), error_recovery=False) @@ -262,7 +263,7 @@ def _execute_array_values(evaluator, array): values.append(LazyKnownContexts(objects)) return {FakeSequence(evaluator, array.array_type, values)} else: - return execute_evaluated(array) + return array.execute_annotation() @evaluator_method_cache() @@ -288,6 +289,7 @@ def infer_param(execution_context, param): class_context = execution_context.var_args.instance.class_context types |= eval_docstring(class_context.py__doc__()) + debug.dbg('Found param types for docstring %s', types, color='BLUE') return types diff --git a/jedi/evaluate/syntax_tree.py b/jedi/evaluate/syntax_tree.py index 806f94b4..76aa5e13 100644 --- a/jedi/evaluate/syntax_tree.py +++ b/jedi/evaluate/syntax_tree.py @@ -160,6 +160,7 @@ def eval_trailer(context, base_contexts, trailer): # https://github.com/davidhalter/jedi/issues/663 result = ContextSet() for typ in list(foo): + continue if isinstance(typ, (ClassContext, TreeInstance)): typing_module_types = pep0484.py__simple_getitem__(context, typ, node) if typing_module_types is not None: @@ -678,7 +679,7 @@ def eval_subscript_list(evaluator, context, index): return ContextSet(iterable.Slice(context, *result)) elif index.type == 'subscriptlist': - return NO_CONTEXTS + return ContextSet(iterable.SequenceLiteralContext(evaluator, context, index)) # No slices return context.eval_node(index) diff --git a/jedi/plugins/__init__.py b/jedi/plugins/__init__.py index 7f341d3b..c5a188ae 100644 --- a/jedi/plugins/__init__.py +++ b/jedi/plugins/__init__.py @@ -35,5 +35,5 @@ class _PluginCallbacks(object): plugin_manager = _PluginManager([ StdlibPlugin, FlaskPlugin, - #TypeshedPlugin, + TypeshedPlugin, ]) diff --git a/jedi/plugins/typeshed.py b/jedi/plugins/typeshed.py index 0c582567..bddb9e9d 100644 --- a/jedi/plugins/typeshed.py +++ b/jedi/plugins/typeshed.py @@ -10,6 +10,7 @@ from jedi.evaluate.base_context import ContextSet, iterator_to_context_set from jedi.evaluate.filters import AbstractTreeName, ParserTreeFilter, \ TreeNameDefinition from jedi.evaluate.context import ModuleContext, FunctionContext, ClassContext +from jedi.evaluate.context.typing import TypingModuleWrapper from jedi.evaluate.compiled import CompiledObject from jedi.evaluate.syntax_tree import tree_name_to_contexts from jedi.evaluate.utils import to_list @@ -133,7 +134,7 @@ class TypeshedPlugin(BasePlugin): ) import_name = import_names[-1] map_ = None - if len(import_names) == 1 and import_name != 'typing': + if len(import_names) == 1: map_ = self._cache_stub_file_map(evaluator.grammar.version_info) elif isinstance(parent_module_context, StubModuleContext): map_ = _merge_create_stub_map(parent_module_context.py__path__()) @@ -152,9 +153,10 @@ class TypeshedPlugin(BasePlugin): stub_module_context = StubOnlyModuleContext( context_set, evaluator, stub_module_node, path, code_lines=[] ) - return ContextSet.from_iterable( - _merge_modules(context_set, stub_module_context) - ) + modules = _merge_modules(context_set, stub_module_context) + if import_names == ('typing',): + modules = [TypingModuleWrapper('typing', m) for m in modules] + return ContextSet.from_iterable(modules) # If no stub is found, just return the default. return context_set return wrapper