-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add message name is parameter and nonlocal #7577
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 message name is parameter and nonlocal #7577
Conversation
At first glance since this is a new check, could you also please add a good.py/bad.py example please? Here is where they live: https://github.com/PyCQA/pylint/tree/main/doc/data/messages. These can be tested locally with |
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.
Could you also add some test with nested scopes and functions?
|
||
def tomato(is_tasty: bool = True): | ||
nonlocal color | ||
nonlocal is_tasty # [name-is-parameter-and-nonlocal] |
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.
nonlocal is_tasty # [name-is-parameter-and-nonlocal] | |
nonlocal is_tasty # [name-is-parameter-and-nonlocal] | |
def tomato(*is_tasty): | |
nonlocal is_tasty # [name-is-parameter-and-nonlocal] |
I think this would also raise a syntax error, right?
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 guess for that I can do something like
node_arg_names = {arg.name for arg in node.args.args}
for local_name, local_nodes in node.locals.items():
for local_node in local_nodes:
if isinstance(local_node, nodes.Arguments):
node_arg_names.add(local_name)
Not sure if there's an easier way to retrieve locals that are considered arguments.
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.
For nested scopes and functions, are you looking for something like this?
def grape(is_tasty: bool = True):
nonlocal is_tasty # [name-is-parameter-and-nonlocal]
def grape_juice(is_tasty: bool = True):
nonlocal is_tasty # [name-is-parameter-and-nonlocal]
grape_juice()
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.
Doesn't node.args.arguments()
work? According to the docstring that should be able to return all names that are used as parameters.
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.
Yeah, and variants of it. I haven't tested this out locally to see which combinations return a syntax error, but I think it is good to add some (regression tests) for possible combinations:
- outer scope with nonlocal inner scope with nonlocal
- outer scope without nonlocal inner scope with nonlocal
- outer scope with nonlocal inner scope without nonlocal
etc.
Scoping rules can create annoying false positives for messages like this.
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.
Yeah, and variants of it
Cool, I'll be coming back to this later today.
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.
Shit, that means that arguments
is broken. It should also return vararg
. I think we need to push a fix to astroid
for this: any implementation we create in pylint
to solve this would eventually end up being duplicate code with a good implementation of arguments
.
Would you want to fix astroid
for this? Should be relatively straightforward: a test with *
and **
and an assertion on the output of arguments()
. I think/hope we will already have other tests for the output of arguments
.
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.
Sure! I can attempt that later today, this might also be related pylint-dev/astroid#761 - but not totally 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.
Similar issue for node.args.find_argname
when using a vararg as a parameter:
(Pdb) node.args.find_argname("tasty")
(None, None)
I wouldn't be against separating these astroid issues into an independent workflow so we can let the current issue proceed to merge. We could add a maintenance issue and update the code here once they are resolved?
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.
Indeed let's not block this on too much refactoring and fix stuff as we run into them. I think this is a very clear use-case for a potential fix and I'd be happy to help release a patch version of astroid
for it. I think there are other issues waiting on a patch as well, a regression in dataclasses for example.
"E0134": ( | ||
"Name %r is parameter and nonlocal", | ||
"name-is-parameter-and-nonlocal", | ||
"Emitted when a nonlocal variable is also a parameter.", |
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.
"Emitted when a nonlocal variable is also a parameter.", | |
"Emitted when a nonlocal variable is also a parameter within a function defintion.", |
@@ -204,6 +204,11 @@ class BasicErrorChecker(_BasicChecker): | |||
"which results in an error since Python 3.6.", | |||
{"minversion": (3, 6)}, | |||
), | |||
"E0134": ( | |||
"Name %r is parameter and nonlocal", |
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.
"Name %r is parameter and nonlocal", | |
"Name %r is both parameter and nonlocal", |
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 good to change to .. is both parameter and nonlocal
, but python says .. is parameter and nonlocal
:
>>> def tomato(is_tasty):
... nonlocal is_tasty
...
File "<stdin>", line 2
SyntaxError: name 'is_tasty' is parameter and nonlocal
wdyt?
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.
Hm, I'd say let's overwrite Python here and use more "readable" English. As pylint output is often read form IDEs I think we an be a little bit more verbose than the traditional error messages Python uses.
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.
Totally agree.
doc/user_guide/checkers/features.rst
Outdated
@@ -65,6 +65,8 @@ Basic checker Messages | |||
Emitted when a name is used prior a global declaration, which results in an | |||
error since Python 3.6. This message can't be emitted when using Python < | |||
3.6. | |||
:name-is-parameter-and-nonlocal (E0134): *Name %r is parameter and nonlocal* |
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.
You're one of the very few contributors that actually generated the doc before opening a merge request, conratulation 👍 (Actually, you're probably the first one 😄 )
@@ -327,6 +333,19 @@ def _check_name_used_prior_global(self, node: nodes.FunctionDef) -> None: | |||
"used-prior-global-declaration", node=node_name, args=(name,) | |||
) | |||
|
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.
We can probably optimize by using util.only_required_for_messages
here.
This seems like an easy PR for a maintainer to pick up. @ramonsaraiva are you still planning to return to this? If not, would you mind if I picked it up? |
All good if you want to pick it up @DanielNoord - things got busier around here. I'll keep an eye on the solution! |
The tests here show a genuine failing false positive. |
🤖 Effect of this PR on checked open source code: 🤖 Effect on django:
This comment was generated for commit de419ac |
Type of Changes
Description
Adds a new message for when a
nonlocal
name is also a parameter in a function scopeCloses #6882