Skip to main content

A flake8 extension that implements misc. lints

Project description

flake8-pie CircleCI pypi

A flake8 extension that implements misc. lints

lints

PIE781: assign-and-return

Based on Clippy's let_and_return and Microsoft's TSLint rule no-unnecessary-local-variable.

For more info on the structure of this lint, see the accompanying blog post.

examples

# error
def foo():
   x = bar()
   return x

# allowed
def foo():
   x, _ = bar()
   return x

PIE783: celery-explicit-names

Warn about Celery task definitions that don't have explicit names.

Note: this lint is kind of naive considering any decorator with a .task() method or any decorator called shared_task() a Celery decorator.

examples

# error
@app.task()
def foo():
    pass

# ok
@app.task(name="app_name.tasks.foo")
def foo():
    pass

PIE784: celery-explicit-crontab-args

The crontab class provided by Celery has some default args that are suprising to new users. Specifically, crontab(hour="0,12") won't run a task at midnight and noon, it will run the task at every minute during those two hours. This lint makes that call an error, forcing you to write crontab(hour="0, 12", minute="*").

Additionally, the lint is a bit more complex in that it requires you specify every smaller increment than the largest time increment you provide. So if you provide days_of_week, then you need to provide hours and minutes explicitly.

Note: if you like the default behavior of crontab() then you can either disable this lint or pass "*" for the kwarg value, e.g., minutes="*".

Also, since this lint is essentially a naive search for calls to a crontab() function, if you have a function named the same then this will cause false positives.

PIE785: celery-require-tasks-expire

Celery tasks can bunch up if they don't have expirations.

This enforces specifying expirations in both the celery beat config dict and in .apply_async() calls.

The same caveat applies about how this lint is naive.

PIE786: precise-exception-handlers

Be precise in what exceptions you catch. Bare except: handlers, catching BaseException, or catching Exception can lead to unexpected bugs.

examples

# error
try:
    save_file(name="export.csv")
except:
    pass

# error
try:
    save_file(name="export.csv")
except BaseException:
    pass

# error
try:
    save_file(name="export.csv")
except Exception:
    pass

# error
try:
    save_file(name="export.csv")
except (ValueError, Exception):
    pass


# ok
try:
    save_file(name="export.csv")
except OSError:
    pass

PIE787: no-len-condition

Empty collections are fasley in Python so calling len() is unnecessary when checking for emptiness in an if statement/expression.

Comparing to explicit scalars is allowed.

# error
if len(foo): ...
if not len(foo): ...

# ok
if foo: ...
if not foo: ...
if len(foo) > 0: ...
if len(foo) == 0: ...

PIE788: no-bool-condition

If statements/expressions evalute the truthiness of the their test argument, so calling bool() is unnecessary.

Comparing to True/False is allowed.

# error
if bool(foo): ...
if not bool(foo): ...

# ok
if foo: ...
if not foo: ...
if bool(foo) is True: ...
if bool(foo) is False: ...

PIE789: prefer-isinstance-type-compare

Using type() doesn't take into account subclassess and type checkers won't refine the type, use isinstance instead.

# error
if type(foo) == str: ...
if type(foo) is str: ...
if type(foo) in [int, str]: ...

# ok
if isinstance(foo, str): ...
if isinstance(foo, (int, str)): ...

PIE790: no-unnecessary-pass

pass is unnecessary when definining a class or function with an empty body.

# error
class BadError(Exception):
    """
    some doc comment
    """
    pass

def foo() -> None:
    """
    some function
    """
    pass

# ok
class BadError(Exception):
    """
    some doc comment
    """

def foo() -> None:
    """
    some function
    """

PIE791: no-pointless-statements

Comparisions without an assignment or assertion are probably a typo.

# error
"foobar" in data
res.json() == []
user.is_authenticated() is True

# ok
assert "foobar" in data
foo = res.json() == []
use.is_authenticated()

PIE792: no-inherit-object

Inheriting from object isn't necessary in Python 3.

# error
class Foo(object):
    ...

# ok
class Foo:
    ...

PIE793: prefer-dataclass

Attempts to find cases where the @dataclass decorator is unintentionally missing.

# error
class Foo:
    z: dict[int, int]
    def __init__(self) -> None: ...

class Bar:
    x: list[str]

# ok
class Bar(Foo):
    z: dict[int, int]

@dataclass
class Bar:
    x: list[str]

PIE794: dupe-class-field-definitions

Finds duplicate definitions for the same field, which can occur in large ORM model definitions.

# error
class User(BaseModel):
    email = fields.EmailField()
    # ...80 more properties...
    email = fields.EmailField()

# ok
class User(BaseModel):
    email = fields.EmailField()
    # ...80 more properties...

PIE795: prefer-stdlib-enums

Instead of defining various constant properties on a class, use the stdlib enum which typecheckers support for type refinement.

# error
class Foo:
    A = "A"
    B = "B"
    C = "C"

# ok
import enum
class Foo(enum.Enum):
    A = "A"
    B = "B"
    C = "C"

PIE796: prefer-unique-enums

By default the stdlib enum allows multiple field names to map to the same value, this lint requires each enum value be unique.

# error
class Foo(enum.Enum):
    A = "A"
    B = "B"
    C = "C"
    D = "C"

# ok
class Foo(enum.Enum):
    A = "A"
    B = "B"
    C = "C"
    D = "D"

PIE797: no-unecessary-if-expr

Call bool() directly rather than reimplementing its functionality.

# error
foo(is_valid=True if buzz() else False)

# ok
foo(is_valid=bool(buzz()))

dev

# install dependencies
poetry install

s/lint
s/test

uploading a new version to PyPi

# increment `Flake8PieCheck.version` and pyproject.toml `version`

# build new distribution files and upload to pypi
# Note: this will ask for login credentials
rm -rf dist && poetry publish --build

Project details


Download files

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

Files for flake8-pie, version 0.7.1
Filename, size File type Python version Upload date Hashes
Filename, size flake8_pie-0.7.1-py3-none-any.whl (65.7 kB) File type Wheel Python version py3 Upload date Hashes View
Filename, size flake8-pie-0.7.1.tar.gz (17.3 kB) File type Source Python version None Upload date Hashes View

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring DigiCert DigiCert EV certificate Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page