flake8-plugin-utils


Nameflake8-plugin-utils JSON
Version 1.3.3 PyPI version JSON
download
home_pagehttps://pypi.org/project/flake8-plugin-utils
SummaryThe package provides base classes and utils for flake8 plugin writing
upload_time2023-06-26 16:42:20
maintainer
docs_urlNone
authorAfonasev Evgeniy
requires_python>=3.6,<4.0
licenseMIT
keywords flake8 plugin utils
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI
coveralls test coverage No coveralls.
            # flake8-plugin-utils

[![pypi](https://badge.fury.io/py/flake8-plugin-utils.svg)](https://pypi.org/project/flake8-plugin-utils)
[![Python: 3.6+](https://img.shields.io/badge/Python-3.6+-blue.svg)](https://pypi.org/project/flake8-plugin-utils)
[![Downloads](https://img.shields.io/pypi/dm/flake8-plugin-utils.svg)](https://pypistats.org/packages/flake8-plugin-utils)
[![Build Status](https://travis-ci.org/Afonasev/flake8-plugin-utils.svg?branch=master)](https://travis-ci.org/Afonasev/flake8-plugin-utils)
[![Code coverage](https://codecov.io/gh/afonasev/flake8-plugin-utils/branch/master/graph/badge.svg)](https://codecov.io/gh/afonasev/flake8-plugin-utils)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://en.wikipedia.org/wiki/MIT_License)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)

The package provides base classes and utils for flake8 plugin writing.

## Installation

```bash
pip install flake8-plugin-utils
```

## Example

Write simple plugin

```python
from flake8_plugin_utils import Error, Visitor, Plugin

class MyError(Error):
    code = 'X100'
    message = 'my error'

class MyVisitor(Visitor):
    def visit_ClassDef(self, node):
        self.error_from_node(MyError, node)

class MyPlugin(Plugin):
    name = 'MyPlugin'
    version = '0.1.0'
    visitors = [MyVisitor]
```

and test it with pytest

```python
from flake8_plugin_utils import assert_error, assert_not_error

def test_code_with_error():
    assert_error(MyVisitor, 'class Y: pass', MyError)

def test_code_without_error():
    assert_not_error(MyVisitor, 'x = 1')
```

### Configuration

To add configuration to a plugin, do the following:

1. Implement classmethod `add_options` in your plugin class, as per the
[flake8 docs](https://flake8.pycqa.org/en/latest/plugin-development/plugin-parameters.html#registering-options).
1. Override classmethod `parse_options_to_config` in your plugin class
to return any object holding the options you need.
1. If you need a custom `__init__` for your visitor, make sure it accepts
a keyword argument named `config` and pass it to `super().__init__`
1. Use `self.config` in visitor code.

Example:

```python
from flake8_plugin_utils import Error, Visitor, Plugin, assert_error

class MyError(Error):
    code = 'X100'
    message = 'my error with {thing}'

class MyConfig:
    def __init__(self, config_option):
        self.config_option = config_option

class MyVisitorWithConfig(Visitor):
    def visit_ClassDef(self, node):
        self.error_from_node(
            MyError, node, thing=f'{node.name} {self.config.config_option}'
        )

class MyPluginWithConfig(Plugin):
    name = 'MyPluginWithConfig'
    version = '0.0.1'
    visitors = [MyVisitorWithConfig]

    @classmethod
    def add_options(cls, options_manager):
        options_manager.add_option('--config_option', parse_from_config=True, ...)

    @classmethod
    def parse_options_to_config(cls, option_manager, options, args):
        return MyConfig(config_option=options.config_option)


def test_code_with_error():
    assert_error(
        MyVisitorWithConfig,
        'class Y: pass',
        MyError,
        config=MyConfig(config_option='123'),
        thing='Y 123',
    )
```

### Formatting

Your `Error`s can take formatting arguments in their `message`:

```python
from flake8_plugin_utils import Error, Visitor, assert_error

class MyFormattedError(Error):
    code = 'X101'
    message = 'my error with {thing}'

class MyFormattedVisitor(Visitor):
    def visit_ClassDef(self, node):
        self.error_from_node(MyFormattedError, node, thing=node.name)

def test_code_with_error():
    assert_error(
        MyFormattedVisitor,
        'class Y: pass',
        MyFormattedError,
        thing='Y',
    )
```

### Usage with typing/mypy

The `Plugin` and `Visitor` classes are generic with the config class as type
parameter.  If your plugin does not have any config, inherit it from
`Plugin[None]` and the visitors from `Visitor[None]`.  Otherwise, use the
config class as the type parameter (e.g. `Plugin[MyConfig]` and
`Visitor[MyConfig]` in the above example).

### Utility functions

* `assert_error`, `assert_not_error`
Utilities for testing visitors (see examples above).

* `is_true`, `is_false`, `is_none`
Convenience functions to check if an AST node represents a
`True`/`False`/`None` value.

* `check_equivalent_nodes`
Checks if two given AST nodes are equivalent.
The nodes are considered equivalent in the following cases:
  * dicts -- if they contain same key-value pairs, possibly in different order,
  with duplicates and `**expansions` taken into account
  * sets -- if they contain same elements, possibly in different order,
  with duplicates taken into account
  * anything else -- if they represent the same AST, regardless of formatting
  (with any dicts in sets inside checked according to the rules above)

## For developers

### Show help

    make help

### Create venv and install deps

    make init

### Install git precommit hook

    make precommit

### Run linters, autoformat, tests etc.

    make pretty lint test

### Bump new version

    make bump_major
    make bump_minor
    make bump_patch

## Change Log

Unreleased
-----

* ...

1.3.3 - 2022-01-14
-----

* add py.typed file (#78)

1.3.2 - 2021-05-05
-----

* Drop noqa detection (#56)
* docs: Add help for Makefile

1.3.1 - 2020-08-06
-----

* Fix handling of encoding when loading files (#37)

1.3.0 - 2020-03-26
-----

* add `check_equivalent_nodes` utility function

1.2.0 - 2020-03-06
-----

* add `config` argument to `assert_error` and `assert_not_error`

1.1.1 - 2020-03-02
-----

* ignore encoding errors when reading strings for noqa validation

1.1.0 - 2020-03-01
-----

* add ability for plugins to parse and use configuration
**NB: this change breaks type-checking if you use typing/mypy. Change your
code to inherit from `Plugin[None]` and `Visitor[None]` to fix.**

1.0.0 - 2019-05-23
-----

* add message formatting to Error

0.2.1 - 2019-04-01
-----

* don`t strip before src dedent in _error_from_src
* add is_none, is_true, is_false util functions

0.2.0 - 2019.02.21
-----

* add assert methods

0.1.0 - 2019.02.09
-----

* initial

            

Raw data

            {
    "_id": null,
    "home_page": "https://pypi.org/project/flake8-plugin-utils",
    "name": "flake8-plugin-utils",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.6,<4.0",
    "maintainer_email": "",
    "keywords": "flake8,plugin,utils",
    "author": "Afonasev Evgeniy",
    "author_email": "ea.afonasev@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/51/14/53727a2bc5bbda1e1f7e266e0e2d2718e5eb6c943a1e8cc2b33e5af002e0/flake8-plugin-utils-1.3.3.tar.gz",
    "platform": null,
    "description": "# flake8-plugin-utils\n\n[![pypi](https://badge.fury.io/py/flake8-plugin-utils.svg)](https://pypi.org/project/flake8-plugin-utils)\n[![Python: 3.6+](https://img.shields.io/badge/Python-3.6+-blue.svg)](https://pypi.org/project/flake8-plugin-utils)\n[![Downloads](https://img.shields.io/pypi/dm/flake8-plugin-utils.svg)](https://pypistats.org/packages/flake8-plugin-utils)\n[![Build Status](https://travis-ci.org/Afonasev/flake8-plugin-utils.svg?branch=master)](https://travis-ci.org/Afonasev/flake8-plugin-utils)\n[![Code coverage](https://codecov.io/gh/afonasev/flake8-plugin-utils/branch/master/graph/badge.svg)](https://codecov.io/gh/afonasev/flake8-plugin-utils)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://en.wikipedia.org/wiki/MIT_License)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)\n\nThe package provides base classes and utils for flake8 plugin writing.\n\n## Installation\n\n```bash\npip install flake8-plugin-utils\n```\n\n## Example\n\nWrite simple plugin\n\n```python\nfrom flake8_plugin_utils import Error, Visitor, Plugin\n\nclass MyError(Error):\n    code = 'X100'\n    message = 'my error'\n\nclass MyVisitor(Visitor):\n    def visit_ClassDef(self, node):\n        self.error_from_node(MyError, node)\n\nclass MyPlugin(Plugin):\n    name = 'MyPlugin'\n    version = '0.1.0'\n    visitors = [MyVisitor]\n```\n\nand test it with pytest\n\n```python\nfrom flake8_plugin_utils import assert_error, assert_not_error\n\ndef test_code_with_error():\n    assert_error(MyVisitor, 'class Y: pass', MyError)\n\ndef test_code_without_error():\n    assert_not_error(MyVisitor, 'x = 1')\n```\n\n### Configuration\n\nTo add configuration to a plugin, do the following:\n\n1. Implement classmethod `add_options` in your plugin class, as per the\n[flake8 docs](https://flake8.pycqa.org/en/latest/plugin-development/plugin-parameters.html#registering-options).\n1. Override classmethod `parse_options_to_config` in your plugin class\nto return any object holding the options you need.\n1. If you need a custom `__init__` for your visitor, make sure it accepts\na keyword argument named `config` and pass it to `super().__init__`\n1. Use `self.config` in visitor code.\n\nExample:\n\n```python\nfrom flake8_plugin_utils import Error, Visitor, Plugin, assert_error\n\nclass MyError(Error):\n    code = 'X100'\n    message = 'my error with {thing}'\n\nclass MyConfig:\n    def __init__(self, config_option):\n        self.config_option = config_option\n\nclass MyVisitorWithConfig(Visitor):\n    def visit_ClassDef(self, node):\n        self.error_from_node(\n            MyError, node, thing=f'{node.name} {self.config.config_option}'\n        )\n\nclass MyPluginWithConfig(Plugin):\n    name = 'MyPluginWithConfig'\n    version = '0.0.1'\n    visitors = [MyVisitorWithConfig]\n\n    @classmethod\n    def add_options(cls, options_manager):\n        options_manager.add_option('--config_option', parse_from_config=True, ...)\n\n    @classmethod\n    def parse_options_to_config(cls, option_manager, options, args):\n        return MyConfig(config_option=options.config_option)\n\n\ndef test_code_with_error():\n    assert_error(\n        MyVisitorWithConfig,\n        'class Y: pass',\n        MyError,\n        config=MyConfig(config_option='123'),\n        thing='Y 123',\n    )\n```\n\n### Formatting\n\nYour `Error`s can take formatting arguments in their `message`:\n\n```python\nfrom flake8_plugin_utils import Error, Visitor, assert_error\n\nclass MyFormattedError(Error):\n    code = 'X101'\n    message = 'my error with {thing}'\n\nclass MyFormattedVisitor(Visitor):\n    def visit_ClassDef(self, node):\n        self.error_from_node(MyFormattedError, node, thing=node.name)\n\ndef test_code_with_error():\n    assert_error(\n        MyFormattedVisitor,\n        'class Y: pass',\n        MyFormattedError,\n        thing='Y',\n    )\n```\n\n### Usage with typing/mypy\n\nThe `Plugin` and `Visitor` classes are generic with the config class as type\nparameter.  If your plugin does not have any config, inherit it from\n`Plugin[None]` and the visitors from `Visitor[None]`.  Otherwise, use the\nconfig class as the type parameter (e.g. `Plugin[MyConfig]` and\n`Visitor[MyConfig]` in the above example).\n\n### Utility functions\n\n* `assert_error`, `assert_not_error`\nUtilities for testing visitors (see examples above).\n\n* `is_true`, `is_false`, `is_none`\nConvenience functions to check if an AST node represents a\n`True`/`False`/`None` value.\n\n* `check_equivalent_nodes`\nChecks if two given AST nodes are equivalent.\nThe nodes are considered equivalent in the following cases:\n  * dicts -- if they contain same key-value pairs, possibly in different order,\n  with duplicates and `**expansions` taken into account\n  * sets -- if they contain same elements, possibly in different order,\n  with duplicates taken into account\n  * anything else -- if they represent the same AST, regardless of formatting\n  (with any dicts in sets inside checked according to the rules above)\n\n## For developers\n\n### Show help\n\n    make help\n\n### Create venv and install deps\n\n    make init\n\n### Install git precommit hook\n\n    make precommit\n\n### Run linters, autoformat, tests etc.\n\n    make pretty lint test\n\n### Bump new version\n\n    make bump_major\n    make bump_minor\n    make bump_patch\n\n## Change Log\n\nUnreleased\n-----\n\n* ...\n\n1.3.3 - 2022-01-14\n-----\n\n* add py.typed file (#78)\n\n1.3.2 - 2021-05-05\n-----\n\n* Drop noqa detection (#56)\n* docs: Add help for Makefile\n\n1.3.1 - 2020-08-06\n-----\n\n* Fix handling of encoding when loading files (#37)\n\n1.3.0 - 2020-03-26\n-----\n\n* add `check_equivalent_nodes` utility function\n\n1.2.0 - 2020-03-06\n-----\n\n* add `config` argument to `assert_error` and `assert_not_error`\n\n1.1.1 - 2020-03-02\n-----\n\n* ignore encoding errors when reading strings for noqa validation\n\n1.1.0 - 2020-03-01\n-----\n\n* add ability for plugins to parse and use configuration\n**NB: this change breaks type-checking if you use typing/mypy. Change your\ncode to inherit from `Plugin[None]` and `Visitor[None]` to fix.**\n\n1.0.0 - 2019-05-23\n-----\n\n* add message formatting to Error\n\n0.2.1 - 2019-04-01\n-----\n\n* don`t strip before src dedent in _error_from_src\n* add is_none, is_true, is_false util functions\n\n0.2.0 - 2019.02.21\n-----\n\n* add assert methods\n\n0.1.0 - 2019.02.09\n-----\n\n* initial\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "The package provides base classes and utils for flake8 plugin writing",
    "version": "1.3.3",
    "project_urls": {
        "Homepage": "https://pypi.org/project/flake8-plugin-utils",
        "Repository": "https://github.com/afonasev/flake8-plugin-utils"
    },
    "split_keywords": [
        "flake8",
        "plugin",
        "utils"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "daa723c012c9becaacb24e9ba8e359e48db5f96982d505e38a3e7003902c5b9f",
                "md5": "b19c78a6bafd15a9530ce31a87262641",
                "sha256": "e4848c57d9d50f19100c2d75fa794b72df068666a9041b4b0409be923356a3ed"
            },
            "downloads": -1,
            "filename": "flake8_plugin_utils-1.3.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "b19c78a6bafd15a9530ce31a87262641",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.6,<4.0",
            "size": 9664,
            "upload_time": "2023-06-26T16:42:23",
            "upload_time_iso_8601": "2023-06-26T16:42:23.939022Z",
            "url": "https://files.pythonhosted.org/packages/da/a7/23c012c9becaacb24e9ba8e359e48db5f96982d505e38a3e7003902c5b9f/flake8_plugin_utils-1.3.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "511453727a2bc5bbda1e1f7e266e0e2d2718e5eb6c943a1e8cc2b33e5af002e0",
                "md5": "ed50d2e9196cfa51cb67308fb6f35912",
                "sha256": "39f6f338d038b301c6fd344b06f2e81e382b68fa03c0560dff0d9b1791a11a2c"
            },
            "downloads": -1,
            "filename": "flake8-plugin-utils-1.3.3.tar.gz",
            "has_sig": false,
            "md5_digest": "ed50d2e9196cfa51cb67308fb6f35912",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6,<4.0",
            "size": 10459,
            "upload_time": "2023-06-26T16:42:20",
            "upload_time_iso_8601": "2023-06-26T16:42:20.946486Z",
            "url": "https://files.pythonhosted.org/packages/51/14/53727a2bc5bbda1e1f7e266e0e2d2718e5eb6c943a1e8cc2b33e5af002e0/flake8-plugin-utils-1.3.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-06-26 16:42:20",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "afonasev",
    "github_project": "flake8-plugin-utils",
    "travis_ci": true,
    "coveralls": false,
    "github_actions": false,
    "lcname": "flake8-plugin-utils"
}
        
Elapsed time: 0.10503s