From 3b0dd533c94c0cce55058c40beb7120ad652ac37 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 22 Feb 2024 22:22:55 -0500 Subject: [PATCH] Respect runtime-required decorators for function signatures --- .../src/checkers/ast/annotation.rs | 19 ++++++ crates/ruff_linter/src/checkers/ast/mod.rs | 65 +++++++++++++------ ...ort_runtime_evaluated_decorators_3.py.snap | 27 -------- 3 files changed, 65 insertions(+), 46 deletions(-) diff --git a/crates/ruff_linter/src/checkers/ast/annotation.rs b/crates/ruff_linter/src/checkers/ast/annotation.rs index 753fd3f30e93f1..d1b15ad5ac292e 100644 --- a/crates/ruff_linter/src/checkers/ast/annotation.rs +++ b/crates/ruff_linter/src/checkers/ast/annotation.rs @@ -1,3 +1,4 @@ +use ruff_python_ast::StmtFunctionDef; use ruff_python_semantic::{ScopeKind, SemanticModel}; use crate::rules::flake8_type_checking; @@ -71,4 +72,22 @@ impl AnnotationContext { Self::TypingOnly } + + pub(super) fn from_function( + function_def: &StmtFunctionDef, + semantic: &SemanticModel, + settings: &LinterSettings, + ) -> Self { + if flake8_type_checking::helpers::runtime_required_function( + function_def, + &settings.flake8_type_checking.runtime_required_decorators, + semantic, + ) { + Self::RuntimeRequired + } else if semantic.future_annotations() { + Self::TypingOnly + } else { + Self::RuntimeEvaluated + } + } } diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index df79cf9f087ed5..c8753bd961a6ab 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -584,7 +584,8 @@ where // Function annotations are always evaluated at runtime, unless future annotations // are enabled. - let runtime_annotation = !self.semantic.future_annotations(); + let annotation = + AnnotationContext::from_function(function_def, &self.semantic, self.settings); // The first parameter may be a single dispatch. let mut singledispatch = @@ -608,10 +609,18 @@ where if let Some(expr) = ¶meter_with_default.parameter.annotation { if singledispatch { self.visit_runtime_required_annotation(expr); - } else if runtime_annotation { - self.visit_runtime_evaluated_annotation(expr); } else { - self.visit_annotation(expr); + match annotation { + AnnotationContext::RuntimeRequired => { + self.visit_runtime_required_annotation(expr); + } + AnnotationContext::RuntimeEvaluated => { + self.visit_runtime_evaluated_annotation(expr); + } + AnnotationContext::TypingOnly => { + self.visit_annotation(expr); + } + } }; } if let Some(expr) = ¶meter_with_default.default { @@ -621,28 +630,46 @@ where } if let Some(arg) = ¶meters.vararg { if let Some(expr) = &arg.annotation { - if runtime_annotation { - self.visit_runtime_evaluated_annotation(expr); - } else { - self.visit_annotation(expr); - }; + match annotation { + AnnotationContext::RuntimeRequired => { + self.visit_runtime_required_annotation(expr); + } + AnnotationContext::RuntimeEvaluated => { + self.visit_runtime_evaluated_annotation(expr); + } + AnnotationContext::TypingOnly => { + self.visit_annotation(expr); + } + } } } if let Some(arg) = ¶meters.kwarg { if let Some(expr) = &arg.annotation { - if runtime_annotation { - self.visit_runtime_evaluated_annotation(expr); - } else { - self.visit_annotation(expr); - }; + match annotation { + AnnotationContext::RuntimeRequired => { + self.visit_runtime_required_annotation(expr); + } + AnnotationContext::RuntimeEvaluated => { + self.visit_runtime_evaluated_annotation(expr); + } + AnnotationContext::TypingOnly => { + self.visit_annotation(expr); + } + } } } for expr in returns { - if runtime_annotation { - self.visit_runtime_evaluated_annotation(expr); - } else { - self.visit_annotation(expr); - }; + match annotation { + AnnotationContext::RuntimeRequired => { + self.visit_runtime_required_annotation(expr); + } + AnnotationContext::RuntimeEvaluated => { + self.visit_runtime_evaluated_annotation(expr); + } + AnnotationContext::TypingOnly => { + self.visit_annotation(expr); + } + } } let definition = docstrings::extraction::extract_definition( diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-standard-library-import_runtime_evaluated_decorators_3.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-standard-library-import_runtime_evaluated_decorators_3.py.snap index fc68794213d2e2..618928099681a6 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-standard-library-import_runtime_evaluated_decorators_3.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-standard-library-import_runtime_evaluated_decorators_3.py.snap @@ -30,31 +30,4 @@ runtime_evaluated_decorators_3.py:6:18: TCH003 [*] Move standard library import 13 16 | 14 17 | @attrs.define(auto_attribs=True) -runtime_evaluated_decorators_3.py:7:29: TCH003 [*] Move standard library import `collections.abc.Sequence` into a type-checking block - | -5 | from dataclasses import dataclass -6 | from uuid import UUID # TCH003 -7 | from collections.abc import Sequence - | ^^^^^^^^ TCH003 -8 | from pydantic import validate_call - | - = help: Move into type-checking block - -ℹ Unsafe fix -4 4 | from array import array -5 5 | from dataclasses import dataclass -6 6 | from uuid import UUID # TCH003 -7 |-from collections.abc import Sequence -8 7 | from pydantic import validate_call -9 8 | -10 9 | import attrs -11 10 | from attrs import frozen - 11 |+from typing import TYPE_CHECKING - 12 |+ - 13 |+if TYPE_CHECKING: - 14 |+ from collections.abc import Sequence -12 15 | -13 16 | -14 17 | @attrs.define(auto_attribs=True) -