Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/martinthoma/flake8-simplify
❄ A flake8 plugin that helps you to simplify code
https://github.com/martinthoma/flake8-simplify
code-quality flake8 flake8-extensions flake8-plugin linter python python-style python3
Last synced: 14 days ago
JSON representation
❄ A flake8 plugin that helps you to simplify code
- Host: GitHub
- URL: https://github.com/martinthoma/flake8-simplify
- Owner: MartinThoma
- License: mit
- Created: 2020-09-19T16:59:41.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2023-12-25T09:43:40.000Z (11 months ago)
- Last Synced: 2024-10-21T15:44:20.342Z (22 days ago)
- Topics: code-quality, flake8, flake8-extensions, flake8-plugin, linter, python, python-style, python3
- Language: Python
- Homepage:
- Size: 216 KB
- Stars: 186
- Watchers: 5
- Forks: 19
- Open Issues: 52
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
[![PyPI version](https://badge.fury.io/py/flake8-simplify.svg)](https://badge.fury.io/py/flake8-simplify)
[![Code on Github](https://img.shields.io/badge/Code-GitHub-brightgreen)](https://github.com/MartinThoma/flake8-simplify)
[![Actions Status](https://github.com/MartinThoma/flake8-simplify/workflows/Unit%20Tests/badge.svg)](https://github.com/MartinThoma/flake8-simplify/actions)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)# flake8-simplify
A [flake8](https://flake8.pycqa.org/en/latest/index.html) plugin that helps you simplify your code.
## Installation
Install with `pip`:
```bash
pip install flake8-simplify
```Python 3.8 to 3.11 are supported.
## Usage
Just call `flake8 .` in your package or `flake your.py`:
```
$ flake8 .
./foo/__init__.py:690:12: SIM101 Multiple isinstance-calls which can be merged into a single call for variable 'other'
```## Rules
Python-specific rules:
* `SIM101`: Multiple isinstance-calls which can be merged into a single call by
using a tuple as a second argument ([example](#SIM101))
* [`SIM104`](https://github.com/MartinThoma/flake8-simplify/issues/4) ![](https://shields.io/badge/-legacyfix-inactive): Use 'yield from iterable' (introduced in Python 3.3, see [PEP 380](https://docs.python.org/3/whatsnew/3.3.html#pep-380)) ([example](#SIM104))
* [`SIM105`](https://github.com/MartinThoma/flake8-simplify/issues/5): Use ['contextlib.suppress(...)'](https://docs.python.org/3/library/contextlib.html#contextlib.suppress) instead of try-except-pass ([example](#SIM105))
* [`SIM107`](https://github.com/MartinThoma/flake8-simplify/issues/9): Don't use `return` in try/except and finally ([example](#SIM107))
* [`SIM108`](https://github.com/MartinThoma/flake8-simplify/issues/12): Use the ternary operator if it's reasonable ([example](#SIM108))
* [`SIM109`](https://github.com/MartinThoma/flake8-simplify/issues/11): Use a tuple to compare a value against multiple values ([example](#SIM109))
* [`SIM110`](https://github.com/MartinThoma/flake8-simplify/issues/15): Use [any(...)](https://docs.python.org/3/library/functions.html#any) ([example](#SIM110))
* [`SIM111`](https://github.com/MartinThoma/flake8-simplify/issues/15): Use [all(...)](https://docs.python.org/3/library/functions.html#all) ([example](#SIM111))
* [`SIM113`](https://github.com/MartinThoma/flake8-simplify/issues/18): Use enumerate instead of manually incrementing a counter ([example](#SIM113))
* [`SIM114`](https://github.com/MartinThoma/flake8-simplify/issues/10): Combine conditions via a logical or to prevent duplicating code ([example](#SIM114))
* [`SIM115`](https://github.com/MartinThoma/flake8-simplify/issues/17): Use context handler for opening files ([example](#SIM115))
* [`SIM116`](https://github.com/MartinThoma/flake8-simplify/issues/31): Use a dictionary instead of many if/else equality checks ([example](#SIM116))
* [`SIM117`](https://github.com/MartinThoma/flake8-simplify/issues/35): Merge with-statements that use the same scope ([example](#SIM117))
* `SIM119`: ![](https://img.shields.io/badge/-removed-lightgrey) Moved to [flake8-scream](https://github.com/MartinThoma/flake8-scream) due to [issue 63](https://github.com/MartinThoma/flake8-simplify/issues/63)
* `SIM120` ![](https://shields.io/badge/-legacyfix-inactive): Use 'class FooBar:' instead of 'class FooBar(object):' ([example](#SIM120))
* `SIM121`: Reserved for [SIM908](#sim908) once it's stable
* `SIM125`: Reserved for [SIM905](#sim905) once it's stable
* `SIM126`: Reserved for [SIM906](#sim906) once it's stable
* `SIM127`: Reserved for [SIM907](#sim907) once it's stableSimplifying Comparisons:
* `SIM201`: Use 'a != b' instead of 'not a == b' ([example](#SIM201))
* `SIM202`: Use 'a == b' instead of 'not a != b' ([example](#SIM202))
* `SIM203`: Use 'a not in b' instead of 'not (a in b)' ([example](#SIM203))
* `SIM204`: ![](https://img.shields.io/badge/-removed-lightgrey) Moved to [flake8-scream](https://github.com/MartinThoma/flake8-scream) due to [issue 116](https://github.com/MartinThoma/flake8-simplify/issues/116)
* `SIM205`: ![](https://img.shields.io/badge/-removed-lightgrey) Moved to [flake8-scream](https://github.com/MartinThoma/flake8-scream) due to [issue 116](https://github.com/MartinThoma/flake8-simplify/issues/116)
* `SIM206`: ![](https://img.shields.io/badge/-removed-lightgrey) Moved to [flake8-scream](https://github.com/MartinThoma/flake8-scream) due to [issue 116](https://github.com/MartinThoma/flake8-simplify/issues/116)
* `SIM207`: ![](https://img.shields.io/badge/-removed-lightgrey) Moved to [flake8-scream](https://github.com/MartinThoma/flake8-scream) due to [issue 116](https://github.com/MartinThoma/flake8-simplify/issues/116)
* `SIM208`: Use 'a' instead of 'not (not a)' ([example](#SIM208))
* `SIM210`: Use 'bool(a)' instead of 'True if a else False' ([example](#SIM210))
* `SIM211`: Use 'not a' instead of 'False if a else True' ([example](#SIM211))
* [`SIM212`](https://github.com/MartinThoma/flake8-simplify/issues/6): Use 'a if a else b' instead of 'b if not a else a' ([example](#SIM212))
* [`SIM220`](https://github.com/MartinThoma/flake8-simplify/issues/6): Use 'False' instead of 'a and not a' ([example](#SIM220))
* [`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](#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:
* [`SIM401`](https://github.com/MartinThoma/flake8-simplify/issues/72): Use 'a_dict.get(key, "default_value")' instead of an if-block ([example](#SIM401))
* [`SIM118`](https://github.com/MartinThoma/flake8-simplify/issues/40): Use 'key in dict' instead of 'key in dict.keys()' ([example](#SIM118))
* `SIM119` Reserved for [SIM911](#sim911) once it's stableGeneral Code Style:
* `SIM102`: Use a single if-statement instead of nested if-statements ([example](#SIM102))
* [`SIM103`](https://github.com/MartinThoma/flake8-simplify/issues/3): Return the boolean condition directly ([example](#SIM103))
* [`SIM106`](https://github.com/MartinThoma/flake8-simplify/issues/8): Handle error-cases first ([example](#SIM106)). This rule was removed due to too many false-positives.
* [`SIM112`](https://github.com/MartinThoma/flake8-simplify/issues/19): Use CAPITAL environment variables ([example](#SIM112))
* `SIM122` / SIM902: ![](https://img.shields.io/badge/-removed-lightgrey) Moved to [flake8-scream](https://github.com/MartinThoma/flake8-scream) due to [issue 125](https://github.com/MartinThoma/flake8-simplify/issues/125)
* `SIM123` / SIM902: ![](https://img.shields.io/badge/-removed-lightgrey) Moved to [flake8-scream](https://github.com/MartinThoma/flake8-scream) due to [issue 130](https://github.com/MartinThoma/flake8-simplify/issues/130)
* `SIM124`: Reserved for SIM909 once it's stable**Experimental rules:**
Getting rules right for every possible case is hard. I don't want to disturb
peoples workflows. For this reason, flake8-simplify introduces new rules with
the `SIM9` prefix. Every new rule will start with SIM9 and stay there for at
least 6 months. In this time people can give feedback. Once the rule is stable,
the code will change to another number.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))
* [`SIM906`](https://github.com/MartinThoma/flake8-simplify/issues/101): Merge nested os.path.join calls ([example](#SIM906))
* [`SIM907`](https://github.com/MartinThoma/flake8-simplify/issues/64): Use Optional[Type] instead of Union[Type, None] ([example](#SIM907))
* [`SIM908`](https://github.com/MartinThoma/flake8-simplify/issues/50): Use dict.get(key) ([example](#SIM908))
* [`SIM909`](https://github.com/MartinThoma/flake8-simplify/issues/114): Avoid reflexive assignments ([example](#SIM909))
* [`SIM910`](https://github.com/MartinThoma/flake8-simplify/issues/171): Avoid to use `dict.get(key, None)` ([example](#SIM910))
* [`SIM911`](https://github.com/MartinThoma/flake8-simplify/issues/161): Avoid using `zip(dict.keys(), dict.values())` ([example](#SIM911))## Disabling Rules
You might have good reasons to
[ignore some flake8 rules](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html).
To do that, use the standard Flake8 configuration. For example, within the `setup.cfg` file:```python
[flake8]
ignore = SIM106, SIM113, SIM119, SIM9
```## Examples
### SIM101
```python
# Bad
isinstance(a, int) or isinstance(a, float)# Good
isinstance(a, (int, float))
```### SIM102
```python
# Bad
if a:
if b:
c# Good
if a and b:
c
```### SIM105
```python
# Bad
try:
foo()
except ValueError:
pass# Good
from contextlib import suppresswith suppress(ValueError):
foo()
```Please note that `contextlib.suppress` is roughly 3x slower than `try/except`
([source](https://github.com/MartinThoma/flake8-simplify/issues/91)).### SIM107
```python
# Bad: This example returns 3!
def foo():
try:
1 / 0
return "1"
except:
return "2"
finally:
return "3"# Good
def foo():
return_value = None
try:
1 / 0
return_value = "1"
except:
return_value = "2"
finally:
return_value = "3" # you might also want to check if "except" happened
return return_value
```### SIM108
```python
# Bad
if a:
b = c
else:
b = d# Good
b = c if a else d
```### SIM109
```python
# Bad
if a == b or a == c:
d# Good
if a in (b, c):
d
```### SIM110
```python
# Bad
for x in iterable:
if check(x):
return True
return False# Good
return any(check(x) for x in iterable)
```### SIM111
```python
# Bad
for x in iterable:
if check(x):
return False
return True# Good
return all(not check(x) for x in iterable)
```### SIM112
```python
# Bad
os.environ["foo"]
os.environ.get("bar")# Good
os.environ["FOO"]
os.environ.get("BAR")
```### SIM113
Using [`enumerate`](https://docs.python.org/3/library/functions.html#enumerate) in simple cases like the loops below is a tiny bit easier to read as the reader does not have to figure out where / when the loop variable is incremented (or if it is incremented in multiple places).
```python
# Bad
idx = 0
for el in iterable:
...
idx += 1# Good
for idx, el in enumerate(iterable):
...
```### SIM114
```python
# Bad
if a:
b
elif c:
b# Good
if a or c:
b
```### SIM115
```python
# Bad
f = open(...)
... # (do something with f)
f.close()# Good
with open(...) as f:
... # (do something with f)
```### SIM116
```python
# Bad
if a == "foo":
return "bar"
elif a == "bar":
return "baz"
elif a == "boo":
return "ooh"
else:
return 42# Good
mapping = {"foo": "bar", "bar": "baz", "boo": "ooh"}
return mapping.get(a, 42)
```### SIM117
```python
# Bad
with A() as a:
with B() as b:
print("hello")# Good
with A() as a, B() as b:
print("hello")
```Thank you for pointing this one out, [Aaron Gokaslan](https://github.com/Skylion007)!
### SIM118
```python
# Bad
key in a_dict.keys()# Good
key in a_dict
```Thank you for pointing this one out, [Aaron Gokaslan](https://github.com/Skylion007)!
### SIM120
```python
# Bad
class FooBar(object):
...# Good
class FooBar:
...
```Both notations are equivalent in Python 3, but the second one is shorter.
### SIM201
```python
# Bad
not a == b# Good
a != b
```### SIM202
```python
# Bad
not a != b# Good
a == b
```### SIM203
```python
# Bad
not a in b# Good
a not in b
```### SIM208
```python
# Bad
not (not a)# Good
a
```### SIM210
```python
# Bad
True if a else False# Good
bool(a)
```### SIM211
```python
# Bad
False if a else True# Good
not a
```### SIM212
```python
# Bad
b if not a else a# Good
a if a else b
```### SIM220
```python
# Bad
a and not a# Good
False
```### SIM221
```python
# Bad
a or not a# Good
True
```### SIM222
```python
# Bad
... or True# Good
True
```### SIM223
```python
# Bad
... and False# Good
False
```### SIM300
```python
# Bad; This is called a "Yoda-Condition"
42 == age# Good
age == 42
```### SIM401
```python
# Bad
if key in a_dict:
value = a_dict[key]
else:
value = "default_value"# Good
thing = a_dict.get(key, "default_value")
```### SIM901
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 23rd of January and will end on 23rd of August 2022.
```python
# Bad
bool(a == b)# Good
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 = {}
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"]
```### SIM906
This rule will be renamed to `SIM126` after its 6-month trial period is over.
Please report any issues you encounter with this rule!The trial period starts on 20th of February and will end on 20th of September 2022.
```python
# Bad
os.path.join(a, os.path.join(b, c))# Good
os.path.join(a, b, c)
```### SIM907
This rule will be renamed to `SIM127` after its 6-month trial period is over.
Please report any issues you encounter with this rule!The trial period starts on 28th of March and will end on 28th of September 2022.
```python
# Bad
def foo(a: Union[int, None]) -> Union[int, None]:
return a# Good
def foo(a: Optional[int]) -> Optional[int]:
return a
```### SIM908
This rule will be renamed to `SIM121` after its 6-month trial period is over.
Please report any issues you encounter with this rule!The trial period starts on 28th of March and will end on 28th of September 2022.
```python
# Bad
name = "some_default"
if "some_key" in some_dict:
name = some_dict["some_key"]# Good
name = some_dict.get("some_key", "some_default")
```### SIM909
Thank you Ryan Delaney for the idea!
This rule will be renamed to `SIM124` after its 6-month trial period is over.
Please report any issues you encounter with this rule!The trial period starts on 28th of March and will end on 28th of September 2022.
```python
# Bad
foo = foo# Good: Nothing. Reflexive assignments have no purpose.
```or
```python
# Bad
foo = foo = 42# Good
foo = 42
```### SIM910
```python
# Bad
dict.get(key, None)# Good
dict.get(key)
```