Skip to content

Commit

Permalink
enhancement: add lint rules plugin support
Browse files Browse the repository at this point in the history
Add plugin support using setuptools (pkg_resources) plugin mechanism to
YAML Lint to allow users to add their own custom lint rule plugins.

Signed-off-by: Satoru SATOH <satoru.satoh@gmail.com>
  • Loading branch information
ssato committed Sep 9, 2020
1 parent 0a88c55 commit 10467a4
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 3 deletions.
44 changes: 44 additions & 0 deletions docs/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,47 @@ Basic example of running the linter from Python:
.. automodule:: yamllint.linter
:members:

Develop rule plugins
---------------------

yamllint provides a plugin mechanism using setuptools (pkg_resources) to allow
adding custom rules. So, you can extend yamllint and add rules with your own
custom yamllint rule plugins if you developed them.

Yamllint rule plugins must satisfy the followings.

#. It must be a python package installable using pip and distributed under
GPLv3+ same as yamllint.
#. It must contains the entry point configuration in setup.cfg or something
similar packaging configuration files, to make it installed and working as a
yamllint plugin like below. (<plugin_name> is that plugin name and
<plugin_src_dir> is a dir where the rule modules exist.)
::

[options.entry_points]
yamllint.plugins =
<plugin_name> = <plugin_src_dir>

#. It must contain custom yamllint rule modules:

- Each rule module must define a couple of global variables, ID and TYPE. ID
must not conflicts with other rules' ID.
- Each rule module must define a function named 'check' to test input data
complies with the rule.
- Each rule module may have other global variables.

- CONF to define its configuration parameters and those types.
- DEFAULT to provide default values for each configuration parameters.

#. It must define a global variable RULES_MAP to provide mappings of rule ID
and rule modules to yamllint like this.
::

RULES_MAP = {
# rule ID: rule module
a_custom_rule.ID: a_custom_rule
}

To develop yamllint rules, the default rules themselves in yamllint may become
good references.
58 changes: 58 additions & 0 deletions yamllint/plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2020 Satoru SATOH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Plugin module utilizing setuptools (pkg_resources) to allow users to add their
own custom lint rules.
"""
try:
from pkg_resources import iter_entry_points
except ImportError:
def iter_entry_points(group):
"""Dummy function for pkg_resources.iter_entry_points."""
yield


PACKAGE_GROUP = "yamllint.plugins"


def validate_rule_module(rule_mod):
"""Test if given rule module is valid.
"""
return getattr(rule_mod, "ID", False
) and callable(getattr(rule_mod, "check", False))


def load_plugin_rules_itr(group=PACKAGE_GROUP):
"""Load custom lint rule plugins."""
for entry in iter_entry_points(group):
try:
rules_mod = entry.load()
yield rules_mod.RULES_MAP
except AttributeError:
pass # TBD


def update_rules_map(rules_map):
"""Update and add plugins rules to given rules' map."""
for plugin_rules_map in load_plugin_rules_itr():
for rule_id, rule_mod in plugin_rules_map.items():
if not validate_rule_module(rule_mod):
continue

if rule_id not in rules_map:
rules_map[rule_id] = rule_mod

return rules_map
11 changes: 8 additions & 3 deletions yamllint/rules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import yamllint.plugins

from yamllint.rules import (
braces,
brackets,
Expand Down Expand Up @@ -63,8 +65,11 @@
}


def get(id):
if id not in _RULES:
def get(id, rules=None):
if rules is None:
rules = yamllint.plugins.update_rules_map(_RULES)

if id not in rules:
raise ValueError('no such rule: "%s"' % id)

return _RULES[id]
return rules[id]

0 comments on commit 10467a4

Please sign in to comment.