-
-
Notifications
You must be signed in to change notification settings - Fork 381
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
[#212] Forbid variables self reassignment #262
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.
Thanks!
WrongVariableAssignmentVisitor, | ||
) | ||
|
||
wrong_fragment = """ |
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.
Can you please also test this example:
test_variable = 5
test_variable = test_variable = 5
and
test_variable = 5
test_variable = other = test_variable = 5
All these examples should raise a violation.
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.
Fixed
@Roxe322 please, also fix merge conflicts. |
""" | ||
|
||
|
||
def test_self_variable_reassignment( |
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.
Please, also check this example:
x, y = x, y
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.
Fixed
Ooops, some fails, I need to check it |
Can you please explain me what is going wrong there? In my local machine pytest passing successfully :( |
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.
@Roxe322 thanks for your time and effort.
This code has a lot of major problems, that are hard to address in a single review. However, I will point some things that I find the most important:
- Some code looks pretty identical and can be moved to the function of its own, like:
wemake-python-styleguide/wemake_python_styleguide/visitors/ast/naming.py
Lines 178 to 179 in f2a091e
if getattr(node.value, 'id', None) in target_names or ( len(target_names) != len(list(set(target_names)))): wemake-python-styleguide/wemake_python_styleguide/visitors/ast/naming.py
Lines 190 to 192 in f2a091e
if node_values in target_names or (len(target_names) != len(list(set(target_names))) ): - I am not sure what are you trying to do in these lines:
wemake-python-styleguide/wemake_python_styleguide/visitors/ast/naming.py
Lines 182 to 185 in f2a091e
target_names = [getattr(target, 'elts', None) for target in node.targets if isinstance(target, ast.Tuple)] for index in range(len(target_names)): target_names[index] = [name.id for name in target_names[index]] - Try to adapt to the code style we have in a project. You won't find a single other example of neither of these things:
wemake-python-styleguide/wemake_python_styleguide/visitors/ast/naming.py
Lines 190 to 192 in f2a091e
if node_values in target_names or (len(target_names) != len(list(set(target_names))) ): wemake-python-styleguide/wemake_python_styleguide/visitors/ast/naming.py
Lines 182 to 183 in f2a091e
target_names = [getattr(target, 'elts', None) for target in node.targets if isinstance(target, ast.Tuple)]
I have asked @AlwxSin to help you with this PR.
Thanks again for your hard work! I would be happy to merge this PR as soon as it will be refactored.
|
||
|
||
@pytest.mark.parametrize('fragment', [wrong_fragment, |
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 use different style for this kind of things: please, see
wemake-python-styleguide/tests/test_visitors/test_ast/test_naming/test_variable_usages.py
Lines 37 to 48 in 56ce9b9
@pytest.mark.parametrize('code', [ | |
calling_function, | |
called_function, | |
calling_method, | |
called_method, | |
accessing_attribute, | |
accessed_attribute, | |
raising_variable, | |
returning_variable, | |
awaiting_variable, | |
yielding_variable, | |
]) |
def test_self_variable_reassignment( | ||
assert_errors, parse_ast_tree, default_options, | ||
assert_errors, parse_ast_tree, fragment, default_options, |
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 use code
variable name for code samples, not fragment
. Please, rename it.
""" | ||
|
||
#: Error message shown to the user. | ||
error_template = 'Found reassigning variable to itself' |
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.
Can you please provide the name of the variable here? Currently you miss '{0}'
in template string
target_names = [target.id for target in node.targets | ||
if isinstance(target, ast.Name)] | ||
if target_names: | ||
if getattr(node.value, 'id', None) in target_names or ( |
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.
Each step in this if
should be a separate variable:
taget_name = getattr(node.value, 'id', None)
PLEASE_FIND_CORRECT_NAME = len(target_names) != len(set(target_names))
if target_name in target_names or PLEASE_FIND_CORRECT_NAME: ...
Replace PLEASE_FIND_CORRECT_NAME
with anything you find appropriate.
else: | ||
target_names = [getattr(target, 'elts', None) for target | ||
in node.targets if isinstance(target, ast.Tuple)] | ||
for index in range(len(target_names)): |
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.
Use enumerate
instead. range(len())
is an anti-pattern.
node_values = [] | ||
if isinstance(node.value, ast.Tuple): | ||
node_values = [getattr(node_value, 'id', None) for node_value | ||
in node.value.elts] |
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.
Please, do not abuse line breaking like this. This linter currently unable to catch these things. But, I will be more than happy to create new rules to forbid this kind of line breaks.
node_values = [getattr(node_value, 'id', None) for node_value | ||
in node.value.elts] | ||
if node_values in target_names or (len(target_names) != | ||
len(list(set(target_names))) |
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.
There's no need to cast set
to list
.
Thank you for your detailed review, I will try to fix all problems. About line breaking - it is very hard to fit into 80 symbols line length, and because of this such ugly line breakings are in my code :) |
@Roxe322 the general rule is: if it is hard to fit your logic into one line - you have too many logic. |
Your linter one time told me that I have too much local variables in one method, so I tried not to meet this warning again :) |
Pull Request Test Coverage Report for Build 520
💛 - Coveralls |
@sobolevn Fixed all problems, I think :) |
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.
Just a single thing to fix before merging!
Awesome job!
@@ -171,26 +173,34 @@ def visit_Assign(self, node: ast.Assign) -> None: | |||
class WrongVariableAssignmentVisitor(BaseNodeVisitor): | |||
"""Finds wrong variables assignments.""" | |||
|
|||
def create_target_names(self, target: AssignTarget) -> AssignTargetNameList: |
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.
Should start with _
, since it is a protected method.
wrong_fragment_tuple_assignment = """ | ||
x = 1 | ||
y = 2 | ||
x, y = x, y |
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.
One more thing. Can you please test this case:
x = 1
y = 2
x, y = y, x
This should be a perfectly valid case. But, I am not sure if that is working right now.
@@ -23,6 +24,8 @@ | |||
from wemake_python_styleguide.visitors.decorators import alias | |||
|
|||
VariableDef = Union[ast.Name, ast.Attribute, ast.ExceptHandler] | |||
AssignTarget = List[ast.expr] |
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.
AssignTarget = List[ast.expr] | |
AssignTargets = List[ast.expr] |
@@ -23,6 +24,8 @@ | |||
from wemake_python_styleguide.visitors.decorators import alias | |||
|
|||
VariableDef = Union[ast.Name, ast.Attribute, ast.ExceptHandler] | |||
AssignTarget = List[ast.expr] | |||
AssignTargetNameList = List[Union[str, Tuple[str]]] |
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.
AssignTargetNameList = List[Union[str, Tuple[str]]] | |
AssignTargetsNameList = List[Union[str, Tuple[str]]] |
@Roxe322 awesome job! |
Thank you! I'm glad to help you :) |
I have made things!
CHANGELOG.md
Related issues
Closes: #212