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

Fixes no error thrown for Final fields declared with init=False and not assigned inside __init__ #14285

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

jakezych
Copy link

Fixes #13119

For all final field assignments to class variables in a dataclass i.e. x: Final[int] = field(), this PR checks whether this field is being properly assigned in the __init__ or __post_init__ methods. In the case where the field has the argument init=True, no error is thrown if the field is assigned inside __init__ or __post_init__ which was previously a false positive. This addresses the last example given in the issue thread.

In the case where the field has the argument init=False and it is not being assigned in __init__ or __post_init__, on the line where it is a declared, the error "Final name must be initialized with a value" is now thrown. A new error was added and is thrown if that same field is accessed through the class "Final field "a" not set". This addresses the original false negative given in the issue thread.

Tests Added (all in check-dataclasses.test):
testErrorFinalFieldNoInitNoArgumentPassed
testNoErrorFinalFieldDelayedInit
testErrorFinalFieldInitNoArgumentPassed
testFinalFieldGeneratedInitArgumentPassed
testFinalFieldInit
testFinalFieldPostInit

testFinalDefiningNoRhs in check-final.test was modified to now throw the new error when an unset final attribute is accessed

@github-actions

This comment has been minimized.

@tmke8
Copy link
Contributor

tmke8 commented Dec 12, 2022

From the mypy primer output it seems like you need to exclude class definitions in stub files from your check.

@jakezych
Copy link
Author

I think I fixed the issue. Could someone rerun the workflows?

@github-actions

This comment has been minimized.

@jakezych
Copy link
Author

I'll see if I can set up mypy primer locally to get a better idea of what's going wrong.

@jakezych
Copy link
Author

I tested the changes locally on yarl and discord.py and it was not throwing the errors anymore.

@github-actions
Copy link
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

@tmke8
Copy link
Contributor

tmke8 commented Dec 20, 2022

If the mistake is in a third-party library, does it still show an error in my own code? So, for example:

some_library/__init__.py:

from dataclasses import dataclass, field
from typing import Final

@dataclass
class C:
    x: Final[int] = field(init=False)

    def __post_init__(self) -> None:
        self.y = 3  # typo here

my_code.py:

from some_library import C

C().x  # an error here?

@jakezych
Copy link
Author

jakezych commented Dec 20, 2022

I'm a bit confused about the self.y = 3 #typo here line. Shouldn't it be okay to assign a final field in the __post_init__ method?

I'm silly and didn't read the difference between x and y.

I do understand your main point here though, and I'll take a look at how my code handles errors in imported libraries.

@jakezych
Copy link
Author

It does show errors from third-party libraries, I assume we want to suppress those?

@tmke8
Copy link
Contributor

tmke8 commented Dec 22, 2022

It does show errors from third-party libraries, I assume we want to suppress those?

Nevermind actually, I realized that a lot of other mypy checks have the same problem. So it should be fine!

@JukkaL
Copy link
Collaborator

JukkaL commented Apr 22, 2023

This PR is going in the right direction, but it feels to me like the implementation is too complicated, especially considering this is a relatively minor edge case. In particular, iterating over the body of a function in the dataclass plugin is not the best way to approach this, and it seems to duplicate functionality in the semantic analyzer which will make things harder to maintain and understand. Also I'd prefer it checkmember.py wouldn't need to be changed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

no error when setting init=False on Final field in dataclass
5 participants