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

RUF045 added in Ruff 0.9.7 should ignore ClassVar fields #16297

Open
kkom opened this issue Feb 21, 2025 · 4 comments · May be fixed by #16299
Open

RUF045 added in Ruff 0.9.7 should ignore ClassVar fields #16297

kkom opened this issue Feb 21, 2025 · 4 comments · May be fixed by #16299
Labels
bug Something isn't working

Comments

@kkom
Copy link

kkom commented Feb 21, 2025

Description

According to Python's docs class variables should be ignored by the dataclass mechanism: https://docs.python.org/3/library/dataclasses.html#class-variables

Which means that I think they don't need an explicit type hint in cases like this:

@dataclass
class MyBaseClass:
    class_var_field: ClassVar[int]

@dataclass
class MySubClass(MyBaseClass):
    class_var_field = 7

But ruff now catches class_var_field = 7 as a lint violation.

@dylwil3 dylwil3 added the bug Something isn't working label Feb 21, 2025
@MichaReiser
Copy link
Member

I'm not very familiar with data classes but I'm not sure if this is a bug. It does seem to me that the MySubClass.class_var_field and MyBaseClass.class_var_field are two different fields.

>>> from typing import ClassVar
... from dataclasses import dataclass
...
... @dataclass
... class MyBaseClass:
...     class_var_field: ClassVar[int]
...
... @dataclass
... class MySubClass(MyBaseClass):
...     class_var_field = 7
...     other_field: int = 8
...
>>> MyBaseClass.class_var_field
Traceback (most recent call last):
  File "<python-input-34>", line 1, in <module>
    MyBaseClass.class_var_field
AttributeError: type object 'MyBaseClass' has no attribute 'class_var_field'
>>> MySubClass.class_var_field
7

But I might be wrong here. Even if that's not the case. I still think that we want to warn here because it may, after all, have been the intention to create an instance variable here.

@InSyncWithFoo
Copy link
Contributor

@MichaReiser The point of this rule is to avoid attributes that look like dataclass fields but are not fields:

>>> from typing import ClassVar
>>> from dataclasses import dataclass
>>>
>>> @dataclass
... class MyBaseClass:
...     class_var_field: ClassVar[int]
...
>>> @dataclass
... class MySubClass(MyBaseClass):
...     class_var_field = 7  # Not part of the generated `__init__`
...     other_field: int = 8
...
>>> help(MySubClass.__init__)
Help on function __init__ in module __main__:

__init__(self, other_field: __dataclass_type_other_field__ = 8) -> __dataclass___init___return_type__
    Initialize self.  See help(type(self)) for accurate signature.

ClassVar annotations, inherited or not, help clarifying the intention of the attribute. It signifies that the user is already aware of the attribute and so the attribute should not be reported.

@MichaReiser
Copy link
Member

The part I don't see his how inheritance is relevant here. I can remove the base class and get the exact same fields for MySubClass:

... @dataclass
... class MySubClass:
...      class_var_field = 7
...      other_field: int = 8

__init__(self, other_field: int = 8) -> None
    Initialize self.  See help(type(self)) for accurate signature.

@InSyncWithFoo
Copy link
Contributor

@MichaReiser Inheritance is indeed not the point.

  • The rule prevents accidental class variables.
  • To avoid this, the user can make their intention explicit by adding ClassVar.
  • ClassVar annotations are inheritable in the type system, so an inherited one should be deem equally explicit.

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

Successfully merging a pull request may close this issue.

4 participants