1
0
forked from VimPlug/jedi

Ignore Final/ClassVar if they don't have a generic assignment

This commit is contained in:
Dave Halter
2026-04-28 17:05:43 +02:00
parent 6473ddc28c
commit 75f1d064d5
4 changed files with 58 additions and 20 deletions
+2 -2
View File
@@ -34,7 +34,7 @@ _TYPE_ALIAS_TYPES = {
'Deque': 'collections.deque', 'Deque': 'collections.deque',
} }
_PROXY_TYPES = ['Optional', 'Union', 'ClassVar', 'Annotated', 'Final'] _PROXY_TYPES = ['Optional', 'Union', 'ClassVar', 'Annotated', 'Final']
_IGNORE_ANNOTATION_PARTS = ['ClassVar', 'Annotated', 'Final'] IGNORE_ANNOTATION_PARTS = ['ClassVar', 'Annotated', 'Final']
class TypingModuleName(NameWrapper): class TypingModuleName(NameWrapper):
@@ -115,7 +115,7 @@ class ProxyWithGenerics(BaseTypingClassWithGenerics):
elif string_name == 'Type': elif string_name == 'Type':
# The type is actually already given in the index_value # The type is actually already given in the index_value
return self._generics_manager[0] return self._generics_manager[0]
elif string_name in _IGNORE_ANNOTATION_PARTS: elif string_name in IGNORE_ANNOTATION_PARTS:
# For now don't do anything here, ClassVars are always used. # For now don't do anything here, ClassVars are always used.
return self._generics_manager[0].execute_annotation() return self._generics_manager[0].execute_annotation()
+12 -3
View File
@@ -30,6 +30,7 @@ from jedi.inference.names import TreeNameDefinition
from jedi.inference.context import CompForContext from jedi.inference.context import CompForContext
from jedi.inference.value.decorator import Decoratee from jedi.inference.value.decorator import Decoratee
from jedi.plugins import plugin_manager from jedi.plugins import plugin_manager
from jedi.inference.gradual.typing import ProxyTypingValue, IGNORE_ANNOTATION_PARTS
operator_to_magic_method = { operator_to_magic_method = {
'+': '__add__', '+': '__add__',
@@ -701,16 +702,24 @@ def tree_name_to_values(inference_state, context, tree_name):
correct_scope = parser_utils.get_parent_scope(name) == context.tree_node correct_scope = parser_utils.get_parent_scope(name) == context.tree_node
ann_assign = expr_stmt.children[1] ann_assign = expr_stmt.children[1]
if correct_scope: if correct_scope:
found_annotation = True
if ( if (
(ann_assign.children[1].type == 'name') (ann_assign.children[1].type == 'name')
and (ann_assign.children[1].value == tree_name.value) and (ann_assign.children[1].value == tree_name.value)
and context.parent_context and context.parent_context
): ):
context = context.parent_context context = context.parent_context
value_set |= annotation.infer_annotation( found = annotation.infer_annotation(
context, expr_stmt.children[1].children[1] context, expr_stmt.children[1].children[1]
).execute_annotation() )
set_found_annotation = True
if len(found) == 1:
first = next(iter(found))
set_found_annotation = not (
isinstance(first, ProxyTypingValue)
and first.name.string_name in IGNORE_ANNOTATION_PARTS
)
found_annotation = set_found_annotation
value_set |= found.execute_annotation()
if found_annotation: if found_annotation:
return value_set return value_set
-12
View File
@@ -555,15 +555,3 @@ def typed_dict_test_foo(arg: Bar):
arg['an_int'] arg['an_int']
#? int() #? int()
arg['another_variable'] arg['another_variable']
# -------------------------
# Final
# -------------------------
x: Final[str] = 1
y: Final = 1
#? str()
x
# TODO
#?
y
+44 -3
View File
@@ -59,6 +59,7 @@ class VarClass:
var_class1: typing.ClassVar[str] = 1 var_class1: typing.ClassVar[str] = 1
var_class2: typing.ClassVar[bytes] var_class2: typing.ClassVar[bytes]
var_class3 = None var_class3 = None
var_class4: typing.ClassVar = ""
def __init__(self): def __init__(self):
#? int() #? int()
@@ -71,7 +72,7 @@ class VarClass:
d.var_class2 d.var_class2
#? [] #? []
d.int d.int
#? ['var_class1', 'var_class2', 'var_instance1', 'var_instance2', 'var_class3'] #? ['var_class1', 'var_class2', 'var_instance1', 'var_instance2', 'var_class3', 'var_class4']
self.var_ self.var_
class VarClass2(VarClass): class VarClass2(VarClass):
@@ -81,7 +82,7 @@ class VarClass2(VarClass):
#? int() #? int()
self.var_class3 self.var_class3
#? ['var_class1', 'var_class2', 'var_instance1', 'var_class3', 'var_instance2'] #? ['var_class1', 'var_class2', 'var_class4', 'var_instance1', 'var_class3', 'var_instance2']
VarClass.var_ VarClass.var_
#? int() #? int()
VarClass.var_instance1 VarClass.var_instance1
@@ -91,11 +92,13 @@ VarClass.var_instance2
VarClass.var_class1 VarClass.var_class1
#? bytes() #? bytes()
VarClass.var_class2 VarClass.var_class2
#? str()
VarClass.var_class4
#? [] #? []
VarClass.int VarClass.int
d = VarClass() d = VarClass()
#? ['var_class1', 'var_class2', 'var_class3', 'var_instance1', 'var_instance2'] #? ['var_class1', 'var_class2', 'var_class3', 'var_class4', 'var_instance1', 'var_instance2']
d.var_ d.var_
#? int() #? int()
d.var_instance1 d.var_instance1
@@ -105,6 +108,8 @@ d.var_instance2
d.var_class1 d.var_class1
#? bytes() #? bytes()
d.var_class2 d.var_class2
#? str()
d.var_class4
#? [] #? []
d.int d.int
@@ -117,3 +122,39 @@ class DC:
#? int() #? int()
DC().name DC().name
# -------------------------
# Final
# -------------------------
# TODO this is wrong, but shouldn't matter that much
#? 0 int()
x: typing.Final[str] = 1
#? 0 int()
y: typing.Final = 1
#? str()
x
#? int()
y
def f(x: typing.Final[str]):
#? str()
x
class C:
x: typing.Final[bytes] = 1
#? 4 str()
y: typing.Final = ""
#? bytes()
x
#? str()
y
#? bytes()
C.x
#? str()
C.y
#? bytes()
C().x
#? str()
C().y