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
Added new rule for checking for infinite while loops. Added test call… #1066
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! Great progress here. 👍
I have added some suggestions about your solution.
@@ -203,6 +206,25 @@ def _check_multiline_loop(self, node: _AnyLoop) -> None: | |||
self.add_violation(MultilineLoopViolation(node)) | |||
break | |||
|
|||
def _has_break_raise_or_return(self, node: _AnyLoop) -> bool: |
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.
This can be moved to logic/
dir
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.
Done!
return True | ||
return False | ||
|
||
def _check_infinite_while_loop(self, node: _AnyLoop) -> 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.
This can be moved to a another or a new visitor, that is ast.While
specific.
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.
Shouldn't this be inside of this class though, since it says at line 129 "Checks ''for'' and ''while'' loops" ? It seems like _check_multiline_loop also is ast.While specific.
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 think what I have implemented as of now might work, but let me know!
Pull Request Test Coverage Report for Build 2441
💛 - Coveralls |
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 a lot!
Now, let me show how integration test can be created:
def infinite_loop():
while True:
print('forever')
And put it into tests/fixtures/noqa.py
with correct noqa
comment.
Then adjust tests/test_checker/test_noqa.py
to contain this violation.
That's it! Awesome job! 👍
from wemake_python_styleguide.compat.aliases import ForNodes | ||
from wemake_python_styleguide.types import AnyFor, ContextNodes | ||
|
||
_AnyLoop = Union[AnyFor, ast.While] |
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 go to types.py
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.
Done!
@@ -29,3 +32,36 @@ def get_parent(node: ast.AST) -> Optional[ast.AST]: | |||
def get_context(node: ast.AST) -> Optional[ContextNodes]: | |||
"""Returns the context or ``None`` if node has no context.""" | |||
return getattr(node, 'wps_context', None) | |||
|
|||
|
|||
def does_loop_contain_node( # TODO: move, reuse in annotations.py |
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, remove TODO
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.
Done!
loop: Optional[_AnyLoop], | ||
to_check: ast.Break, | ||
) -> bool: | ||
"""Helper function for has_break method.""" |
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, change the description to something more meaningful.
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.
Done!
@@ -29,3 +32,36 @@ def get_parent(node: ast.AST) -> Optional[ast.AST]: | |||
def get_context(node: ast.AST) -> Optional[ContextNodes]: | |||
"""Returns the context or ``None`` if node has no context.""" | |||
return getattr(node, 'wps_context', None) | |||
|
|||
|
|||
def does_loop_contain_node( # TODO: move, reuse in annotations.py |
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.
Do we actually use this method from outside? If not, it should be protected. _does_loop_contain_node
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.
Done!
@@ -208,22 +178,21 @@ def _check_multiline_loop(self, node: _AnyLoop) -> None: | |||
|
|||
def _has_break_raise_or_return(self, node: _AnyLoop) -> bool: |
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.
This method can still be moved into logic/
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.
Sorry, that function should not have been in this commit in that location. It has been moved to logic/.
@@ -338,6 +307,6 @@ def _check_implicit_items(self, node: ast.For) -> None: | |||
|
|||
def _is_assigned_target(self, node: ast.Subscript) -> bool: | |||
parent = nodes.get_parent(node) | |||
if not isinstance(parent, (*AssignNodes, ast.AugAssign)): |
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.
Are you sure about this change? Why do you have to remove ast.AugAssign
?
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.
Sorry, I don't remember changing that, I must've pulled an outdated version of the code. Fixed.
I added one more small change so that "while True:" loops are defined with print("forever") inside. Hopefully everything is correct now! |
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.
Done! Only one little refactor remains.
Thanks a lot for your work!
from wemake_python_styleguide.visitors.ast.loops import WrongLoopVisitor | ||
|
||
incorrect_loop1 = """ | ||
while True: |
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.
Looks like there are a lot of repetitive patterns in these samples.
Let's parametrize them!
Like so:
incorrect_loop_template = """
while True:
{0}
"""
And later we can use this way of testing:
@pytest.mark.parametrize('keyword', [
'break',
'return',
'return some',
'raise Some',
'raise Some()',
'raise',
]
def test_whatever(keyword):
code = template.format(keyword)
We also need to test a nested keyword:
while True:
if some:
{0}
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.
Alright I think I fixed everything! Let me know what you think!
incorrect_loop_template_nonfunction3 = """ | ||
while True: | ||
if some: | ||
print('forever') |
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.
This is not parametrized
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.
How can I parametrize this in a way where {0} accepts any command except for (raise, return, and break)?
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.
Basically my most recent pull request has (raise, return, break, etc) parametrized, but not "print('forever')" which should just be replaced by any command that is not (raise, return, break, etc).
I think there is an issue with the configuration on the laptop I pushed my code with most recently. I originally was working on my Mac, which just crashed, so I switched to wsl on my Windows computer. I think the code will build properly if I submit on my Mac, but I can't use it right now :/ Is there any work around with this because I'm not sure I can satisfy the Travis CI build with my current laptop? |
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.
At last! @ragohin now this is ready to be merged. I will include this feature into 0.15
version.
You PR was not polished, so I had to work on it for a bit more.
I hope that you will get your bonus credits for this work. Good luck!
Thanks a lot for taking the time to contribute to our project. I really appreciate that! 👍
Codecov Report
@@ Coverage Diff @@
## master #1066 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 105 105
Lines 5512 5531 +19
Branches 1217 1224 +7
=========================================
+ Hits 5512 5531 +19
Continue to review full report at Codecov.
|
…ed test_infinite_while_loops.py
I have made things!
Checklist
CHANGELOG.md
Related issues
Closes #1007