pfnet / pysen

Python linting made easy. Also a casual yet honorific way to address individuals who have entered an organization prior to you.
MIT License
487 stars 20 forks source link

Add `flake8_ignore` option #50

Closed puchupala closed 7 months ago

puchupala commented 7 months ago

Support flake8_ignore options in [tool.pysen.lint] section of pysen.toml or pyproject.toml.

Example

[tool.pysen]
version = "0.11.0"

[tool.pysen.lint]
enable_flake8 = true
flake8_ignore = ["W"]
sergeant-wizard commented 7 months ago

Thanks for the contribution.

One of the goals of pysen is to share a standardized coding rule over several projects, so we try to avoid adding features that allow individual projects to have their own rules as much as possible.

From readme

Q. How do I change specific settings for linter X? A. We prioritize convention over configuration. However you can always create your own plugin. See: Create a plugin to customize pysen

If you want to disable flake8 entirely you can try pysen --disable flake8 run lint. You can also use plugins, which understandably is indeed too complicated than need be, but that unfortunately is the price you have to pay to adopt non-standard rules.

puchupala commented 7 months ago

Thank you very much for the explanation.

Actually, we are trying to enforce a more strict linting rules by introducing flake8-functions into our workflow. However, this may be a drastic changes so we want to enable the new validators gradually.

There is a solution using cusomizable_pysen. However, this solution requires us to forego using pysen to manage linter configurations and manage them entirely by ourselves when we want to simply slightly modify the rules. Maintaining custom linter configuration compatibility with future pysen may be difficult.

I understand that disable some rules do conflict with pysen principles, however, customizing linter ignore rules is a very common task. Having an example showing how to use pysen plugin to achieve this or a contrib plugin package that allow for a slightly more control over linting behavior (instead of the all-or-nothing approach of customizable_pysen) would be extremely helpful.

sergeant-wizard commented 7 months ago

Examples for plugins are under examples/plugin_example/plugin.py.

puchupala commented 7 months ago

Thank you very much for the pointer. 🙇

However, what I mean was a specific example demonstrating how to create a plugin that allow for modifying flake8 ignore list. (as I imagine this to be a very common use case of flake8)

Still, the current plugin example is helpful. I will try to figure something out from the current example.

puchupala commented 7 months ago

A little hackish but this works.

pyproject.toml

[tool.pysen]
version = "0.11.0"
builder = "./pysen_custom_flake8.py"

[tool.pysen.lint]
enable_flake8 = true

[tool.pysen.plugin.custom_flake8]
script = "./pysen_custom_flake8.py"

[tool.pysen.plugin.custom_flake8.config]
extend_ignore = ["CFQ"]

pysen_custom_flake8.py

import pathlib
from typing import Optional, Sequence

from pysen.component import ComponentBase
from pysen.pyproject import build as pysen_build
from pysen.manifest import ManifestBase
from pysen.plugin import PluginBase
from pysen.pyproject_model import Config, PluginConfig
from pysen.pyproject_model import parse
from pysen.flake8 import Flake8

def _parse_extend_ignore(src_path: pathlib.Path) -> Sequence[str]:
    config = parse(src_path)
    for p in config.plugin:
        if p.location == "tool.pysen.plugin.custom_flake8":
            return p.config.get("extend_ignore", [])
    return []

def build(
    components: Sequence[ComponentBase], src_path: Optional[pathlib.Path]
) -> ManifestBase:
    extend_ignore = _parse_extend_ignore(src_path)
    for component in components:
        if isinstance(component, Flake8):
            if component.setting.ignore:
                component.setting.ignore += extend_ignore
            else:
                component.setting.ignore = extend_ignore
    return pysen_build(components, src_path)

# @note (puchupala): Need a dummy plugin so that pysen won't complain about the
#       added config section.
def plugin() -> PluginBase:
    class DummyPlugin(PluginBase):
        def load(
            self, file_path: pathlib.Path, config_data: PluginConfig, root: Config
        ) -> Sequence[ComponentBase]:
            return []
    return DummyPlugin()