Add autofix for flake8-type-checking (#4742)

This commit is contained in:
Charlie Marsh 2023-05-31 13:53:36 -04:00 committed by GitHub
parent 4bd395a850
commit 1a53996f53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1204 additions and 42 deletions

View file

@ -123,3 +123,88 @@ pub(crate) fn remove_imports<'a>(
Ok(Some(state.to_string()))
}
/// Given an import statement, remove any imports that are not specified in the `imports` slice.
///
/// Returns the modified import statement.
pub(crate) fn retain_imports(
imports: &[&str],
stmt: &Stmt,
locator: &Locator,
stylist: &Stylist,
) -> Result<String> {
let module_text = locator.slice(stmt.range());
let mut tree = match_statement(module_text)?;
let Statement::Simple(body) = &mut tree else {
bail!("Expected Statement::Simple");
};
let (aliases, import_module) = match body.body.first_mut() {
Some(SmallStatement::Import(import_body)) => (&mut import_body.names, None),
Some(SmallStatement::ImportFrom(import_body)) => {
if let ImportNames::Aliases(names) = &mut import_body.names {
(
names,
Some((&import_body.relative, import_body.module.as_ref())),
)
} else {
bail!("Expected: ImportNames::Aliases");
}
}
_ => bail!("Expected: SmallStatement::ImportFrom | SmallStatement::Import"),
};
// Preserve the trailing comma (or not) from the last entry.
let trailing_comma = aliases.last().and_then(|alias| alias.comma.clone());
aliases.retain(|alias| {
imports.iter().any(|import| {
let full_name = match import_module {
Some((relative, module)) => {
let module = module.map(compose_module_path);
let member = compose_module_path(&alias.name);
let mut full_name = String::with_capacity(
relative.len() + module.as_ref().map_or(0, String::len) + member.len() + 1,
);
for _ in 0..relative.len() {
full_name.push('.');
}
if let Some(module) = module {
full_name.push_str(&module);
full_name.push('.');
}
full_name.push_str(&member);
full_name
}
None => compose_module_path(&alias.name),
};
full_name == *import
})
});
// But avoid destroying any trailing comments.
if let Some(alias) = aliases.last_mut() {
let has_comment = if let Some(comma) = &alias.comma {
match &comma.whitespace_after {
ParenthesizableWhitespace::SimpleWhitespace(_) => false,
ParenthesizableWhitespace::ParenthesizedWhitespace(whitespace) => {
whitespace.first_line.comment.is_some()
}
}
} else {
false
};
if !has_comment {
alias.comma = trailing_comma;
}
}
let mut state = CodegenState {
default_newline: &stylist.line_ending(),
default_indent: stylist.indentation(),
..CodegenState::default()
};
tree.codegen(&mut state);
Ok(state.to_string())
}

View file

@ -61,7 +61,7 @@ pub(crate) fn delete_stmt(
}
}
/// Generate a `Fix` to remove any unused imports from an `import` statement.
/// Generate a `Fix` to remove the specified imports from an `import` statement.
pub(crate) fn remove_unused_imports<'a>(
unused_imports: impl Iterator<Item = &'a str>,
stmt: &Stmt,

View file

