Skip to main content

flake8 plugin which checks for code that can be simplified

Project description

PyPI version Code on Github Actions Status Code style: black

flake8-simplify

A flake8 plugin that helps you simplify your code.

Installation

Install with pip:

pip install flake8-simplify

Python 3.6 to 3.8 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:

Simplifying Comparations:

Simplifying usage of dictionaries:

  • SIM401: Use 'a_dict.get(key, "default_value")' instead of an if-block (example)
  • SIM118: Use 'key in dict' instead of 'key in dict.keys()' (example)

General Code Style:

  • SIM102: Use a single if-statement instead of nested if-statements (example)
  • SIM103: Return the boolean condition directly (example)
  • SIM106: Handle error-cases first (example). This rule was removed due to too many false-positives.
  • SIM112: Use CAPITAL environment variables (example)
  • SIM122 / SIM902: Moved to flake8-scream due to issue 125
  • SIM123: Reserved for SIM903 once it's stable
  • 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)
  • SIM903: Use keyword-argument instead of magic number (example)
  • SIM904: Assign values to dictionary directly at initialization (example)
  • SIM905: Split string directly if only constants are used (example)
  • SIM906: Merge nested os.path.join calls (example)
  • SIM907: Use Optional[Type] instead of Union[Type, None] (example)
  • SIM908: Use dict.get(key) (example)

Disabling Rules

You might have good reasons to ignore some flake8 rules. To do that, use the standard Flake8 configuration. For example, within the setup.cfg file:

[flake8]
ignore = SIM106, SIM113, SIM119, SIM9

Examples

SIM101

# Bad
isinstance(a, int) or isinstance(a, float)

# Good
isinstance(a, (int, float))

SIM102

# Bad
if a:
    if b:
        c

# Good
if a and b:
    c

SIM105

# Bad
try:
    foo()
except ValueError:
    pass

# Good
from contextlib import suppress

with suppress(ValueError):
    foo()

Please note that contextlib.suppress is roughly 3x slower than try/except (source).

SIM107

# 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

# Bad
if a:
    b = c
else:
    b = d

# Good
b = c if a else d

SIM109

# Bad
if a == b or a == c:
    d

# Good
if a in (b, c):
    d

SIM110

# Bad
for x in iterable:
    if check(x):
        return True
return False

# Good
return any(check(x) for x in iterable)

SIM111

# Bad
for x in iterable:
    if check(x):
        return False
return True

# Good
return all(not check(x) for x in iterable)

SIM112

# Bad
os.environ["foo"]
os.environ.get("bar")

# Good
os.environ["FOO"]
os.environ.get("BAR")

SIM113

Using 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).

# Bad
idx = 0
for el in iterable:
    ...
    idx += 1

# Good
for idx, el in enumerate(iterable):
    ...

SIM114

# Bad
if a:
    b
elif c:
    b

# Good
if a or c:
    b

SIM115

# Bad
f = open(...)
...  # (do something with f)
f.close()

# Good
with open(...) as f:
    ...  # (do something with f)

SIM116

# 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

# 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!

SIM118

# Bad
key in a_dict.keys()

# Good
key in a_dict

Thank you for pointing this one out, Aaron Gokaslan!

SIM120

# Bad
class FooBar(object):
    ...


# Good
class FooBar:
    ...

Both notations are equivalent in Python 3, but the second one is shorter.

SIM201

# Bad
not a == b

# Good
a != b

SIM202

# Bad
not a != b

# Good
a == b

SIM203

# Bad
not a in b

# Good
a not in b

SIM208

# Bad
not (not a)

# Good
a

SIM210

# Bad
True if a else False

# Good
bool(a)

SIM211

# Bad
False if a else True

# Good
not a

SIM212

# Bad
b if not a else a

# Good
a if a else b

SIM220

# Bad
a and not a

# Good
False

SIM221

# Bad
a or not a

# Good
True

SIM222

# Bad
... or True

# Good
True

SIM223

# Bad
... and False

# Good
False

SIM300

# Bad; This is called a "Yoda-Condition"
42 == age

# Good
age == 42

SIM401

# 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.

# Bad
bool(a == b)

# Good
a == b

SIM903

This rule will be renamed to SIM123 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.

# Bad
foo(42, 1.234)

# Good
foo(the_answer=42, flux_compensation=1.234)

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.

# 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.

# 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.

# 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.

# 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.

# 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!

The trial period starts on 28th of March and will end on 28th of September 2022.

# Bad
foo = foo

# Good: Nothing. Reflexive assignments have no purpose.

or

# Bad
foo = foo = 42

# Good
foo = 42

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flake8_simplify-0.19.1.tar.gz (25.0 kB view details)

Uploaded Source

Built Distribution

flake8_simplify-0.19.1-py3-none-any.whl (27.0 kB view details)

Uploaded Python 3

File details

Details for the file flake8_simplify-0.19.1.tar.gz.

File metadata

  • Download URL: flake8_simplify-0.19.1.tar.gz
  • Upload date:
  • Size: 25.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.62.3 importlib-metadata/4.11.1 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.10.2

File hashes

Hashes for flake8_simplify-0.19.1.tar.gz
Algorithm Hash digest
SHA256 f6af250964a761b8833003756bc6fae740d16a4e8dd7af58ddd6f503f2ada182
MD5 04ad6daef7dc0acccfda678286cbab66
BLAKE2b-256 407fbd257fae943374c1e778c26b0f49915303fc8c646546a305e536d3d80956

See more details on using hashes here.

File details

Details for the file flake8_simplify-0.19.1-py3-none-any.whl.

File metadata

  • Download URL: flake8_simplify-0.19.1-py3-none-any.whl
  • Upload date:
  • Size: 27.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.62.3 importlib-metadata/4.11.1 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.10.2

File hashes

Hashes for flake8_simplify-0.19.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8fe2de815cc4d859c429f448b23699c95f7e130ec871d7d27ec982c9b86d778c
MD5 ad86f8bec1d27461a01e2fa8dc046ef5
BLAKE2b-256 26fa767654587994ae643608112f1e05c86c59c109ab64310c748156615990bc

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page