Skip to content

Commit

Permalink
SIM905: Add rule to explictly create a list (#98)
Browse files Browse the repository at this point in the history
No string splitting anymore.

This will be SIM225 once the trial period is over

Closes #86
  • Loading branch information
MartinThoma committed Feb 13, 2022
1 parent 3d8f96e commit 93ee318
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 2 deletions.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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:
Expand All @@ -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

Expand Down Expand Up @@ -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 = {}
Expand All @@ -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"]
```
29 changes: 29 additions & 0 deletions flake8_simplify.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Core Library
import ast
import itertools
import json
import sys
from collections import defaultdict
from typing import (
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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]] = []
Expand All @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions tests/test_simplify.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()'"
}

0 comments on commit 93ee318

Please sign in to comment.