@ -1,6 +1,4 @@
//! Insert statements into Python code.
#![allow(dead_code)]
use ruff_text_size::TextSize;
use rustpython_parser::ast::{Ranged, Stmt};
use rustpython_parser::{lexer, Mode, Tok};
@ -182,6 +180,7 @@ impl<'a> Insertion<'a> {
},
// Once we've seen the newline, we're looking for the indentation of the block body.
Awaiting::Indent => match tok {
Tok::Comment(..) => {}
Tok::NonLogicalNewline => {}
Tok::Indent => {
// This is like:

View file

@ -7,10 +7,12 @@ use libcst_native::{Codegen, CodegenState, ImportAlias, Name, NameOrAttribute};
use ruff_text_size::TextSize;
use rustpython_parser::ast::{self, Ranged, Stmt, Suite};
use crate::autofix;
use ruff_diagnostics::Edit;
use ruff_python_ast::imports::{AnyImport, Import, ImportFrom};
use ruff_python_ast::source_code::{Locator, Stylist};
use ruff_python_semantic::model::SemanticModel;
use ruff_textwrap::indent;
use crate::cst::matchers::{match_aliases, match_import_from, match_statement};
use crate::importer::insertion::Insertion;
@ -73,6 +75,55 @@ impl<'a> Importer<'a> {
}
}
/// Move an existing import into a `TYPE_CHECKING` block.
///
/// If there are no existing `TYPE_CHECKING` blocks, a new one will be added at the top
/// of the file. Otherwise, it will be added after the most recent top-level
/// `TYPE_CHECKING` block.
pub(crate) fn typing_import_edit(
&self,
import: &StmtImport,
at: TextSize,
semantic_model: &SemanticModel,
) -> Result<TypingImportEdit> {
// Generate the modified import statement.
let content = autofix::codemods::retain_imports(
&[import.full_name],
import.stmt,
self.locator,
self.stylist,
)?;
// Import the `TYPE_CHECKING` symbol from the typing module.
let (type_checking_edit, type_checking) = self.get_or_import_symbol(
&ImportRequest::import_from("typing", "TYPE_CHECKING"),
at,
semantic_model,
)?;
// Add the import to a `TYPE_CHECKING` block.
let add_import_edit = if let Some(block) = self.preceding_type_checking_block(at) {
// Add the import to the `TYPE_CHECKING` block.
self.add_to_type_checking_block(&content, block.start())
} else {
// Add the import to a new `TYPE_CHECKING` block.
self.add_type_checking_block(
&format!(
"{}if {type_checking}:{}{}",
self.stylist.line_ending().as_str(),
self.stylist.line_ending().as_str(),
indent(&content, self.stylist.indentation())
),
at,
)?
};
Ok(TypingImportEdit {
type_checking_edit,
add_import_edit,
})
}
/// Generate an [`Edit`] to reference the given symbol. Returns the [`Edit`] necessary to make
/// the symbol available in the current scope along with the bound name of the symbol.
///
@ -204,14 +255,6 @@ impl<'a> Importer<'a> {
}
}
/// Return the import statement that precedes the given position, if any.
fn preceding_import(&self, at: TextSize) -> Option<&Stmt> {
self.runtime_imports
.partition_point(|stmt| stmt.start() < at)
.checked_sub(1)
.map(|idx| self.runtime_imports[idx])
}
/// Return the top-level [`Stmt`] that imports the given module using `Stmt::ImportFrom`
/// preceding the given position, if any.
fn find_import_from(&self, module: &str, at: TextSize) -> Option<&Stmt> {
@ -258,6 +301,62 @@ impl<'a> Importer<'a> {
statement.codegen(&mut state);
Ok(Edit::range_replacement(state.to_string(), stmt.range()))
}
/// Add a `TYPE_CHECKING` block to the given module.
fn add_type_checking_block(&self, content: &str, at: TextSize) -> Result<Edit> {
let insertion = if let Some(stmt) = self.preceding_import(at) {
// Insert after the last top-level import.
Insertion::end_of_statement(stmt, self.locator, self.stylist)
} else {
// Insert at the start of the file.
Insertion::start_of_file(self.python_ast, self.locator, self.stylist)
};
if insertion.is_inline() {
Err(anyhow::anyhow!(
"Cannot insert `TYPE_CHECKING` block inline"
))
} else {
Ok(insertion.into_edit(content))
}
}
/// Add an import statement to an existing `TYPE_CHECKING` block.
fn add_to_type_checking_block(&self, content: &str, at: TextSize) -> Edit {
Insertion::start_of_block(at, self.locator, self.stylist).into_edit(content)
}
/// Return the import statement that precedes the given position, if any.
fn preceding_import(&self, at: TextSize) -> Option<&'a Stmt> {
self.runtime_imports
.partition_point(|stmt| stmt.start() < at)
.checked_sub(1)
.map(|idx| self.runtime_imports[idx])
}
/// Return the `TYPE_CHECKING` block that precedes the given position, if any.
fn preceding_type_checking_block(&self, at: TextSize) -> Option<&'a Stmt> {
let block = self.type_checking_blocks.first()?;
if block.start() <= at {
Some(block)
} else {
None
}
}
}
/// An edit to an import to a typing-only context.
#[derive(Debug)]
pub(crate) struct TypingImportEdit {
/// The edit to add the `TYPE_CHECKING` symbol to the module.
type_checking_edit: Edit,
/// The edit to add the import to a `TYPE_CHECKING` block.
add_import_edit: Edit,
}
impl TypingImportEdit {
pub(crate) fn into_edits(self) -> Vec<Edit> {
vec![self.type_checking_edit, self.add_import_edit]
}
}
#[derive(Debug)]
@ -301,6 +400,14 @@ impl<'a> ImportRequest<'a> {
}
}
/// An existing module or member import, located within an import statement.
pub(crate) struct StmtImport<'a> {
/// The import statement.
pub(crate) stmt: &'a Stmt,
/// The "full name" of the imported module or member.
pub(crate) full_name: &'a str,
}
/// The result of an [`Importer::get_or_import_symbol`] call.
#[derive(Debug)]
pub(crate) enum ResolutionError {

View file

@ -2,6 +2,7 @@ use std::fmt::{Display, Formatter};
use std::num::NonZeroUsize;
use colored::{Color, ColoredString, Colorize, Styles};
use itertools::Itertools;
use ruff_text_size::{TextRange, TextSize};
use similar::{ChangeTag, TextDiff};
@ -37,7 +38,12 @@ impl Display for Diff<'_> {
let mut output = String::with_capacity(self.source_code.source_text().len());
let mut last_end = TextSize::default();
for edit in self.fix.edits() {
for edit in self
.fix
.edits()
.iter()
.sorted_unstable_by_key(|edit| edit.start())
{
output.push_str(
self.source_code
.slice(TextRange::new(last_end, edit.start())),

View file

@ -11,8 +11,8 @@ mod tests {
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;
use crate::test::test_path;
use crate::registry::{Linter, Rule};
use crate::test::{test_path, test_snippet};
use crate::{assert_messages, settings};
#[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TCH001.py"))]
@ -134,4 +134,159 @@ mod tests {
assert_messages!(snapshot, diagnostics);
Ok(())
}
#[test_case(
r#"
from __future__ import annotations
import pandas as pd
def f(x: pd.DataFrame):
pass
"#,
"no_typing_import"
)]
#[test_case(
r#"
from __future__ import annotations
from typing import TYPE_CHECKING
import pandas as pd
def f(x: pd.DataFrame):
pass
"#,
"typing_import_before_package_import"
)]
#[test_case(
r#"
from __future__ import annotations
import pandas as pd
from typing import TYPE_CHECKING
def f(x: pd.DataFrame):
pass
"#,
"typing_import_after_package_import"
)]
#[test_case(
r#"
from __future__ import annotations
import pandas as pd
def f(x: pd.DataFrame):
pass
from typing import TYPE_CHECKING
"#,
"typing_import_after_usage"
)]
#[test_case(
r#"
from __future__ import annotations
from typing import TYPE_CHECKING
import pandas as pd
if TYPE_CHECKING:
import os
def f(x: pd.DataFrame):
pass
"#,
"type_checking_block_own_line"
)]
#[test_case(
r#"
from __future__ import annotations
from typing import TYPE_CHECKING
import pandas as pd
if TYPE_CHECKING: import os
def f(x: pd.DataFrame):
pass
"#,
"type_checking_block_inline"
)]
#[test_case(
r#"
from __future__ import annotations
from typing import TYPE_CHECKING
import pandas as pd
if TYPE_CHECKING:
# This is a comment.
import os
def f(x: pd.DataFrame):
pass
"#,
"type_checking_block_comment"
)]
#[test_case(
r#"
from __future__ import annotations
from typing import TYPE_CHECKING
import pandas as pd
def f(x: pd.DataFrame):
pass
if TYPE_CHECKING:
import os
"#,
"type_checking_block_after_usage"
)]
#[test_case(
r#"
from __future__ import annotations
from pandas import (
DataFrame, # DataFrame
Series, # Series
)
def f(x: DataFrame):
pass
"#,
"import_from"
)]
#[test_case(
r#"
from __future__ import annotations
from typing import TYPE_CHECKING
from pandas import (
DataFrame, # DataFrame
Series, # Series
)
if TYPE_CHECKING:
import os
def f(x: DataFrame):
pass
"#,
"import_from_type_checking_block"
)]
fn contents(contents: &str, snapshot: &str) {
let diagnostics = test_snippet(
contents,
&settings::Settings::for_rules(&Linter::Flake8TypeChecking),
);
assert_messages!(snapshot, diagnostics);
}
}

