From 12f3eb5fcd4fce8908903ef9c2352c30280f70d7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 15 Oct 2023 16:05:28 -0400 Subject: [PATCH] Add `assigned_stmts()` to PEP 695 nodes (#2320) (#2321) (cherry picked from commit d7be465ef2951dc5ea45b495ed31b69e0e8e4017) Co-authored-by: Jacob Walls --- ChangeLog | 3 +++ astroid/nodes/node_classes.py | 15 +++++++++++++++ astroid/protocols.py | 14 ++++++++++++++ tests/test_protocols.py | 29 ++++++++++++++++++++++++++++- 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 00813e9a97..78e559298a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,9 @@ What's New in astroid 3.0.1? ============================ Release date: TBA +* Fix crashes linting code using PEP 695 (Python 3.12) generic type syntax. + + Closes pylint-dev/pylint#9098 What's New in astroid 3.0.0? diff --git a/astroid/nodes/node_classes.py b/astroid/nodes/node_classes.py index 59e4fc8a20..0caef414a1 100644 --- a/astroid/nodes/node_classes.py +++ b/astroid/nodes/node_classes.py @@ -3405,6 +3405,11 @@ def _infer( ) -> Iterator[ParamSpec]: yield self + assigned_stmts = protocols.generic_type_assigned_stmts + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + class Pass(_base_nodes.NoChildrenNode, _base_nodes.Statement): """Class representing an :class:`ast.Pass` node. @@ -4153,6 +4158,11 @@ def _infer( ) -> Iterator[TypeVar]: yield self + assigned_stmts = protocols.generic_type_assigned_stmts + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + class TypeVarTuple(_base_nodes.AssignTypeNode): """Class representing a :class:`ast.TypeVarTuple` node. @@ -4192,6 +4202,11 @@ def _infer( ) -> Iterator[TypeVarTuple]: yield self + assigned_stmts = protocols.generic_type_assigned_stmts + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + UNARY_OP_METHOD = { "+": "__pos__", diff --git a/astroid/protocols.py b/astroid/protocols.py index e69ab5d6da..02e2111101 100644 --- a/astroid/protocols.py +++ b/astroid/protocols.py @@ -915,3 +915,17 @@ def match_as_assigned_stmts( and self.pattern is None ): yield self.parent.parent.subject + + +@decorators.yes_if_nothing_inferred +def generic_type_assigned_stmts( + self: nodes.TypeVar | nodes.TypeVarTuple | nodes.ParamSpec, + node: nodes.AssignName, + context: InferenceContext | None = None, + assign_path: None = None, +) -> Generator[nodes.NodeNG, None, None]: + """Return empty generator (return -> raises StopIteration) so inferred value + is Uninferable. + """ + return + yield diff --git a/tests/test_protocols.py b/tests/test_protocols.py index 8c318252b3..3466609cf1 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -13,7 +13,7 @@ import astroid from astroid import extract_node, nodes -from astroid.const import PY310_PLUS +from astroid.const import PY310_PLUS, PY312_PLUS from astroid.exceptions import InferenceError from astroid.manager import AstroidManager from astroid.util import Uninferable, UninferableBase @@ -415,3 +415,30 @@ def test_assigned_stmts_match_as(): assert match_as.name assigned_match_as = next(match_as.name.assigned_stmts()) assert assigned_match_as == subject + + +@pytest.mark.skipif(not PY312_PLUS, reason="Generic typing syntax requires python 3.12") +class TestGenericTypeSyntax: + @staticmethod + def test_assigned_stmts_type_var(): + """The result is 'Uninferable' and no exception is raised.""" + assign_stmts = extract_node("type Point[T] = tuple[float, float]") + type_var: nodes.TypeVar = assign_stmts.type_params[0] + assigned = next(type_var.name.assigned_stmts()) + assert assigned is Uninferable + + @staticmethod + def test_assigned_stmts_type_var_tuple(): + """The result is 'Uninferable' and no exception is raised.""" + assign_stmts = extract_node("type Alias[*Ts] = tuple[*Ts]") + type_var_tuple: nodes.TypeVarTuple = assign_stmts.type_params[0] + assigned = next(type_var_tuple.name.assigned_stmts()) + assert assigned is Uninferable + + @staticmethod + def test_assigned_stmts_param_spec(): + """The result is 'Uninferable' and no exception is raised.""" + assign_stmts = extract_node("type Alias[**P] = Callable[P, int]") + param_spec: nodes.ParamSpec = assign_stmts.type_params[0] + assigned = next(param_spec.name.assigned_stmts()) + assert assigned is Uninferable