-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Avoid TC201 false positives for top-level type checking decls #170
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
5896c03
fix: Avoid TC201 false positives for top-level type checking decls
Daverball 55e96da
Fixes offset in py312 specific test
Daverball b7d4789
Don't visit the value expression for an explicit `TypeAlias`
Daverball ed10ad6
fix: Extracts names from wrapped annotations for matching.
Daverball 9d1f7d7
Improves name regex for `Literal` and adds tests.
Daverball a0fa5cc
Properly handles 3.12 style type aliases.
Daverball 92ac05d
Adds a couple of test cases for type aliases to TC001-TC003 tests
Daverball 802482c
Fixes comment [skip ci]
Daverball 568801a
Updates a couple of docstrings to better reflect current state
Daverball bcc4654
Improves robustness of detecting type checking only declarations
Daverball File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import pytest | ||
|
||
from flake8_type_checking.constants import NAME_RE | ||
|
||
examples = [ | ||
('', []), | ||
('int', ['int']), | ||
('dict[str, int]', ['dict', 'str', 'int']), | ||
# make sure literals don't add names for their contents | ||
('Literal["a"]', ['Literal']), | ||
("Literal['a']", ['Literal']), | ||
('Literal[0]', ['Literal']), | ||
('Literal[1.0]', ['Literal']), | ||
# booleans are a special case and difficult to reject using a RegEx | ||
# for now it seems harmless to include them in the names, but if | ||
# we do something more sophisticated with the names we may want to | ||
# explicitly remove True/False from the result set | ||
('Literal[True]', ['Literal', 'True']), | ||
# try some potentially upcoming syntax | ||
('*Ts | _T & S', ['Ts', '_T', 'S']), | ||
# even when it's formatted badly | ||
('*Ts|_T&P', ['Ts', '_T', 'P']), | ||
('Union[Dict[str, Any], Literal["Foo", "Bar"], _T]', ['Union', 'Dict', 'str', 'Any', 'Literal', '_T']), | ||
] | ||
|
||
|
||
@pytest.mark.parametrize(('example', 'expected'), examples) | ||
def test_name_extraction(example, expected): | ||
assert NAME_RE.findall(example) == expected |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
""" | ||
File tests TC007: | ||
Type alias should be wrapped in quotes | ||
""" | ||
import sys | ||
import textwrap | ||
|
||
import pytest | ||
|
||
from flake8_type_checking.constants import TC007 | ||
from tests.conftest import _get_error | ||
|
||
examples = [ | ||
# No error | ||
('', set()), | ||
('x: TypeAlias = "int"', set()), | ||
('from typing import Dict\nx: TypeAlias = Dict[int]', set()), | ||
('if TYPE_CHECKING:\n\tfrom typing import Dict\nx: TypeAlias = Dict', {'3:15 ' + TC007.format(alias='Dict')}), | ||
("if TYPE_CHECKING:\n\tfrom typing import Dict\nx: TypeAlias = 'Dict'", set()), | ||
("if TYPE_CHECKING:\n\tfrom typing import Dict as d\nx: TypeAlias = 'd[int]'", set()), | ||
('if TYPE_CHECKING:\n\tfrom typing import Dict\nx: TypeAlias = Dict[int]', {'3:15 ' + TC007.format(alias='Dict')}), | ||
# Regression test for issue #163 | ||
( | ||
textwrap.dedent(''' | ||
from typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from collections.abc import Sequence | ||
from typing_extensions import TypeAlias | ||
|
||
Foo: TypeAlias = Sequence[int] | ||
'''), | ||
set(), | ||
), | ||
# Inverse regression test for issue #163 | ||
( | ||
textwrap.dedent(''' | ||
from typing import TYPE_CHECKING | ||
from typing_extensions import TypeAlias | ||
|
||
if TYPE_CHECKING: | ||
from collections.abc import Sequence | ||
|
||
Foo: TypeAlias = Sequence[int] | ||
'''), | ||
{ | ||
'8:17 ' + TC007.format(alias='Sequence'), | ||
}, | ||
), | ||
] | ||
|
||
if sys.version_info >= (3, 12): | ||
# RHS on an explicit TypeAlias with 3.12 syntax should not emit a TC007 | ||
examples.append( | ||
( | ||
textwrap.dedent(''' | ||
if TYPE_CHECKING: | ||
from collections.abc import Sequence | ||
|
||
type Foo = Sequence[int] | ||
'''), | ||
set(), | ||
) | ||
) | ||
|
||
|
||
@pytest.mark.parametrize(('example', 'expected'), examples) | ||
def test_TC007_errors(example, expected): | ||
assert _get_error(example, error_code_filter='TC007') == expected |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
""" | ||
File tests TC008: | ||
Type alias is wrapped in unnecessary quotes | ||
""" | ||
from __future__ import annotations | ||
|
||
import sys | ||
import textwrap | ||
|
||
import pytest | ||
|
||
from flake8_type_checking.constants import TC008 | ||
from tests.conftest import _get_error | ||
|
||
examples = [ | ||
('', set()), | ||
("x: TypeAlias = 'int'", {'1:15 ' + TC008.format(alias='int')}), | ||
# this used to emit an error before fixing #164 if we wanted to handle | ||
# this case once again we could add a whitelist of subscriptable types | ||
("x: TypeAlias = 'Dict[int]'", set()), | ||
("from __future__ import annotations\nx: TypeAlias = 'int'", {'2:15 ' + TC008.format(alias='int')}), | ||
("if TYPE_CHECKING:\n\tfrom typing import Dict\nx: TypeAlias = 'Dict'", set()), | ||
("if TYPE_CHECKING:\n\tfrom typing import Dict\nx: TypeAlias = 'Dict[int]'", set()), | ||
( | ||
"from __future__ import annotations\nfrom typing import Dict\nx: TypeAlias = Dict['int']", | ||
{'3:20 ' + TC008.format(alias='int')}, | ||
), | ||
( | ||
"from __future__ import annotations\nif TYPE_CHECKING:\n\tfrom typing import Dict\nx: TypeAlias = Dict['int']", | ||
{'4:20 ' + TC008.format(alias='int')}, | ||
), | ||
( | ||
textwrap.dedent(''' | ||
from __future__ import annotations | ||
|
||
if TYPE_CHECKING: | ||
import something | ||
|
||
x: TypeAlias = "something" | ||
'''), | ||
set(), | ||
), | ||
( | ||
# Regression test for Issue #164 | ||
textwrap.dedent(''' | ||
from wtforms import Field | ||
from wtforms.fields.core import UnboundField | ||
|
||
foo: TypeAlias = 'UnboundField[Field]' | ||
'''), | ||
set(), | ||
), | ||
( | ||
# avoid false positive for annotations that make | ||
# use of a newly defined class | ||
textwrap.dedent(''' | ||
class Foo(Protocol): | ||
pass | ||
|
||
x: TypeAlias = 'Foo | None' | ||
'''), | ||
set(), | ||
), | ||
( | ||
# Regression test for Issue #168 | ||
textwrap.dedent(''' | ||
if TYPE_CHECKING: | ||
Foo: TypeAlias = str | int | ||
|
||
Bar: TypeAlias = 'Foo' | ||
'''), | ||
set(), | ||
), | ||
( | ||
# Inverse regression test for Issue #168 | ||
# The declarations are inside a Protocol so they should not | ||
# count towards declarations inside a type checking block | ||
textwrap.dedent(''' | ||
if TYPE_CHECKING: | ||
class X(Protocol): | ||
Foo: str | int | ||
|
||
Bar: TypeAlias = 'Foo' | ||
'''), | ||
{ | ||
'6:17 ' + TC008.format(alias='Foo'), | ||
}, | ||
), | ||
] | ||
|
||
if sys.version_info >= (3, 12): | ||
examples.extend( | ||
[ | ||
( | ||
# new style type alias should never be wrapped | ||
textwrap.dedent(''' | ||
if TYPE_CHECKING: | ||
type Foo = 'str' | ||
|
||
type Bar = 'Foo' | ||
'''), | ||
{ | ||
'3:15 ' + TC008.format(alias='str'), | ||
'5:11 ' + TC008.format(alias='Foo'), | ||
}, | ||
) | ||
] | ||
) | ||
|
||
|
||
@pytest.mark.parametrize(('example', 'expected'), examples) | ||
def test_TC008_errors(example, expected): | ||
assert _get_error(example, error_code_filter='TC008') == expected |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should update the readme for this too, if needed 👍