Treat callables within type definitions as default-non-types (#3329)

This commit is contained in:
Charlie Marsh
2023-03-03 18:07:30 -05:00
committed by GitHub
parent 367cc43c42
commit d216b2aaa8
7 changed files with 125 additions and 21 deletions

View File

@@ -0,0 +1,23 @@
"""Test case: strings used within calls within type annotations."""
from typing import Callable
import bpy
from mypy_extensions import VarArg
from foo import Bar
class LightShow(bpy.types.Operator):
label = "Create Character"
name = "lightshow.letter_creation"
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # OK
def f(x: Callable[[VarArg("os")], None]): # F821
pass
f(Callable[["Bar"], None])
f(Callable[["Baz"], None])

View File

@@ -0,0 +1,25 @@
"""Test case: strings used within calls within type annotations."""
from __future__ import annotations
from typing import Callable
import bpy
from mypy_extensions import VarArg
from foo import Bar
class LightShow(bpy.types.Operator):
label = "Create Character"
name = "lightshow.letter_creation"
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # OK
def f(x: Callable[[VarArg("os")], None]): # F821
pass
f(Callable[["Bar"], None])
f(Callable[["Baz"], None])

View File

@@ -3635,9 +3635,16 @@ where
Some(Callable::NamedTuple)
} else if self.match_typing_call_path(&call_path, "TypedDict") {
Some(Callable::TypedDict)
} else if ["Arg", "DefaultArg", "NamedArg", "DefaultNamedArg"]
.iter()
.any(|target| call_path.as_slice() == ["mypy_extensions", target])
} else if [
"Arg",
"DefaultArg",
"NamedArg",
"DefaultNamedArg",
"VarArg",
"KwArg",
]
.iter()
.any(|target| call_path.as_slice() == ["mypy_extensions", target])
{
Some(Callable::MypyExtension)
} else {
@@ -3760,7 +3767,17 @@ where
}
}
None => {
visitor::walk_expr(self, expr);
// If we're in a type definition, we need to treat the arguments to any
// other callables as non-type definitions (i.e., we don't want to treat
// any strings as deferred type definitions).
self.visit_expr(func);
for arg in args {
visit_non_type_definition!(self, arg);
}
for keyword in keywords {
let KeywordData { value, .. } = &keyword.node;
visit_non_type_definition!(self, value);
}
}
}
}

View File

@@ -104,6 +104,8 @@ mod tests {
#[test_case(Rule::UndefinedName, Path::new("F821_8.pyi"); "F821_8")]
#[test_case(Rule::UndefinedName, Path::new("F821_9.py"); "F821_9")]
#[test_case(Rule::UndefinedName, Path::new("F821_10.py"); "F821_10")]
#[test_case(Rule::UndefinedName, Path::new("F821_11.py"); "F821_11")]
#[test_case(Rule::UndefinedName, Path::new("F821_12.py"); "F821_12")]
#[test_case(Rule::UndefinedExport, Path::new("F822_0.py"); "F822_0")]
#[test_case(Rule::UndefinedExport, Path::new("F822_1.py"); "F822_1")]
#[test_case(Rule::UndefinedExport, Path::new("F822_2.py"); "F822_2")]

View File

@@ -0,0 +1,27 @@
---
source: crates/ruff/src/rules/pyflakes/mod.rs
expression: diagnostics
---
- kind:
UndefinedName:
name: os
location:
row: 18
column: 26
end_location:
row: 18
column: 30
fix: ~
parent: ~
- kind:
UndefinedName:
name: Baz
location:
row: 23
column: 12
end_location:
row: 23
column: 17
fix: ~
parent: ~

View File

@@ -0,0 +1,27 @@
---
source: crates/ruff/src/rules/pyflakes/mod.rs
expression: diagnostics
---
- kind:
UndefinedName:
name: os
location:
row: 20
column: 26
end_location:
row: 20
column: 30
fix: ~
parent: ~
- kind:
UndefinedName:
name: Baz
location:
row: 25
column: 12
end_location:
row: 25
column: 17
fix: ~
parent: ~

View File

@@ -478,21 +478,4 @@ expression: diagnostics
row: 67
column: 49
parent: ~
- kind:
QuotedAnnotation: ~
location:
row: 69
column: 14
end_location:
row: 69
column: 17
fix:
content: X
location:
row: 69
column: 14
end_location:
row: 69
column: 17
parent: ~