# flake8-pie [](https://circleci.com/gh/sbdchd/flake8-pie) [](https://pypi.org/project/flake8-pie/)
> A flake8 extension that implements misc. lints
## lints
### PIE781: assign-and-return
Based on Clippy's
[`let_and_return`](https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return)
and Microsoft's TSLint rule
[`no-unnecessary-local-variable`](https://github.com/Microsoft/tslint-microsoft-contrib).
For more info on the structure of this lint, see the [accompanying blog
post](https://steve.dignam.xyz/2018/12/16/creating-a-flake8-lint/).
#### examples
```python
# error
def foo():
x = bar()
return x
# allowed
def foo():
x, _ = bar()
return x
```
### PIE783: celery-explicit-names
Warn about [Celery](https://pypi.org/project/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
```python
# 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 `hour`s and `minute`s
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
```python
# 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.
```python
# 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.
```python
# 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.
```python
# 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.
```python
# 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.
```python
# 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.
```python
# error
class Foo(object):
...
# ok
class Foo:
...
```
### PIE793: prefer-dataclass
Attempts to find cases where the `@dataclass` decorator is unintentionally
missing.
```python
# 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.
```python
# 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.
```python
# 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.
```python
# 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-unnecessary-if-expr
Call `bool()` directly rather than reimplementing its functionality.
```python
# error
foo(is_valid=True if buzz() else False)
# ok
foo(is_valid=bool(buzz()))
```
### PIE798: no-unnecessary-class
Instead of using class to namespace functions, use a module.
```python
# error
class UserManager:
class User(NamedTuple):
name: str
@classmethod
def update_user(cls, user: User) -> None:
...
@staticmethod
def sync_users() -> None:
...
# ok
class User(NamedTuple):
name: str
def update_user(user: User) -> None:
...
def sync_users() -> None:
...
```
### PIE799: prefer-col-init
Check that values are passed in when collections are created rather than
creating an empty collection and then inserting.
```python
# error
bars = []
bar = bar()
bars.append(bar)
# ok
bar = bar()
bars = [bar]
# error
s = deque()
s.append(foo)
# ok
s = deque([foo])
```
### PIE800: no-unnecessary-spread
Check for unnecessary dict unpacking.
```python
# error
{**foo, **{"bar": 10}}
# ok
{**foo, "bar": 10}
```
### PIE801: prefer-simple-return
Return boolean expressions directly instead of returning `True` and `False`.
```python
# error
def main():
if foo > 5:
return True
return False
# error
def main():
if foo > 5:
return True
else:
return False
# ok
def main():
return foo > 5
```
### PIE802: prefer-simple-any-all
Remove unnecessary comprehensions for `any` and `all`
```python
# error
any([x.id for x in bar])
all([x.id for x in bar])
# ok
all(x.id for x in bar)
any(x.id for x in bar)
any({x.id for x in bar})
```
### PIE803: prefer-logging-interpolation
Don't format strings before logging. Let `logging` interpolate arguments.
This 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).
```python
# error
logger.info("Login error for %s" % user)
logger.info("Login error for %s, %s" % (user_id, name))
# error
logger.info("Login error for {}".format(user))
logger.info("Login error for {}, {}".format(user_id, name))
# error
logger.info(f"Login error for {user}")
logger.info(f"Login error for {user_id}, {name}")
# ok
logger.info("Login error for %s", user)
logger.info("Login error for %s, %s", user_id, name)
```
### PIE804: no-unnecessary-dict-kwargs
As long as the keys of the dict are valid Python identifier names, we can safely
remove the surrounding dict.
```python
# error
foo(**{"bar": True})
# ok
foo(bar=True)
foo(**buzz)
foo(**{"bar foo": True})
```
## dev
```shell
# install dependencies
poetry install
s/lint
s/test
```
### PIE805: prefer-literal
Currently only checks for byte string literals.
```python
# error
"foo".encode()
# ok
b"foo"
"😀".encode()
```
### PIE806: no-assert-except
Instead of `assert`ing and catching the exception, use an if statement.
```python
# error
try:
assert "@" in bar
except AssertionError:
...
# ok
if "@" in bar:
...
```
### PIE807: prefer-list-builtin
`lambda: []` is equivalent to the builtin `list`
```python
# error
@dataclass
class Foo:
foo: List[str] = field(default_factory=lambda: [])
# ok
@dataclass
class Foo:
foo: List[str] = field(default_factory=list)
```
### PIE808: prefer-simple-range
We can leave out the first argument to `range` in some cases since the default
start position is 0.
```python
# err
range(0, 10)
# ok
range(10)
range(x, 10)
range(0, 10, x)
```
### PIE809: django-prefer-bulk
Bulk create multiple objects instead of executing O(N) queries.
```python
# error
[Item.objects.create(item) for item in items]
# error
[Item.objects.create(item) for item in [bar for bar in buzz]]
# error
(Item.objects.create(item) for item in items)
# ok
Item.objects.insert(items)
Item.objects.create(item)
```
## development
### examining the AST
You can use `astpretty` to dump the AST of a piece of code.
```shell
./.venv/bin/astpretty <(pbpaste)
```
### uploading a new version to [PyPi](https://pypi.org)
```shell
# 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
```
Raw data
{
"_id": null,
"home_page": "https://github.com/sbdchd/flake8-pie",
"name": "flake8-pie",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": "",
"keywords": "flake8,lint",
"author": "Steve Dignam",
"author_email": "steve@dignam.xyz",
"download_url": "https://files.pythonhosted.org/packages/ae/11/cebfe54fda9897188ca40d1cd755d1be13908ceedfea9e500fae162f06a9/flake8-pie-0.16.0.tar.gz",
"platform": null,
"description": "# flake8-pie [](https://circleci.com/gh/sbdchd/flake8-pie) [](https://pypi.org/project/flake8-pie/)\n\n> A flake8 extension that implements misc. lints\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 fasley 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) > 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() -> None:\n \"\"\"\n some function\n \"\"\"\n pass\n\n# ok\nclass BadError(Exception):\n \"\"\"\n some doc comment\n \"\"\"\n\ndef foo() -> 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) -> 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) -> None:\n ...\n\n @staticmethod\n def sync_users() -> None:\n ...\n\n# ok\nclass User(NamedTuple):\n name: str\n\ndef update_user(user: User) -> None:\n ...\n\ndef sync_users() -> 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 > 5:\n return True\n return False\n\n# error\ndef main():\n if foo > 5:\n return True\n else:\n return False\n\n# ok\ndef main():\n return foo > 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\"\ud83d\ude00\".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## 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 <(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 && poetry publish --build\n```\n",
"bugtrack_url": null,
"license": "BSD-2-Clause",
"summary": "A flake8 extension that implements misc. lints",
"version": "0.16.0",
"split_keywords": [
"flake8",
"lint"
],
"urls": [
{
"comment_text": "",
"digests": {
"md5": "fa13e3baac37f1475229fa84734c715c",
"sha256": "24cd7849b0eee22e2328b9e9d2a1dea40013b0a3106864bbadd06a4b05dbb71f"
},
"downloads": -1,
"filename": "flake8_pie-0.16.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "fa13e3baac37f1475229fa84734c715c",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 49171,
"upload_time": "2022-08-08T23:51:14",
"upload_time_iso_8601": "2022-08-08T23:51:14.535335Z",
"url": "https://files.pythonhosted.org/packages/83/4c/e953c2402b663155525faba5315df8abe6c02ea7f989cbf43327f5088fc3/flake8_pie-0.16.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "1e85d8671cbe994dbe0d44422d2667d7",
"sha256": "b8dcb7b92706fa33d05d92a4b3e49b7a9fd3f0041849166275b646ba50e515ba"
},
"downloads": -1,
"filename": "flake8-pie-0.16.0.tar.gz",
"has_sig": false,
"md5_digest": "1e85d8671cbe994dbe0d44422d2667d7",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 28334,
"upload_time": "2022-08-08T23:51:12",
"upload_time_iso_8601": "2022-08-08T23:51:12.620779Z",
"url": "https://files.pythonhosted.org/packages/ae/11/cebfe54fda9897188ca40d1cd755d1be13908ceedfea9e500fae162f06a9/flake8-pie-0.16.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2022-08-08 23:51:12",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "sbdchd",
"github_project": "flake8-pie",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"circle": true,
"tox": true,
"lcname": "flake8-pie"
}