Get a first typeshed example fully working as intended

This commit is contained in:
Dave Halter
2018-08-02 00:15:54 +02:00
parent c8caa8f4ac
commit 61de28f741
3 changed files with 101 additions and 103 deletions

View File

@@ -64,9 +64,7 @@ class ClassName(TreeNameDefinition):
@iterator_to_context_set @iterator_to_context_set
def infer(self): def infer(self):
# TODO this _name_to_types might get refactored and be a part of the # We're using a different context to infer, so we cannot call super().
# parent class. Once it is, we can probably just overwrite method to
# achieve this.
from jedi.evaluate.syntax_tree import tree_name_to_contexts from jedi.evaluate.syntax_tree import tree_name_to_contexts
inferred = tree_name_to_contexts( inferred = tree_name_to_contexts(
self.parent_context.evaluator, self._name_context, self.tree_name) self.parent_context.evaluator, self._name_context, self.tree_name)

View File

@@ -5,7 +5,8 @@ from pkg_resources import resource_filename
from jedi._compatibility import FileNotFoundError from jedi._compatibility import FileNotFoundError
from jedi.plugins.base import BasePlugin from jedi.plugins.base import BasePlugin
from jedi.evaluate.cache import evaluator_function_cache from jedi.evaluate.cache import evaluator_function_cache
from jedi.evaluate.base_context import Context, ContextSet, NO_CONTEXTS from jedi.cache import memoize_method
from jedi.evaluate.base_context import ContextSet, iterator_to_context_set
from jedi.evaluate.filters import AbstractTreeName, ParserTreeFilter, \ from jedi.evaluate.filters import AbstractTreeName, ParserTreeFilter, \
TreeNameDefinition TreeNameDefinition
from jedi.evaluate.context import ModuleContext, FunctionContext, ClassContext from jedi.evaluate.context import ModuleContext, FunctionContext, ClassContext
@@ -70,6 +71,29 @@ def _load_stub(evaluator, path):
return evaluator.parse(path=path, cache=True) return evaluator.parse(path=path, cache=True)
def _merge_modules(context_set, stub_context):
if not context_set:
# If there are no results for normal modules, just
# use a normal context for stub modules and don't
# merge the actual module contexts with stubs.
yield stub_context
return
for context in context_set:
# TODO what about compiled?
if isinstance(context, ModuleContext):
yield ModuleStubContext(
context.evaluator,
stub_context,
context.tree_node,
context._path,
context.code_lines
)
else:
# TODO do we want this?
yield context
class TypeshedPlugin(BasePlugin): class TypeshedPlugin(BasePlugin):
_version_cache = {} _version_cache = {}
@@ -120,24 +144,12 @@ class TypeshedPlugin(BasePlugin):
# TODO maybe empty cache? # TODO maybe empty cache?
pass pass
else: else:
code_lines = [] # TODO use code_lines
args = ( stub_module_context = ModuleContext(
evaluator, evaluator, stub_module_node, path, code_lines=[]
stub_module_node,
path,
code_lines,
) )
if not context_set:
# If there are no results for normal modules, just
# use a normal context for stub modules and don't
# merge the actual module contexts with stubs.
return ModuleContext(*args)
return ContextSet.from_iterable( return ContextSet.from_iterable(
ModuleStubContext( _merge_modules(context_set, stub_module_context)
*args,
context,
parent_module_context,
) for context in context_set
) )
# If no stub is found, just return the default. # If no stub is found, just return the default.
return context_set return context_set
@@ -152,34 +164,47 @@ class StubName(TreeNameDefinition):
""" """
def __init__(self, parent_context, tree_name, stub_parent_context, stub_tree_name): def __init__(self, parent_context, tree_name, stub_parent_context, stub_tree_name):
super(StubName, self).__init__(parent_context.actual_context, tree_name) super(StubName, self).__init__(parent_context, tree_name)
self._stub_parent_context = stub_parent_context self._stub_parent_context = stub_parent_context
self._stub_tree_name = stub_tree_name self._stub_tree_name = stub_tree_name
@memoize_method
@iterator_to_context_set
def infer(self): def infer(self):
def iterate(contexts): actual_contexts = super(StubName, self).infer()
for c in contexts: stub_contexts = tree_name_to_contexts(
if isinstance(c, FunctionContext):
yield FunctionStubContext(
c.evaluator,
c.parent_context,
c.tree_node,
)
elif isinstance(c, ClassContext):
yield ClassStubContext(
c.evaluator,
c.parent_context,
c.tree_node
)
else:
yield c
contexts = tree_name_to_contexts(
self.parent_context.evaluator, self.parent_context.evaluator,
self._stub_parent_context, self._stub_parent_context,
self._stub_tree_name self._stub_tree_name
) )
return ContextSet.from_iterable(iterate(contexts))
if not actual_contexts:
for c in stub_contexts:
yield c
for actual_context in actual_contexts:
for stub_context in stub_contexts:
if isinstance(stub_context, FunctionContext) \
and isinstance(actual_context, FunctionContext):
yield FunctionStubContext(
actual_context.evaluator,
stub_context,
actual_context.parent_context,
actual_context.tree_node,
)
elif isinstance(stub_context, ClassContext) \
and isinstance(actual_context, ClassContext):
yield ClassStubContext(
actual_context.evaluator,
stub_context,
actual_context.parent_context,
actual_context.tree_node,
)
else:
yield stub_context
if not stub_contexts:
yield actual_context
class StubParserTreeFilter(ParserTreeFilter): class StubParserTreeFilter(ParserTreeFilter):
@@ -201,16 +226,17 @@ class StubParserTreeFilter(ParserTreeFilter):
# match, just use the stub name. The user will be directed there # match, just use the stub name. The user will be directed there
# for all API accesses. Otherwise the user will be directed to the # for all API accesses. Otherwise the user will be directed to the
# non-stub positions (see StubName). # non-stub positions (see StubName).
if not found_actual_names: if not len(found_actual_names):
yield TreeNameDefinition(self.context, name) yield TreeNameDefinition(self.context, name)
for non_stub_name in found_actual_names: else:
assert isinstance(non_stub_name, AbstractTreeName), non_stub_name for non_stub_name in found_actual_names:
yield self.name_class( assert isinstance(non_stub_name, AbstractTreeName), non_stub_name
non_stub_name.parent_context, yield self.name_class(
non_stub_name.tree_name, non_stub_name.parent_context,
self.context, non_stub_name.tree_name,
name, self.context,
) name,
)
def _is_name_reachable(self, name): def _is_name_reachable(self, name):
if not super(StubParserTreeFilter, self)._is_name_reachable(name): if not super(StubParserTreeFilter, self)._is_name_reachable(name):
@@ -226,55 +252,18 @@ class StubParserTreeFilter(ParserTreeFilter):
return True return True
class StubProxy(object): class _MixedStubContextMixin(object):
def __init__(self, stub_context, parent_context): """
self._stub_context = stub_context Mixes the actual contexts with the stub module contexts.
self._parent_context = parent_context """
def __init__(self, evaluator, stub_context, *args, **kwargs):
def get_filters(self, *args, **kwargs): super(_MixedStubContextMixin, self).__init__(evaluator, *args, **kwargs)
for f in self._stub_context.get_filters(*args, **kwargs): self.stub_context = stub_context
yield StubFilterWrapper(f)
# We have to overwrite everything that has to do with trailers, name
# lookups and filters to make it possible to route name lookups towards
# compiled objects and the rest towards tree node contexts.
def py__getattribute__(self, *args, **kwargs):
return self._stub_context.py__getattribute__(*args, **kwargs)
#context_results = self._context.py__getattribute__(
# *args, **kwargs
#)
typeshed_results = list(self._stub_context.py__getattribute__(
*args, **kwargs
))
if not typeshed_results:
return NO_CONTEXTS
return ContextSet.from_iterable(
StubProxy(c) for c in typeshed_results
)
def get_root_context(self):
if self._parent_context is None:
return self
return self._parent_context.get_root_context()
def __getattr__(self, name):
return getattr(self._stub_context, name)
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self._stub_context)
class ModuleStubContext(ModuleContext): class _StubContextFilterMixin(_MixedStubContextMixin):
def __init__(self, evaluator, stub_module_node, path, code_lines,
actual_context, parent_module_context):
super(ModuleStubContext, self).__init__(evaluator, stub_module_node, path, code_lines),
self._parent_module_context = parent_module_context
self.actual_context = actual_context
def get_filters(self, search_global, until_position=None, origin_scope=None): def get_filters(self, search_global, until_position=None, origin_scope=None):
filters = super(ModuleStubContext, self).get_filters( filters = super(_StubContextFilterMixin, self).get_filters(
search_global, until_position, origin_scope search_global, until_position, origin_scope
) )
yield StubParserTreeFilter( yield StubParserTreeFilter(
@@ -282,7 +271,7 @@ class ModuleStubContext(ModuleContext):
# and wrap it. # and wrap it.
next(filters), next(filters),
self.evaluator, self.evaluator,
context=self, context=self.stub_context,
until_position=until_position, until_position=until_position,
origin_scope=origin_scope, origin_scope=origin_scope,
search_global=search_global, search_global=search_global,
@@ -291,9 +280,13 @@ class ModuleStubContext(ModuleContext):
yield f yield f
class ClassStubContext(ClassContext): class ModuleStubContext(_StubContextFilterMixin, ModuleContext):
pass pass
class FunctionStubContext(FunctionContext): class ClassStubContext(_StubContextFilterMixin, ClassContext):
pass
class FunctionStubContext(_MixedStubContextMixin, FunctionContext):
pass pass

View File

@@ -48,16 +48,23 @@ def test_function(Script):
def test_class(Script): def test_class(Script):
def_, = Script('import threading; threading.Lock').goto_definitions() def_, = Script('import threading; threading.Thread').goto_definitions()
context = def_._name._context context = def_._name._context
assert isinstance(context, typeshed.ClassStubContext), context assert isinstance(context, typeshed.ClassStubContext), context
def test_instance(Script): def test_instance(Script):
s = Script('import threading; threading.Lock()') s = Script('import threading; threading.Thread()')
def test_class_function(Script):
def_, = Script('import threading; threading.Thread.getName').goto_definitions()
context = def_._name._context
assert isinstance(context, typeshed.FunctionStubContext), context
def test_method(Script): def test_method(Script):
s = Script('import threading; threading.Lock().locked') code = 'import threading; threading.Thread().getName'
def_, = Script(code).goto_definitions()
context = def_._name._context
assert isinstance(context, typeshed.ClassStubContext), context