mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 05:54:25 +08:00
Fix ClassVar filter for instances
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user