-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a new check
dict-init-mutate
(#7794)
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
- Loading branch information
1 parent
ed404d3
commit f7d681b
Showing
10 changed files
with
136 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fruit_prices = {} # [dict-init-mutate] | ||
fruit_prices['apple'] = 1 | ||
fruit_prices['banana'] = 10 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
fruit_prices = {"apple": 1, "banana": 10} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[MAIN] | ||
load-plugins=pylint.extensions.dict_init_mutate, |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Add new extension checker ``dict-init-mutate`` that flags mutating a dictionary immediately | ||
after the dictionary was created. | ||
|
||
Closes #2876 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html | ||
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE | ||
# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt | ||
|
||
"""Check for use of dictionary mutation after initialization.""" | ||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING | ||
|
||
from astroid import nodes | ||
|
||
from pylint.checkers import BaseChecker | ||
from pylint.checkers.utils import only_required_for_messages | ||
from pylint.interfaces import HIGH | ||
|
||
if TYPE_CHECKING: | ||
from pylint.lint.pylinter import PyLinter | ||
|
||
|
||
class DictInitMutateChecker(BaseChecker): | ||
name = "dict-init-mutate" | ||
msgs = { | ||
"C3401": ( | ||
"Declare all known key/values when initializing the dictionary.", | ||
"dict-init-mutate", | ||
"Dictionaries can be initialized with a single statement " | ||
"using dictionary literal syntax.", | ||
) | ||
} | ||
|
||
@only_required_for_messages("dict-init-mutate") | ||
def visit_assign(self, node: nodes.Assign) -> None: | ||
""" | ||
Detect dictionary mutation immediately after initialization. | ||
At this time, detecting nested mutation is not supported. | ||
""" | ||
if not isinstance(node.value, nodes.Dict): | ||
return | ||
|
||
dict_name = node.targets[0] | ||
if len(node.targets) != 1 or not isinstance(dict_name, nodes.AssignName): | ||
return | ||
|
||
first_sibling = node.next_sibling() | ||
if ( | ||
not first_sibling | ||
or not isinstance(first_sibling, nodes.Assign) | ||
or len(first_sibling.targets) != 1 | ||
): | ||
return | ||
|
||
sibling_target = first_sibling.targets[0] | ||
if not isinstance(sibling_target, nodes.Subscript): | ||
return | ||
|
||
sibling_name = sibling_target.value | ||
if not isinstance(sibling_name, nodes.Name): | ||
return | ||
|
||
if sibling_name.name == dict_name.name: | ||
self.add_message("dict-init-mutate", node=node, confidence=HIGH) | ||
|
||
|
||
def register(linter: PyLinter) -> None: | ||
linter.register_checker(DictInitMutateChecker(linter)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
"""Example cases for dict-init-mutate""" | ||
# pylint: disable=use-dict-literal, invalid-name | ||
|
||
base = {} | ||
|
||
fruits = {} | ||
for fruit in ["apple", "orange"]: | ||
fruits[fruit] = 1 | ||
fruits[fruit] += 1 | ||
|
||
count = 10 | ||
fruits = {"apple": 1} | ||
fruits["apple"] += count | ||
|
||
config = {} # [dict-init-mutate] | ||
config['pwd'] = 'hello' | ||
|
||
config = {} # [dict-init-mutate] | ||
config['dir'] = 'bin' | ||
config['user'] = 'me' | ||
config['workers'] = 5 | ||
print(config) | ||
|
||
config = {} # Not flagging calls to update for now | ||
config.update({"dir": "bin"}) | ||
|
||
config = {} # [dict-init-mutate] | ||
config['options'] = {} # Identifying nested assignment not supporting this yet. | ||
config['options']['debug'] = False | ||
config['options']['verbose'] = True | ||
|
||
|
||
config = {} | ||
def update_dict(di): | ||
"""Update a dictionary""" | ||
di["one"] = 1 | ||
|
||
update_dict(config) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[MAIN] | ||
load-plugins=pylint.extensions.dict_init_mutate, |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
dict-init-mutate:15:0:15:11::Declare all known key/values when initializing the dictionary.:HIGH | ||
dict-init-mutate:18:0:18:11::Declare all known key/values when initializing the dictionary.:HIGH | ||
dict-init-mutate:27:0:27:11::Declare all known key/values when initializing the dictionary.:HIGH |