Skip to content

Commit

Permalink
Adds a new rule to forbid extra syntax in pm subjects, closes #3217 (#…
Browse files Browse the repository at this point in the history
…3229)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
sobolevn and pre-commit-ci[bot] authored Dec 26, 2024
1 parent 9b071f6 commit b9d65b3
Show file tree
Hide file tree
Showing 57 changed files with 204 additions and 120 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ Semantic versioning in our case means:
- Adds a new rule to find too many `PEP695` type params
- Adds a new rule to find useless ternary expressions, #1706
- Adds a new rule to forbid `raise SystemError`, use `sys.exit` instead, #1786
- Adds a new rule to forbid extra syntax in `match ...` subjects, #3217
- Adds support to run `wemake-python-styleguide` as a `pre-commit` hook, #2588
- GitHub Action can now use `cwd:` parameter to specify
where your configuration file is, #2474
Expand Down
4 changes: 4 additions & 0 deletions tests/fixtures/noqa/noqa.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,10 @@ def foo2_func():
case SomeClass():
my_print('second')

match [some_value]: # noqa: WPS536
case SomeClass():
my_print('first')

class Baseline:
def method(self, number):
return number + 1
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 @@ -270,6 +270,7 @@
'WPS533': 1,
'WPS534': 1,
'WPS535': 1,
'WPS536': 1,
'WPS600': 1,
'WPS601': 1,
'WPS602': 2,
Expand Down
7 changes: 6 additions & 1 deletion tests/test_violations/test_codes.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
from collections import Counter

import pytest

from wemake_python_styleguide.compat.constants import PY311


def test_all_unique_violation_codes(all_violations):
"""Ensures that all violations have unique violation codes."""
codes = [int(violation.code) for violation in all_violations]
assert len(set(codes)) == len(all_violations)


def test_all_violations_are_final(all_violations):
@pytest.mark.skipif(not PY311, reason='@final has runtime effect since 3.11')
def test_all_violations_are_final(all_violations): # pragma: >=3.11 cover
"""Ensures that all violations are final."""
for violation_type in all_violations:
assert getattr(violation_type, '__final__', False), violation_type
Expand Down
67 changes: 67 additions & 0 deletions tests/test_visitors/test_ast/test_pm/test_extra_subject_syntax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import pytest

from wemake_python_styleguide.violations.refactoring import (
ExtraMatchSubjectSyntaxViolation,
)
from wemake_python_styleguide.visitors.ast.pm import MatchSubjectVisitor

template = """
match {0}:
case SomeClass():
my_print('first')
"""


@pytest.mark.parametrize(
'code',
[
'[Some()]',
'[first, second]',
'{var, other}',
'{test : result}',
'{test : "value"}',
'(one,)',
],
)
def test_wrong_usage_of_subjects(
assert_errors,
parse_ast_tree,
code,
default_options,
):
"""Ensures that extra syntax in subjects are forbidden."""
tree = parse_ast_tree(template.format(code))

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

assert_errors(visitor, [ExtraMatchSubjectSyntaxViolation])


@pytest.mark.parametrize(
'code',
[
'first',
'call()',
'attr.value',
'(first, second)',
'(many, items, here)',
# Will raise another violation:
'[1, 2]',
'()',
'[]',
],
)
def test_correct_usage_of_subjects(
assert_errors,
parse_ast_tree,
code,
default_options,
):
"""Ensures that it is possible to have correct subjects."""
tree = parse_ast_tree(template.format(code))

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

assert_errors(visitor, [])
3 changes: 1 addition & 2 deletions wemake_python_styleguide/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,9 @@
import tokenize
import traceback
from collections.abc import Iterator, Sequence
from typing import ClassVar, TypeAlias
from typing import ClassVar, TypeAlias, final

from flake8.options.manager import OptionManager
from typing_extensions import final

from wemake_python_styleguide import constants, types
from wemake_python_styleguide import version as pkg_version
Expand Down
3 changes: 1 addition & 2 deletions wemake_python_styleguide/logic/complexity/functions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from collections import defaultdict
from typing import TypeAlias
from typing import TypeAlias, final