View file

@ -1,10 +1,14 @@
use ruff_diagnostics::{Diagnostic, Violation};
use rustpython_parser::ast::Stmt;
use ruff_diagnostics::{AutofixKind, Diagnostic, Fix, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic::binding::{
Binding, BindingKind, FromImportation, Importation, SubmoduleImportation,
};
use crate::autofix;
use crate::checkers::ast::Checker;
use crate::importer::StmtImport;
use crate::registry::AsRule;
use crate::rules::isort::{categorize, ImportSection, ImportType};
@ -49,6 +53,8 @@ pub struct TypingOnlyFirstPartyImport {
}
impl Violation for TypingOnlyFirstPartyImport {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
#[derive_message_formats]
fn message(&self) -> String {
format!(
@ -56,6 +62,10 @@ impl Violation for TypingOnlyFirstPartyImport {
self.full_name
)
}
fn autofix_title(&self) -> Option<String> {
Some("Move into type-checking block".to_string())
}
}
/// ## What it does
@ -99,6 +109,8 @@ pub struct TypingOnlyThirdPartyImport {
}
impl Violation for TypingOnlyThirdPartyImport {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
#[derive_message_formats]
fn message(&self) -> String {
format!(
@ -106,6 +118,10 @@ impl Violation for TypingOnlyThirdPartyImport {
self.full_name
)
}
fn autofix_title(&self) -> Option<String> {
Some("Move into type-checking block".to_string())
}
}
/// ## What it does
@ -149,6 +165,8 @@ pub struct TypingOnlyStandardLibraryImport {
}
impl Violation for TypingOnlyStandardLibraryImport {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
#[derive_message_formats]
fn message(&self) -> String {
format!(
@ -156,6 +174,10 @@ impl Violation for TypingOnlyStandardLibraryImport {
self.full_name
)
}
fn autofix_title(&self) -> Option<String> {
Some("Move into type-checking block".to_string())
}
}
/// Return `true` if `this` is implicitly loaded via importing `that`.
@ -285,6 +307,10 @@ pub(crate) fn typing_only_runtime_import(
return;
}
let Some(reference_id) = binding.references.first() else {
return;
};
if binding.context.is_runtime()
&& binding.is_used()
&& binding.references().all(|reference_id| {
@ -307,7 +333,7 @@ pub(crate) fn typing_only_runtime_import(
.unwrap();
// Categorize the import.
let diagnostic = match categorize(
let mut diagnostic = match categorize(
full_name,
Some(level),
&checker.settings.src,
@ -342,6 +368,43 @@ pub(crate) fn typing_only_runtime_import(
}
};
if checker.patch(diagnostic.kind.rule()) {
diagnostic.try_set_fix(|| {
// Step 1) Remove the import.
// SAFETY: All non-builtin bindings have a source.
let source = binding.source.unwrap();
let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect();
let stmt = checker.semantic_model().stmts[source];
let parent = checker
.semantic_model()
.stmts
.parent_id(source)
.map(|id| checker.semantic_model().stmts[id]);
let remove_import_edit = autofix::edits::remove_unused_imports(
std::iter::once(full_name),
stmt,
parent,
&deleted,
checker.locator,
checker.indexer,
checker.stylist,
)?;
// Step 2) Add the import to a `TYPE_CHECKING` block.
let reference = checker.semantic_model().references.resolve(*reference_id);
let add_import_edit = checker.importer.typing_import_edit(
&StmtImport { stmt, full_name },
reference.range().start(),
checker.semantic_model(),
)?;
Ok(Fix::suggested_edits(
remove_import_edit,
add_import_edit.into_edits(),
))
});
}
if checker.enabled(diagnostic.kind.rule()) {
diagnostics.push(diagnostic);
}

View file

@ -1,7 +1,7 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
exempt_modules.py:14:12: TCH002 Move third-party import `flask` into a type-checking block
exempt_modules.py:14:12: TCH002 [*] Move third-party import `flask` into a type-checking block
|
14 | def f():
15 | import flask
@ -9,5 +9,22 @@ exempt_modules.py:14:12: TCH002 Move third-party import `flask` into a type-chec
16 |
17 | x: flask
|
= help: Move into type-checking block
Suggested fix
1 |+from typing import TYPE_CHECKING
2 |+
3 |+if TYPE_CHECKING:
4 |+ import flask
1 5 | def f():
2 6 | import pandas as pd
3 7 |
--------------------------------------------------------------------------------
11 15 |
12 16 |
13 17 | def f():
14 |- import flask
15 18 |
16 19 | x: flask

View file

@ -0,0 +1,31 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
<filename>:5:5: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
5 | from pandas import (
6 | DataFrame, # DataFrame
| ^^^^^^^^^ TCH002
7 | Series, # Series
8 | )
|
= help: Move into type-checking block
Suggested fix
2 2 | from __future__ import annotations
3 3 |
4 4 | from pandas import (
5 |- DataFrame, # DataFrame
6 5 | Series, # Series
7 6 | )
7 |+from typing import TYPE_CHECKING
8 |+
9 |+if TYPE_CHECKING:
10 |+ from pandas import (
11 |+ DataFrame, # DataFrame
12 |+ )
8 13 |
9 14 | def f(x: DataFrame):
10 15 | pass

View file

@ -0,0 +1,30 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
<filename>:7:5: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
7 | from pandas import (
8 | DataFrame, # DataFrame
| ^^^^^^^^^ TCH002
9 | Series, # Series
10 | )
|
= help: Move into type-checking block
Suggested fix
4 4 | from typing import TYPE_CHECKING
5 5 |
6 6 | from pandas import (
7 |- DataFrame, # DataFrame
8 7 | Series, # Series
9 8 | )
10 9 |
11 10 | if TYPE_CHECKING:
11 |+ from pandas import (
12 |+ DataFrame, # DataFrame
13 |+ )
12 14 | import os
13 15 |
14 16 | def f(x: DataFrame):

View file

@ -0,0 +1,28 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
<filename>:4:8: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
4 | from __future__ import annotations
5 |
6 | import pandas as pd
| ^^^^^^^^^^^^ TCH002
7 |
8 | def f(x: pd.DataFrame):
|
= help: Move into type-checking block
Suggested fix
1 1 |
2 2 | from __future__ import annotations
3 3 |
4 |-import pandas as pd
4 |+from typing import TYPE_CHECKING
5 |+
6 |+if TYPE_CHECKING:
7 |+ import pandas as pd
5 8 |
6 9 | def f(x: pd.DataFrame):
7 10 | pass

View file

@ -1,7 +1,7 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
strict.py:27:21: TCH002 Move third-party import `pkg.A` into a type-checking block
strict.py:27:21: TCH002 [*] Move third-party import `pkg.A` into a type-checking block
|
27 | # In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
28 | import pkg
@ -10,8 +10,27 @@ strict.py:27:21: TCH002 Move third-party import `pkg.A` into a type-checking blo
30 |
31 | def test(value: A):
|
= help: Move into type-checking block
strict.py:35:21: TCH002 Move third-party import `pkg.A` into a type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ from pkg import A
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
24 28 | def f():
25 29 | # In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
26 30 | import pkg
27 |- from pkg import A
28 31 |
29 32 | def test(value: A):
30 33 | return pkg.B()
strict.py:35:21: TCH002 [*] Move third-party import `pkg.A` into a type-checking block
|
35 | def f():
36 | # In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
@ -20,8 +39,28 @@ strict.py:35:21: TCH002 Move third-party import `pkg.A` into a type-checking blo
38 |
39 | def test(value: A):
|
= help: Move into type-checking block
strict.py:54:25: TCH002 Move third-party import `pkg.bar.A` into a type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ from pkg import A
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
32 36 |
33 37 | def f():
34 38 | # In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
35 |- from pkg import A, B
39 |+ from pkg import B
36 40 |
37 41 | def test(value: A):
38 42 | return B()
strict.py:54:25: TCH002 [*] Move third-party import `pkg.bar.A` into a type-checking block
|
54 | # In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
55 | import pkg
@ -30,8 +69,27 @@ strict.py:54:25: TCH002 Move third-party import `pkg.bar.A` into a type-checking
57 |
58 | def test(value: A):
|
= help: Move into type-checking block
strict.py:62:12: TCH002 Move third-party import `pkg` into a type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ from pkg.bar import A
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
51 55 | def f():
52 56 | # In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
53 57 | import pkg
54 |- from pkg.bar import A
55 58 |
56 59 | def test(value: A):
57 60 | return pkg.B()
strict.py:62:12: TCH002 [*] Move third-party import `pkg` into a type-checking block
|
62 | def f():
63 | # In un-strict mode, this shouldn't rase an error, since `pkg.bar` is used at runtime.
@ -39,8 +97,27 @@ strict.py:62:12: TCH002 Move third-party import `pkg` into a type-checking block
| ^^^ TCH002
65 | import pkg.bar as B
|
= help: Move into type-checking block
strict.py:71:12: TCH002 Move third-party import `pkg.foo` into a type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ import pkg
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
59 63 |
60 64 | def f():
61 65 | # In un-strict mode, this shouldn't rase an error, since `pkg.bar` is used at runtime.
62 |- import pkg
63 66 | import pkg.bar as B
64 67 |
65 68 | def test(value: pkg.A):
strict.py:71:12: TCH002 [*] Move third-party import `pkg.foo` into a type-checking block
|
71 | def f():
72 | # In un-strict mode, this shouldn't rase an error, since `pkg.foo.bar` is used at runtime.
@ -48,8 +125,27 @@ strict.py:71:12: TCH002 Move third-party import `pkg.foo` into a type-checking b
| ^^^^^^^^^^^^ TCH002
74 | import pkg.foo.bar as B
|
= help: Move into type-checking block
strict.py:80:12: TCH002 Move third-party import `pkg` into a type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ import pkg.foo as F
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
68 72 |
69 73 | def f():
70 74 | # In un-strict mode, this shouldn't rase an error, since `pkg.foo.bar` is used at runtime.
71 |- import pkg.foo as F
72 75 | import pkg.foo.bar as B
73 76 |
74 77 | def test(value: F.Foo):
strict.py:80:12: TCH002 [*] Move third-party import `pkg` into a type-checking block
|
80 | def f():
81 | # In un-strict mode, this shouldn't rase an error, since `pkg.foo.bar` is used at runtime.
@ -57,8 +153,27 @@ strict.py:80:12: TCH002 Move third-party import `pkg` into a type-checking block
| ^^^ TCH002
83 | import pkg.foo.bar as B
|
= help: Move into type-checking block
strict.py:91:12: TCH002 Move third-party import `pkg` into a type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ import pkg
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
77 81 |
78 82 | def f():
79 83 | # In un-strict mode, this shouldn't rase an error, since `pkg.foo.bar` is used at runtime.
80 |- import pkg
81 84 | import pkg.foo.bar as B
82 85 |
83 86 | def test(value: pkg.A):
strict.py:91:12: TCH002 [*] Move third-party import `pkg` into a type-checking block
|
91 | # Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
92 | # testing the implementation.
@ -66,8 +181,27 @@ strict.py:91:12: TCH002 Move third-party import `pkg` into a type-checking block
| ^^^ TCH002
94 | import pkgfoo.bar as B
|
= help: Move into type-checking block
strict.py:101:12: TCH002 Move third-party import `pkg.foo` into a type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ import pkg
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
88 92 | # In un-strict mode, this _should_ rase an error, since `pkgfoo.bar` is used at runtime.
89 93 | # Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
90 94 | # testing the implementation.
91 |- import pkg
92 95 | import pkgfoo.bar as B
93 96 |
94 97 | def test(value: pkg.A):
strict.py:101:12: TCH002 [*] Move third-party import `pkg.foo` into a type-checking block
|
101 | # In un-strict mode, this shouldn't raise an error, since `pkg.bar` is used at runtime.
102 | import pkg.bar as B
@ -76,5 +210,24 @@ strict.py:101:12: TCH002 Move third-party import `pkg.foo` into a type-checking
104 |
105 | def test(value: F.Foo):
|
= help: Move into type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ import pkg.foo as F
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
98 102 | def f():
99 103 | # In un-strict mode, this shouldn't raise an error, since `pkg.bar` is used at runtime.
100 104 | import pkg.bar as B
101 |- import pkg.foo as F
102 105 |
103 106 | def test(value: F.Foo):
104 107 | return B.Bar()

View file

@ -0,0 +1,28 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
<filename>:6:8: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
6 | from typing import TYPE_CHECKING
7 |
8 | import pandas as pd
| ^^^^^^^^^^^^ TCH002
9 |
10 | def f(x: pd.DataFrame):
|
= help: Move into type-checking block
Suggested fix
3 3 |
4 4 | from typing import TYPE_CHECKING
5 5 |
6 |-import pandas as pd
7 6 |
7 |+if TYPE_CHECKING:
8 |+ import pandas as pd
9 |+
8 10 | def f(x: pd.DataFrame):
9 11 | pass
10 12 |

View file

@ -0,0 +1,28 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
<filename>:6:8: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
6 | from typing import TYPE_CHECKING
7 |
8 | import pandas as pd
| ^^^^^^^^^^^^ TCH002
9 |
10 | if TYPE_CHECKING:
|
= help: Move into type-checking block
Suggested fix
3 3 |
4 4 | from typing import TYPE_CHECKING
5 5 |
6 |-import pandas as pd
7 6 |
8 7 | if TYPE_CHECKING:
9 8 | # This is a comment.
9 |+ import pandas as pd
10 10 | import os
11 11 |
12 12 | def f(x: pd.DataFrame):

View file

@ -0,0 +1,27 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
<filename>:6:8: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
6 | from typing import TYPE_CHECKING
7 |
8 | import pandas as pd
| ^^^^^^^^^^^^ TCH002
9 |
10 | if TYPE_CHECKING: import os
|
= help: Move into type-checking block
Suggested fix
3 3 |
4 4 | from typing import TYPE_CHECKING
5 5 |
6 |-import pandas as pd
7 6 |
8 |-if TYPE_CHECKING: import os
7 |+if TYPE_CHECKING: import pandas as pd; import os
9 8 |
10 9 | def f(x: pd.DataFrame):
11 10 | pass

View file

@ -0,0 +1,27 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
<filename>:6:8: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
6 | from typing import TYPE_CHECKING
7 |
8 | import pandas as pd
| ^^^^^^^^^^^^ TCH002
9 |
10 | if TYPE_CHECKING:
|
= help: Move into type-checking block
Suggested fix
3 3 |
4 4 | from typing import TYPE_CHECKING
5 5 |
6 |-import pandas as pd
7 6 |
8 7 | if TYPE_CHECKING:
8 |+ import pandas as pd
9 9 | import os
10 10 |
11 11 | def f(x: pd.DataFrame):

View file

@ -1,7 +1,7 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
TCH001.py:20:19: TCH001 Move application import `.TYP001` into a type-checking block
TCH001.py:20:19: TCH001 [*] Move application import `.TYP001` into a type-checking block
|
20 | def f():
21 | from . import TYP001
@ -9,5 +9,26 @@ TCH001.py:20:19: TCH001 Move application import `.TYP001` into a type-checking b
22 |
23 | x: TYP001
|
= help: Move into type-checking block
Suggested fix
2 2 |
3 3 | For typing-only import detection tests, see `TCH002.py`.
4 4 | """
5 |+from typing import TYPE_CHECKING
6 |+
7 |+if TYPE_CHECKING:
8 |+ from . import TYP001
5 9 |
6 10 |
7 11 | def f():
--------------------------------------------------------------------------------
17 21 |
18 22 |
19 23 | def f():
20 |- from . import TYP001
21 24 |
22 25 | x: TYP001
23 26 |

View file

@ -1,7 +1,7 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
TCH003.py:8:12: TCH003 Move standard library import `os` into a type-checking block
TCH003.py:8:12: TCH003 [*] Move standard library import `os` into a type-checking block
|
8 | def f():
9 | import os
@ -9,5 +9,22 @@ TCH003.py:8:12: TCH003 Move standard library import `os` into a type-checking bl
10 |
11 | x: os
|
= help: Move into type-checking block
Suggested fix
2 2 |
3 3 | For typing-only import detection tests, see `TCH002.py`.
4 4 | """
5 |+from typing import TYPE_CHECKING
6 |+
7 |+if TYPE_CHECKING:
8 |+ import os
5 9 |
6 10 |
7 11 | def f():
8 |- import os
9 12 |
10 13 | x: os
11 14 |

View file

@ -1,7 +1,7 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
runtime_evaluated_base_classes_3.py:5:18: TCH003 Move standard library import `uuid.UUID` into a type-checking block
runtime_evaluated_base_classes_3.py:5:18: TCH003 [*] Move standard library import `uuid.UUID` into a type-checking block
|
5 | import datetime
6 | import pathlib
@ -10,5 +10,22 @@ runtime_evaluated_base_classes_3.py:5:18: TCH003 Move standard library import `u
8 |
9 | import pydantic
|
= help: Move into type-checking block
Suggested fix
2 2 |
3 3 | import datetime
4 4 | import pathlib
5 |-from uuid import UUID # TCH003
6 5 |
7 6 | import pydantic
8 7 | from pydantic import BaseModel
8 |+from typing import TYPE_CHECKING
9 |+
10 |+if TYPE_CHECKING:
11 |+ from uuid import UUID
9 12 |
10 13 |
11 14 | class A(pydantic.BaseModel):

View file

@ -1,7 +1,7 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
runtime_evaluated_decorators_3.py:6:18: TCH003 Move standard library import `uuid.UUID` into a type-checking block
runtime_evaluated_decorators_3.py:6:18: TCH003 [*] Move standard library import `uuid.UUID` into a type-checking block
|
6 | from array import array
7 | from dataclasses import dataclass
@ -10,5 +10,22 @@ runtime_evaluated_decorators_3.py:6:18: TCH003 Move standard library import `uui
9 |
10 | import attrs
|
= help: Move into type-checking block
Suggested fix
3 3 | import datetime
4 4 | from array import array
5 5 | from dataclasses import dataclass
6 |-from uuid import UUID # TCH003
7 6 |
8 7 | import attrs
9 8 | from attrs import frozen
9 |+from typing import TYPE_CHECKING
10 |+
11 |+if TYPE_CHECKING:
12 |+ from uuid import UUID
10 13 |
11 14 |
12 15 | @attrs.define(auto_attribs=True)

View file

@ -1,7 +1,7 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
TCH002.py:5:12: TCH002 Move third-party import `pandas` into a type-checking block
TCH002.py:5:12: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
5 | def f():
6 | import pandas as pd # TCH002
@ -9,8 +9,23 @@ TCH002.py:5:12: TCH002 Move third-party import `pandas` into a type-checking blo
7 |
8 | x: pd.DataFrame
|
= help: Move into type-checking block
TCH002.py:11:24: TCH002 Move third-party import `pandas.DataFrame` into a type-checking block
Suggested fix
1 1 | """Tests to determine accurate detection of typing-only imports."""
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ import pandas as pd
2 6 |
3 7 |
4 8 | def f():
5 |- import pandas as pd # TCH002
6 9 |
7 10 | x: pd.DataFrame
8 11 |
TCH002.py:11:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
11 | def f():
12 | from pandas import DataFrame # TCH002
@ -18,8 +33,27 @@ TCH002.py:11:24: TCH002 Move third-party import `pandas.DataFrame` into a type-c
13 |
14 | x: DataFrame
|
= help: Move into type-checking block
TCH002.py:17:24: TCH002 Move third-party import `pandas.DataFrame` into a type-checking block
Suggested fix
1 1 | """Tests to determine accurate detection of typing-only imports."""
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ from pandas import DataFrame
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
8 12 |
9 13 |
10 14 | def f():
11 |- from pandas import DataFrame # TCH002
12 15 |
13 16 | x: DataFrame
14 17 |
TCH002.py:17:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
17 | def f():
18 | from pandas import DataFrame as df # TCH002
@ -27,8 +61,27 @@ TCH002.py:17:24: TCH002 Move third-party import `pandas.DataFrame` into a type-c
19 |
20 | x: df
|
= help: Move into type-checking block
TCH002.py:23:12: TCH002 Move third-party import `pandas` into a type-checking block
Suggested fix
1 1 | """Tests to determine accurate detection of typing-only imports."""
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ from pandas import DataFrame as df
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
14 18 |
15 19 |
16 20 | def f():
17 |- from pandas import DataFrame as df # TCH002
18 21 |
19 22 | x: df
20 23 |
TCH002.py:23:12: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
23 | def f():
24 | import pandas as pd # TCH002
@ -36,8 +89,27 @@ TCH002.py:23:12: TCH002 Move third-party import `pandas` into a type-checking bl
25 |
26 | x: pd.DataFrame = 1
|
= help: Move into type-checking block
TCH002.py:29:24: TCH002 Move third-party import `pandas.DataFrame` into a type-checking block
Suggested fix
1 1 | """Tests to determine accurate detection of typing-only imports."""
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ import pandas as pd
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
20 24 |
21 25 |
22 26 | def f():
23 |- import pandas as pd # TCH002
24 27 |
25 28 | x: pd.DataFrame = 1
26 29 |
TCH002.py:29:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
29 | def f():
30 | from pandas import DataFrame # TCH002
@ -45,8 +117,27 @@ TCH002.py:29:24: TCH002 Move third-party import `pandas.DataFrame` into a type-c
31 |
32 | x: DataFrame = 2
|
= help: Move into type-checking block
TCH002.py:35:24: TCH002 Move third-party import `pandas.DataFrame` into a type-checking block
Suggested fix
1 1 | """Tests to determine accurate detection of typing-only imports."""
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ from pandas import DataFrame
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
26 30 |
27 31 |
28 32 | def f():
29 |- from pandas import DataFrame # TCH002
30 33 |
31 34 | x: DataFrame = 2
32 35 |
TCH002.py:35:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
|
35 | def f():
36 | from pandas import DataFrame as df # TCH002
@ -54,8 +145,27 @@ TCH002.py:35:24: TCH002 Move third-party import `pandas.DataFrame` into a type-c
37 |
38 | x: df = 3
|
= help: Move into type-checking block
TCH002.py:41:12: TCH002 Move third-party import `pandas` into a type-checking block
Suggested fix
1 1 | """Tests to determine accurate detection of typing-only imports."""
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ from pandas import DataFrame as df
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
32 36 |
33 37 |
34 38 | def f():
35 |- from pandas import DataFrame as df # TCH002
36 39 |
37 40 | x: df = 3
38 41 |
TCH002.py:41:12: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
41 | def f():
42 | import pandas as pd # TCH002
@ -63,8 +173,27 @@ TCH002.py:41:12: TCH002 Move third-party import `pandas` into a type-checking bl
43 |
44 | x: "pd.DataFrame" = 1
|
= help: Move into type-checking block
TCH002.py:47:12: TCH002 Move third-party import `pandas` into a type-checking block
Suggested fix
1 1 | """Tests to determine accurate detection of typing-only imports."""
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ import pandas as pd
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
38 42 |
39 43 |
40 44 | def f():
41 |- import pandas as pd # TCH002
42 45 |
43 46 | x: "pd.DataFrame" = 1
44 47 |
TCH002.py:47:12: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
47 | def f():
48 | import pandas as pd # TCH002
@ -72,5 +201,24 @@ TCH002.py:47:12: TCH002 Move third-party import `pandas` into a type-checking bl
49 |
50 | x = dict["pd.DataFrame", "pd.DataFrame"]
|
= help: Move into type-checking block
Suggested fix
1 1 | """Tests to determine accurate detection of typing-only imports."""
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ import pandas as pd
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
44 48 |
45 49 |
46 50 | def f():
47 |- import pandas as pd # TCH002
48 51 |
49 52 | x = dict["pd.DataFrame", "pd.DataFrame"]
50 53 |

View file

@ -1,7 +1,7 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
runtime_evaluated_base_classes_2.py:3:8: TCH002 Move third-party import `geopandas` into a type-checking block
runtime_evaluated_base_classes_2.py:3:8: TCH002 [*] Move third-party import `geopandas` into a type-checking block
|
3 | from __future__ import annotations
4 |
@ -10,8 +10,26 @@ runtime_evaluated_base_classes_2.py:3:8: TCH002 Move third-party import `geopand
6 | import pydantic
7 | import pyproj # TCH002
|
= help: Move into type-checking block
runtime_evaluated_base_classes_2.py:5:8: TCH002 Move third-party import `pyproj` into a type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 2 |
3 |-import geopandas as gpd # TCH002
4 3 | import pydantic
5 4 | import pyproj # TCH002
6 5 | from pydantic import BaseModel
7 6 |
8 7 | import numpy
8 |+from typing import TYPE_CHECKING
9 |+
10 |+if TYPE_CHECKING:
11 |+ import geopandas as gpd
9 12 |
10 13 |
11 14 | class A(BaseModel):
runtime_evaluated_base_classes_2.py:5:8: TCH002 [*] Move third-party import `pyproj` into a type-checking block
|
5 | import geopandas as gpd # TCH002
6 | import pydantic
@ -19,5 +37,22 @@ runtime_evaluated_base_classes_2.py:5:8: TCH002 Move third-party import `pyproj`
| ^^^^^^ TCH002
8 | from pydantic import BaseModel
|
= help: Move into type-checking block
Suggested fix
2 2 |
3 3 | import geopandas as gpd # TCH002
4 4 | import pydantic
5 |-import pyproj # TCH002
6 5 | from pydantic import BaseModel
7 6 |
8 7 | import numpy
8 |+from typing import TYPE_CHECKING
9 |+
10 |+if TYPE_CHECKING:
11 |+ import pyproj
9 12 |
10 13 |
11 14 | class A(BaseModel):

View file

@ -1,12 +1,26 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
runtime_evaluated_decorators_2.py:10:8: TCH002 Move third-party import `numpy` into a type-checking block
runtime_evaluated_decorators_2.py:10:8: TCH002 [*] Move third-party import `numpy` into a type-checking block
|
10 | from attrs import frozen
11 |
12 | import numpy # TCH002
| ^^^^^ TCH002
|
= help: Move into type-checking block
Suggested fix
7 7 | import pyproj
8 8 | from attrs import frozen
9 9 |
10 |-import numpy # TCH002
10 |+from typing import TYPE_CHECKING
11 |+
12 |+if TYPE_CHECKING:
13 |+ import numpy
11 14 |
12 15 |
13 16 | @attrs.define(auto_attribs=True)

View file

@ -1,7 +1,7 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
strict.py:54:25: TCH002 Move third-party import `pkg.bar.A` into a type-checking block
strict.py:54:25: TCH002 [*] Move third-party import `pkg.bar.A` into a type-checking block
|
54 | # In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
55 | import pkg
@ -10,8 +10,27 @@ strict.py:54:25: TCH002 Move third-party import `pkg.bar.A` into a type-checking
57 |
58 | def test(value: A):
|
= help: Move into type-checking block
strict.py:91:12: TCH002 Move third-party import `pkg` into a type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ from pkg.bar import A
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
51 55 | def f():
52 56 | # In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
53 57 | import pkg
54 |- from pkg.bar import A
55 58 |
56 59 | def test(value: A):
57 60 | return pkg.B()
strict.py:91:12: TCH002 [*] Move third-party import `pkg` into a type-checking block
|
91 | # Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
92 | # testing the implementation.
@ -19,5 +38,24 @@ strict.py:91:12: TCH002 Move third-party import `pkg` into a type-checking block
| ^^^ TCH002
94 | import pkgfoo.bar as B
|
= help: Move into type-checking block
Suggested fix
1 1 | from __future__ import annotations
2 |+from typing import TYPE_CHECKING
3 |+
4 |+if TYPE_CHECKING:
5 |+ import pkg
2 6 |
3 7 |
4 8 | def f():
--------------------------------------------------------------------------------
88 92 | # In un-strict mode, this _should_ rase an error, since `pkgfoo.bar` is used at runtime.
89 93 | # Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
90 94 | # testing the implementation.
91 |- import pkg
92 95 | import pkgfoo.bar as B
93 96 |
94 97 | def test(value: pkg.A):

View file

@ -0,0 +1,29 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
<filename>:4:8: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
4 | from __future__ import annotations
5 |
6 | import pandas as pd
| ^^^^^^^^^^^^ TCH002
7 |
8 | from typing import TYPE_CHECKING
|
= help: Move into type-checking block
Suggested fix
1 1 |
2 2 | from __future__ import annotations
3 3 |
4 |-import pandas as pd
5 4 |
6 5 | from typing import TYPE_CHECKING
7 6 |
7 |+if TYPE_CHECKING:
8 |+ import pandas as pd
9 |+
8 10 | def f(x: pd.DataFrame):
9 11 | pass

View file

@ -0,0 +1,15 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
<filename>:4:8: TCH002 Move third-party import `pandas` into a type-checking block
|
4 | from __future__ import annotations
5 |
6 | import pandas as pd
| ^^^^^^^^^^^^ TCH002
7 |
8 | def f(x: pd.DataFrame):
|
= help: Move into type-checking block

View file

@ -0,0 +1,27 @@
---
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
---
<filename>:6:8: TCH002 [*] Move third-party import `pandas` into a type-checking block
|
6 | from typing import TYPE_CHECKING
7 |
8 | import pandas as pd
| ^^^^^^^^^^^^ TCH002
9 |
10 | def f(x: pd.DataFrame):
|
= help: Move into type-checking block
Suggested fix
3 3 |
4 4 | from typing import TYPE_CHECKING
5 5 |
6 |-import pandas as pd
7 6 |
7 |+if TYPE_CHECKING:
8 |+ import pandas as pd
9 |+
8 10 | def f(x: pd.DataFrame):
9 11 | pass