flake8-pie


Nameflake8-pie JSON
Version 0.16.0 PyPI version JSON
download
home_pagehttps://github.com/sbdchd/flake8-pie
SummaryA flake8 extension that implements misc. lints
upload_time2022-08-08 23:51:12
maintainer
docs_urlNone
authorSteve Dignam
requires_python>=3.7
licenseBSD-2-Clause
keywords flake8 lint
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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/)

> 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 [![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> 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"
}
        
Elapsed time: 0.01827s