Skip to content

Commit

Permalink
Closes #602
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolevn committed Jul 13, 2019
1 parent c9bfe3a commit b65ef65
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 44 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ big cudos to the developers of this wonderful tool.
- Forces to write `isinstance(some, (A, B))`
instead of `isinstance(some, A) or isinstance(some, B)`
- Forbids to use `isinstance(some (A,))`
- Forces to merge `a == b or a == c` into `a in {b, c}` and
to merge `a != b and a != c` into `a not in {b, c}`

### Bugfixes

Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/noqa.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ def some_function():
for index in [1, 2]: # noqa: Z335
print(index)

print(one == 'a' or one == 'b') # noqa: Z336

try:
anti_z444 = 1
except BaseException: # noqa: Z424
Expand Down
1 change: 1 addition & 0 deletions tests/test_checker/test_noqa.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def test_noqa_fixture_disabled(absolute_path, all_violations):
'Z333': 1,
'Z334': 1,
'Z335': 1,
'Z336': 1,

'Z400': 0,
'Z401': 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
ImplicitComplexCompareViolation,
)
from wemake_python_styleguide.visitors.ast.conditions import (
BooleanConditionVisitor,
ImplicitBoolPatternsVisitor,
)

# Wrong:
Expand Down Expand Up @@ -87,7 +87,7 @@ def test_implicit_complex_compare(
"""Testing implicit complex compare."""
tree = parse_ast_tree(code.format(*comparators))

visitor = BooleanConditionVisitor(default_options, tree=tree)
visitor = ImplicitBoolPatternsVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [ImplicitComplexCompareViolation])
Expand Down Expand Up @@ -126,7 +126,7 @@ def test_implicit_complex_compare_reversed(
"""Testing implicit complex compare."""
tree = parse_ast_tree(code.format(*comparators))

visitor = BooleanConditionVisitor(default_options, tree=tree)
visitor = ImplicitBoolPatternsVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [ImplicitComplexCompareViolation])
Expand Down Expand Up @@ -166,7 +166,7 @@ def test_compare_wrong_values(
"""Testing implicit complex compare."""
tree = parse_ast_tree(code.format(*comparators))

visitor = BooleanConditionVisitor(default_options, tree=tree)
visitor = ImplicitBoolPatternsVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [])
Expand Down Expand Up @@ -206,7 +206,7 @@ def test_regular_compare(
"""Testing implicit complex compare."""
tree = parse_ast_tree(code.format(*comparators))

visitor = BooleanConditionVisitor(default_options, tree=tree)
visitor = ImplicitBoolPatternsVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [])
Expand All @@ -228,7 +228,7 @@ def test_regular_short_compare(
"""Testing implicit complex compare."""
tree = parse_ast_tree(code)

visitor = BooleanConditionVisitor(default_options, tree=tree)
visitor = ImplicitBoolPatternsVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [])
104 changes: 104 additions & 0 deletions tests/test_visitors/test_ast/test_conditions/test_implicit_in.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-

import pytest

from wemake_python_styleguide.violations.consistency import (
ImplicitInConditionViolation,
)
from wemake_python_styleguide.visitors.ast.conditions import (
ImplicitBoolPatternsVisitor,
)

# Correct:

eq_and = '{0} == some1 and {1} == some2'
noteq_or = '{0} != some1 or {1} != some2'

# Wrong:

eq_or = '{0} == some1 or {1} == some2'
noteq_and = '{0} != some1 and {1} != some2'


@pytest.mark.parametrize('code', [
eq_and,
noteq_or,
eq_or,
noteq_and,
])
@pytest.mark.parametrize('first, second', [
('first', 'second'),
('one.attr', 'one'),
('first', 'first()'),
('value.method()', 'value.method'),
('value.method(1)', 'value.method(2)'),
])
def test_different_in_values(
code,
first,
second,
assert_errors,
parse_ast_tree,
default_options,
):
"""Testing regular conditions."""
tree = parse_ast_tree(code.format(first, second))

visitor = ImplicitBoolPatternsVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [])


