Skip to content

Commit

Permalink
Add the ability to run custom rules included in a project
Browse files Browse the repository at this point in the history
  • Loading branch information
pdericson committed Sep 22, 2016
1 parent 9b72a2d commit 0b985a0
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ If ``-c`` is not provided, yamllint will look for a configuration file in the
following locations (by order of preference):

- ``.yamllint`` in the current working directory
- ``.yamllint/config`` in the current working directory
- ``$XDG_CONFIG_HOME/yamllint/config``
- ``~/.config/yamllint/config``

Expand Down
62 changes: 62 additions & 0 deletions docs/custom_rules.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
Custom Rules
============

There are times when you might like to add custom rules to your
project. This could be because the rules you'd like to enforce are
not general enough to consider including in upstream yamllint.

yamllint will look for custom rules in ``.yamllint/rules``. To enable
a custom rule you need to explicitly reference the rule in your
config.

Example
~~~~~~~

In this example there is a custom rule called ``truthy`` that will
complain if ambiguous truthy values are not quoted.

This is the directory structure:

.. code:: plain
.
|-- .yamllint
| |-- config
| `-- rules
| |-- __init__.py
| `-- truthy.py
`-- example.yml
2 directories, 4 files
This is an example yaml file with ambiguous truthy values:

.. code:: yaml
---
a: y
b: yes
c: on
d: True
This is an example config file:

.. code:: yaml
---
extends: default
rules:
truthy: enable
Lint problems from the custom rule are now included in the yamllint
output:

.. code:: plain
$ yamllint example.yml
example.yml
2:3 error ambiguous truthy value is not quoted (truthy)
3:3 error ambiguous truthy value is not quoted (truthy)
4:3 error ambiguous truthy value is not quoted (truthy)
5:3 error ambiguous truthy value is not quoted (truthy)
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ Table of contents
disable_with_comments
development
text_editors
custom_rules
73 changes: 73 additions & 0 deletions tests/rules/test_custom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé
#
# 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/>.

import os
import shutil
import tempfile

from tests.common import RuleTestCase
from yamllint.config import YamlLintConfigError


class CustomTestCase(RuleTestCase):
rule_id = 'custom'

@classmethod
def setUpClass(self):
self.tmpd = tempfile.mkdtemp()
rules = os.path.join(self.tmpd, '.yamllint', 'rules')
os.makedirs(rules)

with open(os.path.join(rules, '__init__.py'), 'w'):
pass

with open(os.path.join(rules, 'custom.py'), 'w') as f:
f.write("""ID = 'custom'
TYPE = 'token'
def check(*args, **kwargs):
if 0:
yield
""")

self.orig_cwd = os.getcwd()
os.chdir(self.tmpd)

def test_disabled(self):
conf = 'custom: disable\n'

self.check('---\n', conf)

def test_enabled(self):
conf = 'custom: enable\n'

self.check('---\n', conf)

def test_config_present(self):
conf = 'custom: enable\n'

self.check('---\n', conf)

def test_config_missing(self):
conf = ''

with self.assertRaises(YamlLintConfigError):
self.check('---\n', conf)

@classmethod
def tearDownClass(self):
os.chdir(self.orig_cwd)
shutil.rmtree(self.tmpd)
2 changes: 2 additions & 0 deletions yamllint/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ def run(argv=None):
conf = YamlLintConfig(file=args.config_file)
elif os.path.isfile('.yamllint'):
conf = YamlLintConfig(file='.yamllint')
elif os.path.isfile('.yamllint/config'):
conf = YamlLintConfig(file='.yamllint/config')
elif os.path.isfile(user_global_config):
conf = YamlLintConfig(file=user_global_config)
else:
Expand Down
13 changes: 13 additions & 0 deletions yamllint/rules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
# 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 importlib
import os
import sys

from yamllint.rules import (
braces,
brackets,
Expand Down Expand Up @@ -54,6 +58,15 @@


def get(id):
if id not in _RULES:
try:
if os.path.isdir('.yamllint'):
sys.path.append('.yamllint')
module = importlib.import_module('rules.' + id)
_RULES[module.ID] = module
except ImportError as exc:
pass

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

Expand Down

0 comments on commit 0b985a0

Please sign in to comment.