diff --git a/.gitignore b/.gitignore index bdb15c94..bd82fbba 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ record.json /.pytest_cache /.mypy_cache /venv/ +.nvimrc +poetry.lock +pyproject.toml diff --git a/jedi/inference/finder.py b/jedi/inference/finder.py index aac58732..a162fe21 100644 --- a/jedi/inference/finder.py +++ b/jedi/inference/finder.py @@ -32,8 +32,8 @@ def filter_name(filters, name_or_str): """ string_name = name_or_str.value if isinstance(name_or_str, Name) else name_or_str names = [] - for filter in filters: - names = filter.get(string_name) + for filter_ in filters: + names = filter_.get(string_name) if names: break diff --git a/jedi/inference/value/instance.py b/jedi/inference/value/instance.py index 63f220e0..57f46de5 100644 --- a/jedi/inference/value/instance.py +++ b/jedi/inference/value/instance.py @@ -1,6 +1,7 @@ from abc import abstractproperty from parso.tree import search_ancestor +from parso.python.tree import Name from jedi import debug from jedi import settings @@ -231,6 +232,8 @@ class _BaseTreeInstance(AbstractInstanceValue): func_node = new new = search_ancestor(new, 'funcdef', 'classdef') if class_context.tree_node is new: + if isinstance(func_node, Name): + func_node = new func = FunctionValue.from_context(class_context, func_node) bound_method = BoundMethod(self, class_context, func) if func_node.name.value == '__init__': @@ -492,7 +495,9 @@ class SelfName(TreeNameDefinition): @property def parent_context(self): - return self._instance.create_instance_context(self.class_context, self.tree_name) + _instance = self._instance + _context = _instance.create_instance_context(self.class_context, self.tree_name) + return _context def get_defining_qualified_value(self): return self._instance @@ -502,9 +507,9 @@ class SelfName(TreeNameDefinition): if stmt is not None: if stmt.children[1].type == "annassign": from jedi.inference.gradual.annotation import infer_annotation - values = infer_annotation( - self.parent_context, stmt.children[1].children[1] - ).execute_annotation() + _parent_context = self.parent_context + _infer = infer_annotation(_parent_context, stmt.children[1].children[1]) + values = _infer.execute_annotation() if values: return values return super().infer() @@ -582,6 +587,11 @@ class SelfAttributeFilter(ClassFilter): # filter. if self._is_in_right_scope(trailer.parent.children[0], name): yield name + elif trailer.type == "expr_stmt" \ + and len(trailer.parent.children) == 2: + if name.is_definition() and self._access_possible(name): + if trailer.children[1].type == "annassign": + yield name def _is_in_right_scope(self, self_name, name): self_context = self._node_context.create_context(self_name) diff --git a/test/completion/pep0526_variables.py b/test/completion/pep0526_variables.py index e50ebe57..59085118 100644 --- a/test/completion/pep0526_variables.py +++ b/test/completion/pep0526_variables.py @@ -58,6 +58,7 @@ class VarClass: var_instance2: float var_class1: typing.ClassVar[str] = 1 var_class2: typing.ClassVar[bytes] + var_class3 = None def __init__(self): #? int() @@ -70,11 +71,17 @@ class VarClass: d.var_class2 #? [] d.int - #? ['var_class1', 'var_class2', 'var_instance1', 'var_instance2'] + #? ['var_class1', 'var_class2', 'var_instance1', 'var_instance2', 'var_class3'] self.var_ +class VarClass2(VarClass): + var_class3: typing.ClassVar[int] -#? ['var_class1', 'var_class2', 'var_instance1'] + def __init__(self): + #? int() + self.var_class3 + +#? ['var_class1', 'var_class2', 'var_instance1', 'var_class3'] VarClass.var_ #? int() VarClass.var_instance1 @@ -88,7 +95,7 @@ VarClass.var_class2 VarClass.int d = VarClass() -#? ['var_class1', 'var_class2', 'var_instance1', 'var_instance2'] +#? ['var_class1', 'var_class2', 'var_class3', 'var_instance1', 'var_instance2'] d.var_ #? int() d.var_instance1 diff --git a/test/test_api/test_completion.py b/test/test_api/test_completion.py index de46223e..7762bc78 100644 --- a/test/test_api/test_completion.py +++ b/test/test_api/test_completion.py @@ -41,6 +41,26 @@ def test_in_empty_space(Script): assert def_.name == 'X' +def test_classvar_completion(Script): + code = dedent('''\ + from typing import ClassVar # 1 + class Foo: # 2 + var_class = None # 3 + def __init__(self, var_class=None): # 4 + self.var_class = var_class # 5 + class Bar(Foo): # 6 + var_class: ClassVar[int] # 7 + + def __init__(self): # 9 + self.var_class. + int(). + ''') + expected_value = set(completion.name for completion in Script(code).complete(11, 14)) + for completion in Script(code).complete(10, 23): + expected_value.remove(completion.name) + assert len(expected_value) == 0 + + def test_indent_value(Script): """ If an INDENT is the next supposed token, we should still be able to