Skip to content
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

Type-checking block errors not reported for imports used in cast() string annotation #127

Open
bryanforbes opened this issue Aug 17, 2022 · 10 comments
Labels
bug Something isn't working

Comments

@bryanforbes
Copy link

Given the following code:

from __future__ import annotations

from collections.abc import Callable
from typing import Any, cast

from pendulum.datetime import DateTime

from .foo import Foo

a = cast('Callable[..., Any]', {})
b = cast('DateTime', {})
c = cast('Foo', {})

And the following config:

[flake8]
select = TC,TC1

No errors are reported at line 3 (TC003), line 6 (TC002), and line 8 (TC001).

If the following function is added in the code, the errors are triggered:

def asdf(a: Callable[..., Any], b: DateTime, c: Foo) -> None:
    ...
@sondrelg
Copy link
Member

This is intentional, since F401 which comes built-into flake8 already flags unused imports. Do you have a use case where F401 is not flagged, and we still don't flag anything?

@bryanforbes
Copy link
Author

If I change the config to be select = F,TC,TC1, no errors are triggered (including F401).

@sondrelg
Copy link
Member

Huh yeah that's strange. @Goldziher know what we're missing here?

@Goldziher
Copy link
Member

I would suspect flake8 parses the values as strings. If you remove the strings from cast, what happens?

@bryanforbes
Copy link
Author

bryanforbes commented Aug 17, 2022

Removing the strings (changing them to ex. cast(DateTime, {})) raises TC006 but no other errors since all of those symbols are referenced in running code now

@sondrelg
Copy link
Member

yep, looks like

from collections.abc import Callable
from typing import Any, cast

from pendulum.datetime import DateTime

from .foo import Foo

a = cast('Callable[..., Any]', {})
- b = cast('DateTime', {})
c = cast('Foo', {})

Triggers the F401:

test.py:4:1: F401 'pendulum.datetime.DateTime' imported but unused

@sondrelg sondrelg added the bug Something isn't working label Aug 18, 2022
@sondrelg
Copy link
Member

I suppose we should try to parse string values as well, and still raise TC errors when something's present. If that's not possible, I think we'll need to revert to always raising.

@sondrelg
Copy link
Member

After looking at this for a bit, the problem is harder than just noting down the string value of every cast and crosschecking imports against it. In the example string literal "Callable[..., Any]" Callable is contained in the string, but to match it to an import we either need to fully parse all the parts of complex annotations (which sounds like it will add a ton of complexity to the plugin for very little value), or do partial matches (import in annotation) which seems error prone, and a poor solution in general.

A second option would be to revert #125, which means we would start raising TC[1,2,3] errors when imports are not used at all again.

And a third option would be to do nothing. To accept that casts contain a blind spot. Unused imports are still caught by a lot of other linters and IDEs, so this seems like it might be reasonable. Seems like it would warrant a note in the readme though.

I hope I'm wrong and that there's a fourth elegant solution which doesn't require a trade-off, but AFAICT there isn't 🤷 Suggestions are very welcome.

I'm leaning towards option 2 or 3, but don't have a strong preference. What do you think?

@bryanforbes
Copy link
Author

Thanks for the explanation! If it's going to add a bunch of complexity to the plugin, I vote for the third option and closing this.

@sondrelg
Copy link
Member

sondrelg commented Sep 1, 2022

I don't love any of these solutions. In some ways I prefer flagging unused imports since a false positive seems better than a false negative. The only downside is there's a lot more false positives. Maybe we can keep this open for now in the hope someone has a suggestion for a fourth option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants