Ruff unconditionally assumes any string in a type parameter is a forward annotation
lanzz opened this issue · comments
I have an alias for Annotated
, to be used in a container class:
class Container:
# class that inspects its type annotations at runtime and does stuff with them
Field = typing.Annotated
class MyContainer(Container):
foo_field: Field[int, 'metadata'] # ruff raises F281 "Undefined name `metadata` here
bar_field: Field[str, 'not a valid identifier'] # ruff raises F722 "Syntax error in forward annotation" here
foo_ann: typing.Annotated[int, 'metadata'] # no complaints here
bar_ann: typing.Annotated[str, 'not a valid identifier'] # no complaints here either
Ultimately, the Field
alias is just sugar to make the definitions of subclasses of Container
better self-documenting. I understand this is a pretty niche use and it's very likely not feasible for Ruff to figure out that Field
is alias for Annotated
(especially since in practice that alias definition is in a completely different module), but is there a way to explicitly instruct Ruff to treat those instances of Field
the same way it treats actual Annotated
type definitions, without having to suppress the rule explicitly on every single line that uses Field
? If I could define ignores by line regex that would work (e.g. ignore F281, F722 for lines matching :\s+Field\[
), but I don't think Ruff supports that, or at least I could not find it in the docs.
I don't know that we have a great workaround here right now. If we can't resolve a symbol, and it's subscripted in an annotation, we do assume that any strings within the subscript are forward annotations, since they're almost always generics.
I suspect this will eventually "just work" as we're doing a lot of foundational work right now to make Ruff capable of this kind of inference. But in the interim, struggling to think of anything other than adding # ruff: noqa: F281, F722
to the file.