mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-19 03:55:57 +08:00
Implement a lot more for typing
This commit is contained in:
@@ -71,6 +71,17 @@ class Context(BaseContext):
|
|||||||
return f.filter_name(filters)
|
return f.filter_name(filters)
|
||||||
return f.find(filters, attribute_lookup=not search_global)
|
return f.find(filters, attribute_lookup=not search_global)
|
||||||
|
|
||||||
|
def py__getitem__(self, index_context_set, contextualized_node):
|
||||||
|
from jedi.evaluate import analysis
|
||||||
|
# TODO this context is probably not right.
|
||||||
|
analysis.add(
|
||||||
|
contextualized_node.context,
|
||||||
|
'type-error-not-subscriptable',
|
||||||
|
contextualized_node.node,
|
||||||
|
message="TypeError: '%s' object is not subscriptable" % self
|
||||||
|
)
|
||||||
|
return NO_CONTEXTS
|
||||||
|
|
||||||
def execute_annotation(self):
|
def execute_annotation(self):
|
||||||
return execute_evaluated(self)
|
return execute_evaluated(self)
|
||||||
|
|
||||||
@@ -171,24 +182,13 @@ class ContextualizedName(ContextualizedNode):
|
|||||||
|
|
||||||
def _get_item(context, index_contexts, contextualized_node):
|
def _get_item(context, index_contexts, contextualized_node):
|
||||||
from jedi.evaluate.compiled import CompiledObject
|
from jedi.evaluate.compiled import CompiledObject
|
||||||
from jedi.evaluate.context.iterable import Slice, Sequence
|
from jedi.evaluate.context.iterable import Slice
|
||||||
|
|
||||||
# The actual getitem call.
|
# The actual getitem call.
|
||||||
simple_getitem = getattr(context, 'py__simple_getitem__', None)
|
simple_getitem = getattr(context, 'py__simple_getitem__', None)
|
||||||
getitem = getattr(context, 'py__getitem__', None)
|
|
||||||
|
|
||||||
if getitem is None and simple_getitem is None:
|
|
||||||
from jedi.evaluate import analysis
|
|
||||||
# TODO this context is probably not right.
|
|
||||||
analysis.add(
|
|
||||||
contextualized_node.context,
|
|
||||||
'type-error-not-subscriptable',
|
|
||||||
contextualized_node.node,
|
|
||||||
message="TypeError: '%s' object is not subscriptable" % context
|
|
||||||
)
|
|
||||||
return NO_CONTEXTS
|
|
||||||
|
|
||||||
result = ContextSet()
|
result = ContextSet()
|
||||||
|
unused_contexts = set()
|
||||||
for index_context in index_contexts:
|
for index_context in index_contexts:
|
||||||
if simple_getitem is not None:
|
if simple_getitem is not None:
|
||||||
index = index_context
|
index = index_context
|
||||||
@@ -207,11 +207,16 @@ def _get_item(context, index_contexts, contextualized_node):
|
|||||||
except SimpleGetItemNotFound:
|
except SimpleGetItemNotFound:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
unused_contexts.add(index_context)
|
||||||
|
|
||||||
# The index was somehow not good enough or simply a wrong type.
|
# The index was somehow not good enough or simply a wrong type.
|
||||||
# Therefore we now iterate through all the contexts and just take
|
# Therefore we now iterate through all the contexts and just take
|
||||||
# all results.
|
# all results.
|
||||||
if getitem is not None:
|
if unused_contexts or not index_contexts:
|
||||||
result |= getitem(index_context, contextualized_node)
|
result |= context.py__getitem__(
|
||||||
|
ContextSet.from_set(unused_contexts),
|
||||||
|
contextualized_node
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ class CompiledObject(Context):
|
|||||||
return ContextSet(create_from_access_path(self.evaluator, access))
|
return ContextSet(create_from_access_path(self.evaluator, access))
|
||||||
|
|
||||||
@CheckAttribute()
|
@CheckAttribute()
|
||||||
def py__getitem__(self, index_context, contextualized_node):
|
def py__getitem__(self, index_context_set, contextualized_node):
|
||||||
return ContextSet.from_iterable(
|
return ContextSet.from_iterable(
|
||||||
create_from_access_path(self.evaluator, access)
|
create_from_access_path(self.evaluator, access)
|
||||||
for access in self.access_handle.py__getitem__all_values()
|
for access in self.access_handle.py__getitem__all_values()
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ class Sequence(BuiltinOverwrite, IterableMixin):
|
|||||||
def parent(self):
|
def parent(self):
|
||||||
return self.evaluator.builtins_module
|
return self.evaluator.builtins_module
|
||||||
|
|
||||||
def py__getitem__(self, index_context, contextualized_node):
|
def py__getitem__(self, index_context_set, contextualized_node):
|
||||||
if self.array_type == 'dict':
|
if self.array_type == 'dict':
|
||||||
return self._dict_values()
|
return self._dict_values()
|
||||||
return iterate_contexts(ContextSet(self))
|
return iterate_contexts(ContextSet(self))
|
||||||
|
|||||||
@@ -218,3 +218,11 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)):
|
|||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return ContextName(self, self.tree_node.name)
|
return ContextName(self, self.tree_node.name)
|
||||||
|
|
||||||
|
def py__getitem__(self, index_context_set, contextualized_node):
|
||||||
|
for cls in list(self.py__mro__()):
|
||||||
|
pass
|
||||||
|
print('ha', self, list(self.py__mro__()))
|
||||||
|
|
||||||
|
#print(index_context_set)
|
||||||
|
return super(ClassContext, self).py__getitem__(index_context_set, contextualized_node)
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ contexts.
|
|||||||
from parso.python import tree
|
from parso.python import tree
|
||||||
|
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.evaluate.compiled import builtin_from_name
|
from jedi.evaluate.compiled import builtin_from_name, CompiledObject
|
||||||
from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS
|
from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS, Context
|
||||||
from jedi.evaluate.context.iterable import SequenceLiteralContext
|
from jedi.evaluate.context.iterable import SequenceLiteralContext
|
||||||
|
from jedi.evaluate.filters import FilterWrapper, NameWrapper
|
||||||
|
|
||||||
_PROXY_TYPES = 'Optional Union Callable Type ClassVar Tuple Generic Protocol'.split()
|
_PROXY_TYPES = 'Optional Union Callable Type ClassVar Tuple Generic Protocol'.split()
|
||||||
_TYPE_ALIAS_TYPES = 'List Dict DefaultDict Set FrozenSet Counter Deque ChainMap'.split()
|
_TYPE_ALIAS_TYPES = 'List Dict DefaultDict Set FrozenSet Counter Deque ChainMap'.split()
|
||||||
@@ -26,24 +27,20 @@ class _TypingBase(object):
|
|||||||
return '%s(%s)' % (self.__class__.__name__, self._context)
|
return '%s(%s)' % (self.__class__.__name__, self._context)
|
||||||
|
|
||||||
|
|
||||||
class TypingModuleWrapper(_TypingBase):
|
class TypingModuleName(NameWrapper):
|
||||||
def py__getattribute__(self, name_or_str, *args, **kwargs):
|
def infer(self):
|
||||||
result = self._context.py__getattribute__(name_or_str)
|
return ContextSet.from_iterable(
|
||||||
if kwargs.get('is_goto'):
|
self._remap(context) for context in self._wrapped_name.infer()
|
||||||
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(self, context):
|
||||||
def _remap(context, name):
|
name = self.string_name
|
||||||
if name in _PROXY_TYPES:
|
print('name', name)
|
||||||
|
if name in (_PROXY_TYPES + _TYPE_ALIAS_TYPES):
|
||||||
|
print('NAME', name)
|
||||||
return TypingProxy(name, context)
|
return TypingProxy(name, context)
|
||||||
elif name in _TYPE_ALIAS_TYPES:
|
|
||||||
# TODO
|
|
||||||
raise NotImplementedError
|
|
||||||
elif name == 'TypeVar':
|
elif name == 'TypeVar':
|
||||||
raise NotImplementedError
|
return TypeVarClass(context.evaluator)
|
||||||
return TypeVar(context)
|
|
||||||
elif name == 'Any':
|
elif name == 'Any':
|
||||||
return Any(context)
|
return Any(context)
|
||||||
elif name == 'TYPE_CHECKING':
|
elif name == 'TYPE_CHECKING':
|
||||||
@@ -66,11 +63,18 @@ def _remap(context, name):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class TypingModuleFilterWrapper(FilterWrapper):
|
||||||
|
name_wrapper_class = TypingModuleName
|
||||||
|
|
||||||
|
|
||||||
class TypingProxy(_TypingBase):
|
class TypingProxy(_TypingBase):
|
||||||
py__simple_getitem__ = None
|
py__simple_getitem__ = None
|
||||||
|
|
||||||
def py__getitem__(self, index_context, contextualized_node):
|
def py__getitem__(self, index_context_set, contextualized_node):
|
||||||
return ContextSet(TypingProxyWithIndex(self._name, self._context, index_context))
|
return ContextSet.from_iterable(
|
||||||
|
TypingProxyWithIndex(self._name, self._context, index_context)
|
||||||
|
for index_context in index_context_set
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class _WithIndexBase(_TypingBase):
|
class _WithIndexBase(_TypingBase):
|
||||||
@@ -94,6 +98,10 @@ class _WithIndexBase(_TypingBase):
|
|||||||
class TypingProxyWithIndex(_WithIndexBase):
|
class TypingProxyWithIndex(_WithIndexBase):
|
||||||
def execute_annotation(self):
|
def execute_annotation(self):
|
||||||
name = self._name
|
name = self._name
|
||||||
|
if name in _TYPE_ALIAS_TYPES:
|
||||||
|
debug.warning('type aliases are not yet implemented')
|
||||||
|
return NO_CONTEXTS
|
||||||
|
|
||||||
if name == 'Union':
|
if name == 'Union':
|
||||||
# This is kind of a special case, because we have Unions (in Jedi
|
# This is kind of a special case, because we have Unions (in Jedi
|
||||||
# ContextSets).
|
# ContextSets).
|
||||||
@@ -181,4 +189,71 @@ class Any(_TypingBase):
|
|||||||
# Any is basically object, when it comes to type inference/completions.
|
# Any is basically object, when it comes to type inference/completions.
|
||||||
# This is obviously not correct, but let's just use this for now.
|
# This is obviously not correct, but let's just use this for now.
|
||||||
context = ContextSet(builtin_from_name(self.evaluator, u'object'))
|
context = ContextSet(builtin_from_name(self.evaluator, u'object'))
|
||||||
super(_WithIndexBase, self).__init__(context)
|
super(Any, self).__init__(context)
|
||||||
|
|
||||||
|
def execute_annotation(self):
|
||||||
|
debug.warning('Used Any, which is not implemented, yet.')
|
||||||
|
return NO_CONTEXTS
|
||||||
|
|
||||||
|
|
||||||
|
class GenericClass(object):
|
||||||
|
def __init__(self, class_context, ):
|
||||||
|
self._class_context = class_context
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(self._class_context, name)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s(%s)' % (self.__class__.__name__, self._class_context)
|
||||||
|
|
||||||
|
|
||||||
|
class TypeVarClass(Context):
|
||||||
|
def py__call__(self, arguments):
|
||||||
|
unpacked = arguments.unpack()
|
||||||
|
|
||||||
|
key, lazy_context = next(unpacked, (None, None))
|
||||||
|
name = self._find_name(lazy_context)
|
||||||
|
# The name must be given, otherwise it's useless.
|
||||||
|
if name is None or key is not None:
|
||||||
|
debug.warning('Found a variable without a name %s', arguments)
|
||||||
|
return NO_CONTEXTS
|
||||||
|
|
||||||
|
return ContextSet(TypeVar(self.evaluator, name, unpacked))
|
||||||
|
|
||||||
|
def _find_name(self, lazy_context):
|
||||||
|
if lazy_context is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
context_set = lazy_context.infer()
|
||||||
|
if not context_set:
|
||||||
|
return None
|
||||||
|
if len(context_set) > 1:
|
||||||
|
debug.warning('Found multiple contexts for a type variable: %s', context_set)
|
||||||
|
|
||||||
|
name_context = next(iter(context_set))
|
||||||
|
if isinstance(name_context, CompiledObject):
|
||||||
|
return name_context.get_safe_value(default=None)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class TypeVar(Context):
|
||||||
|
def __init__(self, evaluator, name, unpacked_args):
|
||||||
|
super(TypeVar, self).__init__(evaluator)
|
||||||
|
self._name = name
|
||||||
|
self._unpacked_args = unpacked_args
|
||||||
|
|
||||||
|
def _unpack(self):
|
||||||
|
# TODO
|
||||||
|
constraints = ContextSet()
|
||||||
|
bound = None
|
||||||
|
covariant = False
|
||||||
|
contravariant = False
|
||||||
|
for key, lazy_context in unpacked:
|
||||||
|
if key is None:
|
||||||
|
constraints |= lazy_context.infer()
|
||||||
|
else:
|
||||||
|
if name == 'bound':
|
||||||
|
bound = lazy_context.infer()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s: %s>' % (self.__class__.__name__, self._name)
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ def infer_param(execution_context, param):
|
|||||||
class_context = execution_context.var_args.instance.class_context
|
class_context = execution_context.var_args.instance.class_context
|
||||||
types |= eval_docstring(class_context.py__doc__())
|
types |= eval_docstring(class_context.py__doc__())
|
||||||
|
|
||||||
debug.dbg('Found param types for docstring %s', types, color='BLUE')
|
debug.dbg('Found param types for docstring: %s', types, color='BLUE')
|
||||||
return types
|
return types
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,37 @@ class AbstractFilter(object):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class FilterWrapper(object):
|
||||||
|
name_wrapper_class = None
|
||||||
|
|
||||||
|
def __init__(self, wrapped_filter):
|
||||||
|
self._wrapped_filter = wrapped_filter
|
||||||
|
|
||||||
|
def wrap_names(self, names):
|
||||||
|
return [self.name_wrapper_class(name) for name in names]
|
||||||
|
|
||||||
|
def get(self, name):
|
||||||
|
return self.wrap_names(self._wrapped_filter.get(name))
|
||||||
|
|
||||||
|
def values(self, name):
|
||||||
|
return self.wrap_names(self._wrapped_filter.values())
|
||||||
|
|
||||||
|
|
||||||
|
class NameWrapper(object):
|
||||||
|
def __init__(self, wrapped_name):
|
||||||
|
self._wrapped_name = wrapped_name
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def infer(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(self._wrapped_name, name)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s(%s)' % (self.__class__.__name__, self._wrapped_name)
|
||||||
|
|
||||||
|
|
||||||
class AbstractUsedNamesFilter(AbstractFilter):
|
class AbstractUsedNamesFilter(AbstractFilter):
|
||||||
name_class = TreeNameDefinition
|
name_class = TreeNameDefinition
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ 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
|
||||||
from jedi.evaluate.context.typing import TypingModuleWrapper
|
from jedi.evaluate.context.typing import TypingModuleFilterWrapper
|
||||||
from jedi.evaluate.compiled import CompiledObject
|
from jedi.evaluate.compiled import CompiledObject
|
||||||
from jedi.evaluate.syntax_tree import tree_name_to_contexts
|
from jedi.evaluate.syntax_tree import tree_name_to_contexts
|
||||||
from jedi.evaluate.utils import to_list
|
from jedi.evaluate.utils import to_list
|
||||||
@@ -149,13 +149,15 @@ class TypeshedPlugin(BasePlugin):
|
|||||||
# TODO maybe empty cache?
|
# TODO maybe empty cache?
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
if import_names == ('typing',):
|
||||||
|
module_cls = TypingModuleWrapper
|
||||||
|
else:
|
||||||
|
module_cls = StubOnlyModuleContext
|
||||||
# TODO use code_lines
|
# TODO use code_lines
|
||||||
stub_module_context = StubOnlyModuleContext(
|
stub_module_context = module_cls(
|
||||||
context_set, evaluator, stub_module_node, path, code_lines=[]
|
context_set, evaluator, stub_module_node, path, code_lines=[]
|
||||||
)
|
)
|
||||||
modules = _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)
|
return ContextSet.from_iterable(modules)
|
||||||
# If no stub is found, just return the default.
|
# If no stub is found, just return the default.
|
||||||
return context_set
|
return context_set
|
||||||
@@ -300,7 +302,11 @@ class StubModuleContext(_StubContextFilterMixin, ModuleContext):
|
|||||||
|
|
||||||
|
|
||||||
class StubClassContext(_StubContextFilterMixin, ClassContext):
|
class StubClassContext(_StubContextFilterMixin, ClassContext):
|
||||||
pass
|
def __getattribute__(self, name):
|
||||||
|
if name == ('py__getitem__', 'py__bases__'):
|
||||||
|
# getitem is always done in the stub class.
|
||||||
|
return getattr(self.stub_context, name)
|
||||||
|
return super(StubClassContext, self).__getattribute__(name)
|
||||||
|
|
||||||
|
|
||||||
class StubFunctionContext(_MixedStubContextMixin, FunctionContext):
|
class StubFunctionContext(_MixedStubContextMixin, FunctionContext):
|
||||||
@@ -337,3 +343,11 @@ class StubOnlyModuleContext(ModuleContext):
|
|||||||
)
|
)
|
||||||
for f in filters:
|
for f in filters:
|
||||||
yield f
|
yield f
|
||||||
|
|
||||||
|
|
||||||
|
class TypingModuleWrapper(StubOnlyModuleContext):
|
||||||
|
def get_filters(self, *args, **kwargs):
|
||||||
|
filters = super(TypingModuleWrapper, self).get_filters(*args, **kwargs)
|
||||||
|
yield TypingModuleFilterWrapper(next(filters))
|
||||||
|
for f in filters:
|
||||||
|
yield f
|
||||||
|
|||||||
Reference in New Issue
Block a user