Fix ClassVar filter for instances

This commit is contained in:
Dave Halter
2019-06-26 22:56:30 +02:00
parent fafd6b2ac6
commit a9ff58683e
5 changed files with 74 additions and 15 deletions

View File

@@ -462,12 +462,13 @@ class InstanceClassFilter(AbstractFilter):
origin_scope=origin_scope,
is_instance=True,
))
assert isinstance(self._class_filter, ClassFilter), self._class_filter
def get(self, name):
return self._convert(self._class_filter.get(name))
return self._convert(self._class_filter.get(name, from_instance=True))
def values(self):
return self._convert(self._class_filter.values())
return self._convert(self._class_filter.values(from_instance=True))
def _convert(self, names):
return [LazyInstanceClassName(self._instance, self._class_context, n) for n in names]

View File

@@ -109,13 +109,27 @@ class ClassFilter(ParserTreeFilter):
node = get_cached_parent_scope(self._used_names, node)
return False
def _access_possible(self, name):
def _access_possible(self, name, from_instance):
# Filter for ClassVar variables
# TODO this is not properly done, yet. It just checks for the string
# ClassVar in the annotation, which can be quite imprecise. If we
# wanted to do this correct, we would have to resolve the ClassVar.
if not from_instance:
expr_stmt = name.get_definition()
if expr_stmt is not None and expr_stmt.type == 'expr_stmt':
annassign = expr_stmt.children[1]
if annassign.type == 'annassign':
# TODO this is not proper matching
if 'ClassVar' not in annassign.children[1].get_code():
return False
# Filter for name mangling of private variables like __foo
return not name.value.startswith('__') or name.value.endswith('__') \
or self._equals_origin_scope()
def _filter(self, names):
def _filter(self, names, from_instance=False):
names = super(ClassFilter, self)._filter(names)
return [name for name in names if self._access_possible(name)]
return [name for name in names if self._access_possible(name, from_instance)]
class ClassMixin(object):

View File

@@ -74,20 +74,22 @@ class AbstractUsedNamesFilter(AbstractFilter):
self._used_names = self._module_node.get_used_names()
self.context = context
def get(self, name):
def get(self, name, **filter_kwargs):
return self._convert_names(self._filter(
_get_definition_names(self._used_names, name)
_get_definition_names(self._used_names, name),
**filter_kwargs,
))
def _convert_names(self, names):
return [self.name_class(self.context, name) for name in names]
def values(self):
def values(self, **filter_kwargs):
return self._convert_names(
name
for name_key in self._used_names
for name in self._filter(
_get_definition_names(self._used_names, name_key)
_get_definition_names(self._used_names, name_key),
**filter_kwargs,
)
)

View File

@@ -19,7 +19,7 @@ from jedi.evaluate.filters import FilterWrapper
from jedi.evaluate.names import NameWrapper, AbstractTreeName, \
AbstractNameDefinition, ContextName
from jedi.evaluate.helpers import is_string
from jedi.evaluate.context.klass import ClassMixin
from jedi.evaluate.context.klass import ClassMixin, ClassFilter
_PROXY_CLASS_TYPES = 'Tuple Generic Protocol Callable Type'.split()
_TYPE_ALIAS_TYPES = {
@@ -54,12 +54,15 @@ class _BaseTypingContext(Context):
return self._tree_name
def get_filters(self, *args, **kwargs):
# TODO this is obviously wrong.
class EmptyFilter():
def get(self, name):
# TODO this is obviously wrong. Is it though?
class EmptyFilter(ClassFilter):
def __init__(self):
pass
def get(self, name, **kwargs):
return []
def values(self):
def values(self, **kwargs):
return []
yield EmptyFilter()

View File

@@ -295,7 +295,6 @@ import typing as t
def union2(x: t.Union[int, str]):
#? int() str()
x
from typing import Union
def union3(x: Union[int, str]):
#? int() str()
@@ -398,3 +397,43 @@ def cast_tests():
#? str()
cast_tests()
# -------------------------
# instance/class vars
# -------------------------
# python >= 3.6
class VarClass:
var_instance1: int = 1
var_instance2: float
var_class1: typing.ClassVar[str] = 1
var_class2: typing.ClassVar[bytes]
#? ['var_class1', 'var_class2']
VarClass.var_
#?
VarClass.var_instance1
#?
VarClass.var_instance2
#? str()
VarClass.var_class1
#? bytes()
VarClass.var_class2
#? []
VarClass.int
d = VarClass()
#? ['var_class1', 'var_class2', 'var_instance1', 'var_instance2']
d.var_
#? int()
d.var_instance1
#? float()
d.var_instance2
#? str()
d.var_class1
#? bytes()
d.var_class2
#? []
d.int