diff --git a/README.md b/README.md index c7828e6..79277bf 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,8 @@ Python-specific rules: * [`SIM117`](https://github.com/MartinThoma/flake8-simplify/issues/35): Merge with-statements that use the same scope ([example](#SIM117)) * [`SIM119`](https://github.com/MartinThoma/flake8-simplify/issues/37) ![](https://shields.io/badge/-legacyfix-inactive): Use dataclasses for data containers ([example](#SIM119)) * `SIM120` ![](https://shields.io/badge/-legacyfix-inactive): Use 'class FooBar:' instead of 'class FooBar(object):' ([example](#SIM120)) -* `SIM124`: Reserved for SIM904 once it's stable +* `SIM124`: Reserved for [SIM904](#sim904) once it's stable +* `SIM125`: Reserved for [SIM905](#sim905) once it's stable Simplifying Comparations: @@ -67,7 +68,7 @@ Simplifying Comparations: * [`SIM221`](https://github.com/MartinThoma/flake8-simplify/issues/6): Use 'True' instead of 'a or not a' ([example](#SIM221)) * [`SIM222`](https://github.com/MartinThoma/flake8-simplify/issues/6): Use 'True' instead of '... or True' ([example](#SIM222)) * [`SIM223`](https://github.com/MartinThoma/flake8-simplify/issues/6): Use 'False' instead of '... and False' ([example](#SIM223)) -* [`SIM224`](https://github.com/MartinThoma/flake8-simplify/issues/88): Reserved for SIM901 once it's stable +* [`SIM224`](https://github.com/MartinThoma/flake8-simplify/issues/88): Reserved for [SIM901](#sim901) once it's stable * [`SIM300`](https://github.com/MartinThoma/flake8-simplify/issues/16): Use 'age == 42' instead of '42 == age' ([example](#SIM300)) Simplifying usage of dictionaries: @@ -94,6 +95,7 @@ Current experimental rules: * `SIM901`: Use comparisons directly instead of wrapping them in a `bool(...)` call ([example](#SIM901)) * `SIM904`: Assign values to dictionary directly at initialization ([example](#SIM904)) +* [`SIM905`](https://github.com/MartinThoma/flake8-simplify/issues/86): Split string directly if only constants are used ([example](#SIM905)) ## Disabling Rules @@ -550,6 +552,12 @@ a == b ### SIM904 +This rule will be renamed to `SIM224` after its 6-month trial period is over. +Please report any issues you encounter with this rule! + +The trial period starts on 12th of February and will end on 12th of September 2022. + + ```python # Bad a = {} @@ -558,3 +566,18 @@ a["b"] = "c" # Good a = {"b": "c"} ``` + +### SIM905 + +This rule will be renamed to `SIM225` after its 6-month trial period is over. +Please report any issues you encounter with this rule! + +The trial period starts on 13th of February and will end on 13th of September 2022. + +```python +# Bad +domains = "de com net org".split() + +# Good +domains = ["de", "com", "net", "org"] +``` diff --git a/flake8_simplify.py b/flake8_simplify.py index f91318a..7231454 100644 --- a/flake8_simplify.py +++ b/flake8_simplify.py @@ -1,6 +1,7 @@ # Core Library import ast import itertools +import json import sys from collections import defaultdict from typing import ( @@ -109,6 +110,7 @@ def __init__(self, orig: ast.Call) -> None: ) SIM901 = "SIM901 Use '{better}' instead of '{current}'" SIM904 = "SIM904 Initialize dictionary '{dict_name}' directly" +SIM905 = "SIM905 Use '{expected}' instead of '{actual}'" # ast.Constant in Python 3.8, ast.NameConstant in Python 3.6 and 3.7 BOOL_CONST_TYPES = (ast.Constant, ast.NameConstant) @@ -1896,6 +1898,32 @@ def _get_sim904(node: ast.Assign) -> List[Tuple[int, int, str]]: return errors +def _get_sim905(node: ast.Call) -> List[Tuple[int, int, str]]: + errors: List[Tuple[int, int, str]] = [] + if not ( + isinstance(node.func, ast.Attribute) + and node.func.attr == "split" + and isinstance(node.func.value, (ast.Str, ast.Constant)) + ): + return errors + + if isinstance(node.func.value, ast.Constant): + value = node.func.value.value + else: + value = node.func.value.s + + expected = json.dumps(value.split()) + actual = to_source(node.func.value) + ".split()" + errors.append( + ( + node.lineno, + node.col_offset, + SIM905.format(expected=expected, actual=actual), + ) + ) + return errors + + class Visitor(ast.NodeVisitor): def __init__(self) -> None: self.errors: List[Tuple[int, int, str]] = [] @@ -1907,6 +1935,7 @@ def visit_Assign(self, node: ast.Assign) -> Any: def visit_Call(self, node: ast.Call) -> Any: self.errors += _get_sim115(Call(node)) self.errors += _get_sim901(node) + self.errors += _get_sim905(node) self.generic_visit(node) def visit_With(self, node: ast.With) -> Any: diff --git a/tests/test_simplify.py b/tests/test_simplify.py index 0304702..04c4295 100644 --- a/tests/test_simplify.py +++ b/tests/test_simplify.py @@ -943,3 +943,11 @@ def test_sim904(): a['b'] = 'c'""" ) assert results == {"1:0 SIM904 Initialize dictionary 'a' directly"} + + +def test_sim905(): + results = _results("""domains = "de com net org".split()""") + assert results == { + '1:10 SIM905 Use \'["de", "com", "net", "org"]\' ' + "instead of '\"de com net org\".split()'" + }