import attr
from typing_extensions import final

from wemake_python_styleguide.types import (
AnyFunctionDef,
Expand Down
3 changes: 1 addition & 2 deletions wemake_python_styleguide/options/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,10 @@ class of violations that are forbidden to ignore inline, defaults to
"""

from collections.abc import Mapping, Sequence
from typing import ClassVar, Final, TypeAlias
from typing import ClassVar, Final, TypeAlias, final

import attr
from flake8.options.manager import OptionManager
from typing_extensions import final

from wemake_python_styleguide.options import defaults

Expand Down
3 changes: 2 additions & 1 deletion wemake_python_styleguide/options/validation.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import final

import attr
from typing_extensions import final

from wemake_python_styleguide.options import defaults
from wemake_python_styleguide.types import ConfigurationOptions
Expand Down
2 changes: 2 additions & 0 deletions wemake_python_styleguide/presets/types/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
loops,
modules,
operators,
pm,
redundancy,
statements,
subscripts,
Expand Down Expand Up @@ -82,6 +83,7 @@
subscripts.CorrectKeyVisitor,
decorators.WrongDecoratorVisitor,
redundancy.RedundantEnumerateVisitor,
pm.MatchSubjectVisitor,
# Modules:
modules.EmptyModuleContentsVisitor,
modules.MagicModuleFunctionsVisitor,
Expand Down
4 changes: 1 addition & 3 deletions wemake_python_styleguide/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@
"""

import ast
from typing import TypeAlias

from typing_extensions import Protocol
from typing import Protocol, TypeAlias

#: In cases we need to work with both import types.
AnyImport: TypeAlias = ast.Import | ast.ImportFrom
Expand Down
4 changes: 1 addition & 3 deletions wemake_python_styleguide/violations/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ class SomeViolation(ASTViolation):
import enum
import tokenize
from collections.abc import Callable
from typing import ClassVar, TypeAlias

from typing_extensions import final
from typing import ClassVar, TypeAlias, final

#: General type for all possible nodes where error happens.
ErrorNode: TypeAlias = ast.AST | tokenize.TokenInfo | None
Expand Down
2 changes: 1 addition & 1 deletion wemake_python_styleguide/violations/best_practices.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
"""

from typing_extensions import final
from typing import final

from wemake_python_styleguide.violations.base import (
ASTViolation,
Expand Down
2 changes: 1 addition & 1 deletion wemake_python_styleguide/violations/complexity.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
"""

from typing_extensions import final
from typing import final

from wemake_python_styleguide.violations.base import (
ASTViolation,
Expand Down
2 changes: 1 addition & 1 deletion wemake_python_styleguide/violations/consistency.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
"""

from typing_extensions import final
from typing import final

from wemake_python_styleguide.violations.base import (
ASTViolation,
Expand Down
2 changes: 1 addition & 1 deletion wemake_python_styleguide/violations/naming.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
"""

from typing_extensions import final
from typing import final

from wemake_python_styleguide.violations.base import (
ASTViolation,
Expand Down
2 changes: 1 addition & 1 deletion wemake_python_styleguide/violations/oop.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"""

from typing_extensions import final
from typing import final

from wemake_python_styleguide.violations.base import ASTViolation

Expand Down
37 changes: 36 additions & 1 deletion wemake_python_styleguide/violations/refactoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
DuplicateIfConditionViolation
UselessTernaryViolation
DuplicateCasePatternViolation
ExtraMatchSubjectSyntaxViolation
Refactoring opportunities
-------------------------
Expand Down Expand Up @@ -89,10 +90,11 @@
.. autoclass:: DuplicateIfConditionViolation
.. autoclass:: UselessTernaryViolation
.. autoclass:: DuplicateCasePatternViolation
.. autoclass:: ExtraMatchSubjectSyntaxViolation
"""

from typing_extensions import final
from typing import final

from wemake_python_styleguide.violations.base import (
ASTViolation,
Expand Down Expand Up @@ -1414,3 +1416,36 @@ class DuplicateCasePatternViolation(ASTViolation):

error_template = 'Found duplicate `case` pattern: {0}'
code = 535


@final
class ExtraMatchSubjectSyntaxViolation(ASTViolation):
"""
Forbid extra syntax around ``match`` like ``[]`` or ``{ ... }``.
Reasoning:
Adding extra lists / sets / dicts around your ``match`` subjects
is just adding more complexity.
Solution:
Use raw values or tuples instead.
Example::
# Correct:
match some:
case SomeClass(): ...
match (first, second):
case (1, 2): ...
# Wrong:
match [first, second]:
case [1, 2]: ...
.. versionadded:: 1.0.0
"""

error_template = 'Found `match` subject with extra syntax: {0}'
code = 536
2 changes: 1 addition & 1 deletion wemake_python_styleguide/violations/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"""

from typing_extensions import final
from typing import final

from wemake_python_styleguide.violations.base import SimpleViolation

Expand Down
4 changes: 1 addition & 3 deletions wemake_python_styleguide/visitors/ast/blocks.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import ast
from collections import defaultdict
from typing import TypeAlias, cast

from typing_extensions import final
from typing import TypeAlias, cast, final

from wemake_python_styleguide.compat.aliases import ForNodes, WithNodes
from wemake_python_styleguide.logic import walk
Expand Down
4 changes: 1 addition & 3 deletions wemake_python_styleguide/visitors/ast/builtins.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import ast
import string
from collections.abc import Sequence
from typing import ClassVar, Final, TypeAlias

from typing_extensions import final
from typing import ClassVar, Final, TypeAlias, final

from wemake_python_styleguide import constants
from wemake_python_styleguide.compat.aliases import (
Expand Down
4 changes: 1 addition & 3 deletions wemake_python_styleguide/visitors/ast/classes.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import ast
from collections import defaultdict
from typing import ClassVar

from typing_extensions import final
from typing import ClassVar, final

from wemake_python_styleguide import constants, types
from wemake_python_styleguide.compat.aliases import AssignNodes, FunctionNodes
Expand Down
7 changes: 3 additions & 4 deletions wemake_python_styleguide/visitors/ast/compares.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import ast
from typing import ClassVar

from typing_extensions import final
from typing import ClassVar, final

from wemake_python_styleguide.logic import nodes, walk
from wemake_python_styleguide.logic.naming.name_nodes import is_same_variable
Expand Down Expand Up @@ -187,7 +185,8 @@ def _check_constant_condition(
if is_match and not pattern_matching.is_constant_subject(real_node):
return
if not is_match and not isinstance(
real_node, self._forbidden_nodes
real_node,
self._forbidden_nodes,
):
return
self.add_violation(ConstantConditionViolation(node))
Expand Down
4 changes: 1 addition & 3 deletions wemake_python_styleguide/visitors/ast/complexity/access.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import ast
from itertools import takewhile
from typing import ClassVar, cast

from typing_extensions import final
from typing import ClassVar, cast, final

from wemake_python_styleguide.logic.tree import attributes
from wemake_python_styleguide.types import AnyAccess, AnyNodes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import ast

from typing_extensions import final
from typing import final

from wemake_python_styleguide.logic.complexity.annotations import (
get_annotation_complexity,
Expand Down
3 changes: 1 addition & 2 deletions wemake_python_styleguide/visitors/ast/complexity/calls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import ast
from itertools import takewhile

from typing_extensions import final
from typing import final

from wemake_python_styleguide.logic.tree.calls import parts
from wemake_python_styleguide.violations.complexity import (
Expand Down
3 changes: 1 addition & 2 deletions wemake_python_styleguide/visitors/ast/complexity/classes.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import ast
from collections import defaultdict

from typing_extensions import final
from typing import final

from wemake_python_styleguide.logic.naming import access
from wemake_python_styleguide.logic.nodes import get_parent
Expand Down
Loading

0 comments on commit b9d65b3

Please sign in to comment.