-
-
Notifications
You must be signed in to change notification settings - Fork 276
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
Fix frame() error on inferred node #1263
Fix frame() error on inferred node #1263
Conversation
On which commit of In any case, whenever This was quite difficult to do correctly and took me some tries to get right, but you're more than welcome to start working towards it! I'm happy to provide support a long the way and answer any questions you might have! |
I encountered this on 2.7.3, with pylint 2.10.2 I believe. |
Actually, in my case the latest master fixes this issue. Thanks though! |
It came back. I needed this fix to make inference work locally again today. |
Does this seem good? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for reopening the issue!
Would you mind adding a test for this as well? You can probably base it on the code that is currently throwing an error for you or look at how we tested StatementMissing
in #1217!
astroid/nodes/node_ng.py
Outdated
@@ -311,6 +310,13 @@ def frame( | |||
|
|||
:returns: The first parent frame node. | |||
""" | |||
if self.parent is None: | |||
if isinstance( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this isinstance
check should be here. If self
is either of these nodes
we shouldn't be in this function anyway as they have their frame()
defined in their class definitions.
I think just raising the exception is fine.
I looked at NamedExpr.frame()
which seems to raise ParentMissingError
. I think if we go ahead with raising FrameMissing
we might use this exception there as well. It shouldn't be a breaking change since FrameMissing
inherits from ParentMissingError
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The isinstance
check is indeed unnecessary. Just this would be enough:
if not self.parent:
raise ParentMissingError(target=self)
return self.parent.frame()
--
Technically this is a small breaking change as users could be fine with catching AttributeError
.
We added future=True
to statement()
so just for completeness we should probably do that here, too. You can take a look at #1217 as example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I do the same for .statement() as well?
@tushar-deepsource What do you mean with that? @DanielNoord mentioned it already but we reworked statement()
in #1217. With future=True
this should already work.
This change will also need a changelog entry and tests.
Would you feel confident doing these changes? It should be relatively straightforward if you follow the lead of #1217. Please let us know if you need help or if we should take this one over. For a first PR this is quite complex.
astroid/exceptions.py
Outdated
class FrameMissing(ParentMissingError): | ||
"""Raised when a call to node.frame() does not return a node. This is because | ||
a node in the chain does not have a parent attribute and therefore does not | ||
return a node for frame(). | ||
|
||
Standard attributes: | ||
target: The node for which the parent lookup failed. | ||
""" | ||
|
||
def __init__(self, target: "nodes.NodeNG") -> None: | ||
# pylint: disable-next=bad-super-call | ||
super(ParentMissingError, self).__init__( | ||
message=f"Frame not found on {target!r}" | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if it's actually necessary to create a new exception subclass. All existing frame()
and scope()
methods raise just a ParentMissingError
.
@DanielNoord I don't actually remember the exact details why we decided to add StatementMissingError
maybe you do? Was there an actual need to have a more specific error or was it just an idea that we adopted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this followed from the logic that we were catching the AttributeError
ourselves so it sort of naturally followed that we had to create a new error to catch.
It might be a bit overkill, but I think it makes sense to make a new subclass: the more fine grained the error is the better users will know how to handle it.
astroid/nodes/node_ng.py
Outdated
@@ -311,6 +310,13 @@ def frame( | |||
|
|||
:returns: The first parent frame node. | |||
""" | |||
if self.parent is None: | |||
if isinstance( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The isinstance
check is indeed unnecessary. Just this would be enough:
if not self.parent:
raise ParentMissingError(target=self)
return self.parent.frame()
--
Technically this is a small breaking change as users could be fine with catching AttributeError
.
We added future=True
to statement()
so just for completeness we should probably do that here, too. You can take a look at #1217 as example.
To double check: I'd have to essentially replicate the type stubs and tests for If the changes so far make sense, I can go ahead and do that. |
I'll wait for @cdce8p to comment on whether he thinks that subclassing Other than that I think the following todo list should bring this PR a long way towards getting merged:
Follow-up PR: For all of these changes #1217 and #1235 and the discussions within those PR could help. |
Thanks for the summery @DanielNoord! That looks about right to me.
Usually just subclassing an exception doesn't really hurt much. However in this case it also doesn't add any more information. I personally don't really see a need to be even more specific than |
Fine with me! @tushar-deepsource Let me know if you need any help with implementing this. Happy to help as always π |
Should be good now |
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall!
@Pierre-Sassoulas @DanielNoord What do you guys think, is a changelog entry necessary? We didn't add one for #1217. In theory everyone that depends on the AttributeError
should get a DeprecationWarning
. We can (or maybe should ?) include a note when we eventually release 3.0.0
.
@@ -314,7 +313,7 @@ def statement( | |||
return self.parent.statement(future=future) | |||
|
|||
def frame( | |||
self, | |||
self, *, future: Literal[None, True] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out the overload idea doesn't actually work as I though. Sorry for the confusion. The default with None
is needed for the parent calls.
...
return self.parent.frame(future=future)
I've already opened another PR to fix the node.statement
calls #1317.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is a changelog entry necessary?
I think so, could you add a notice about the deprecation in the changelog @tushar-deepsource , please ? :)
sure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you ! This is blocked by the release of 2.9.1, we'll merge as soon as 2.9.1 is out :)
@Pierre-Sassoulas This should go in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the comment @DanielNoord :) Could you move the changelog to 2.9.1 @tushar-deepsource ? I can't suggest/apply it in interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π
I think we could merge this one as is. The test errors are unrelated to the changes made here. |
Description
Ran into this issue today. The
parent
of an inferred node wasNone
, sonode.frame()
was crashing. This should fix that.Type of Changes