{"id":13733426,"url":"https://github.com/sbdchd/flake8-pie","last_synced_at":"2025-05-09T18:33:52.438Z","repository":{"id":44850651,"uuid":"162019996","full_name":"sbdchd/flake8-pie","owner":"sbdchd","description":":pizza: A flake8 extension that implements misc. lints","archived":true,"fork":false,"pushed_at":"2023-02-21T21:33:40.000Z","size":206,"stargazers_count":64,"open_issues_count":25,"forks_count":10,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-19T11:09:34.179Z","etag":null,"topics":["flake8","lint","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sbdchd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-12-16T16:30:28.000Z","updated_at":"2025-04-03T09:40:30.000Z","dependencies_parsed_at":"2024-01-06T09:53:15.175Z","dependency_job_id":"a5aefa2d-27a3-4aad-9872-2ab84655001c","html_url":"https://github.com/sbdchd/flake8-pie","commit_stats":{"total_commits":86,"total_committers":9,"mean_commits":9.555555555555555,"dds":"0.40697674418604646","last_synced_commit":"5281898ad207279eb300cf39e09d8cd428aea0b6"},"previous_names":["sbdchd/flake8-assign-and-return"],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbdchd%2Fflake8-pie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbdchd%2Fflake8-pie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbdchd%2Fflake8-pie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbdchd%2Fflake8-pie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sbdchd","download_url":"https://codeload.github.com/sbdchd/flake8-pie/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253176446,"owners_count":21866143,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flake8","lint","python"],"created_at":"2024-08-03T03:00:42.855Z","updated_at":"2025-05-09T18:33:47.382Z","avatar_url":"https://github.com/sbdchd.png","language":"Python","funding_links":[],"categories":["All-in-one"],"sub_categories":[],"readme":"# flake8-pie [![CircleCI](https://circleci.com/gh/sbdchd/flake8-pie.svg?style=svg)](https://circleci.com/gh/sbdchd/flake8-pie) [![pypi](https://img.shields.io/pypi/v/flake8-pie.svg)](https://pypi.org/project/flake8-pie/)\n\n\u003e A flake8 extension that implements misc. lints\n\n\n# **DEPRECATED**: See Ruff's [`flake8-pie` implementation instead](https://github.com/charliermarsh/ruff/blob/06e426f509b6d718d77895dc7bdeaa4df5efae6e/docs/faq.md)\n\n## lints\n\n### PIE781: assign-and-return\n\nBased on Clippy's\n[`let_and_return`](https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return)\nand Microsoft's TSLint rule\n[`no-unnecessary-local-variable`](https://github.com/Microsoft/tslint-microsoft-contrib).\n\nFor more info on the structure of this lint, see the [accompanying blog\npost](https://steve.dignam.xyz/2018/12/16/creating-a-flake8-lint/).\n\n#### examples\n\n```python\n# error\ndef foo():\n   x = bar()\n   return x\n\n# allowed\ndef foo():\n   x, _ = bar()\n   return x\n```\n\n### PIE783: celery-explicit-names\n\nWarn about [Celery](https://pypi.org/project/celery/) task definitions that don't have explicit names.\n\nNote: this lint is kind of naive considering any decorator with a `.task()`\nmethod or any decorator called `shared_task()` a Celery decorator.\n\n#### examples\n\n```python\n# error\n@app.task()\ndef foo():\n    pass\n\n# ok\n@app.task(name=\"app_name.tasks.foo\")\ndef foo():\n    pass\n```\n\n### PIE784: celery-explicit-crontab-args\n\nThe `crontab` class provided by Celery has some default args that are\nsuprising to new users. Specifically, `crontab(hour=\"0,12\")` won't run a task\nat midnight and noon, it will run the task at every minute during those two\nhours. This lint makes that call an error, forcing you to write\n`crontab(hour=\"0, 12\", minute=\"*\")`.\n\nAdditionally, the lint is a bit more complex in that it requires you specify\nevery smaller increment than the largest time increment you provide. So if you\nprovide `days_of_week`, then you need to provide `hour`s and `minute`s\nexplicitly.\n\nNote: if you like the default behavior of `crontab()` then you can either\ndisable this lint or pass `\"*\"` for the `kwarg` value, e.g., `minutes=\"*\"`.\n\nAlso, since this lint is essentially a naive search for calls to a\n`crontab()` function, if you have a function named the same then this will\ncause false positives.\n\n### PIE785: celery-require-tasks-expire\n\nCelery tasks can bunch up if they don't have expirations.\n\nThis enforces specifying expirations in both the celery beat config dict and\nin `.apply_async()` calls.\n\nThe same caveat applies about how this lint is naive.\n\n### PIE786: precise-exception-handlers\n\nBe precise in what exceptions you catch. Bare `except:` handlers, catching `BaseException`, or catching `Exception` can lead to unexpected bugs.\n\n#### examples\n\n```python\n# error\ntry:\n    save_file(name=\"export.csv\")\nexcept:\n    pass\n\n# error\ntry:\n    save_file(name=\"export.csv\")\nexcept BaseException:\n    pass\n\n# error\ntry:\n    save_file(name=\"export.csv\")\nexcept Exception:\n    pass\n\n# error\ntry:\n    save_file(name=\"export.csv\")\nexcept (ValueError, Exception):\n    pass\n\n\n# ok\ntry:\n    save_file(name=\"export.csv\")\nexcept OSError:\n    pass\n```\n\n### PIE787: no-len-condition\n\nEmpty collections are falsey in Python so calling `len()` is unnecessary when\nchecking for emptiness in an if statement/expression.\n\nComparing to explicit scalars is allowed.\n\n```python\n# error\nif len(foo): ...\nif not len(foo): ...\n\n# ok\nif foo: ...\nif not foo: ...\nif len(foo) \u003e 0: ...\nif len(foo) == 0: ...\n```\n\n### PIE788: no-bool-condition\n\nIf statements/expressions evalute the truthiness of the their test argument,\nso calling `bool()` is unnecessary.\n\nComparing to `True`/`False` is allowed.\n\n```python\n# error\nif bool(foo): ...\nif not bool(foo): ...\n\n# ok\nif foo: ...\nif not foo: ...\nif bool(foo) is True: ...\nif bool(foo) is False: ...\n```\n\n### PIE789: prefer-isinstance-type-compare\n\nUsing `type()` doesn't take into account subclassess and type checkers won't\nrefine the type, use `isinstance` instead.\n\n```python\n# error\nif type(foo) == str: ...\nif type(foo) is str: ...\nif type(foo) in [int, str]: ...\n\n# ok\nif isinstance(foo, str): ...\nif isinstance(foo, (int, str)): ...\n```\n\n### PIE790: no-unnecessary-pass\n\n`pass` is unnecessary when definining a `class` or function with an empty\nbody.\n\n```python\n# error\nclass BadError(Exception):\n    \"\"\"\n    some doc comment\n    \"\"\"\n    pass\n\ndef foo() -\u003e None:\n    \"\"\"\n    some function\n    \"\"\"\n    pass\n\n# ok\nclass BadError(Exception):\n    \"\"\"\n    some doc comment\n    \"\"\"\n\ndef foo() -\u003e None:\n    \"\"\"\n    some function\n    \"\"\"\n```\n\n### PIE791: no-pointless-statements\n\nComparisions without an assignment or assertion are probably a typo.\n\n```python\n# error\n\"foobar\" in data\nres.json() == []\nuser.is_authenticated() is True\n\n# ok\nassert \"foobar\" in data\nfoo = res.json() == []\nuse.is_authenticated()\n```\n\n### PIE792: no-inherit-object\n\nInheriting from `object` isn't necessary in Python 3.\n\n```python\n# error\nclass Foo(object):\n    ...\n\n# ok\nclass Foo:\n    ...\n```\n\n### PIE793: prefer-dataclass\n\nAttempts to find cases where the `@dataclass` decorator is unintentionally\nmissing.\n\n```python\n# error\nclass Foo:\n    z: dict[int, int]\n    def __init__(self) -\u003e None: ...\n\nclass Bar:\n    x: list[str]\n\n# ok\nclass Bar(Foo):\n    z: dict[int, int]\n\n@dataclass\nclass Bar:\n    x: list[str]\n```\n\n### PIE794: dupe-class-field-definitions\n\nFinds duplicate definitions for the same field, which can occur in large ORM\nmodel definitions.\n\n```python\n# error\nclass User(BaseModel):\n    email = fields.EmailField()\n    # ...80 more properties...\n    email = fields.EmailField()\n\n# ok\nclass User(BaseModel):\n    email = fields.EmailField()\n    # ...80 more properties...\n```\n\n### PIE795: prefer-stdlib-enums\n\nInstead of defining various constant properties on a class, use the stdlib\nenum which typecheckers support for type refinement.\n\n```python\n# error\nclass Foo:\n    A = \"A\"\n    B = \"B\"\n    C = \"C\"\n\n# ok\nimport enum\nclass Foo(enum.Enum):\n    A = \"A\"\n    B = \"B\"\n    C = \"C\"\n```\n\n### PIE796: prefer-unique-enums\n\nBy default the stdlib enum allows multiple field names to map to the same\nvalue, this lint requires each enum value be unique.\n\n```python\n# error\nclass Foo(enum.Enum):\n    A = \"A\"\n    B = \"B\"\n    C = \"C\"\n    D = \"C\"\n\n# ok\nclass Foo(enum.Enum):\n    A = \"A\"\n    B = \"B\"\n    C = \"C\"\n    D = \"D\"\n```\n\n### PIE797: no-unnecessary-if-expr\n\nCall `bool()` directly rather than reimplementing its functionality.\n\n```python\n# error\nfoo(is_valid=True if buzz() else False)\n\n# ok\nfoo(is_valid=bool(buzz()))\n```\n\n### PIE798: no-unnecessary-class\n\nInstead of using class to namespace functions, use a module.\n\n```python\n# error\nclass UserManager:\n    class User(NamedTuple):\n        name: str\n\n    @classmethod\n    def update_user(cls, user: User) -\u003e None:\n        ...\n\n    @staticmethod\n    def sync_users() -\u003e None:\n        ...\n\n# ok\nclass User(NamedTuple):\n    name: str\n\ndef update_user(user: User) -\u003e None:\n    ...\n\ndef sync_users() -\u003e None:\n    ...\n```\n\n### PIE799: prefer-col-init\n\nCheck that values are passed in when collections are created rather than\ncreating an empty collection and then inserting.\n\n```python\n# error\nbars = []\nbar = bar()\nbars.append(bar)\n\n# ok\nbar = bar()\nbars = [bar]\n\n# error\ns = deque()\ns.append(foo)\n\n# ok\ns = deque([foo])\n```\n\n### PIE800: no-unnecessary-spread\n\nCheck for unnecessary dict unpacking.\n\n```python\n# error\n{**foo, **{\"bar\": 10}}\n\n# ok\n{**foo, \"bar\": 10}\n```\n\n### PIE801: prefer-simple-return\n\nReturn boolean expressions directly instead of returning `True` and `False`.\n\n```python\n# error\ndef main():\n    if foo \u003e 5:\n        return True\n    return False\n\n# error\ndef main():\n    if foo \u003e 5:\n        return True\n    else:\n        return False\n\n# ok\ndef main():\n    return foo \u003e 5\n```\n\n### PIE802: prefer-simple-any-all\n\nRemove unnecessary comprehensions for `any` and `all`\n\n```python\n# error\nany([x.id for x in bar])\nall([x.id for x in bar])\n\n# ok\nall(x.id for x in bar)\nany(x.id for x in bar)\nany({x.id for x in bar})\n```\n\n### PIE803: prefer-logging-interpolation\n\nDon't format strings before logging. Let `logging` interpolate arguments.\n\nThis allows Sentry to aggregate logs, prevents raising exceptions if interpolation fails, and improves performance if the log level is disabled. See [\"PyCQA/pylint#1788\"](https://github.com/PyCQA/pylint/issues/1788#issuecomment-461279687).\n\n```python\n# error\nlogger.info(\"Login error for %s\" % user)\nlogger.info(\"Login error for %s, %s\" % (user_id, name))\n\n# error\nlogger.info(\"Login error for {}\".format(user))\nlogger.info(\"Login error for {}, {}\".format(user_id, name))\n\n# error\nlogger.info(f\"Login error for {user}\")\nlogger.info(f\"Login error for {user_id}, {name}\")\n\n# ok\nlogger.info(\"Login error for %s\", user)\nlogger.info(\"Login error for %s, %s\", user_id, name)\n```\n\n### PIE804: no-unnecessary-dict-kwargs\n\nAs long as the keys of the dict are valid Python identifier names, we can safely\nremove the surrounding dict.\n\n```python\n# error\nfoo(**{\"bar\": True})\n\n# ok\nfoo(bar=True)\nfoo(**buzz)\nfoo(**{\"bar foo\": True})\n```\n\n## dev\n\n```shell\n# install dependencies\npoetry install\n\ns/lint\ns/test\n```\n\n### PIE805: prefer-literal\n\nCurrently only checks for byte string literals.\n\n```python\n# error\n\"foo\".encode()\n\n# ok\nb\"foo\"\n\"😀\".encode()\n```\n\n### PIE806: no-assert-except\n\nInstead of `assert`ing and catching the exception, use an if statement.\n\n```python\n# error\ntry:\n    assert \"@\" in bar\nexcept AssertionError:\n    ...\n\n# ok\nif \"@\" in bar:\n    ...\n```\n\n### PIE807: prefer-list-builtin\n\n`lambda: []` is equivalent to the builtin `list`\n\n```python\n# error\n@dataclass\nclass Foo:\n    foo: List[str] = field(default_factory=lambda: [])\n\n# ok\n@dataclass\nclass Foo:\n    foo: List[str] = field(default_factory=list)\n```\n\n### PIE808: prefer-simple-range\n\nWe can leave out the first argument to `range` in some cases since the default\nstart position is 0.\n\n```python\n# err\nrange(0, 10)\n\n# ok\nrange(10)\nrange(x, 10)\nrange(0, 10, x)\n```\n\n### PIE809: django-prefer-bulk\n\nBulk create multiple objects instead of executing O(N) queries.\n\n```python\n# error\n[Item.objects.create(item) for item in items]\n\n# error\n[Item.objects.create(item) for item in [bar for bar in buzz]]\n\n# error\n(Item.objects.create(item) for item in items)\n\n# ok\nItem.objects.insert(items)\nItem.objects.create(item)\n```\n\n### PIE810: single-starts-ends-with\n\nInstead of calling `startswith` or `endswith` on the same string for multiple\nprefixes, pass the prefixes as a tuple in a single `startswith` or `endswith`\ncall.\n\n```python\n# error\nfoo.startswith(\"foo\") or foo.startswith(\"bar\")\n\n# error\nfoo.endswith(\"foo\") or foo.endswith(\"bar\")\n\n# error\nfoo.startswith(\"foo\") or str.startswith(foo, \"bar\")\n\n# ok\nfoo.startswith((\"foo\",  \"bar\"))\n\n# ok\nfoo.endswith((\"foo\",  \"bar\"))\n\n# ok\nfoo.startswith(\"foo\") or foo.endswith(\"bar\")\n```\n\n## development\n\n### examining the AST\nYou can use `astpretty` to dump the AST of a piece of code.\n\n```shell\n./.venv/bin/astpretty \u003c(pbpaste)\n```\n\n### uploading a new version to [PyPi](https://pypi.org)\n\n```shell\n# increment `Flake8PieCheck.version` and pyproject.toml `version`\n\n# build new distribution files and upload to pypi\n# Note: this will ask for login credentials\nrm -rf dist \u0026\u0026 poetry publish --build\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsbdchd%2Fflake8-pie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsbdchd%2Fflake8-pie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsbdchd%2Fflake8-pie/lists"}