@pytest.mark.parametrize('code', [
eq_and,
noteq_or,
])
@pytest.mark.parametrize('first, second', [
('first', 'first'),
('one.attr', 'one.attr'),
('first()', 'first()'),
('value.method(1)', 'value.method(2)'),
])
def test_safe_patterns_in_values(
code,
first,
second,
assert_errors,
parse_ast_tree,
default_options,
):
"""Testing safe in patterns."""
tree = parse_ast_tree(code.format(first, second))

visitor = ImplicitBoolPatternsVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [])


@pytest.mark.parametrize('code', [
eq_or,
noteq_and,
])
@pytest.mark.parametrize('first, second', [
('first', 'first'),
('one.attr', 'one.attr'),
('first()', 'first()'),
('value.method(1)', 'value.method(1)'),
])
def test_wrong_patterns_in_values(
code,
first,
second,
assert_errors,
parse_ast_tree,
default_options,
):
"""Testing safe in patterns."""
tree = parse_ast_tree(code.format(first, second))

visitor = ImplicitBoolPatternsVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [ImplicitInConditionViolation])
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
ImplicitTernaryViolation,
)
from wemake_python_styleguide.visitors.ast.conditions import (
BooleanConditionVisitor,
ImplicitBoolPatternsVisitor,
)

# Correct:
Expand Down Expand Up @@ -49,7 +49,7 @@ def test_implicit_ternary(
"""Testing implicit ternary."""
tree = parse_ast_tree(code.format(first, second))

visitor = BooleanConditionVisitor(default_options, tree=tree)
visitor = ImplicitBoolPatternsVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [ImplicitTernaryViolation])
Expand Down Expand Up @@ -85,7 +85,7 @@ def test_regular_compare_not_ternary(
"""Testing regular compares and not ternaries."""
tree = parse_ast_tree(code.format(first, second))

visitor = BooleanConditionVisitor(default_options, tree=tree)
visitor = ImplicitBoolPatternsVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [])
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
from wemake_python_styleguide.violations.best_practices import (
SameElementsInConditionViolation,
)
from wemake_python_styleguide.violations.consistency import (
ImplicitTernaryViolation,
)
from wemake_python_styleguide.visitors.ast.conditions import (
BooleanConditionVisitor,
)
Expand Down Expand Up @@ -84,7 +81,4 @@ def test_duplicate_element_and_ternary(
visitor = BooleanConditionVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [
ImplicitTernaryViolation,
SameElementsInConditionViolation,
])
assert_errors(visitor, [SameElementsInConditionViolation])
1 change: 1 addition & 0 deletions wemake_python_styleguide/presets/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@

conditions.IfStatementVisitor,
conditions.BooleanConditionVisitor,
conditions.ImplicitBoolPatternsVisitor,

# Classes:
classes.WrongClassVisitor,
Expand Down
34 changes: 34 additions & 0 deletions wemake_python_styleguide/violations/consistency.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
ImplicitComplexCompareViolation
ReversedComplexCompareViolation
IncorectLoopIterTypeViolation
ImplicitInConditionViolation
Consistency checks
------------------
Expand Down Expand Up @@ -101,6 +102,7 @@
.. autoclass:: ImplicitComplexCompareViolation
.. autoclass:: ReversedComplexCompareViolation
.. autoclass:: IncorectLoopIterTypeViolation
.. autoclass:: ImplicitInConditionViolation
"""

Expand Down Expand Up @@ -1372,3 +1374,35 @@ class IncorectLoopIterTypeViolation(ASTViolation):

code = 335
error_template = 'Found incorrect `for` loop iter type'


@final
class ImplicitInConditionViolation(ASTViolation):
"""
Forbids to use multiple equality compare with the same variable name.
Reasoning:
Using double+ equality compare with ``or``
or double+ non-equality compare with ``and``
indicates that you have implicit ``in`` or ``not in`` condition.
It is just hidden from you.
Solution:
Refactor compares to use ``in`` or ``not in`` clauses.
Example::
# Correct:
print(some in {'first', 'second'})
print(some not in {'first', 'second'})
# Wrong:
print(some == 'first' or some == 'second')
print(some != 'first' and some != 'second')
.. versionadded:: 0.10.0
"""

code = 336
error_template = 'Found implicit `in` condition'
Loading

0 comments on commit b65ef65

Please sign in to comment.