Skip to content

Commit

Permalink
Add check for wrong keyword in starred dict (#803)
Browse files Browse the repository at this point in the history
* Add check for wrong keyword in starred dict

* Fix code style

* Add tests and move visitor to best_practices
  • Loading branch information
egorvdot authored and sobolevn committed Oct 1, 2019
1 parent 94479b6 commit aeedfc3
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 2 deletions.
2 changes: 1 addition & 1 deletion tests/fixtures/noqa.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ def some_other_function():
file_obj = open('filaname.py') # noqa: WPS515
print(type(file_obj) == int) # noqa: WPS516

print(*[]) # noqa: WPS517
print(*[], **{'@': 1}) # noqa: WPS517, WPS445

for range_len in range(len(file_obj)): # noqa: WPS518
print(range_len)
Expand Down
3 changes: 2 additions & 1 deletion tests/test_checker/test_noqa.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
'WPS442': 2,
'WPS443': 1,
'WPS444': 1,
'WPS445': 1,

'WPS500': 1,
'WPS501': 1,
Expand All @@ -184,7 +185,7 @@
'WPS514': 1,
'WPS515': 1,
'WPS516': 1,
'WPS517': 1,
'WPS517': 2,
'WPS518': 1,
'WPS519': 1,
'WPS520': 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-

import pytest

from wemake_python_styleguide.violations.best_practices import (
WrongNamedKeywordViolation,
)
from wemake_python_styleguide.visitors.ast.statements import (
WrongNamedKeywordVisitor,
)


@pytest.mark.parametrize('code', [
'print(**{"@": "2"})',
'print(**{"2ab": "2"})',
'print(**{"!": "2"})',
'print(**{"&a": "2"})',
'print(**{"()": "2"})',
'print(**{"-b": "2"})',
'print(**{"+1": "2"})',
'print(**{"#FFF": "2"})',
'print(**{"some.value": "2"})',
'print(**{"Username[FIELD]": "2"})',
'print(**{"call()": "2"})',
'print(end="|", **{"2ab": "2"})',
])
def test_wrong_starred_keyword(
assert_errors,
parse_ast_tree,
default_options,
code,
):
"""Testing that pointless starred expression is detected."""
tree = parse_ast_tree(code)

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

assert_errors(visitor, [WrongNamedKeywordViolation])


@pytest.mark.parametrize('code', [
'_list = [1, 2]',
'_dict = {"end": " "}',
'print(*_list, **_dict)',
'print(end="1", **{"a": 1})',
'filter(**{User.USERNAME_FIELD: username})', # noqa: P103
'filter(**{"a2": 1, _: 2})',
'filter(**{"a": 1, b: 2})',
'filter(**{"_a": 1, b: 2})',
'filter(**{"a_": 1, b: 2})',
'filter(**{"a": 1, call(): 2})',
'filter(**{"a": 1, b.method(): 2})',
'filter(**{b["a"]: 2})',
])
def test_correct_starred_keyword(
assert_errors,
parse_ast_tree,
default_options,
code,
):
"""Testing that pointless starred expression is detected."""
tree = parse_ast_tree(code)

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

assert_errors(visitor, [])
1 change: 1 addition & 0 deletions wemake_python_styleguide/presets/types/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
statements.StatementsWithBodiesVisitor,
statements.WrongParametersIndentationVisitor,
statements.PointlessStarredVisitor,
statements.WrongNamedKeywordVisitor,

keywords.WrongRaiseVisitor,
keywords.WrongKeywordVisitor,
Expand Down
29 changes: 29 additions & 0 deletions wemake_python_styleguide/violations/best_practices.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
OuterScopeShadowingViolation
UnhashableTypeInHashViolation
WrongKeywordConditionViolation
WrongNamedKeywordViolation
Best practices
--------------
Expand Down Expand Up @@ -113,6 +114,7 @@
.. autoclass:: OuterScopeShadowingViolation
.. autoclass:: UnhashableTypeInHashViolation
.. autoclass:: WrongKeywordConditionViolation
.. autoclass:: WrongNamedKeywordViolation
"""

Expand Down Expand Up @@ -1743,3 +1745,30 @@ class WrongKeywordConditionViolation(ASTViolation):

error_template = 'Found wrong keyword condition: {0}'
code = 444


class WrongNamedKeywordViolation(ASTViolation):
"""
Forbids to have wrong named keywords in starred dicts.
Reasoning:
Using wrong keywords in starred dict.
Eg.: ``print(**{'@': 1})``.
Solution:
Don't use incorrect identifiers for keywords.
Example::
# Correct:
print(**{'end': '|'})
# Wrong:
print(**{'3end': '|'})
.. versionadded:: 0.13.0
"""

code = 445
error_template = 'Found wrong named keyword in starred dict'
32 changes: 32 additions & 0 deletions wemake_python_styleguide/visitors/ast/statements.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from wemake_python_styleguide.violations.best_practices import (
StatementHasNoEffectViolation,
UnreachableCodeViolation,
WrongNamedKeywordViolation,
)
from wemake_python_styleguide.violations.consistency import (
ParametersIndentationViolation,
Expand Down Expand Up @@ -341,3 +342,34 @@ def _has_non_string_keys(self, node: ast.keyword) -> bool:
if not isinstance(key_node, ast.Str):
return True
return False


@final
class WrongNamedKeywordVisitor(BaseNodeVisitor):
"""Responsible for absence of wrong keywords."""

def visit_Call(self, node: ast.Call) -> None:
"""Checks useless call arguments."""
self._check_double_starred_dict(node.keywords)
self.generic_visit(node)

def _check_double_starred_dict(
self,
keywords: Sequence[ast.keyword],
) -> None:
for keyword in keywords:
if keyword.arg is not None:
continue

if self._has_wrong_keys(keyword):
self.add_violation(WrongNamedKeywordViolation(keyword.value))

def _has_wrong_keys(self, node: ast.keyword) -> bool:
if not isinstance(node.value, ast.Dict):
return False

for key_node in node.value.keys:
if isinstance(key_node, ast.Str):
if not str.isidentifier(key_node.s):
return True
return False

0 comments on commit aeedfc3

Please sign in to comment.