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
Add typing to scope()
#1170
Add typing to scope()
#1170
Conversation
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.
Added a few comments. Unfortunately, they will probably result in more work than you expected, sorry about that.
astroid/nodes/node_ng.py
Outdated
@@ -246,7 +259,7 @@ def frame(self): | |||
""" | |||
return self.parent.frame() | |||
|
|||
def scope(self): | |||
def scope(self) -> Optional["LocalsDictNodeNG"]: |
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.
Regarding LocalsDictNodeNG
: I fear that at some point it will just create another circular dependency. Maybe we should start looking into Protocol
classes for that 🤔 Haven't really done so myself, thus don't know for certain if it would work.
https://docs.python.org/3/library/typing.html#typing.Protocol
--
Another issue: If at some point some scoped node inherits from another mixin class (not LocalsDictNodeNG
, this will break.
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.
Are you sure? Doesn't using "Class"
prevent circular dependency?
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 current code is fine. As you said, using a string as type annotation does prevent the circular dependency.
It's probably just that I'm not really a fan of the class name itself (LocalsDictNodeNG
). Maybe that should be changed at some point 🤔
Anyway, let's leave it for now.
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
Is this failing because of the recent update to |
No. Update: I can reproduce the issue when running |
It has something to do with the import of |
I would suspect some kind of caching issue. Might not be that easy to track down. |
I'm not sure if this solution works for you, but adding |
@@ -77,6 +77,8 @@ | |||
{"classmethod", "staticmethod", "builtins.classmethod", "builtins.staticmethod"} | |||
) | |||
|
|||
T = TypeVar("T") |
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 understand this part, what does T
means ?
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 know how it works under the hood. But for
def scope(self: T) -> T:
"""The first parent node defining a new scope.
:returns: The first parent scope node.
:rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr
"""
return self
It makes the function return with the correct typing. It sets the return type to the type of self
.
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.
Ok I've read the documentation and I think T = TypeVar("T")
is the example for TypeVar
from the python doc, but I guess we can change the name to something else ? Maybe Scope
(Scope = TypeVar("Scope")
) ?
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.
This is something for you to discuss with Marc 😄
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.
@cdce8p what do you think, isn't it better to have an explicit name here ?
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.
Sorry for the discrepancies @DanielNoord I started to not review some MR that Marc is already reviewing because there is a lot to review :)
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.
No worries! With the typing PRs it never hurts to have an additional pair of eyes.
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.
It would be possible to rename the TypeVar
, I just wouldn't recommend it in this case. T
is the usual convention. Additionally, it doesn't have any constraints or bounds. Thus it could be reused in another function later.
That is, for example, different in the TypeVar
we used in pylint-dev/pylint#4978.
astroid/nodes/scoped_nodes.py
Outdated
@@ -1606,6 +1608,7 @@ def blockstart_tolineno(self): | |||
|
|||
:type: int | |||
""" | |||
# pylint: disable-next=no-member |
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 a bit hesitant about these pylint: disable
statements. Maybe it would be better to remove the annotations in astroid/exceptions.py
for now.
They don't add much as the error is only used in two locations anyway.
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 going to fix this in another PR. I'll tag you!
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.
See #1174
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 see now! These are actual bugs, only Arguments
should be assigned to self.args
not a list
.
I was under the impression that these are pylint
bugs.
In that case, these are ok.
|
You don't need to wait for it. That merge conflict should be easy to resolve. |
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.
Some final comments. Besides that, this LGTM!
Thanks @DanielNoord 🐬
Can be merged if @Pierre-Sassoulas is ok with using T
for the generic TypeVar
.
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
@DanielNoord I've just merged the #1174 and #1175. Can you update this one so it can be merged as well? Let me know if I can help. |
@cdce8p Should be good to go after these tests pass! |
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 @DanielNoord 🚀
@cdce8p The check on this latest commit failed although |
@DanielNoord Thanks. Yeah, this isn't an issue. |
Steps
Description
After stumbling across this in pylint-dev/pylint#4995 I thought I might as well make a start with this.
One of the problems I foresee is that in some cases we return
None
innode_ng.py
. I am not sure if this ever happens in a real scenario, but this will trip upmypy
forpylint
as it will want us to assert that any call toNodeNG.scope()
did not returnNone
.Based on the docstring of that function that might be (legacy) code which is never triggered, but I am not sure. Perhaps a maintainer knows more about this.
In any case, if we can remove the
Optional
from the return typing this would be much more useful forpylint
.Type of Changes
Related Issue