-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Implemented new check consider-using-dict-items
#4445
Conversation
it seems like one of the pre-commit checks is now failing due to the new check implemented 🤣
|
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.
Nice new checker. Although I have a feeling the tests are a little light for this kind of checker. I'll check the result on astroid and pygame before approving. (pip3 install git+ssh://[email protected]/yushao2/pylint.git
)
I will try to think of more tests for this in the meantime, thanks! |
I found a new bug where the checker failed to detect a new test case def another_good():
for k in out_of_scope_dict:
k = 1
k = 2
k = 3
print(out_of_scope_dict[k]) #should be okay, but error emitted and my solution to that was to get the subscript's latest assignment line number and compare it to the node (for-loop)'s line number last_definition_lineno = value.lookup(value.name)[1][-1].lineno
if last_definition_lineno > node.lineno:
# Ignore this subscript if it has been redefined after
# the for loop. This checks for the line number using .lookup()
# to get the line number where the iterating object was last
# defined and compare that to the for loop's line number
continue will update my PR with these changes |
consider-using-dict-items
(#3389)consider-using-dict-items
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 really like this one! As a test I used it to run pylint over the codebase of home-assistant which resulted in multiple warnings. This MR will definitely improve code quality.
I though, I could contribute some test cases as well. Some of them are not yet passing though.
b_dict = {}
for k2 in b_dict: # Should not emit warning, key access necessary
b_dict[k2] = 2
for k3 in b_dict: # [consider-using-dict-items]
val = b_dict[k3]
for k4 in b_dict.keys(): # [consider-iterating-dictionary,consider-using-dict-items]
val = b_dict[k4]
class Foo:
c_dict = {}
for k5 in Foo.c_dict: # [consider-using-dict-items]
val = Foo.c_dict[k5]
for k6 in b_dict: # [consider-using-dict-items]
val = b_dict[k6]
b_dict[k6] = 2
val = [(k7, b_dict[k7]) for k7 in b_dict if b_dict[k7]] # [consider-using-dict-items]
val = any(True for k8 in b_dict if b_dict[k8]) # [consider-using-dict-items]
val = {k9: b_dict[k9] for k9 in b_dict} # [consider-using-dict-items]
We could also think about what should happen if an index-lookup is used together with dict.items
.
for k10, v in b_dict.items():
val = v + 2
print(b_dict[k10])
--
I realize this might all be a bit much at once, but keep in mind: The work you have already done is really good! Especially for a first-time contributor 🐬 These are now just the finishing touches. If you encounter any issues or need help, please don't hesitate to ask.
Thank you so much for your review! Admittedly I'm still trying to pick up the ropes here so I'm thankful for the review given and for highlighting new test cases/usage scenarios where I haven't thought of. I'll work on the suggested changes first, then try to look at making the new test cases pass. Thanks once again! |
|
I think this should be in a different error message altogether -- should I try my hand at implementing it within this PR or perhaps I should create a separate PR for that. I think it might be better to not bloat up this PR |
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.
A few more comments. This is already looking really good!
As mentioned here, you can move _check_if_dict_keys_used
to pylint/checkers/utils.py
. I would also recommend renaming it.
A nice to have would be if you could order the individual test cases a bit. The way they are currently, it's sometimes difficult to understand what they actually should test.
--
think this should be in a different error message altogether -- should I try my hand at implementing it within this PR or perhaps I should create a separate PR for that. I think it might be better to not bloat up this PR
I fully agree. If you like, you could take a stab at it after this PR is done.
@yushao2 Just one more thing: Could you edit the previous commit messages and remove the issue link and the |
I just went through the reviews and did the necessary changes -- pardon the amount of commits here, I'll squash the commits in the PR when all looks good and ready to merge |
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.
A few more comments. Mainly about the utils
function.
FYI: I pushed a small commit with minor style changes. I though it wasn't worth leaving extra comments for them. Just so you know.
I just went through the reviews and did the necessary changes -- pardon the amount of commits here, I'll squash the commits in the PR when all looks good and ready to merge
You don't need to. I'll squash merge the PR anyway. Just keep what you think is logical or what would improve the review process, i.e., I would appreciate if you don't squash your next changes into the old ones 😄
- Also, added accompanying test cases
Seems like it was necessary after all. py36 - 38 tests are failing. Just interesting that py39 is fine. if isinstance(value, astroid.Index):
value = value.value |
I'll work on this again in a bit -- got interrupted by some schoolwork matters |
No hurry. Schoolwork is more important
Just looked at the astroid code, turns out |
pylint/checkers/utils.py
Outdated
# Is it a proper keys call? | ||
is_iterating_dict_keys = ( | ||
isinstance(node.iter, astroid.Call) | ||
and isinstance(node.iter.func, astroid.Attribute) | ||
and node.iter.func.attrname == "keys" | ||
and isinstance(safe_infer(node.iter.func), astroid.BoundMethod) | ||
) | ||
# Is it a proper dictionary? | ||
is_iterating_dict = isinstance( | ||
node.iter, (astroid.Name, astroid.Attribute) | ||
) and isinstance(safe_infer(node.iter), astroid.Dict) | ||
|
||
if is_iterating_dict or is_iterating_dict_keys: | ||
return node.iter.as_string().partition(".keys")[0] | ||
|
||
return 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.
I should have probably explained my suggestion better. IMO this function is now a lot more difficult to understand. What do you think of this one here?
# Is it a proper keys call?
if (
isinstance(node.iter, astroid.Call)
and isinstance(node.iter.func, astroid.Attribute)
and node.iter.func.attrname == "keys"
):
inferred = safe_infer(node.iter.func)
if not isinstance(inferred, astroid.BoundMethod):
return None
return node.iter.as_string().rpartition(".keys")[0]
# Is it a dictionary?
if isinstance(node.iter, (astroid.Name, astroid.Attribute)):
inferred = safe_infer(node.iter)
if not isinstance(inferred, astroid.Dict):
return None
return node.iter.as_string()
return None
Also note the use of rpartition
instead of partition
.
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 understand the usage of rpartition here -- the new test case was written because it was failing on (left) partition 😄
Thank you so much for your continued patience with reviewing my code and giving suggestions -- I'm learning a lot from this!
Let me work on the changes and ping again when I'm done
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.
Understood your code and what you meant now -- have changed in 797b050
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 pushed some last minor style changes and did a final test run. Everything seems to work as expected, so I guess this PR is finished 🚀
Thank you for continuing to work on it, even after those long reviews. You really did an amazing job here. Looking forward to seeing more PRs from you 😉
Steps
doc/whatsnew/<current release.rst>
.Description
Implemented a new checker,
consider-using-dict-items
, which checks for instances where afor loop is iterating over dictionary keys, and the same dictionary is being sub-scripted by the key in the
loop body.
Type of Changes
Related Issue
Closes #3389