-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Proposal: use binder (or similar) to track which attributes are defined. #4019
Comments
I can see how the second example can work, but how would the first one? It requires kind of type state or something sophisticated like that |
I think it will be not more complex than the second one. Every attribute has its |
I still don't get it. Maybe I misunderstood the example. How do you know that the field is not initialized? |
Ah, sorry, the first example is indeed bad, since class C:
x: int
c = C() # from analyzing the class body we know that 'C' instances are created
# with field 'x' uninitialized. It is not always 100% clear, in uncertain cases we
# assume it is initialized, but in this case we know it is not.
c.x # this is an error therefore
c.x = 1 # now binder records that name 'x' of NameExpr('c') is initialized |
It is almost always not 100% clear - method calls, any transfer of control, metaclasses a-la NamedTuple, etc. |
My experience is quite opposite. Many classes have trivial class C:
x: int
def start(self) -> None:
self.x = 0
c = C()
if condition:
c.start()
# we know that 'c.x' is initialized here
# but not here, unless condition is know as always True statically. We also will need to track function calls, this is true, but it is not super hard. Note, that at the end of the above example |
I think that this is a volatile path to take - tracking To be clear, I do think it will be an interesting thing to try; it's just that the high level of added complexity (to parts that are already too complicated) and effort does not seem to be worth the relatively weak guarantees, the number of false positives, and the "action from distance" involved in tracking function calls. In other words, tracking field initialization means adding typestate-checking to mypy. It looks like a big leap from what we have right now. If we choose to do it, I'd vote for adding the more general mechanism, so it can be instantiated with other analyzes such as resource handling. |
Fine-grained tracking of whether attributes are defined goes against the philosophy of mypy. In particular mypy tries hard not to do non-local static analysis. I agree with @elazarg's reasoning, and it also makes incremental type checking harder. Doing this for regular variables is another thing entirely and doesn't have the same problems. A more pragmatic approach would be to allow declaring that certain attributes (or all of them) must always be explicitly defined in class A:
x: int
y: str
def __init__(self) -> None:
self.x = 0 # Complain about y not being defined here
def f(self) -> None:
self.z = 1 # Also complain about an attribute defined here
a = A()
del a.x # Not allowed We could probably implement this in a way that we'd have reasonable guarantees that attribute access can't fail for instances of |
OK, let us do this step by step. We can start from tracking global/local variables, then enable a flag to force all definitions in |
Requiring definitions in |
@ethanhs Yes |
I think it should be required by default. From PEP 526 (emphasis mine):
So the PEP clearly intends that the annotated instance variables are initialised inside |
It would be much better to check if any instance variables are left uninitialized than to check if all are initialized in |
Fair enough, that makes sense in the spirit of not wanting to warn about correct programs. |
See #7756 for an important use case (problematic |
this would be a very cool feature for mypy |
Related: #686 |
Updated the title to refer specifically to attributes, since variables are mostly supported now. |
Is this an example of this limitation:
This passes with mypy 1.3.0. |
The variable check is not enabled by default. Try |
Thanks, that fixes it. I'm surprised this is not included in |
It still has a few known issues so it must be explicitly enabled. |
Is this the right place to post any issues (like false positives) encountered using |
You can create new issues. Already reported issues can be seen here: https://github.com/python/mypy/issues?q=is%3Aopen+is%3Aissue+label%3Atopic-partially-defined |
I couldn't find an issue for this, so here is an idea. Currently this passes mypy:
but obviously fails at runtime. Binder successfully tracks execution frames and type narrowing by assignments, it looks like it can also track each
Var
whether it is still just "declared" or already defined. So that we can show errors likeVariable 'var' may be uninitialized here
.EDIT: updated first example
The text was updated successfully, but these errors were encountered: