Merge branch 'master' into dict

This commit is contained in:
Dave Halter
2019-12-28 12:17:04 +01:00
153 changed files with 4231 additions and 1858 deletions
+19 -15
View File
@@ -67,6 +67,7 @@ from parso import python_bytes_to_unicode
from jedi.file_io import FileIO
from jedi import debug
from jedi import settings
from jedi.inference import imports
from jedi.inference import recursion
from jedi.inference.cache import inference_state_function_cache
@@ -76,7 +77,7 @@ from jedi.inference.base_value import ContextualizedNode, \
ValueSet, iterate_values
from jedi.inference.value import ClassValue, FunctionValue
from jedi.inference.syntax_tree import infer_expr_stmt, \
check_tuple_assignments
check_tuple_assignments, tree_name_to_values
from jedi.inference.imports import follow_error_node_imports_if_possible
from jedi.plugins import plugin_manager
@@ -105,14 +106,10 @@ class InferenceState(object):
self.allow_descriptor_getattr = False
self.reset_recursion_limitations()
self.allow_different_encoding = True
def import_module(self, import_names, parent_module_value=None,
sys_path=None, prefer_stubs=True):
if sys_path is None:
sys_path = self.get_sys_path()
return imports.import_module(self, import_names, parent_module_value,
sys_path, prefer_stubs=prefer_stubs)
def import_module(self, import_names, sys_path=None, prefer_stubs=True):
return imports.import_module_by_names(
self, import_names, sys_path, prefer_stubs=prefer_stubs)
@staticmethod
@plugin_manager.decorate()
@@ -146,7 +143,7 @@ class InferenceState(object):
"""Convenience function"""
return self.project._get_sys_path(self, environment=self.environment, **kwargs)
def goto_definitions(self, context, name):
def infer(self, context, name):
def_ = name.get_definition(import_name_always=True)
if def_ is not None:
type_ = def_.type
@@ -170,6 +167,10 @@ class InferenceState(object):
return check_tuple_assignments(n, for_types)
if type_ in ('import_from', 'import_name'):
return imports.infer_import(context, name)
if type_ == 'with_stmt':
return tree_name_to_values(self, context, name)
elif type_ == 'param':
return context.py__getattribute__(name.value, position=name.end_pos)
else:
result = follow_error_node_imports_if_possible(context, name)
if result is not None:
@@ -179,12 +180,15 @@ class InferenceState(object):
def parse_and_get_code(self, code=None, path=None, encoding='utf-8',
use_latest_grammar=False, file_io=None, **kwargs):
if self.allow_different_encoding:
if code is None:
if file_io is None:
file_io = FileIO(path)
code = file_io.read()
code = python_bytes_to_unicode(code, encoding=encoding, errors='replace')
if code is None:
if file_io is None:
file_io = FileIO(path)
code = file_io.read()
# We cannot just use parso, because it doesn't use errors='replace'.
code = python_bytes_to_unicode(code, encoding=encoding, errors='replace')
if len(code) > settings._cropped_file_size:
code = code[:settings._cropped_file_size]
grammar = self.latest_grammar if use_latest_grammar else self.grammar
return grammar.parse(code=code, path=path, file_io=file_io, **kwargs), code
+1 -8
View File
@@ -119,14 +119,7 @@ def add_attribute_error(name_context, lookup_value, name):
# instead of an error, if that happens.
typ = Error
if lookup_value.is_instance() and not lookup_value.is_compiled():
slot_names = lookup_value.get_function_slot_names(u'__getattr__') + \
lookup_value.get_function_slot_names(u'__getattribute__')
for n in slot_names:
# TODO do we even get here?
if isinstance(name, CompiledInstanceName) and \
n.parent_context.obj == object:
typ = Warning
break
# TODO maybe make a warning for __getattr__/__getattribute__
if _check_for_setattr(lookup_value):
typ = Warning
+12 -2
View File
@@ -8,7 +8,7 @@ just one.
"""
from functools import reduce
from operator import add
from parso.python.tree import ExprStmt, SyncCompFor, Name
from parso.python.tree import Name
from jedi import debug
from jedi._compatibility import zip_longest, unicode
@@ -239,6 +239,10 @@ class Value(HelperValueMixin, BaseValue):
"""
return NO_VALUES
def py__get__(self, instance, class_value):
debug.warning("No __get__ defined on %s", self)
return ValueSet([self])
def get_qualified_names(self):
# Returns Optional[Tuple[str, ...]]
return None
@@ -248,7 +252,13 @@ class Value(HelperValueMixin, BaseValue):
return self.parent_context.is_stub()
def _as_context(self):
raise NotImplementedError('Not all values need to be converted to contexts')
raise NotImplementedError('Not all values need to be converted to contexts: %s', self)
def name(self):
raise NotImplementedError
def py__name__(self):
return self.name.string_name
def iterate_values(values, contextualized_node=None, is_async=False):
+1 -1
View File
@@ -1,7 +1,7 @@
from jedi._compatibility import unicode
from jedi.inference.compiled.value import CompiledObject, CompiledName, \
CompiledObjectFilter, CompiledValueName, create_from_access_path
from jedi.inference.base_value import ValueWrapper, LazyValueWrapper
from jedi.inference.base_value import LazyValueWrapper
def builtin_from_name(inference_state, string):
+40 -5
View File
@@ -4,6 +4,7 @@ import types
import sys
import operator as op
from collections import namedtuple
import warnings
from jedi._compatibility import unicode, is_py3, builtins, \
py_version, force_unicode
@@ -323,7 +324,7 @@ class DirectObjectAccess(object):
name = try_to_get_name(type(self._obj))
if name is None:
return ()
return tuple(name.split('.'))
return tuple(force_unicode(n) for n in name.split('.'))
def dir(self):
return list(map(force_unicode, dir(self._obj)))
@@ -335,8 +336,23 @@ class DirectObjectAccess(object):
except TypeError:
return False
def is_allowed_getattr(self, name):
def is_allowed_getattr(self, name, unsafe=False):
# TODO this API is ugly.
if unsafe:
# Unsafe is mostly used to check for __getattr__/__getattribute__.
# getattr_static works for properties, but the underscore methods
# are just ignored (because it's safer and avoids more code
# execution). See also GH #1378.
# Avoid warnings, see comment in the next function.
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
try:
return hasattr(self._obj, name), False
except Exception:
# Obviously has an attribute (propably a property) that
# gets executed, so just avoid all exceptions here.
return False, False
try:
attr, is_get_descriptor = getattr_static(self._obj, name)
except AttributeError:
@@ -350,7 +366,11 @@ class DirectObjectAccess(object):
def getattr_paths(self, name, default=_sentinel):
try:
return_obj = getattr(self._obj, name)
# Make sure no warnings are printed here, this is autocompletion,
# warnings should not be shown. See also GH #1383.
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
return_obj = getattr(self._obj, name)
except Exception as e:
if default is _sentinel:
if isinstance(e, AttributeError):
@@ -366,6 +386,22 @@ class DirectObjectAccess(object):
if inspect.ismodule(return_obj):
return [access]
try:
module = return_obj.__module__
except AttributeError:
pass
else:
if module is not None:
try:
__import__(module)
# For some modules like _sqlite3, the __module__ for classes is
# different, in this case it's sqlite3. So we have to try to
# load that "original" module, because it's not loaded yet. If
# we don't do that, we don't really have a "parent" module and
# we would fall back to builtins.
except ImportError:
pass
module = inspect.getmodule(return_obj)
if module is None:
module = inspect.getmodule(type(return_obj))
@@ -433,7 +469,7 @@ class DirectObjectAccess(object):
default_string=repr(p.default),
has_annotation=p.annotation is not p.empty,
annotation=self._create_access_path(p.annotation),
annotation_string=str(p.default),
annotation_string=str(p.annotation),
kind_name=str(p.kind)
) for p in self._get_signature().parameters.values()
]
@@ -485,7 +521,6 @@ class DirectObjectAccess(object):
Used to return a couple of infos that are needed when accessing the sub
objects of an objects
"""
# TODO is_allowed_getattr might raise an AttributeError
tuples = dict(
(force_unicode(name), self.is_allowed_getattr(name))
for name in self.dir()
+24 -6
View File
@@ -8,20 +8,23 @@ import sys
from jedi.parser_utils import get_cached_code_lines
from jedi._compatibility import unwrap
from jedi import settings
from jedi.inference import compiled
from jedi.cache import underscore_memoization
from jedi.file_io import FileIO
from jedi.inference.base_value import ValueSet, ValueWrapper
from jedi.inference.base_value import ValueSet, ValueWrapper, NO_VALUES
from jedi.inference.helpers import SimpleGetItemNotFound
from jedi.inference.value import ModuleValue
from jedi.inference.cache import inference_state_function_cache
from jedi.inference.cache import inference_state_function_cache, \
inference_state_method_cache
from jedi.inference.compiled.getattr_static import getattr_static
from jedi.inference.compiled.access import compiled_objects_cache, \
ALLOWED_GETITEM_TYPES, get_api_type
from jedi.inference.compiled.value import create_cached_compiled_object
from jedi.inference.gradual.conversion import to_stub
from jedi.inference.context import CompiledContext, TreeContextMixin
from jedi.inference.context import CompiledContext, CompiledModuleContext, \
TreeContextMixin
_sentinel = object()
@@ -56,8 +59,13 @@ class MixedObject(ValueWrapper):
# should be very precise, especially for stuff like `partial`.
return self.compiled_object.get_signatures()
@inference_state_method_cache(default=NO_VALUES)
def py__call__(self, arguments):
return (to_stub(self._wrapped_value) or self._wrapped_value).py__call__(arguments)
# Fallback to the wrapped value if to stub returns no values.
values = to_stub(self._wrapped_value)
if not values:# or self in values:
values = self._wrapped_value
return values.py__call__(arguments)
def get_safe_value(self, default=_sentinel):
if default is _sentinel:
@@ -72,6 +80,8 @@ class MixedObject(ValueWrapper):
raise SimpleGetItemNotFound
def _as_context(self):
if self.parent_context is None:
return MixedModuleContext(self)
return MixedContext(self)
def __repr__(self):
@@ -87,6 +97,10 @@ class MixedContext(CompiledContext, TreeContextMixin):
return self._value.compiled_object
class MixedModuleContext(CompiledModuleContext, MixedContext):
pass
class MixedName(compiled.CompiledName):
"""
The ``CompiledName._compiled_object`` is our MixedObject.
@@ -153,7 +167,11 @@ def _load_module(inference_state, path):
def _get_object_to_check(python_object):
"""Check if inspect.getfile has a chance to find the source."""
if sys.version_info[0] > 2:
python_object = inspect.unwrap(python_object)
try:
python_object = unwrap(python_object)
except ValueError:
# Can return a ValueError when it wraps around
pass
if (inspect.ismodule(python_object) or
inspect.isclass(python_object) or
@@ -277,7 +295,7 @@ def _create(inference_state, access_handle, parent_context, *args):
file_io=file_io,
string_names=string_names,
code_lines=code_lines,
is_package=compiled_object.is_package,
is_package=compiled_object.is_package(),
).as_context()
if name is not None:
inference_state.module_cache.add(string_names, ValueSet([module_context]))
+42 -14
View File
@@ -17,7 +17,7 @@ from jedi.inference.compiled.access import _sentinel
from jedi.inference.cache import inference_state_function_cache
from jedi.inference.helpers import reraise_getitem_errors
from jedi.inference.signature import BuiltinSignature
from jedi.inference.context import CompiledContext
from jedi.inference.context import CompiledContext, CompiledModuleContext
class CheckAttribute(object):
@@ -181,10 +181,12 @@ class CompiledObject(Value):
def _ensure_one_filter(self, is_instance):
return CompiledObjectFilter(self.inference_state, self, is_instance)
@CheckAttribute(u'__getitem__')
def py__simple_getitem__(self, index):
with reraise_getitem_errors(IndexError, KeyError, TypeError):
access = self.access_handle.py__simple_getitem__(index)
try:
access = self.access_handle.py__simple_getitem__(index)
except AttributeError:
return super(CompiledObject, self).py__simple_getitem__(index)
if access is None:
return NO_VALUES
@@ -262,6 +264,12 @@ class CompiledObject(Value):
self.access_handle.execute_operation(other.access_handle, operator)
)
def execute_annotation(self):
if self.access_handle.get_repr() == 'None':
# None as an annotation doesn't need to be executed.
return ValueSet([self])
return super(CompiledObject, self).execute_annotation()
def negate(self):
return create_from_access_path(self.inference_state, self.access_handle.negate())
@@ -269,6 +277,8 @@ class CompiledObject(Value):
return NO_VALUES
def _as_context(self):
if self.parent_context is None:
return CompiledModuleContext(self)
return CompiledContext(self)
@@ -278,8 +288,14 @@ class CompiledName(AbstractNameDefinition):
self.parent_context = parent_context
self.string_name = name
def py__doc__(self, include_signatures=False):
value, = self.infer()
return value.py__doc__()
def _get_qualified_names(self):
parent_qualified_names = self.parent_context.get_qualified_names()
if parent_qualified_names is None:
return None
return parent_qualified_names + (self.string_name,)
def __repr__(self):
@@ -387,26 +403,33 @@ class CompiledObjectFilter(AbstractFilter):
def get(self, name):
return self._get(
name,
lambda: self.compiled_object.access_handle.is_allowed_getattr(name),
lambda: self.compiled_object.access_handle.dir(),
lambda name, unsafe: self.compiled_object.access_handle.is_allowed_getattr(name, unsafe),
lambda name: name in self.compiled_object.access_handle.dir(),
check_has_attribute=True
)
def _get(self, name, allowed_getattr_callback, dir_callback, check_has_attribute=False):
def _get(self, name, allowed_getattr_callback, in_dir_callback, check_has_attribute=False):
"""
To remove quite a few access calls we introduced the callback here.
"""
has_attribute, is_descriptor = allowed_getattr_callback()
if check_has_attribute and not has_attribute:
return []
# Always use unicode objects in Python 2 from here.
name = force_unicode(name)
if (is_descriptor and not self._inference_state.allow_descriptor_getattr) or not has_attribute:
if self._inference_state.allow_descriptor_getattr:
pass
has_attribute, is_descriptor = allowed_getattr_callback(
name,
unsafe=self._inference_state.allow_descriptor_getattr
)
if check_has_attribute and not has_attribute:
return []
if (is_descriptor or not has_attribute) \
and not self._inference_state.allow_descriptor_getattr:
return [self._get_cached_name(name, is_empty=True)]
if self.is_instance and name not in dir_callback():
if self.is_instance and not in_dir_callback(name):
return []
return [self._get_cached_name(name)]
@@ -421,11 +444,16 @@ class CompiledObjectFilter(AbstractFilter):
from jedi.inference.compiled import builtin_from_name
names = []
needs_type_completions, dir_infos = self.compiled_object.access_handle.get_dir_infos()
# We could use `unsafe` here as well, especially as a parameter to
# get_dir_infos. But this would lead to a lot of property executions
# that are probably not wanted. The drawback for this is that we
# have a different name for `get` and `values`. For `get` we always
# execute.
for name in dir_infos:
names += self._get(
name,
lambda: dir_infos[name],
lambda: dir_infos.keys(),
lambda name, unsafe: dir_infos[name],
lambda name: name in dir_infos,
)
# ``dir`` doesn't include the type names.
+15 -9
View File
@@ -298,14 +298,6 @@ class ModuleContext(TreeContextMixin, ValueContext):
def py__file__(self):
return self._value.py__file__()
@property
def py__package__(self):
return self._value.py__package__
@property
def is_package(self):
return self._value.is_package
def get_filters(self, until_position=None, origin_scope=None):
filters = self._value.get_filters(origin_scope)
# Skip the first filter and replace it.
@@ -316,11 +308,14 @@ class ModuleContext(TreeContextMixin, ValueContext):
until_position=until_position,
origin_scope=origin_scope
),
GlobalNameFilter(self, self.tree_node),
self.get_global_filter(),
)
for f in filters: # Python 2...
yield f
def get_global_filter(self):
return GlobalNameFilter(self, self.tree_node)
@property
def string_names(self):
return self._value.string_names
@@ -342,6 +337,9 @@ class NamespaceContext(TreeContextMixin, ValueContext):
def get_filters(self, until_position=None, origin_scope=None):
return self._value.get_filters()
def get_value(self):
return self._value
def py__file__(self):
return self._value.py__file__()
@@ -378,9 +376,17 @@ class CompiledContext(ValueContext):
def get_filters(self, until_position=None, origin_scope=None):
return self._value.get_filters()
class CompiledModuleContext(CompiledContext):
code_lines = None
def get_value(self):
return self._value
@property
def string_names(self):
return self._value.string_names
def py__file__(self):
return self._value.py__file__()
+2 -2
View File
@@ -74,7 +74,7 @@ def dynamic_param_lookup(function_value, param_index):
path = function_value.get_root_context().py__file__()
if path is not None and is_stdlib_path(path):
# We don't want to search for usages in the stdlib. Usually people
# We don't want to search for references in the stdlib. Usually people
# don't work with it (except if you are a core maintainer, sorry).
# This makes everything slower. Just disable it and run the tests,
# you will see the slowdown, especially in 3.6.
@@ -186,7 +186,7 @@ def _check_name_for_execution(inference_state, context, compare_node, name, trai
args = InstanceArguments(value.instance, args)
return args
for value in inference_state.goto_definitions(context, name):
for value in inference_state.infer(context, name):
value_node = value.tree_node
if compare_node == value_node:
yield create_args(value)
+5 -11
View File
@@ -246,24 +246,18 @@ class MergedFilter(object):
return '%s(%s)' % (self.__class__.__name__, ', '.join(str(f) for f in self._filters))
class _BuiltinMappedMethod(Value):
class _BuiltinMappedMethod(ValueWrapper):
"""``Generator.__next__`` ``dict.values`` methods and so on."""
api_type = u'function'
def __init__(self, builtin_value, method, builtin_func):
super(_BuiltinMappedMethod, self).__init__(
builtin_value.inference_state,
parent_context=builtin_value
)
def __init__(self, value, method, builtin_func):
super(_BuiltinMappedMethod, self).__init__(builtin_func)
self._value = value
self._method = method
self._builtin_func = builtin_func
def py__call__(self, arguments):
# TODO add TypeError if params are given/or not correct.
return self._method(self.parent_context)
def __getattr__(self, name):
return getattr(self._builtin_func, name)
return self._method(self._value)
class SpecialMethodFilter(DictFilter):
+83 -15
View File
@@ -12,9 +12,10 @@ from parso import ParserSyntaxError, parse
from jedi._compatibility import force_unicode, Parameter
from jedi.inference.cache import inference_state_method_cache
from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference.gradual.typing import TypeVar, LazyGenericClass, \
AbstractAnnotatedClass
from jedi.inference.gradual.typing import GenericClass
from jedi.inference.gradual.base import DefineGenericBase, GenericClass
from jedi.inference.gradual.generics import TupleGenericManager
from jedi.inference.gradual.typing import TypingClassValueWithIndex
from jedi.inference.gradual.type_var import TypeVar
from jedi.inference.helpers import is_string
from jedi.inference.compiled import builtin_from_name
from jedi.inference.param import get_executed_param_names
@@ -91,7 +92,7 @@ def _split_comment_param_declaration(decl_text):
debug.warning('Comment annotation is not valid Python: %s' % decl_text)
return []
if node.type == 'name':
if node.type in ['name', 'atom_expr', 'power']:
return [node.get_code().strip()]
params = []
@@ -117,13 +118,17 @@ def infer_param(function_value, param, ignore_stars=False):
tuple_ = builtin_from_name(inference_state, 'tuple')
return ValueSet([GenericClass(
tuple_,
generics=(values,),
TupleGenericManager((values,)),
) for c in values])
elif param.star_count == 2:
dct = builtin_from_name(inference_state, 'dict')
generics = (
ValueSet([builtin_from_name(inference_state, 'str')]),
values
)
return ValueSet([GenericClass(
dct,
generics=(ValueSet([builtin_from_name(inference_state, 'str')]), values),
TupleGenericManager(generics),
) for c in values])
pass
return values
@@ -161,7 +166,6 @@ def _infer_param(function_value, param):
"Comments length != Params length %s %s",
params_comments, all_params
)
from jedi.inference.value.instance import InstanceArguments
if function_value.is_bound_method():
if index == 0:
# Assume it's self, which is already handled
@@ -229,7 +233,7 @@ def infer_return_types(function, arguments):
return ValueSet.from_sets(
ann.define_generics(type_var_dict)
if isinstance(ann, (AbstractAnnotatedClass, TypeVar)) else ValueSet({ann})
if isinstance(ann, (DefineGenericBase, TypeVar)) else ValueSet({ann})
for ann in annotation_values
).execute_annotation()
@@ -270,7 +274,39 @@ def infer_type_vars_for_execution(function, arguments, annotation_dict):
annotation_variable_results,
_infer_type_vars(ann, actual_value_set),
)
return annotation_variable_results
def infer_return_for_callable(arguments, param_values, result_values):
result = NO_VALUES
for pv in param_values:
if pv.array_type == 'list':
type_var_dict = infer_type_vars_for_callable(arguments, pv.py__iter__())
result |= ValueSet.from_sets(
v.define_generics(type_var_dict)
if isinstance(v, (DefineGenericBase, TypeVar)) else ValueSet({v})
for v in result_values
).execute_annotation()
return result
def infer_type_vars_for_callable(arguments, lazy_params):
"""
Infers type vars for the Calllable class:
def x() -> Callable[[Callable[..., _T]], _T]: ...
"""
annotation_variable_results = {}
for (_, lazy_value), lazy_callable_param in zip(arguments.unpack(), lazy_params):
callable_param_values = lazy_callable_param.infer()
# Infer unknown type var
actual_value_set = lazy_value.infer()
for v in callable_param_values:
_merge_type_var_dicts(
annotation_variable_results,
_infer_type_vars(v, actual_value_set),
)
return annotation_variable_results
@@ -283,7 +319,7 @@ def _merge_type_var_dicts(base_dict, new_dict):
base_dict[type_var_name] = values
def _infer_type_vars(annotation_value, value_set):
def _infer_type_vars(annotation_value, value_set, is_class_value=False):
"""
This function tries to find information about undefined type vars and
returns a dict from type var name to value set.
@@ -298,8 +334,35 @@ def _infer_type_vars(annotation_value, value_set):
"""
type_var_dict = {}
if isinstance(annotation_value, TypeVar):
return {annotation_value.py__name__(): value_set.py__class__()}
elif isinstance(annotation_value, LazyGenericClass):
if not is_class_value:
return {annotation_value.py__name__(): value_set.py__class__()}
return {annotation_value.py__name__(): value_set}
elif isinstance(annotation_value, TypingClassValueWithIndex):
name = annotation_value.py__name__()
if name == 'Type':
given = annotation_value.get_generics()
if given:
for nested_annotation_value in given[0]:
_merge_type_var_dicts(
type_var_dict,
_infer_type_vars(
nested_annotation_value,
value_set,
is_class_value=True,
)
)
elif name == 'Callable':
given = annotation_value.get_generics()
if len(given) == 2:
for nested_annotation_value in given[1]:
_merge_type_var_dicts(
type_var_dict,
_infer_type_vars(
nested_annotation_value,
value_set.execute_annotation(),
)
)
elif isinstance(annotation_value, GenericClass):
name = annotation_value.py__name__()
if name == 'Iterable':
given = annotation_value.get_generics()
@@ -389,16 +452,21 @@ def find_unknown_type_vars(context, node):
for subscript_node in _unpack_subscriptlist(trailer.children[1]):
check_node(subscript_node)
else:
type_var_set = context.infer_node(node)
for type_var in type_var_set:
if isinstance(type_var, TypeVar) and type_var not in found:
found.append(type_var)
found[:] = _filter_type_vars(context.infer_node(node), found)
found = [] # We're not using a set, because the order matters.
check_node(node)
return found
def _filter_type_vars(value_set, found=()):
new_found = list(found)
for type_var in value_set:
if isinstance(type_var, TypeVar) and type_var not in found:
new_found.append(type_var)
return new_found
def _unpack_subscriptlist(subscriptlist):
if subscriptlist.type == 'subscriptlist':
for subscript in subscriptlist.children[::2]:
+321
View File
@@ -0,0 +1,321 @@
from jedi.inference.cache import inference_state_method_cache
from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \
iterator_to_value_set, LazyValueWrapper, ValueWrapper
from jedi.inference.compiled import builtin_from_name
from jedi.inference.value.klass import ClassFilter
from jedi.inference.value.klass import ClassMixin
from jedi.inference.utils import to_list
from jedi.inference.names import AbstractNameDefinition, ValueName
from jedi.inference.context import ClassContext
from jedi.inference.gradual.generics import TupleGenericManager
class _BoundTypeVarName(AbstractNameDefinition):
"""
This type var was bound to a certain type, e.g. int.
"""
def __init__(self, type_var, value_set):
self._type_var = type_var
self.parent_context = type_var.parent_context
self._value_set = value_set
def infer(self):
def iter_():
for value in self._value_set:
# Replace any with the constraints if they are there.
from jedi.inference.gradual.typing import Any
if isinstance(value, Any):
for constraint in self._type_var.constraints:
yield constraint
else:
yield value
return ValueSet(iter_())
def py__name__(self):
return self._type_var.py__name__()
def __repr__(self):
return '<%s %s -> %s>' % (self.__class__.__name__, self.py__name__(), self._value_set)
class _TypeVarFilter(object):
"""
A filter for all given variables in a class.
A = TypeVar('A')
B = TypeVar('B')
class Foo(Mapping[A, B]):
...
In this example we would have two type vars given: A and B
"""
def __init__(self, generics, type_vars):
self._generics = generics
self._type_vars = type_vars
def get(self, name):
for i, type_var in enumerate(self._type_vars):
if type_var.py__name__() == name:
try:
return [_BoundTypeVarName(type_var, self._generics[i])]
except IndexError:
return [type_var.name]
return []
def values(self):
# The values are not relevant. If it's not searched exactly, the type
# vars are just global and should be looked up as that.
return []
class _AnnotatedClassContext(ClassContext):
def get_filters(self, *args, **kwargs):
filters = super(_AnnotatedClassContext, self).get_filters(
*args, **kwargs
)
for f in filters:
yield f
# The type vars can only be looked up if it's a global search and
# not a direct lookup on the class.
yield self._value.get_type_var_filter()
class DefineGenericBase(LazyValueWrapper):
def __init__(self, generics_manager):
self._generics_manager = generics_manager
def _create_instance_with_generics(self, generics_manager):
raise NotImplementedError
@inference_state_method_cache()
def get_generics(self):
return self._generics_manager.to_tuple()
def define_generics(self, type_var_dict):
from jedi.inference.gradual.type_var import TypeVar
changed = False
new_generics = []
for generic_set in self.get_generics():
values = NO_VALUES
for generic in generic_set:
if isinstance(generic, (GenericClass, TypeVar)):
result = generic.define_generics(type_var_dict)
values |= result
if result != ValueSet({generic}):
changed = True
else:
values |= ValueSet([generic])
new_generics.append(values)
if not changed:
# There might not be any type vars that change. In that case just
# return itself, because it does not make sense to potentially lose
# cached results.
return ValueSet([self])
return ValueSet([self._create_instance_with_generics(
TupleGenericManager(tuple(new_generics))
)])
def is_same_class(self, other):
if not isinstance(other, DefineGenericBase):
return False
if self.tree_node != other.tree_node:
# TODO not sure if this is nice.
return False
given_params1 = self.get_generics()
given_params2 = other.get_generics()
if len(given_params1) != len(given_params2):
# If the amount of type vars doesn't match, the class doesn't
# match.
return False
# Now compare generics
return all(
any(
# TODO why is this ordering the correct one?
cls2.is_same_class(cls1)
for cls1 in class_set1
for cls2 in class_set2
) for class_set1, class_set2 in zip(given_params1, given_params2)
)
def __repr__(self):
return '<%s: %s%s>' % (
self.__class__.__name__,
self._wrapped_value,
list(self.get_generics()),
)
class GenericClass(ClassMixin, DefineGenericBase):
"""
A class that is defined with generics, might be something simple like:
class Foo(Generic[T]): ...
my_foo_int_cls = Foo[int]
"""
def __init__(self, class_value, generics_manager):
super(GenericClass, self).__init__(generics_manager)
self._class_value = class_value
def _get_wrapped_value(self):
return self._class_value
def get_type_var_filter(self):
return _TypeVarFilter(self.get_generics(), self.list_type_vars())
def py__call__(self, arguments):
instance, = super(GenericClass, self).py__call__(arguments)
return ValueSet([_GenericInstanceWrapper(instance)])
def _as_context(self):
return _AnnotatedClassContext(self)
@to_list
def py__bases__(self):
for base in self._wrapped_value.py__bases__():
yield _LazyGenericBaseClass(self, base)
def _create_instance_with_generics(self, generics_manager):
return GenericClass(self._class_value, generics_manager)
def is_sub_class_of(self, class_value):
if super(GenericClass, self).is_sub_class_of(class_value):
return True
return self._class_value.is_sub_class_of(class_value)
class _LazyGenericBaseClass(object):
def __init__(self, class_value, lazy_base_class):
self._class_value = class_value
self._lazy_base_class = lazy_base_class
@iterator_to_value_set
def infer(self):
for base in self._lazy_base_class.infer():
if isinstance(base, GenericClass):
# Here we have to recalculate the given types.
yield GenericClass.create_cached(
base.inference_state,
base._wrapped_value,
TupleGenericManager(tuple(self._remap_type_vars(base))),
)
else:
yield base
def _remap_type_vars(self, base):
from jedi.inference.gradual.type_var import TypeVar
filter = self._class_value.get_type_var_filter()
for type_var_set in base.get_generics():
new = NO_VALUES
for type_var in type_var_set:
if isinstance(type_var, TypeVar):
names = filter.get(type_var.py__name__())
new |= ValueSet.from_sets(
name.infer() for name in names
)
else:
# Mostly will be type vars, except if in some cases
# a concrete type will already be there. In that
# case just add it to the value set.
new |= ValueSet([type_var])
yield new
class _GenericInstanceWrapper(ValueWrapper):
def py__stop_iteration_returns(self):
for cls in self._wrapped_value.class_value.py__mro__():
if cls.py__name__() == 'Generator':
generics = cls.get_generics()
try:
return generics[2].execute_annotation()
except IndexError:
pass
elif cls.py__name__() == 'Iterator':
return ValueSet([builtin_from_name(self.inference_state, u'None')])
return self._wrapped_value.py__stop_iteration_returns()
class _PseudoTreeNameClass(Value):
"""
In typeshed, some classes are defined like this:
Tuple: _SpecialForm = ...
Now this is not a real class, therefore we have to do some workarounds like
this class. Essentially this class makes it possible to goto that `Tuple`
name, without affecting anything else negatively.
"""
def __init__(self, parent_context, tree_name):
super(_PseudoTreeNameClass, self).__init__(
parent_context.inference_state,
parent_context
)
self._tree_name = tree_name
@property
def tree_node(self):
return self._tree_name
def get_filters(self, *args, **kwargs):
# TODO this is obviously wrong. Is it though?
class EmptyFilter(ClassFilter):
def __init__(self):
pass
def get(self, name, **kwargs):
return []
def values(self, **kwargs):
return []
yield EmptyFilter()
def py__class__(self):
# TODO this is obviously not correct, but at least gives us a class if
# we have none. Some of these objects don't really have a base class in
# typeshed.
return builtin_from_name(self.inference_state, u'object')
@property
def name(self):
return ValueName(self, self._tree_name)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self._tree_name.value)
class BaseTypingValue(LazyValueWrapper):
def __init__(self, parent_context, tree_name):
self.inference_state = parent_context.inference_state
self.parent_context = parent_context
self._tree_name = tree_name
@property
def name(self):
return ValueName(self, self._tree_name)
def _get_wrapped_value(self):
return _PseudoTreeNameClass(self.parent_context, self._tree_name)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self._tree_name.value)
class BaseTypingValueWithGenerics(DefineGenericBase):
def __init__(self, parent_context, tree_name, generics_manager):
super(BaseTypingValueWithGenerics, self).__init__(generics_manager)
self.inference_state = parent_context.inference_state
self.parent_context = parent_context
self._tree_name = tree_name
def _get_wrapped_value(self):
return _PseudoTreeNameClass(self.parent_context, self._tree_name)
def __repr__(self):
return '%s(%s%s)' % (self.__class__.__name__, self._tree_name.value,
self._generics_manager)
+3 -2
View File
@@ -136,13 +136,14 @@ def _python_to_stub_names(names, fallback_to_python=False):
yield name
def convert_names(names, only_stubs=False, prefer_stubs=False):
def convert_names(names, only_stubs=False, prefer_stubs=False, prefer_stub_to_compiled=True):
assert not (only_stubs and prefer_stubs)
with debug.increase_indent_cm('convert names'):
if only_stubs or prefer_stubs:
return _python_to_stub_names(names, fallback_to_python=prefer_stubs)
else:
return _try_stub_to_python_names(names, prefer_stub_to_compiled=True)
return _try_stub_to_python_names(
names, prefer_stub_to_compiled=prefer_stub_to_compiled)
def convert_values(values, only_stubs=False, prefer_stubs=False, ignore_compiled=True):
+98
View File
@@ -0,0 +1,98 @@
"""
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
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()))
+15 -12
View File
@@ -1,12 +1,14 @@
from jedi.inference.base_value import ValueWrapper
from jedi.inference.value.module import ModuleValue
from jedi.inference.filters import ParserTreeFilter, \
TreeNameDefinition
from jedi.inference.filters import ParserTreeFilter
from jedi.inference.names import StubName, StubModuleName
from jedi.inference.gradual.typing import TypingModuleFilterWrapper
from jedi.inference.context import ModuleContext
class StubModuleValue(ModuleValue):
_module_name_class = StubModuleName
def __init__(self, non_stub_value_set, *args, **kwargs):
super(StubModuleValue, self).__init__(*args, **kwargs)
self.non_stub_value_set = non_stub_value_set
@@ -51,6 +53,16 @@ class StubModuleValue(ModuleValue):
for f in filters:
yield f
def _as_context(self):
return StubModuleContext(self)
class StubModuleContext(ModuleContext):
def get_filters(self, until_position=None, origin_scope=None):
# Make sure to ignore the position, because positions are not relevant
# for stubs.
return super(StubModuleContext, self).get_filters(origin_scope=origin_scope)
class TypingModuleWrapper(StubModuleValue):
def get_filters(self, *args, **kwargs):
@@ -71,17 +83,8 @@ class TypingModuleContext(ModuleContext):
yield f
# From here on down we make looking up the sys.version_info fast.
class _StubName(TreeNameDefinition):
def infer(self):
inferred = super(_StubName, self).infer()
if self.string_name == 'version_info' and self.get_root_context().py__name__() == 'sys':
return [VersionInfo(c) for c in inferred]
return inferred
class StubFilter(ParserTreeFilter):
name_class = _StubName
name_class = StubName
def _is_name_reachable(self, name):
if not super(StubFilter, self)._is_name_reachable(name):
+111
View File
@@ -0,0 +1,111 @@
from jedi._compatibility import unicode, force_unicode
from jedi import debug
from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference.gradual.base import BaseTypingValue
class TypeVarClass(BaseTypingValue):
def py__call__(self, arguments):
unpacked = arguments.unpack()
key, lazy_value = next(unpacked, (None, None))
var_name = self._find_string_name(lazy_value)
# The name must be given, otherwise it's useless.
if var_name is None or key is not None:
debug.warning('Found a variable without a name %s', arguments)
return NO_VALUES
return ValueSet([TypeVar.create_cached(
self.inference_state,
self.parent_context,
self._tree_name,
var_name,
unpacked
)])
def _find_string_name(self, lazy_value):
if lazy_value is None:
return None
value_set = lazy_value.infer()
if not value_set:
return None
if len(value_set) > 1:
debug.warning('Found multiple values for a type variable: %s', value_set)
name_value = next(iter(value_set))
try:
method = name_value.get_safe_value
except AttributeError:
return None
else:
safe_value = method(default=None)
if self.inference_state.environment.version_info.major == 2:
if isinstance(safe_value, bytes):
return force_unicode(safe_value)
if isinstance(safe_value, (str, unicode)):
return safe_value
return None
class TypeVar(BaseTypingValue):
def __init__(self, parent_context, tree_name, var_name, unpacked_args):
super(TypeVar, self).__init__(parent_context, tree_name)
self._var_name = var_name
self._constraints_lazy_values = []
self._bound_lazy_value = None
self._covariant_lazy_value = None
self._contravariant_lazy_value = None
for key, lazy_value in unpacked_args:
if key is None:
self._constraints_lazy_values.append(lazy_value)
else:
if key == 'bound':
self._bound_lazy_value = lazy_value
elif key == 'covariant':
self._covariant_lazy_value = lazy_value
elif key == 'contravariant':
self._contra_variant_lazy_value = lazy_value
else:
debug.warning('Invalid TypeVar param name %s', key)
def py__name__(self):
return self._var_name
def get_filters(self, *args, **kwargs):
return iter([])
def _get_classes(self):
if self._bound_lazy_value is not None:
return self._bound_lazy_value.infer()
if self._constraints_lazy_values:
return self.constraints
debug.warning('Tried to infer the TypeVar %s without a given type', self._var_name)
return NO_VALUES
def is_same_class(self, other):
# Everything can match an undefined type var.
return True
@property
def constraints(self):
return ValueSet.from_sets(
lazy.infer() for lazy in self._constraints_lazy_values
)
def define_generics(self, type_var_dict):
try:
found = type_var_dict[self.py__name__()]
except KeyError:
pass
else:
if found:
return found
return self._get_classes() or ValueSet({self})
def execute_annotation(self):
return self._get_classes().execute_annotation()
def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self.py__name__())
+3 -2
View File
@@ -44,7 +44,7 @@ def _create_stub_map(directory):
if os.path.isfile(init):
yield entry, init
elif entry.endswith('.pyi') and os.path.isfile(path):
name = entry.rstrip('.pyi')
name = entry[:-4]
if name != '__init__':
yield name, path
@@ -163,6 +163,7 @@ def _try_to_load_stub(inference_state, import_names, python_value_set,
if len(import_names) == 1:
# foo-stubs
for p in sys_path:
p = cast_path(p)
init = os.path.join(p, *import_names) + '-stubs' + os.path.sep + '__init__.pyi'
m = _try_to_load_stub_from_file(
inference_state,
@@ -235,7 +236,7 @@ def _load_from_typeshed(inference_state, python_value_set, parent_module_value,
map_ = _cache_stub_file_map(inference_state.grammar.version_info)
import_name = _IMPORT_MAP.get(import_name, import_name)
elif isinstance(parent_module_value, ModuleValue):
if not parent_module_value.is_package:
if not parent_module_value.is_package():
# Only if it's a package (= a folder) something can be
# imported.
return None
+92 -473
View File
@@ -5,22 +5,18 @@ values.
This file deals with all the typing.py cases.
"""
from jedi._compatibility import unicode, force_unicode
from jedi import debug
from jedi.inference.cache import inference_state_method_cache
from jedi.inference.compiled import builtin_from_name
from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \
iterator_to_value_set, ValueWrapper, LazyValueWrapper
LazyValueWrapper
from jedi.inference.lazy_value import LazyKnownValues
from jedi.inference.value.iterable import SequenceLiteralValue
from jedi.inference.arguments import repack_with_argument_clinic
from jedi.inference.utils import to_list
from jedi.inference.filters import FilterWrapper
from jedi.inference.names import NameWrapper, AbstractTreeName, \
AbstractNameDefinition, ValueName
from jedi.inference.helpers import is_string
from jedi.inference.value.klass import ClassMixin, ClassFilter
from jedi.inference.context import ClassContext
from jedi.inference.names import NameWrapper, ValueName
from jedi.inference.value.klass import ClassMixin
from jedi.inference.gradual.base import BaseTypingValue, BaseTypingValueWithGenerics
from jedi.inference.gradual.type_var import TypeVarClass
from jedi.inference.gradual.generics import LazyGenericManager
_PROXY_CLASS_TYPES = 'Tuple Generic Protocol Callable Type'.split()
_TYPE_ALIAS_TYPES = {
@@ -36,52 +32,6 @@ _TYPE_ALIAS_TYPES = {
_PROXY_TYPES = 'Optional Union ClassVar'.split()
class TypingName(AbstractTreeName):
def __init__(self, value, other_name):
super(TypingName, self).__init__(value.parent_context, other_name.tree_name)
self._value = value
def infer(self):
return ValueSet([self._value])
class _BaseTypingValue(Value):
def __init__(self, inference_state, parent_context, tree_name):
super(_BaseTypingValue, self).__init__(inference_state, parent_context)
self._tree_name = tree_name
@property
def tree_node(self):
return self._tree_name
def get_filters(self, *args, **kwargs):
# TODO this is obviously wrong. Is it though?
class EmptyFilter(ClassFilter):
def __init__(self):
pass
def get(self, name, **kwargs):
return []
def values(self, **kwargs):
return []
yield EmptyFilter()
def py__class__(self):
# TODO this is obviously not correct, but at least gives us a class if
# we have none. Some of these objects don't really have a base class in
# typeshed.
return builtin_from_name(self.inference_state, u'object')
@property
def name(self):
return ValueName(self, self._tree_name)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self._tree_name.value)
class TypingModuleName(NameWrapper):
def infer(self):
return ValueSet(self._remap())
@@ -94,33 +44,40 @@ class TypingModuleName(NameWrapper):
except KeyError:
pass
else:
yield TypeAlias.create_cached(inference_state, self.parent_context, self.tree_name, actual)
yield TypeAlias.create_cached(
inference_state, self.parent_context, self.tree_name, actual)
return
if name in _PROXY_CLASS_TYPES:
yield TypingClassValue.create_cached(inference_state, self.parent_context, self.tree_name)
yield ProxyTypingClassValue.create_cached(
inference_state, self.parent_context, self.tree_name)
elif name in _PROXY_TYPES:
yield TypingValue.create_cached(inference_state, self.parent_context, self.tree_name)
yield ProxyTypingValue.create_cached(
inference_state, self.parent_context, self.tree_name)
elif name == 'runtime':
# We don't want anything here, not sure what this function is
# supposed to do, since it just appears in the stubs and shouldn't
# have any effects there (because it's never executed).
return
elif name == 'TypeVar':
yield TypeVarClass.create_cached(inference_state, self.parent_context, self.tree_name)
yield TypeVarClass.create_cached(
inference_state, self.parent_context, self.tree_name)
elif name == 'Any':
yield Any.create_cached(inference_state, self.parent_context, self.tree_name)
yield Any.create_cached(
inference_state, self.parent_context, self.tree_name)
elif name == 'TYPE_CHECKING':
# This is needed for e.g. imports that are only available for type
# checking or are in cycles. The user can then check this variable.
yield builtin_from_name(inference_state, u'True')
elif name == 'overload':
yield OverloadFunction.create_cached(inference_state, self.parent_context, self.tree_name)
yield OverloadFunction.create_cached(
inference_state, self.parent_context, self.tree_name)
elif name == 'NewType':
yield NewTypeFunction.create_cached(inference_state, self.parent_context, self.tree_name)
yield NewTypeFunction.create_cached(
inference_state, self.parent_context, self.tree_name)
elif name == 'cast':
# TODO implement cast
yield CastFunction.create_cached(inference_state, self.parent_context, self.tree_name)
yield CastFunction.create_cached(
inference_state, self.parent_context, self.tree_name)
elif name == 'TypedDict':
# TODO doesn't even exist in typeshed/typing.py, yet. But will be
# added soon.
@@ -139,21 +96,7 @@ class TypingModuleFilterWrapper(FilterWrapper):
name_wrapper_class = TypingModuleName
class _WithIndexBase(_BaseTypingValue):
def __init__(self, inference_state, parent_context, name, index_value, value_of_index):
super(_WithIndexBase, self).__init__(inference_state, parent_context, name)
self._index_value = index_value
self._context_of_index = value_of_index
def __repr__(self):
return '<%s: %s[%s]>' % (
self.__class__.__name__,
self._tree_name.value,
self._index_value,
)
class TypingValueWithIndex(_WithIndexBase):
class TypingValueWithIndex(BaseTypingValueWithGenerics):
def execute_annotation(self):
string_name = self._tree_name.value
@@ -168,27 +111,36 @@ class TypingValueWithIndex(_WithIndexBase):
| ValueSet([builtin_from_name(self.inference_state, u'None')])
elif string_name == 'Type':
# The type is actually already given in the index_value
return ValueSet([self._index_value])
return self._generics_manager[0]
elif string_name == 'ClassVar':
# For now don't do anything here, ClassVars are always used.
return self._index_value.execute_annotation()
return self._generics_manager[0].execute_annotation()
cls = globals()[string_name]
mapped = {
'Tuple': Tuple,
'Generic': Generic,
'Protocol': Protocol,
'Callable': Callable,
}
cls = mapped[string_name]
return ValueSet([cls(
self.inference_state,
self.parent_context,
self._tree_name,
self._index_value,
self._context_of_index
generics_manager=self._generics_manager,
)])
def gather_annotation_classes(self):
return ValueSet.from_sets(
_iter_over_arguments(self._index_value, self._context_of_index)
return ValueSet.from_sets(self._generics_manager.to_tuple())
def _create_instance_with_generics(self, generics_manager):
return TypingValueWithIndex(
self.parent_context,
self._tree_name,
generics_manager
)
class TypingValue(_BaseTypingValue):
class ProxyTypingValue(BaseTypingValue):
index_class = TypingValueWithIndex
py__simple_getitem__ = None
@@ -198,9 +150,11 @@ class TypingValue(_BaseTypingValue):
self.inference_state,
self.parent_context,
self._tree_name,
index_value,
value_of_index=contextualized_node.context)
for index_value in index_value_set
generics_manager=LazyGenericManager(
context_of_index=contextualized_node.context,
index_value=index_value,
)
) for index_value in index_value_set
)
@@ -222,33 +176,10 @@ class TypingClassValueWithIndex(_TypingClassMixin, TypingValueWithIndex):
pass
class TypingClassValue(_TypingClassMixin, TypingValue):
class ProxyTypingClassValue(_TypingClassMixin, ProxyTypingValue):
index_class = TypingClassValueWithIndex
def _iter_over_arguments(maybe_tuple_value, defining_context):
def iterate():
if isinstance(maybe_tuple_value, SequenceLiteralValue):
for lazy_value in maybe_tuple_value.py__iter__(contextualized_node=None):
yield lazy_value.infer()
else:
yield ValueSet([maybe_tuple_value])
def resolve_forward_references(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(defining_context, value.get_safe_value())
if node is not None:
for c in defining_context.infer_node(node):
yield c
else:
yield value
for value_set in iterate():
yield ValueSet(resolve_forward_references(value_set))
class TypeAlias(LazyValueWrapper):
def __init__(self, parent_context, origin_tree_name, actual):
self.inference_state = parent_context.inference_state
@@ -282,190 +213,91 @@ class TypeAlias(LazyValueWrapper):
cls = next(iter(classes))
return cls
class _ContainerBase(_WithIndexBase):
def _get_getitem_values(self, index):
args = _iter_over_arguments(self._index_value, self._context_of_index)
for i, values in enumerate(args):
if i == index:
return values
debug.warning('No param #%s found for annotation %s', index, self._index_value)
return NO_VALUES
def gather_annotation_classes(self):
return ValueSet([self._get_wrapped_value()])
class Callable(_ContainerBase):
class Callable(BaseTypingValueWithGenerics):
def py__call__(self, arguments):
"""
def x() -> Callable[[Callable[..., _T]], _T]: ...
"""
# The 0th index are the arguments.
return self._get_getitem_values(1).execute_annotation()
try:
param_values = self._generics_manager[0]
result_values = self._generics_manager[1]
except IndexError:
debug.warning('Callable[...] defined without two arguments')
return NO_VALUES
else:
from jedi.inference.gradual.annotation import infer_return_for_callable
return infer_return_for_callable(arguments, param_values, result_values)
class Tuple(_ContainerBase):
class Tuple(LazyValueWrapper):
def __init__(self, parent_context, name, generics_manager):
self.inference_state = parent_context.inference_state
self.parent_context = parent_context
self._generics_manager = generics_manager
def _is_homogenous(self):
# To specify a variable-length tuple of homogeneous type, Tuple[T, ...]
# is used.
if isinstance(self._index_value, SequenceLiteralValue):
entries = self._index_value.get_tree_entries()
if len(entries) == 2 and entries[1] == '...':
return True
return False
return self._generics_manager.is_homogenous_tuple()
def py__simple_getitem__(self, index):
if self._is_homogenous():
return self._get_getitem_values(0).execute_annotation()
return self._generics_manager.get_index_and_execute(0)
else:
if isinstance(index, int):
return self._get_getitem_values(index).execute_annotation()
return self._generics_manager.get_index_and_execute(index)
debug.dbg('The getitem type on Tuple was %s' % index)
return NO_VALUES
def py__iter__(self, contextualized_node=None):
if self._is_homogenous():
yield LazyKnownValues(self._get_getitem_values(0).execute_annotation())
yield LazyKnownValues(self._generics_manager.get_index_and_execute(0))
else:
if isinstance(self._index_value, SequenceLiteralValue):
for i in range(self._index_value.py__len__()):
yield LazyKnownValues(self._get_getitem_values(i).execute_annotation())
for v in self._generics_manager.to_tuple():
yield LazyKnownValues(v.execute_annotation())
def py__getitem__(self, index_value_set, contextualized_node):
if self._is_homogenous():
return self._get_getitem_values(0).execute_annotation()
return self._generics_manager.get_index_and_execute(0)
return ValueSet.from_sets(
_iter_over_arguments(self._index_value, self._context_of_index)
self._generics_manager.to_tuple()
).execute_annotation()
def _get_wrapped_value(self):
tuple_, = self.inference_state.builtins_module \
.py__getattribute__('tuple').execute_annotation()
return tuple_
class Generic(_ContainerBase):
class Generic(BaseTypingValueWithGenerics):
pass
class Protocol(_ContainerBase):
class Protocol(BaseTypingValueWithGenerics):
pass
class Any(_BaseTypingValue):
class Any(BaseTypingValue):
def execute_annotation(self):
debug.warning('Used Any - returned no results')
return NO_VALUES
class TypeVarClass(_BaseTypingValue):
def py__call__(self, arguments):
unpacked = arguments.unpack()
key, lazy_value = next(unpacked, (None, None))
var_name = self._find_string_name(lazy_value)
# The name must be given, otherwise it's useless.
if var_name is None or key is not None:
debug.warning('Found a variable without a name %s', arguments)
return NO_VALUES
return ValueSet([TypeVar.create_cached(
self.inference_state,
self.parent_context,
self._tree_name,
var_name,
unpacked
)])
def _find_string_name(self, lazy_value):
if lazy_value is None:
return None
value_set = lazy_value.infer()
if not value_set:
return None
if len(value_set) > 1:
debug.warning('Found multiple values for a type variable: %s', value_set)
name_value = next(iter(value_set))
try:
method = name_value.get_safe_value
except AttributeError:
return None
else:
safe_value = method(default=None)
if self.inference_state.environment.version_info.major == 2:
if isinstance(safe_value, bytes):
return force_unicode(safe_value)
if isinstance(safe_value, (str, unicode)):
return safe_value
return None
class TypeVar(_BaseTypingValue):
def __init__(self, inference_state, parent_context, tree_name, var_name, unpacked_args):
super(TypeVar, self).__init__(inference_state, parent_context, tree_name)
self._var_name = var_name
self._constraints_lazy_values = []
self._bound_lazy_value = None
self._covariant_lazy_value = None
self._contravariant_lazy_value = None
for key, lazy_value in unpacked_args:
if key is None:
self._constraints_lazy_values.append(lazy_value)
else:
if key == 'bound':
self._bound_lazy_value = lazy_value
elif key == 'covariant':
self._covariant_lazy_value = lazy_value
elif key == 'contravariant':
self._contra_variant_lazy_value = lazy_value
else:
debug.warning('Invalid TypeVar param name %s', key)
def py__name__(self):
return self._var_name
def get_filters(self, *args, **kwargs):
return iter([])
def _get_classes(self):
if self._bound_lazy_value is not None:
return self._bound_lazy_value.infer()
if self._constraints_lazy_values:
return self.constraints
debug.warning('Tried to infer the TypeVar %s without a given type', self._var_name)
return NO_VALUES
def is_same_class(self, other):
# Everything can match an undefined type var.
return True
@property
def constraints(self):
return ValueSet.from_sets(
lazy.infer() for lazy in self._constraints_lazy_values
)
def define_generics(self, type_var_dict):
try:
found = type_var_dict[self.py__name__()]
except KeyError:
pass
else:
if found:
return found
return self._get_classes() or ValueSet({self})
def execute_annotation(self):
return self._get_classes().execute_annotation()
def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self.py__name__())
class OverloadFunction(_BaseTypingValue):
class OverloadFunction(BaseTypingValue):
@repack_with_argument_clinic('func, /')
def py__call__(self, func_value_set):
# Just pass arguments through.
return func_value_set
class NewTypeFunction(_BaseTypingValue):
class NewTypeFunction(BaseTypingValue):
def py__call__(self, arguments):
ordered_args = arguments.unpack()
next(ordered_args, (None, None))
@@ -490,226 +322,13 @@ class NewType(Value):
def py__call__(self, arguments):
return self._type_value_set.execute_annotation()
@property
def name(self):
from jedi.inference.compiled.value import CompiledValueName
return CompiledValueName(self, 'NewType')
class CastFunction(_BaseTypingValue):
class CastFunction(BaseTypingValue):
@repack_with_argument_clinic('type, object, /')
def py__call__(self, type_value_set, object_value_set):
return type_value_set.execute_annotation()
class BoundTypeVarName(AbstractNameDefinition):
"""
This type var was bound to a certain type, e.g. int.
"""
def __init__(self, type_var, value_set):
self._type_var = type_var
self.parent_context = type_var.parent_context
self._value_set = value_set
def infer(self):
def iter_():
for value in self._value_set:
# Replace any with the constraints if they are there.
if isinstance(value, Any):
for constraint in self._type_var.constraints:
yield constraint
else:
yield value
return ValueSet(iter_())
def py__name__(self):
return self._type_var.py__name__()
def __repr__(self):
return '<%s %s -> %s>' % (self.__class__.__name__, self.py__name__(), self._value_set)
class TypeVarFilter(object):
"""
A filter for all given variables in a class.
A = TypeVar('A')
B = TypeVar('B')
class Foo(Mapping[A, B]):
...
In this example we would have two type vars given: A and B
"""
def __init__(self, generics, type_vars):
self._generics = generics
self._type_vars = type_vars
def get(self, name):
for i, type_var in enumerate(self._type_vars):
if type_var.py__name__() == name:
try:
return [BoundTypeVarName(type_var, self._generics[i])]
except IndexError:
return [type_var.name]
return []
def values(self):
# The values are not relevant. If it's not searched exactly, the type
# vars are just global and should be looked up as that.
return []
class AnnotatedClassContext(ClassContext):
def get_filters(self, *args, **kwargs):
filters = super(AnnotatedClassContext, self).get_filters(
*args, **kwargs
)
for f in filters:
yield f
# The type vars can only be looked up if it's a global search and
# not a direct lookup on the class.
yield self._value.get_type_var_filter()
class AbstractAnnotatedClass(ClassMixin, ValueWrapper):
def get_type_var_filter(self):
return TypeVarFilter(self.get_generics(), self.list_type_vars())
def is_same_class(self, other):
if not isinstance(other, AbstractAnnotatedClass):
return False
if self.tree_node != other.tree_node:
# TODO not sure if this is nice.
return False
given_params1 = self.get_generics()
given_params2 = other.get_generics()
if len(given_params1) != len(given_params2):
# If the amount of type vars doesn't match, the class doesn't
# match.
return False
# Now compare generics
return all(
any(
# TODO why is this ordering the correct one?
cls2.is_same_class(cls1)
for cls1 in class_set1
for cls2 in class_set2
) for class_set1, class_set2 in zip(given_params1, given_params2)
)
def py__call__(self, arguments):
instance, = super(AbstractAnnotatedClass, self).py__call__(arguments)
return ValueSet([InstanceWrapper(instance)])
def get_generics(self):
raise NotImplementedError
def define_generics(self, type_var_dict):
changed = False
new_generics = []
for generic_set in self.get_generics():
values = NO_VALUES
for generic in generic_set:
if isinstance(generic, (AbstractAnnotatedClass, TypeVar)):
result = generic.define_generics(type_var_dict)
values |= result
if result != ValueSet({generic}):
changed = True
else:
values |= ValueSet([generic])
new_generics.append(values)
if not changed:
# There might not be any type vars that change. In that case just
# return itself, because it does not make sense to potentially lose
# cached results.
return ValueSet([self])
return ValueSet([GenericClass(
self._wrapped_value,
generics=tuple(new_generics)
)])
def _as_context(self):
return AnnotatedClassContext(self)
def __repr__(self):
return '<%s: %s%s>' % (
self.__class__.__name__,
self._wrapped_value,
list(self.get_generics()),
)
@to_list
def py__bases__(self):
for base in self._wrapped_value.py__bases__():
yield LazyAnnotatedBaseClass(self, base)
class LazyGenericClass(AbstractAnnotatedClass):
def __init__(self, class_value, index_value, value_of_index):
super(LazyGenericClass, self).__init__(class_value)
self._index_value = index_value
self._context_of_index = value_of_index
@inference_state_method_cache()
def get_generics(self):
return list(_iter_over_arguments(self._index_value, self._context_of_index))
class GenericClass(AbstractAnnotatedClass):
def __init__(self, class_value, generics):
super(GenericClass, self).__init__(class_value)
self._generics = generics
def get_generics(self):
return self._generics
class LazyAnnotatedBaseClass(object):
def __init__(self, class_value, lazy_base_class):
self._class_value = class_value
self._lazy_base_class = lazy_base_class
@iterator_to_value_set
def infer(self):
for base in self._lazy_base_class.infer():
if isinstance(base, AbstractAnnotatedClass):
# Here we have to recalculate the given types.
yield GenericClass.create_cached(
base.inference_state,
base._wrapped_value,
tuple(self._remap_type_vars(base)),
)
else:
yield base
def _remap_type_vars(self, base):
filter = self._class_value.get_type_var_filter()
for type_var_set in base.get_generics():
new = NO_VALUES
for type_var in type_var_set:
if isinstance(type_var, TypeVar):
names = filter.get(type_var.py__name__())
new |= ValueSet.from_sets(
name.infer() for name in names
)
else:
# Mostly will be type vars, except if in some cases
# a concrete type will already be there. In that
# case just add it to the value set.
new |= ValueSet([type_var])
yield new
class InstanceWrapper(ValueWrapper):
def py__stop_iteration_returns(self):
for cls in self._wrapped_value.class_value.py__mro__():
if cls.py__name__() == 'Generator':
generics = cls.get_generics()
try:
return generics[2].execute_annotation()
except IndexError:
pass
elif cls.py__name__() == 'Iterator':
return ValueSet([builtin_from_name(self.inference_state, u'None')])
return self._wrapped_value.py__stop_iteration_returns()
-2
View File
@@ -21,8 +21,6 @@ def load_proper_stub_module(inference_state, file_io, import_names, module_node)
if import_names is not None:
actual_value_set = inference_state.import_module(import_names, prefer_stubs=False)
if not actual_value_set:
return None
stub = create_stub_module(
inference_state, actual_value_set, module_node, file_io, import_names
+5 -1
View File
@@ -72,6 +72,10 @@ def infer_call_of_leaf(context, leaf, cut_own_trailer=False):
# different trailers: `( x )`, `[ x ]` and `.x`. In the first two examples
# we should not match anything more than x.
if trailer.type != 'trailer' or leaf not in (trailer.children[0], trailer.children[-1]):
if leaf == ':':
# Basically happens with foo[:] when the cursor is on the colon
from jedi.inference.base_value import NO_VALUES
return NO_VALUES
if trailer.type == 'atom':
return context.infer_node(trailer)
return context.infer_node(leaf)
@@ -90,7 +94,7 @@ def infer_call_of_leaf(context, leaf, cut_own_trailer=False):
base = power.children[start]
if base.type != 'trailer':
break
trailers = power.children[start + 1: index + 1]
trailers = power.children[start + 1:cut]
else:
base = power.children[0]
trailers = power.children[1:cut]
+52 -40
View File
@@ -216,7 +216,7 @@ class Importer(object):
self._fixed_sys_path = None
self._infer_possible = True
if level:
base = module_context.py__package__()
base = module_context.get_value().py__package__()
# We need to care for two cases, the first one is if it's a valid
# Python import. This import has a properly defined module name
# chain like `foo.bar.baz` and an import in baz is made for
@@ -272,47 +272,27 @@ class Importer(object):
for name in self.import_path
)
def _sys_path_with_modifications(self):
def _sys_path_with_modifications(self, is_completion):
if self._fixed_sys_path is not None:
return self._fixed_sys_path
sys_path_mod = (
self._inference_state.get_sys_path()
return (
# For import completions we don't want to see init paths, but for
# inference we want to show the user as much as possible.
# See GH #1446.
self._inference_state.get_sys_path(add_init_paths=not is_completion)
+ sys_path.check_sys_path_modifications(self._module_context)
)
if self._inference_state.environment.version_info.major == 2:
file_path = self._module_context.py__file__()
if file_path is not None:
# Python2 uses an old strange way of importing relative imports.
sys_path_mod.append(force_unicode(os.path.dirname(file_path)))
return sys_path_mod
def follow(self):
if not self.import_path or not self._infer_possible:
return NO_VALUES
import_names = tuple(
force_unicode(i.value if isinstance(i, tree.Name) else i)
for i in self.import_path
)
sys_path = self._sys_path_with_modifications()
sys_path = self._sys_path_with_modifications(is_completion=False)
value_set = [None]
for i, name in enumerate(self.import_path):
value_set = ValueSet.from_sets([
self._inference_state.import_module(
import_names[:i+1],
parent_module_value,
sys_path
) for parent_module_value in value_set
])
if not value_set:
message = 'No module named ' + '.'.join(import_names)
_add_error(self._module_context, name, message)
return NO_VALUES
return value_set
return import_module_by_names(
self._inference_state, self.import_path, sys_path, self._module_context
)
def _get_module_names(self, search_path=None, in_module=None):
"""
@@ -326,13 +306,13 @@ class Importer(object):
for name in self._inference_state.compiled_subprocess.get_builtin_module_names()]
if search_path is None:
search_path = self._sys_path_with_modifications()
search_path = self._sys_path_with_modifications(is_completion=True)
for name in iter_module_names(self._inference_state, search_path):
if in_module is None:
n = ImportName(self._module_context, name)
else:
n = SubModuleName(in_module, name)
n = SubModuleName(in_module.as_context(), name)
names.append(n)
return names
@@ -355,7 +335,7 @@ class Importer(object):
extname = modname[len('flask_'):]
names.append(ImportName(self._module_context, extname))
# Now the old style: ``flaskext.foo``
for dir in self._sys_path_with_modifications():
for dir in self._sys_path_with_modifications(is_completion=True):
flaskext = os.path.join(dir, 'flaskext')
if os.path.isdir(flaskext):
names += self._get_module_names([flaskext])
@@ -365,7 +345,9 @@ class Importer(object):
# Non-modules are not completable.
if value.api_type != 'module': # not a module
continue
names += value.sub_modules_dict().values()
if not value.is_compiled():
# sub_modules_dict is not implemented for compiled modules.
names += value.sub_modules_dict().values()
if not only_modules:
from jedi.inference.gradual.conversion import convert_values
@@ -384,6 +366,36 @@ class Importer(object):
return names
def import_module_by_names(inference_state, import_names, sys_path=None,
module_context=None, prefer_stubs=True):
if sys_path is None:
sys_path = inference_state.get_sys_path()
str_import_names = tuple(
force_unicode(i.value if isinstance(i, tree.Name) else i)
for i in import_names
)
value_set = [None]
for i, name in enumerate(import_names):
value_set = ValueSet.from_sets([
import_module(
inference_state,
str_import_names[:i+1],
parent_module_value,
sys_path,
prefer_stubs=prefer_stubs,
) for parent_module_value in value_set
])
if not value_set:
message = 'No module named ' + '.'.join(str_import_names)
if module_context is not None:
_add_error(module_context, name, message)
else:
debug.warning(message)
return NO_VALUES
return value_set
@plugin_manager.decorate()
@import_module_decorator
def import_module(inference_state, import_names, parent_module_value, sys_path):
@@ -493,7 +505,7 @@ def _load_builtin_module(inference_state, import_names=None, sys_path=None):
return module
def _load_module_from_path(inference_state, file_io, base_names):
def load_module_from_path(inference_state, file_io, base_names=None):
"""
This should pretty much only be used for get_modules_containing_name. It's
here to ensure that a random path is still properly loaded into the Jedi
@@ -540,7 +552,7 @@ def get_module_contexts_containing_name(inference_state, module_contexts, name):
if name not in code:
return None
new_file_io = KnownContentFileIO(file_io.path, code)
m = _load_module_from_path(inference_state, new_file_io, base_names)
m = load_module_from_path(inference_state, new_file_io, base_names)
if isinstance(m, compiled.CompiledObject):
return None
return m.as_context()
@@ -556,7 +568,7 @@ def get_module_contexts_containing_name(inference_state, module_contexts, name):
used_mod_paths.add(path)
folders_with_names_to_be_checked.append((
file_io.get_parent_folder(),
module_context.py__package__()
module_context.get_value().py__package__()
))
yield module_context
@@ -566,7 +578,8 @@ def get_module_contexts_containing_name(inference_state, module_contexts, name):
def get_file_ios_to_check():
for folder_io, base_names in folders_with_names_to_be_checked:
for file_io in check_directory(folder_io):
yield file_io, base_names
if file_io.path not in used_mod_paths:
yield file_io, base_names
for p in settings.additional_dynamic_modules:
p = os.path.abspath(p)
@@ -606,4 +619,3 @@ def follow_error_node_imports_if_possible(context, name):
return Importer(
context.inference_state, names, context.get_root_context(), level).follow()
return None
+100 -5
View File
@@ -3,11 +3,37 @@ from abc import abstractmethod
from parso.tree import search_ancestor
from jedi._compatibility import Parameter
from jedi.parser_utils import clean_scope_docstring, find_statement_documentation
from jedi.inference.utils import unite
from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference import docstrings
from jedi.cache import memoize_method
from jedi.inference.helpers import deep_ast_copy, infer_call_of_leaf
from jedi.plugins import plugin_manager
def _merge_name_docs(names):
doc = ''
for name in names:
if doc:
# In case we have multiple values, just return all of them
# separated by a few dashes.
doc += '\n' + '-' * 30 + '\n'
doc += name.py__doc__()
return doc
def _merge_docs_and_signature(values, doc):
signature_text = '\n'.join(
signature.to_string()
for value in values
for signature in value.get_signatures()
)
if signature_text and doc:
return signature_text + '\n\n' + doc
else:
return signature_text + doc
class AbstractNameDefinition(object):
@@ -59,6 +85,9 @@ class AbstractNameDefinition(object):
def is_import(self):
return False
def py__doc__(self, include_signatures=False):
return ''
@property
def api_type(self):
return self.parent_context.api_type
@@ -93,7 +122,7 @@ class AbstractTreeName(AbstractNameDefinition):
# In case of level == 1, it works always, because it's like a submodule
# lookup.
if import_node is not None and not (import_node.level == 1
and self.get_root_context().is_package):
and self.get_root_context().get_value().is_package()):
# TODO improve the situation for when level is present.
if include_module_names and not import_node.level:
return tuple(n.value for n in import_node.get_path_for_name(self.tree_name))
@@ -165,7 +194,7 @@ class AbstractTreeName(AbstractNameDefinition):
new_dotted.children[index - 1:] = []
values = context.infer_node(new_dotted)
return unite(
value.goto(name, name_context=value.as_context())
value.goto(name, name_context=context)
for value in values
)
@@ -197,6 +226,13 @@ class ValueNameMixin(object):
def infer(self):
return ValueSet([self._value])
def py__doc__(self, include_signatures=False):
doc = self._value.py__doc__()
if include_signatures:
doc = _merge_docs_and_signature([self._value], doc)
return doc
def _get_qualified_names(self):
return self._value.get_qualified_names()
@@ -285,6 +321,19 @@ class TreeNameDefinition(AbstractTreeName):
node = node.parent
return indexes
def py__doc__(self, include_signatures=False):
if self.api_type in ('function', 'class'):
return clean_scope_docstring(self.tree_name.get_definition())
if self.api_type == 'statement' and self.tree_name.is_definition():
return find_statement_documentation(self.tree_name.get_definition())
if self.api_type == 'module':
names = self.goto()
if self not in names:
return _merge_name_docs(names)
return super(TreeNameDefinition, self).py__doc__(include_signatures)
class _ParamMixin(object):
def maybe_positional_argument(self, include_star=True):
@@ -434,9 +483,11 @@ class _ActualTreeParamName(BaseTreeParamName):
class AnonymousParamName(_ActualTreeParamName):
def __init__(self, function_value, tree_name):
super(AnonymousParamName, self).__init__(function_value, tree_name)
@plugin_manager.decorate(name='goto_anonymous_param')
def goto(self):
return super(AnonymousParamName, self).goto()
@plugin_manager.decorate(name='infer_anonymous_param')
def infer(self):
values = super(AnonymousParamName, self).infer()
if values:
@@ -516,7 +567,7 @@ class ImportName(AbstractNameDefinition):
return m
# It's almost always possible to find the import or to not find it. The
# importing returns only one value, pretty much always.
return next(iter(import_values))
return next(iter(import_values)).as_context()
@memoize_method
def infer(self):
@@ -531,6 +582,9 @@ class ImportName(AbstractNameDefinition):
def api_type(self):
return 'module'
def py__doc__(self, include_signatures=False):
return _merge_name_docs(self.goto())
class SubModuleName(ImportName):
_level = 1
@@ -549,3 +603,44 @@ class NameWrapper(object):
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self._wrapped_name)
class StubNameMixin(object):
def py__doc__(self, include_signatures=False):
from jedi.inference.gradual.conversion import convert_names
names = convert_names([self], prefer_stub_to_compiled=False)
if self in names:
doc = super(StubNameMixin, self).py__doc__(include_signatures)
else:
doc = _merge_name_docs(names)
if include_signatures:
parent = self.tree_name.parent
if parent.type in ('funcdef', 'classdef') and parent.name is self.tree_name:
doc = _merge_docs_and_signature(self.infer(), doc)
return doc
# From here on down we make looking up the sys.version_info fast.
class StubName(StubNameMixin, TreeNameDefinition):
def infer(self):
inferred = super(StubName, self).infer()
if self.string_name == 'version_info' and self.get_root_context().py__name__() == 'sys':
from jedi.inference.gradual.stub_value import VersionInfo
return [VersionInfo(c) for c in inferred]
return inferred
class ModuleName(ValueNameMixin, AbstractNameDefinition):
start_pos = 1, 0
def __init__(self, value, name):
self._value = value
self._name = name
@property
def string_name(self):
return self._name
class StubModuleName(StubNameMixin, ModuleName):
pass
+115
View File
@@ -0,0 +1,115 @@
from jedi.inference import imports
from jedi.inference.filters import ParserTreeFilter
def _resolve_names(definition_names, avoid_names=()):
for name in definition_names:
if name in avoid_names:
# Avoiding recursions here, because goto on a module name lands
# on the same module.
continue
if not isinstance(name, imports.SubModuleName):
# SubModuleNames are not actually existing names but created
# names when importing something like `import foo.bar.baz`.
yield name
if name.api_type == 'module':
for name in _resolve_names(name.goto(), definition_names):
yield name
def _dictionarize(names):
return dict(
(n if n.tree_name is None else n.tree_name, n)
for n in names
)
def _find_defining_names(module_context, tree_name):
found_names = _find_names(module_context, tree_name)
found_names |= set(_find_global_variables(found_names, tree_name.value))
for name in list(found_names):
if name.api_type == 'param' or name.tree_name is None \
or name.tree_name.parent.type == 'trailer':
continue
found_names |= set(_add_names_in_same_context(name.parent_context, name.string_name))
return set(_resolve_names(found_names))
def _find_names(module_context, tree_name):
name = module_context.create_name(tree_name)
found_names = set(name.goto())
found_names.add(name)
return set(_resolve_names(found_names))
def _add_names_in_same_context(context, string_name):
if context.tree_node is None:
return
until_position = None
while True:
filter_ = ParserTreeFilter(
parent_context=context,
until_position=until_position,
)
names = set(filter_.get(string_name))
if not names:
break
for name in names:
yield name
ordered = sorted(names, key=lambda x: x.start_pos)
until_position = ordered[0].start_pos
def _find_global_variables(names, search_name):
for name in names:
if name.tree_name is None:
continue
module_context = name.get_root_context()
try:
method = module_context.get_global_filter
except AttributeError:
continue
else:
for global_name in method().get(search_name):
yield global_name
c = module_context.create_context(global_name.tree_name)
for name in _add_names_in_same_context(c, global_name.string_name):
yield name
def find_references(module_context, tree_name):
search_name = tree_name.value
found_names = _find_defining_names(module_context, tree_name)
found_names_dct = _dictionarize(found_names)
module_contexts = set(d.get_root_context() for d in found_names)
module_contexts = set(m for m in module_contexts if not m.is_compiled())
non_matching_reference_maps = {}
inf = module_context.inference_state
potential_modules = imports.get_module_contexts_containing_name(
inf, module_contexts, search_name
)
for module_context in potential_modules:
for name_leaf in module_context.tree_node.get_used_names().get(search_name, []):
new = _dictionarize(_find_names(module_context, name_leaf))
if any(tree_name in found_names_dct for tree_name in new):
found_names_dct.update(new)
for tree_name in new:
for dct in non_matching_reference_maps.get(tree_name, []):
# A reference that was previously searched for matches
# with a now found name. Merge.
found_names_dct.update(dct)
try:
del non_matching_reference_maps[tree_name]
except KeyError:
pass
else:
for name in new:
non_matching_reference_maps.setdefault(name, []).append(new)
return found_names_dct.values()
+4 -2
View File
@@ -56,6 +56,9 @@ class AbstractSignature(_SignatureMixin):
def bind(self, value):
raise NotImplementedError
def matches_signature(self, arguments):
return True
def __repr__(self):
if self.value is self._function_value:
return '<%s: %s>' % (self.__class__.__name__, self.value)
@@ -104,7 +107,7 @@ class TreeSignature(AbstractSignature):
for executed_param_name in executed_param_names)
if debug.enable_notice:
tree_node = self._function_value.tree_node
signature = parser_utils.get_call_signature(tree_node)
signature = parser_utils.get_signature(tree_node)
if matches:
debug.dbg("Overloading match: %s@%s (%s)",
signature, tree_node.start_pos[0], arguments, color='BLUE')
@@ -128,7 +131,6 @@ class BuiltinSignature(AbstractSignature):
return self.value
def bind(self, value):
assert not self.is_bound
return BuiltinSignature(value, self._return_string, is_bound=True)
+22 -3
View File
@@ -45,7 +45,16 @@ def _limit_value_infers(func):
inference_state = context.inference_state
try:
inference_state.inferred_element_counts[n] += 1
if inference_state.inferred_element_counts[n] > 300:
maximum = 300
if context.parent_context is None \
and context.get_value() is inference_state.builtins_module:
# Builtins should have a more generous inference limit.
# It is important that builtins can be executed, otherwise some
# functions that depend on certain builtins features would be
# broken, see e.g. GH #1432
maximum *= 100
if inference_state.inferred_element_counts[n] > maximum:
debug.warning('In value %s there were too many inferences.', n)
return NO_VALUES
except KeyError:
@@ -99,7 +108,7 @@ def infer_node(context, element):
str_element_names = [e.value for e in element_names]
if any(i.value in str_element_names for i in if_names):
for if_name in if_names:
definitions = context.inference_state.goto_definitions(context, if_name)
definitions = context.inference_state.infer(context, if_name)
# Every name that has multiple different definitions
# causes the complexity to rise. The complexity should
# never fall below 1.
@@ -406,7 +415,7 @@ def _infer_expr_stmt(context, stmt, seek_name=None):
if is_setitem:
def to_mod(v):
c = ContextualizedNode(context, subscriptlist)
c = ContextualizedSubscriptListNode(context, subscriptlist)
if v.array_type == 'dict':
return DictModification(v, value_set, c)
elif v.array_type == 'list':
@@ -677,6 +686,11 @@ def tree_name_to_values(inference_state, context, tree_name):
node = tree_name.parent
if node.type == 'global_stmt':
c = context.create_context(tree_name)
if c.is_module():
# In case we are already part of the module, there is no point
# in looking up the global statement anymore, because it's not
# valid at that point anyway.
return NO_VALUES
# For global_stmt lookups, we only need the first possible scope,
# which means the function itself.
filter = next(c.get_filters())
@@ -803,6 +817,11 @@ def check_tuple_assignments(name, value_set):
return value_set
class ContextualizedSubscriptListNode(ContextualizedNode):
def infer(self):
return _infer_subscript_list(self.context, self.node)
def _infer_subscript_list(context, index):
"""
Handles slices in subscript nodes.
-63
View File
@@ -1,63 +0,0 @@
from jedi.inference import imports
def _resolve_names(definition_names, avoid_names=()):
for name in definition_names:
if name in avoid_names:
# Avoiding recursions here, because goto on a module name lands
# on the same module.
continue
if not isinstance(name, imports.SubModuleName):
# SubModuleNames are not actually existing names but created
# names when importing something like `import foo.bar.baz`.
yield name
if name.api_type == 'module':
for name in _resolve_names(name.goto(), definition_names):
yield name
def _dictionarize(names):
return dict(
(n if n.tree_name is None else n.tree_name, n)
for n in names
)
def _find_names(module_context, tree_name):
name = module_context.create_name(tree_name)
found_names = set(name.goto())
found_names.add(name)
return _dictionarize(_resolve_names(found_names))
def usages(module_context, tree_name):
search_name = tree_name.value
found_names = _find_names(module_context, tree_name)
module_contexts = set(d.get_root_context() for d in found_names.values())
module_contexts = set(m for m in module_contexts if not m.is_compiled())
non_matching_usage_maps = {}
inf = module_context.inference_state
potential_modules = imports.get_module_contexts_containing_name(
inf, module_contexts, search_name
)
for module_context in potential_modules:
for name_leaf in module_context.tree_node.get_used_names().get(search_name, []):
new = _find_names(module_context, name_leaf)
if any(tree_name in found_names for tree_name in new):
found_names.update(new)
for tree_name in new:
for dct in non_matching_usage_maps.get(tree_name, []):
# A usage that was previously searched for matches with
# a now found name. Merge.
found_names.update(dct)
try:
del non_matching_usage_maps[tree_name]
except KeyError:
pass
else:
for name in new:
non_matching_usage_maps.setdefault(name, []).append(new)
return found_names.values()
+6
View File
@@ -21,6 +21,12 @@ def to_list(func):
return wrapper
def to_tuple(func):
def wrapper(*args, **kwargs):
return tuple(func(*args, **kwargs))
return wrapper
def unite(iterable):
"""Turns a two dimensional array into a one dimensional."""
return set(typ for types in iterable for typ in types)
+5 -5
View File
@@ -10,7 +10,7 @@ current module will be checked for appearances of ``arr.append``,
content will be added
This can be really cpu intensive, as you can imagine. Because |jedi| has to
follow **every** ``append`` and check wheter it's the right array. However this
follow **every** ``append`` and check whether it's the right array. However this
works pretty good, because in *slow* cases, the recursion detector and other
settings will stop this process.
@@ -188,8 +188,8 @@ class _Modification(ValueWrapper):
class DictModification(_Modification):
def py__iter__(self):
for lazy_context in self._wrapped_value.py__iter__():
def py__iter__(self, contextualized_node=None):
for lazy_context in self._wrapped_value.py__iter__(contextualized_node):
yield lazy_context
yield self._contextualized_key
@@ -198,7 +198,7 @@ class DictModification(_Modification):
class ListModification(_Modification):
def py__iter__(self):
for lazy_context in self._wrapped_value.py__iter__():
def py__iter__(self, contextualized_node=None):
for lazy_context in self._wrapped_value.py__iter__(contextualized_node):
yield lazy_context
yield LazyKnownValues(self._assigned_values)
+12 -9
View File
@@ -21,6 +21,7 @@ from jedi.inference.value import iterable
from jedi import parser_utils
from jedi.inference.parser_cache import get_yield_exprs
from jedi.inference.helpers import values_from_qualified_names
from jedi.inference.gradual.generics import TupleGenericManager
class LambdaName(AbstractNameDefinition):
@@ -96,9 +97,6 @@ class FunctionMixin(object):
class FunctionValue(use_metaclass(CachedMetaClass, FunctionMixin, FunctionAndClassBase)):
def is_function(self):
return True
@classmethod
def from_context(cls, context, tree_node):
def create(tree_node):
@@ -161,6 +159,9 @@ class MethodValue(FunctionValue):
class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
def is_function_execution(self):
return True
def _infer_annotations(self):
raise NotImplementedError
@@ -276,17 +277,19 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
for lazy_value in self.get_yield_lazy_values()
)
def is_generator(self):
return bool(get_yield_exprs(self.inference_state, self.tree_node))
def infer(self):
"""
Created to be used by inheritance.
"""
inference_state = self.inference_state
is_coroutine = self.tree_node.parent.type in ('async_stmt', 'async_funcdef')
is_generator = bool(get_yield_exprs(inference_state, self.tree_node))
from jedi.inference.gradual.typing import GenericClass
from jedi.inference.gradual.base import GenericClass
if is_coroutine:
if is_generator:
if self.is_generator():
if inference_state.environment.version_info < (3, 6):
return NO_VALUES
async_generator_classes = inference_state.typing_module \
@@ -297,7 +300,7 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
generics = (yield_values.py__class__(), NO_VALUES)
return ValueSet(
# In Python 3.6 AsyncGenerator is still a class.
GenericClass(c, generics)
GenericClass(c, TupleGenericManager(generics))
for c in async_generator_classes
).execute_annotation()
else:
@@ -308,10 +311,10 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
# Only the first generic is relevant.
generics = (return_values.py__class__(), NO_VALUES, NO_VALUES)
return ValueSet(
GenericClass(c, generics) for c in async_classes
GenericClass(c, TupleGenericManager(generics)) for c in async_classes
).execute_annotation()
else:
if is_generator:
if self.is_generator():
return ValueSet([iterable.Generator(inference_state, self)])
else:
return self.get_return_values()
+46 -29
View File
@@ -17,8 +17,9 @@ from jedi.inference.arguments import ValuesArguments, TreeArgumentsWrapper
from jedi.inference.value.function import \
FunctionValue, FunctionMixin, OverloadedFunctionValue, \
BaseFunctionExecutionContext, FunctionExecutionContext
from jedi.inference.value.klass import apply_py__get__, ClassFilter
from jedi.inference.value.klass import ClassFilter
from jedi.inference.value.dynamic_arrays import get_dynamic_array_instance
from jedi.parser_utils import function_is_staticmethod, function_is_classmethod
class InstanceExecutedParamName(ParamName):
@@ -41,7 +42,18 @@ class AnonymousMethodExecutionFilter(AnonymousFunctionExecutionFilter):
def _convert_param(self, param, name):
if param.position_index == 0:
return InstanceExecutedParamName(self._instance, self._function_value, name)
if function_is_classmethod(self._function_value.tree_node):
return InstanceExecutedParamName(
self._instance.py__class__(),
self._function_value,
name
)
elif not function_is_staticmethod(self._function_value.tree_node):
return InstanceExecutedParamName(
self._instance,
self._function_value,
name
)
return super(AnonymousMethodExecutionFilter, self)._convert_param(param, name)
@@ -107,11 +119,23 @@ class AbstractInstanceValue(Value):
call_funcs = self.py__getattribute__('__call__').py__get__(self, self.class_value)
return [s.bind(self) for s in call_funcs.get_signatures()]
def get_function_slot_names(self, name):
# Searches for Python functions in classes.
return []
def execute_function_slots(self, names, *inferred_args):
return ValueSet.from_sets(
name.infer().execute_with_values(*inferred_args)
for name in names
)
def __repr__(self):
return "<%s of %s>" % (self.__class__.__name__, self.class_value)
class CompiledInstance(AbstractInstanceValue):
# This is not really a compiled class, it's just an instance from a
# compiled class.
def __init__(self, inference_state, parent_context, class_value, arguments):
super(CompiledInstance, self).__init__(inference_state, parent_context,
class_value)
@@ -130,9 +154,6 @@ class CompiledInstance(AbstractInstanceValue):
def name(self):
return compiled.CompiledValueName(self, self.class_value.name.string_name)
def is_compiled(self):
return True
def is_stub(self):
return False
@@ -215,8 +236,8 @@ class _BaseTreeInstance(AbstractInstanceValue):
# We are inversing this, because a hand-crafted `__getattribute__`
# could still call another hand-crafted `__getattr__`, but not the
# other way around.
names = (self.get_function_slot_names(u'__getattr__') or
self.get_function_slot_names(u'__getattribute__'))
names = (self.get_function_slot_names(u'__getattr__')
or self.get_function_slot_names(u'__getattribute__'))
return self.execute_function_slots(names, name)
def py__getitem__(self, index_value_set, contextualized_node):
@@ -263,7 +284,7 @@ class _BaseTreeInstance(AbstractInstanceValue):
return ValueSet.from_sets(name.infer().execute(arguments) for name in names)
def py__get__(self, obj, class_value):
def py__get__(self, instance, class_value):
"""
obj may be None.
"""
@@ -271,9 +292,9 @@ class _BaseTreeInstance(AbstractInstanceValue):
# `method` is the new parent of the array, don't know if that's good.
names = self.get_function_slot_names(u'__get__')
if names:
if obj is None:
obj = compiled.builtin_from_name(self.inference_state, u'None')
return self.execute_function_slots(names, obj, class_value)
if instance is None:
instance = compiled.builtin_from_name(self.inference_state, u'None')
return self.execute_function_slots(names, instance, class_value)
else:
return ValueSet([self])
@@ -287,12 +308,6 @@ class _BaseTreeInstance(AbstractInstanceValue):
return names
return []
def execute_function_slots(self, names, *inferred_args):
return ValueSet.from_sets(
name.infer().execute_with_values(*inferred_args)
for name in names
)
class TreeInstance(_BaseTreeInstance):
def __init__(self, inference_state, parent_context, class_value, arguments):
@@ -320,7 +335,8 @@ class TreeInstance(_BaseTreeInstance):
for signature in self.class_value.py__getattribute__('__init__').get_signatures():
# Just take the first result, it should always be one, because we
# control the typeshed code.
if not signature.matches_signature(args):
if not signature.matches_signature(args) \
or signature.value.tree_node is None:
# First check if the signature even matches, if not we don't
# need to infer anything.
continue
@@ -481,7 +497,7 @@ class LazyInstanceClassName(object):
@iterator_to_value_set
def infer(self):
for result_value in self._class_member_name.infer():
for c in apply_py__get__(result_value, self._instance, self._instance.py__class__()):
for c in result_value.py__get__(self._instance, self._instance.py__class__()):
yield c
def __getattr__(self, name):
@@ -514,7 +530,7 @@ class InstanceClassFilter(AbstractFilter):
]
def __repr__(self):
return '<%s for %s>' % (self.__class__.__name__, self._class_filter.context)
return '<%s for %s>' % (self.__class__.__name__, self._class_filter)
class SelfAttributeFilter(ClassFilter):
@@ -544,17 +560,18 @@ class SelfAttributeFilter(ClassFilter):
if name.is_definition() and self._access_possible(name, from_instance=True):
# TODO filter non-self assignments instead of this bad
# filter.
if self._is_in_right_scope(name):
if self._is_in_right_scope(trailer.parent.children[0], name):
yield name
def _is_in_right_scope(self, name):
base = name
hit_funcdef = False
while True:
base = search_ancestor(base, 'funcdef', 'classdef', 'lambdef')
if base is self._parser_scope:
return hit_funcdef
hit_funcdef = True
def _is_in_right_scope(self, self_name, name):
self_context = self._node_context.create_context(self_name)
names = self_context.goto(self_name, position=self_name.start_pos)
return any(
n.api_type == 'param'
and n.tree_name.get_definition().position_index == 0
and n.parent_context.tree_node is self._parser_scope
for n in names
)
def _convert_names(self, names):
return [SelfName(self._instance, self._node_context, name) for name in names]
+6 -5
View File
@@ -194,17 +194,18 @@ class Sequence(LazyAttributeOverwrite, IterableMixin):
return (self.merge_types_of_iterate().py__class__(),)
def _get_wrapped_value(self):
from jedi.inference.gradual.typing import GenericClass
from jedi.inference.gradual.base import GenericClass
from jedi.inference.gradual.generics import TupleGenericManager
klass = compiled.builtin_from_name(self.inference_state, self.array_type)
c, = GenericClass(klass, self._get_generics()).execute_annotation()
c, = GenericClass(
klass,
TupleGenericManager(self._get_generics())
).execute_annotation()
return c
def py__bool__(self):
return None # We don't know the length, because of appends.
def py__class__(self):
return compiled.builtin_from_name(self.inference_state, self.array_type)
@safe_property
def parent(self):
return self.inference_state.builtins_module
+13 -23
View File
@@ -25,7 +25,7 @@ py__iter__() Returns a generator of a set of types.
py__class__() Returns the class of an instance.
py__simple_getitem__(index: int/str) Returns a a set of types of the index.
Can raise an IndexError/KeyError.
py__getitem__(indexes: ValueSet) Returns a a set of types of the index.
py__getitem__(indexes: ValueSet) Returns a a set of types of the index.
py__file__() Only on modules. Returns None if does
not exist.
py__package__() -> List[str] Only on modules. For the import system.
@@ -50,23 +50,13 @@ from jedi.inference.base_value import ValueSet, iterator_to_value_set, \
NO_VALUES
from jedi.inference.context import ClassContext
from jedi.inference.value.function import FunctionAndClassBase
from jedi.inference.gradual.generics import LazyGenericManager, TupleGenericManager
from jedi.plugins import plugin_manager
def apply_py__get__(value, instance, class_value):
try:
method = value.py__get__
except AttributeError:
yield value
else:
for descriptor_value in method(instance, class_value):
yield descriptor_value
class ClassName(TreeNameDefinition):
def __init__(self, class_value, tree_name, name_context, apply_decorators):
super(ClassName, self).__init__(class_value.as_context(), tree_name)
self._name_context = name_context
super(ClassName, self).__init__(name_context, tree_name)
self._apply_decorators = apply_decorators
self._class_value = class_value
@@ -75,13 +65,11 @@ class ClassName(TreeNameDefinition):
# We're using a different value to infer, so we cannot call super().
from jedi.inference.syntax_tree import tree_name_to_values
inferred = tree_name_to_values(
self.parent_context.inference_state, self._name_context, self.tree_name)
self.parent_context.inference_state, self.parent_context, self.tree_name)
for result_value in inferred:
if self._apply_decorators:
for c in apply_py__get__(result_value,
instance=None,
class_value=self._class_value):
for c in result_value.py__get__(instance=None, class_value=self._class_value):
yield c
else:
yield result_value
@@ -278,20 +266,22 @@ class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase
)]
def py__getitem__(self, index_value_set, contextualized_node):
from jedi.inference.gradual.typing import LazyGenericClass
from jedi.inference.gradual.base import GenericClass
if not index_value_set:
return ValueSet([self])
return ValueSet(
LazyGenericClass(
GenericClass(
self,
index_value,
value_of_index=contextualized_node.context,
LazyGenericManager(
context_of_index=contextualized_node.context,
index_value=index_value,
)
)
for index_value in index_value_set
)
def define_generics(self, type_var_dict):
from jedi.inference.gradual.typing import GenericClass
from jedi.inference.gradual.base import GenericClass
def remap_type_vars():
"""
@@ -309,7 +299,7 @@ class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase
if type_var_dict:
return ValueSet([GenericClass(
self,
generics=tuple(remap_type_vars())
TupleGenericManager(tuple(remap_type_vars()))
)])
return ValueSet({self})
+13 -20
View File
@@ -3,7 +3,7 @@ import os
from jedi import debug
from jedi.inference.cache import inference_state_method_cache
from jedi.inference.names import ValueNameMixin, AbstractNameDefinition
from jedi.inference.names import AbstractNameDefinition, ModuleName
from jedi.inference.filters import GlobalNameFilter, ParserTreeFilter, DictFilter, MergedFilter
from jedi.inference import compiled
from jedi.inference.base_value import TreeValue
@@ -37,18 +37,6 @@ class _ModuleAttributeName(AbstractNameDefinition):
return compiled.get_string_value_set(self.parent_context.inference_state)
class ModuleName(ValueNameMixin, AbstractNameDefinition):
start_pos = 1, 0
def __init__(self, value, name):
self._value = value
self._name = name
@property
def string_name(self):
return self._name
def iter_module_names(inference_state, paths):
# Python modules/packages
for n in inference_state.compiled_subprocess.list_module_names(paths):
@@ -83,11 +71,11 @@ class SubModuleDictMixin(object):
package).
"""
names = {}
if self.is_package:
if self.is_package():
mods = iter_module_names(self.inference_state, self.py__path__())
for name in mods:
# It's obviously a relative import to the current module.
names[name] = SubModuleName(self, name)
names[name] = SubModuleName(self.as_context(), name)
# In the case of an import like `from x.` we don't need to
# add all the variables, this is only about submodules.
@@ -95,13 +83,15 @@ class SubModuleDictMixin(object):
class ModuleMixin(SubModuleDictMixin):
_module_name_class = ModuleName
def get_filters(self, origin_scope=None):
yield MergedFilter(
ParserTreeFilter(
parent_context=self.as_context(),
origin_scope=origin_scope
),
GlobalNameFilter(self, self.tree_node),
GlobalNameFilter(self.as_context(), self.tree_node),
)
yield DictFilter(self.sub_modules_dict())
yield DictFilter(self._module_attributes_dict())
@@ -121,7 +111,7 @@ class ModuleMixin(SubModuleDictMixin):
@property
@inference_state_method_cache()
def name(self):
return ModuleName(self, self._string_name)
return self._module_name_class(self, self._string_name)
@property
def _string_name(self):
@@ -200,7 +190,7 @@ class ModuleValue(ModuleMixin, TreeValue):
self._path = file_io.path
self.string_names = string_names # Optional[Tuple[str, ...]]
self.code_lines = code_lines
self.is_package = is_package
self._is_package = is_package
def is_stub(self):
if self._path is not None and self._path.endswith('.pyi'):
@@ -224,8 +214,11 @@ class ModuleValue(ModuleMixin, TreeValue):
return os.path.abspath(self._path)
def is_package(self):
return self._is_package
def py__package__(self):
if self.is_package:
if self._is_package:
return self.string_names
return self.string_names[:-1]
@@ -235,7 +228,7 @@ class ModuleValue(ModuleMixin, TreeValue):
is a list of paths (strings).
Returns None if the module is not a package.
"""
if not self.is_package:
if not self._is_package:
return None
# A namespace package is typically auto generated and ~10 lines long.
-1
View File
@@ -61,7 +61,6 @@ class ImplicitNamespaceValue(Value, SubModuleDictMixin):
def is_stub(self):
return False
@property
def is_package(self):
return True