Skip to content

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

Conversation

ramonsaraiva
Copy link
Contributor

@ramonsaraiva ramonsaraiva commented Oct 6, 2022

Type of Changes

Type
✨ New feature

Description

Adds a new message for when a nonlocal name is also a parameter in a function scope

Closes #6882

@mbyrnepr2
Copy link
Member

mbyrnepr2 commented Oct 6, 2022

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.
Some recent additions here: https://github.com/PyCQA/pylint/pull/7576/files
(Often the names of fruit or animals are used for variable naming in the examples)

These can be tested locally with tox test_doc.

@DanielNoord DanielNoord added the Enhancement ✨ Improvement to a component label Oct 6, 2022
@DanielNoord DanielNoord added this to the 2.16.0 milestone Oct 6, 2022
Copy link
Collaborator

@DanielNoord DanielNoord left a 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]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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()

Copy link
Collaborator

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.

Copy link
Collaborator

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.

Copy link
Contributor Author

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.

Copy link
Collaborator

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.

Copy link
Contributor Author

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.

Copy link
Member

@mbyrnepr2 mbyrnepr2 Oct 6, 2022

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?

Copy link
Collaborator

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.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"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",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Name %r is parameter and nonlocal",
"Name %r is both parameter and nonlocal",

Copy link
Contributor Author

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?

Copy link
Collaborator

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree.

@@ -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*
Copy link
Member

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,)
)

Copy link
Member

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.

@DanielNoord
Copy link
Collaborator

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?

@ramonsaraiva
Copy link
Contributor Author

All good if you want to pick it up @DanielNoord - things got busier around here. I'll keep an eye on the solution!

@Pierre-Sassoulas Pierre-Sassoulas added the Needs take over 🛎️ Orignal implementer went away but the code could be salvaged. label Nov 11, 2022
@DanielNoord
Copy link
Collaborator

The tests here show a genuine failing false positive.

@github-actions
Copy link
Contributor

🤖 Effect of this PR on checked open source code: 🤖

Effect on django:
The following messages are now emitted:

  1. name-is-parameter-and-nonlocal:
    Name 'condition' is both parameter and nonlocal
    https://github.com/django/django/blob/1be7e36f85c927560e8c3c1eda05a7e43a66cd22/django/test/testcases.py#L1580

This comment was generated for commit de419ac

@Pierre-Sassoulas Pierre-Sassoulas modified the milestones: 2.16.0, 2.17.0 Jan 8, 2023
@Pierre-Sassoulas Pierre-Sassoulas modified the milestones: 2.17.0, 3.0.0 Mar 7, 2023
@mbyrnepr2 mbyrnepr2 closed this Jun 19, 2023
@jacobtylerwalls jacobtylerwalls removed the Needs take over 🛎️ Orignal implementer went away but the code could be salvaged. label Jun 19, 2023
@Pierre-Sassoulas Pierre-Sassoulas modified the milestones: 3.0.0, 3.0.0a8 Sep 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement ✨ Improvement to a component
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add message name-is-parameter-and-nonlocal
5 participants