Skip to content
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

Issue 2910 #2934

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Semantic versioning in our case means:
- Fixes `OverusedStringViolation` not to include `'...'` string
- Removes `astor` package in favour of `ast.unparse`
- Fixes `IterableUnpackingViolation` with generic types and `TypeVarTuple`
- Removes deprecated ast nodes (`ast.Num`, `ast.Str`, `ast.NameConstant`, `ast.Bytes`, `ast.Ellipsis`)


## 0.19.2
Expand Down
2 changes: 0 additions & 2 deletions wemake_python_styleguide/compat/aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

from typing_extensions import Final

#: We need this tuple to easily work with both types of text nodes:
TextNodes: Final = (ast.Str, ast.Bytes)

#: We need this tuple to easily check that this is a real assign node.
AssignNodes: Final = (ast.Assign, ast.AnnAssign)
Expand Down
4 changes: 2 additions & 2 deletions wemake_python_styleguide/logic/complexity/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

_Annotation: TypeAlias = Union[
ast.expr,
ast.Str,
ast.Constant
]


Expand All @@ -25,7 +25,7 @@ def get_annotation_complexity(annotation_node: _Annotation) -> int:
When annotations are written as strings,
we additionally parse them to ``ast`` nodes.
"""
if isinstance(annotation_node, ast.Str):
if isinstance(annotation_node, ast.Constant) and isinstance(annotation_node.value, str):
# try to parse string-wrapped annotations
try:
annotation_node = ast.parse( # type: ignore
Expand Down
2 changes: 1 addition & 1 deletion wemake_python_styleguide/logic/complexity/overuses.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def is_unary_minus(node: ast.AST) -> bool:
"""
if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
# We allow variables, attributes, subscripts, and `-1`
if isinstance(node.operand, (ast.Constant, ast.Num)):
if isinstance(node.operand, ast.Constant):
return node.operand.n == 1
return True
return False
4 changes: 2 additions & 2 deletions wemake_python_styleguide/logic/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def evaluate_node(node: ast.AST) -> Union[int, float, str, bytes, None]:
"""Returns the value of a node or its evaluation."""
if isinstance(node, ast.Name):
return None
if isinstance(node, (ast.Str, ast.Bytes)):
return node.s
if isinstance(node, ast.Constant) and isinstance(node.value, (str, bytes)):
return node.value
try:
return literal_eval_with_names(node) # type: ignore[no-any-return]
except Exception:
Expand Down
2 changes: 1 addition & 1 deletion wemake_python_styleguide/logic/safe_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def literal_eval_with_names( # noqa: WPS231
because we try to stay as close to the original source as possible.
"""
binary_operators = (ast.Add, ast.Sub)
if isinstance(node, (ast.Constant, ast.NameConstant)):
if isinstance(node, ast.Constant):
return node.value
elif isinstance(node, (ast.Tuple, ast.List, ast.Set, ast.Dict)):
return _convert_iterable(node)
Expand Down
4 changes: 2 additions & 2 deletions wemake_python_styleguide/logic/tree/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
_AnnParts: Final = (
ast.Name,
ast.Attribute,
ast.Str,
ast.List,
ast.Tuple,
ast.Subscript,
Expand All @@ -27,7 +26,8 @@ def is_annotation(node: ast.AST) -> bool:
We use this predicate to allow all types of repetetive
function and instance annotations.
"""
if not isinstance(node, _AnnParts):
if not (isinstance(node, _AnnParts)
or (isinstance(node, ast.Constant) and isinstance(node.value, str))):
return False

annotated = walk.get_closest_parent(node, (*_AnnNodes, *FunctionNodes))
Expand Down
2 changes: 1 addition & 1 deletion wemake_python_styleguide/logic/tree/keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ def _node_returns_bool_const(node: ast.stmt) -> bool:
"""Checks if a Return node would return a boolean constant."""
return (
isinstance(node, ast.Return) and
isinstance(node.value, ast.NameConstant) and
isinstance(node.value, ast.Constant) and
isinstance(node.value.value, bool)
)
2 changes: 1 addition & 1 deletion wemake_python_styleguide/logic/tree/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ def is_doc_string(node: ast.AST) -> bool:
"""
if not isinstance(node, ast.Expr):
return False
return isinstance(node.value, ast.Str)
return isinstance(node.value, ast.Constant) and isinstance(node.value.value, str)
2 changes: 1 addition & 1 deletion wemake_python_styleguide/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from typing_extensions import Protocol, TypeAlias

#: We use this type to represent all string-like nodes.
AnyText: TypeAlias = Union[ast.Str, ast.Bytes]
AnyText: TypeAlias = Union[ast.Constant]

#: In cases we need to work with both import types.
AnyImport: TypeAlias = Union[ast.Import, ast.ImportFrom]
Expand Down
21 changes: 7 additions & 14 deletions wemake_python_styleguide/visitors/ast/builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
from wemake_python_styleguide.compat.aliases import (
AssignNodesWithWalrus,
FunctionNodes,
TextNodes,
)
from wemake_python_styleguide.logic import nodes, safe_eval, source, walk
from wemake_python_styleguide.logic.tree import (
Expand Down Expand Up @@ -153,10 +152,8 @@ class WrongFormatStringVisitor(base.BaseNodeVisitor):
"""Restricts usage of ``f`` strings."""

_valid_format_index: ClassVar[AnyNodes] = (
*TextNodes,
ast.Num,
ast.Constant,
ast.Name,
ast.NameConstant,
)
_single_use_types: ClassVar[AnyNodes] = (
ast.Call,
Expand Down Expand Up @@ -271,13 +268,13 @@ class WrongNumberVisitor(base.BaseNodeVisitor):

_non_magic_modulo: ClassVar[int] = 10

def visit_Num(self, node: ast.Num) -> None:
def visit_Num(self, node: ast.Constant) -> None:
"""Checks wrong constants inside the code."""
self._check_is_magic(node)
self._check_is_approximate_constant(node)
self.generic_visit(node)

def _check_is_magic(self, node: ast.Num) -> None:
def _check_is_magic(self, node: ast.Constant) -> None:
parent = operators.get_parent_ignoring_unary(node)
if isinstance(parent, self._allowed_parents):
return
Expand All @@ -292,7 +289,7 @@ def _check_is_magic(self, node: ast.Num) -> None:
best_practices.MagicNumberViolation(node, text=str(node.n)),
)

def _check_is_approximate_constant(self, node: ast.Num) -> None:
def _check_is_approximate_constant(self, node: ast.Constant) -> None:
try:
precision = len(str(node.n).split('.')[1])
except IndexError:
Expand Down Expand Up @@ -404,9 +401,7 @@ class WrongCollectionVisitor(base.BaseNodeVisitor):
"""Ensures that collection definitions are correct."""

_elements_in_sets: ClassVar[AnyNodes] = (
*TextNodes,
ast.Num,
ast.NameConstant,
ast.Constant,
ast.Name,
)

Expand All @@ -421,9 +416,7 @@ class WrongCollectionVisitor(base.BaseNodeVisitor):
)

_elements_to_eval: ClassVar[AnyNodes] = (
*TextNodes,
ast.Num,
ast.NameConstant,
ast.Constant,
ast.Tuple,
ast.List,
ast.Set,
Expand Down Expand Up @@ -461,7 +454,7 @@ def _check_float_keys(self, keys: _HashItems) -> None:

real_key = operators.unwrap_unary_node(dict_key)
is_float_key = (
isinstance(real_key, ast.Num) and
isinstance(real_key, ast.Constant) and
isinstance(real_key.n, float)
)
if is_float_key or evaluates_to_float:
Expand Down
6 changes: 3 additions & 3 deletions wemake_python_styleguide/visitors/ast/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,8 @@ def _check_slots(self, node: types.AnyAssign) -> None:
self._count_slots_items(node, node.value)

def _slot_item_name(self, node: ast.AST) -> Optional[str]:
if isinstance(node, ast.Str):
return node.s
if isinstance(node, ast.Constant) and isinstance(node.value, str):
return node.value
if isinstance(node, ast.Starred):
return source.node_to_string(node)
return None
Expand All @@ -359,7 +359,7 @@ def _are_correct_slots(self, slots: List[ast.AST]) -> bool:
return all(
slot.s.isidentifier()
for slot in slots
if isinstance(slot, ast.Str)
if isinstance(slot, ast.Constant) and isinstance(slot.value, str)
)


Expand Down
34 changes: 21 additions & 13 deletions wemake_python_styleguide/visitors/ast/compares.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from typing_extensions import final

from wemake_python_styleguide.compat.aliases import AssignNodes, TextNodes
from wemake_python_styleguide.compat.aliases import AssignNodes
from wemake_python_styleguide.compat.functions import get_assign_targets
from wemake_python_styleguide.logic import nodes, source, walk
from wemake_python_styleguide.logic.naming.name_nodes import is_same_variable
Expand Down Expand Up @@ -56,7 +56,7 @@ def visit_Compare(self, node: ast.Compare) -> None:

def _is_correct_len(self, sign: ast.cmpop, comparator: ast.AST) -> bool:
"""Helper function which tells what calls to ``len()`` are valid."""
if isinstance(operators.unwrap_unary_node(comparator), ast.Num):
if isinstance(operators.unwrap_unary_node(comparator), ast.Constant):
numeric_value = ast.literal_eval(comparator)
if numeric_value == 0:
return False
Expand Down Expand Up @@ -135,8 +135,6 @@ class WrongConstantCompareVisitor(BaseNodeVisitor):
ast.SetComp,

# We allow `ast.NameConstant`
ast.Num,
*TextNodes,
)

def visit_Compare(self, node: ast.Compare) -> None:
Expand Down Expand Up @@ -177,6 +175,12 @@ def _check_is_constant_compare(
)
if isinstance(unwrapped, self._forbidden_for_is):
self.add_violation(WrongIsCompareViolation(comparator))
elif isinstance(unwrapped, ast.Constant):
if (isinstance(unwrapped.value, (int, float, complex))
and not isinstance(unwrapped.value, bool)
or (isinstance(unwrapped.value, (str, bytes)))):
self.add_violation(WrongIsCompareViolation(comparator))



@final
Expand Down Expand Up @@ -265,9 +269,6 @@ class WrongConditionalVisitor(BaseNodeVisitor):

_forbidden_nodes: ClassVar[AnyNodes] = (
# Constants:
*TextNodes,
ast.Num,
ast.NameConstant,

# Collections:
ast.List,
Expand Down Expand Up @@ -310,6 +311,11 @@ def _check_constant_condition(self, node: ast.AST) -> None:
real_node = operators.unwrap_unary_node(get_assigned_expr(node))
if isinstance(real_node, self._forbidden_nodes):
self.add_violation(ConstantConditionViolation(node))
elif (isinstance(real_node, ast.Constant)
and ((isinstance(real_node.value, (int, float, complex))
and not isinstance(real_node.value, bool))
or isinstance(real_node.value, (str, bytes, bool, type(None))))):
self.add_violation(ConstantConditionViolation(node))

def _check_simplifiable_if(self, node: ast.If) -> None:
if not ifs.is_elif(node) and not ifs.root_if(node):
Expand All @@ -320,9 +326,9 @@ def _check_simplifiable_if(self, node: ast.If) -> None:

def _check_simplifiable_ifexpr(self, node: ast.IfExp) -> None:
conditions = set()
if isinstance(node.body, ast.NameConstant):
if isinstance(node.body, ast.Constant) and isinstance(node.body.value, (bool, type(None))):
conditions.add(node.body.value)
if isinstance(node.orelse, ast.NameConstant):
if isinstance(node.orelse, ast.Constant) and isinstance(node.orelse.value, (bool, type(None))):
conditions.add(node.orelse.value)

if conditions == {True, False}:
Expand All @@ -347,7 +353,8 @@ def _is_simplifiable_assign(
wrong_length = len(node_body) != 1
if wrong_length or not isinstance(node_body[0], AssignNodes):
return None
if not isinstance(node_body[0].value, ast.NameConstant):
if not (isinstance(node_body[0].value, ast.Constant)
and isinstance(node_body[0].value.value, (bool, type(None)))):
return None
if node_body[0].value.value is None:
return None
Expand Down Expand Up @@ -415,7 +422,7 @@ def _check_comparators(self, node: ast.Compare) -> None:
self._check_wrong_comparators(real)

def _check_single_item_container(self, node: ast.AST) -> None:
is_text_violated = isinstance(node, TextNodes) and len(node.s) == 1
is_text_violated = isinstance(node, ast.Constant) and isinstance(node.value, (str, bytes)) and len(node.s) == 1
is_dict_violated = isinstance(node, ast.Dict) and len(node.keys) == 1
is_iter_violated = (
isinstance(node, (ast.List, ast.Tuple, ast.Set)) and
Expand All @@ -442,8 +449,9 @@ def visit_Compare(self, node: ast.Compare) -> None:
def _is_float_or_complex(self, node: ast.AST) -> bool:
node = operators.unwrap_unary_node(node)
return (
isinstance(node, ast.Num) and
isinstance(node.n, (float, complex))
isinstance(node, ast.Constant) and
isinstance(node.value, (float, complex)) and not
isinstance(node.value, bool)
)

def _check_float_complex_compare(self, node: ast.Compare) -> None:
Expand Down
21 changes: 11 additions & 10 deletions wemake_python_styleguide/visitors/ast/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from wemake_python_styleguide.compat.aliases import (
ForNodes,
FunctionNodes,
TextNodes,
)
from wemake_python_styleguide.constants import (
FUNCTIONS_BLACKLIST,
Expand Down Expand Up @@ -104,7 +103,7 @@ def _check_boolean_arguments(self, node: ast.Call) -> None:
return # Calls with single boolean argument are allowed

for arg in node.args:
if not isinstance(arg, ast.NameConstant):
if not (isinstance(arg, ast.Constant) and isinstance(arg.value, (bool, type(None)))):
continue

is_ignored = self._is_call_ignored(node)
Expand Down Expand Up @@ -183,7 +182,7 @@ def _check_floating_nan(self, node: ast.Call) -> None:
if len(node.args) != 1:
return

if not isinstance(node.args[0], (ast.Str, ast.Bytes)):
if not (isinstance(node.args[0], ast.Constant) and isinstance(node.args[0].value, (str, bytes))):
return

if not functions.given_function_called(node, 'float'):
Expand Down Expand Up @@ -256,8 +255,10 @@ def _check_range_len(self, node: ast.Call) -> None:
is_three_args_range = (
self._is_multiple_args_range_with_len(node) and
args_len == 3 and
isinstance(step_arg, ast.Num) and
abs(step_arg.n) == 1
isinstance(step_arg, ast.Constant) and
isinstance(step_arg.value, (int, float, complex)) and
not isinstance(step_arg.value, bool) and
abs(step_arg.value) == 1
)
if any([is_one_arg_range, is_two_args_range, is_three_args_range]):
self.add_violation(ImplicitEnumerateViolation(node))
Expand Down Expand Up @@ -425,13 +426,9 @@ class FunctionSignatureVisitor(base.BaseNodeVisitor):
"""

_allowed_default_value_types: ClassVar[AnyNodes] = (
*TextNodes,
ast.Name,
ast.Attribute,
ast.NameConstant,
ast.Tuple,
ast.Num,
ast.Ellipsis,
)

def visit_any_function_and_lambda(
Expand Down Expand Up @@ -483,7 +480,11 @@ def _check_complex_argument_defaults(
) else [real_arg]

has_incorrect_part = any(
not isinstance(part, self._allowed_default_value_types)
not (isinstance(part, self._allowed_default_value_types)
or (isinstance(part, ast.Constant)
and ((isinstance(part.value, (int, float, complex))
and not isinstance(part.value, bool))
or isinstance(part.value, (str, bytes, bool, type(None), type(Ellipsis))))))
for part in parts
)

Expand Down
Loading