Skip to content

Commit c512e74

Browse files
committed
Fix unsubscriptable-object for generic classes (PEP 695)
1 parent 16da308 commit c512e74

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ Release date: TBA
2222
Refs pylint-dev/#9626
2323
Refs pylint-dev/#9623
2424

25+
* Fix ``unsubscriptable-object`` error for generic classes using the PEP 695 syntax (Python 3.12).
26+
27+
Closes pylint-dev/#9406
28+
2529

2630
What's New in astroid 3.2.1?
2731
============================

astroid/brain/brain_typing.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,28 @@ def infer_typing_attr(
196196
return node.infer(context=ctx)
197197

198198

199+
def _looks_like_generic_class_pep695(node: ClassDef) -> bool:
200+
"""Check if class is uses type parameter. Python 3.12+."""
201+
return len(node.type_params) > 0
202+
203+
204+
def infer_typing_generic_class_pep695(
205+
node: ClassDef, ctx: context.InferenceContext | None = None
206+
) -> Iterator[ClassDef]:
207+
"""Add __class_getitem__ for generic classes. Python 3.12+."""
208+
try:
209+
value = next(node.infer())
210+
except (InferenceError, StopIteration) as exc:
211+
raise UseInferenceDefault from exc
212+
213+
if not (isinstance(value, ClassDef) and value.type_params):
214+
raise UseInferenceDefault
215+
216+
func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
217+
value.locals["__class_getitem__"] = [func_to_add]
218+
return iter([value])
219+
220+
199221
def _looks_like_typedDict( # pylint: disable=invalid-name
200222
node: FunctionDef | ClassDef,
201223
) -> bool:
@@ -490,3 +512,8 @@ def register(manager: AstroidManager) -> None:
490512

491513
if PY312_PLUS:
492514
register_module_extender(manager, "typing", _typing_transform)
515+
manager.register_transform(
516+
ClassDef,
517+
inference_tip(infer_typing_generic_class_pep695),
518+
_looks_like_generic_class_pep695,
519+
)

tests/brain/test_brain.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,18 @@ def test_typing_generic_subscriptable(self):
627627
assert isinstance(inferred, nodes.ClassDef)
628628
assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef)
629629

630+
@test_utils.require_version(minver="3.12")
631+
def test_typing_generic_subscriptable_pep695(self):
632+
"""Test class using type parameters is subscriptable with __class_getitem__ (added in PY312)"""
633+
node = builder.extract_node(
634+
"""
635+
class Foo[T]: ...
636+
"""
637+
)
638+
inferred = next(node.infer())
639+
assert isinstance(inferred, nodes.ClassDef)
640+
assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef)
641+
630642
@test_utils.require_version(minver="3.9")
631643
def test_typing_annotated_subscriptable(self):
632644
"""Test typing.Annotated is subscriptable with __class_getitem__"""

0 commit comments

Comments
 (0)