diff --git a/docs/disable_with_comments.rst b/docs/disable_with_comments.rst
new file mode 100644
index 0000000..10477fb
--- /dev/null
+++ b/docs/disable_with_comments.rst
@@ -0,0 +1,75 @@
+Disable with comments
+=====================
+
+Disabling checks for a specific line
+------------------------------------
+
+To prevent yamllint from reporting problems for a specific line, add a directive
+comment (``# yamllint disable-line ...``) on that line, or on the line above.
+For instance:
+
+.. code-block:: yaml
+
+ # The following mapping contains the same key twice,
+ # but I know what I'm doing:
+ key: value 1
+ key: value 2 # yamllint disable-line rule:key-duplicates
+
+ - This line is waaaaaaaaaay too long but yamllint will not report anything about it. # yamllint disable-line rule:line-length
+ This line will be checked by yamllint.
+
+or:
+
+.. code-block:: yaml
+
+ # The following mapping contains the same key twice,
+ # but I know what I'm doing:
+ key: value 1
+ # yamllint disable-line rule:key-duplicates
+ key: value 2
+
+ # yamllint disable-line rule:line-length
+ - This line is waaaaaaaaaay too long but yamllint will not report anything about it.
+ This line will be checked by yamllint.
+
+It it possible, although not recommend, to disabled **all** rules for a
+specific line:
+
+.. code-block:: yaml
+
+ # yamllint disable-line
+ - { all : rules ,are disabled for this line}
+
+If you need to disable multiple rules, it is allowed to chain rules like this:
+``# yamllint disable-line rule:hyphens rule:commas rule:indentation``.
+
+Disabling checks for all (or part of) the file
+----------------------------------------------
+
+To prevent yamllint from reporting problems for the whoe file, or for a block of
+lines within the file, use ``# yamllint disable ...`` and ``# yamllint enable
+...`` directive comments. For instance:
+
+.. code-block:: yaml
+
+ # yamllint disable rule:colons
+ - Lorem : ipsum
+ dolor : sit amet,
+ consectetur : adipiscing elit
+ # yamllint enable rule:colons
+
+ - rest of the document...
+
+It it possible, although not recommend, to disabled **all** rules:
+
+.. code-block:: yaml
+
+ # yamllint disable
+ - Lorem :
+ ipsum:
+ dolor : [ sit,amet]
+ - consectetur : adipiscing elit
+ # yamllint enable
+
+If you need to disable multiple rules, it is allowed to chain rules like this:
+``# yamllint disable rule:hyphens rule:commas rule:indentation``.
diff --git a/docs/index.rst b/docs/index.rst
index 773b143..97e1fc7 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -23,5 +23,6 @@ Table of contents
quickstart
configuration
rules
+ disable_with_comments
development
text_editors
diff --git a/tests/test_yamllint_directives.py b/tests/test_yamllint_directives.py
new file mode 100644
index 0000000..8c6e865
--- /dev/null
+++ b/tests/test_yamllint_directives.py
@@ -0,0 +1,304 @@
+# -*- 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 .
+
+from tests.common import RuleTestCase
+
+
+class YamllintDirectivesTestCase(RuleTestCase):
+ conf = ('commas: disable\n'
+ 'trailing-spaces: {}\n'
+ 'colons: {max-spaces-before: 1}\n')
+
+ def test_disable_directive(self):
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces \n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(3, 18, 'trailing-spaces'),
+ problem2=(4, 8, 'colons'),
+ problem3=(6, 7, 'colons'),
+ problem4=(6, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces \n'
+ '# yamllint disable\n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem=(3, 18, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '# yamllint disable\n'
+ '- trailing spaces \n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '# yamllint enable\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(8, 7, 'colons'),
+ problem2=(8, 26, 'trailing-spaces'))
+
+ def test_disable_directive_with_rules(self):
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces \n'
+ '# yamllint disable rule:trailing-spaces\n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(3, 18, 'trailing-spaces'),
+ problem2=(5, 8, 'colons'),
+ problem3=(7, 7, 'colons'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '# yamllint disable rule:trailing-spaces\n'
+ '- trailing spaces \n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '# yamllint enable rule:trailing-spaces\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(5, 8, 'colons'),
+ problem2=(8, 7, 'colons'),
+ problem3=(8, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '# yamllint disable rule:trailing-spaces\n'
+ '- trailing spaces \n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '# yamllint enable\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(5, 8, 'colons'),
+ problem2=(8, 7, 'colons'),
+ problem3=(8, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '# yamllint disable\n'
+ '- trailing spaces \n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '# yamllint enable rule:trailing-spaces\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem=(8, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '# yamllint disable rule:colons\n'
+ '- trailing spaces \n'
+ '# yamllint disable rule:trailing-spaces\n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '# yamllint enable rule:colons\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(4, 18, 'trailing-spaces'),
+ problem2=(9, 7, 'colons'))
+
+ def test_disable_line_directive(self):
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces \n'
+ '# yamllint disable-line\n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(3, 18, 'trailing-spaces'),
+ problem2=(7, 7, 'colons'),
+ problem3=(7, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces \n'
+ '- bad : colon # yamllint disable-line\n'
+ '- [valid , YAML]\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(3, 18, 'trailing-spaces'),
+ problem2=(6, 7, 'colons'),
+ problem3=(6, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces \n'
+ '- bad : colon\n'
+ '- [valid , YAML] # yamllint disable-line\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(3, 18, 'trailing-spaces'),
+ problem2=(4, 8, 'colons'),
+ problem3=(6, 7, 'colons'),
+ problem4=(6, 26, 'trailing-spaces'))
+
+ def test_disable_line_directive_with_rules(self):
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '# yamllint disable-line rule:colons\n'
+ '- trailing spaces \n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(4, 18, 'trailing-spaces'),
+ problem2=(5, 8, 'colons'),
+ problem3=(7, 7, 'colons'),
+ problem4=(7, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces # yamllint disable-line rule:colons \n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(3, 55, 'trailing-spaces'),
+ problem2=(4, 8, 'colons'),
+ problem3=(6, 7, 'colons'),
+ problem4=(6, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces \n'
+ '# yamllint disable-line rule:colons\n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(3, 18, 'trailing-spaces'),
+ problem2=(7, 7, 'colons'),
+ problem3=(7, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces \n'
+ '- bad : colon # yamllint disable-line rule:colons\n'
+ '- [valid , YAML]\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(3, 18, 'trailing-spaces'),
+ problem2=(6, 7, 'colons'),
+ problem3=(6, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces \n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '# yamllint disable-line rule:colons\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(3, 18, 'trailing-spaces'),
+ problem2=(4, 8, 'colons'),
+ problem3=(7, 26, 'trailing-spaces'))
+ self.check('---\n'
+ '- [valid , YAML]\n'
+ '- trailing spaces \n'
+ '- bad : colon\n'
+ '- [valid , YAML]\n'
+ '# yamllint disable-line rule:colons rule:trailing-spaces\n'
+ '- bad : colon and spaces \n'
+ '- [valid , YAML]\n',
+ self.conf,
+ problem1=(3, 18, 'trailing-spaces'),
+ problem2=(4, 8, 'colons'))
+
+ def test_directive_on_last_line(self):
+ conf = 'new-line-at-end-of-file: {}'
+ self.check('---\n'
+ 'no new line',
+ conf,
+ problem=(2, 12, 'new-line-at-end-of-file'))
+ self.check('---\n'
+ '# yamllint disable\n'
+ 'no new line',
+ conf)
+ self.check('---\n'
+ 'no new line # yamllint disable',
+ conf)
+
+ def test_indented_directive(self):
+ conf = 'brackets: {min-spaces-inside: 0, max-spaces-inside: 0}'
+ self.check('---\n'
+ '- a: 1\n'
+ ' b:\n'
+ ' c: [ x]\n',
+ conf,
+ problem=(4, 12, 'brackets'))
+ self.check('---\n'
+ '- a: 1\n'
+ ' b:\n'
+ ' # yamllint disable-line rule:brackets\n'
+ ' c: [ x]\n',
+ conf)
+
+ def test_directive_on_itself(self):
+ conf = ('comments: {min-spaces-from-content: 2}\n'
+ 'comments-indentation: {}\n')
+ self.check('---\n'
+ '- a: 1 # comment too close\n'
+ ' b:\n'
+ ' # wrong indentation\n'
+ ' c: [x]\n',
+ conf,
+ problem1=(2, 8, 'comments'),
+ problem2=(4, 2, 'comments-indentation'))
+ self.check('---\n'
+ '# yamllint disable\n'
+ '- a: 1 # comment too close\n'
+ ' b:\n'
+ ' # wrong indentation\n'
+ ' c: [x]\n',
+ conf)
+ self.check('---\n'
+ '- a: 1 # yamllint disable-line\n'
+ ' b:\n'
+ ' # yamllint disable-line\n'
+ ' # wrong indentation\n'
+ ' c: [x]\n',
+ conf)
+ self.check('---\n'
+ '- a: 1 # yamllint disable-line rule:comments\n'
+ ' b:\n'
+ ' # yamllint disable-line rule:comments-indentation\n'
+ ' # wrong indentation\n'
+ ' c: [x]\n',
+ conf)
+ self.check('---\n'
+ '# yamllint disable\n'
+ '- a: 1 # comment too close\n'
+ ' # yamllint enable rule:comments-indentation\n'
+ ' b:\n'
+ ' # wrong indentation\n'
+ ' c: [x]\n',
+ conf,
+ problem=(6, 2, 'comments-indentation'))
diff --git a/yamllint/linter.py b/yamllint/linter.py
index 98919d2..9b55b29 100644
--- a/yamllint/linter.py
+++ b/yamllint/linter.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import re
+
import yaml
from yamllint import parser
@@ -63,6 +65,55 @@ def get_costemic_problems(buffer, conf):
for rule in token_rules:
context[rule.ID] = {}
+ class DisableDirective():
+ def __init__(self):
+ self.rules = set()
+ self.all_rules = set([r.ID for r in rules])
+
+ def process_comment(self, comment):
+ comment = repr(comment)
+
+ if re.match(r'^# yamllint disable( rule:\S+)*\s*$', comment):
+ rules = [item[5:] for item in comment[18:].split(' ')][1:]
+ if len(rules) == 0:
+ self.rules = self.all_rules.copy()
+ else:
+ for id in rules:
+ if id in self.all_rules:
+ self.rules.add(id)
+
+ elif re.match(r'^# yamllint enable( rule:\S+)*\s*$', comment):
+ rules = [item[5:] for item in comment[17:].split(' ')][1:]
+ if len(rules) == 0:
+ self.rules.clear()
+ else:
+ for id in rules:
+ self.rules.discard(id)
+
+ def is_disabled_by_directive(self, problem):
+ return problem.rule in self.rules
+
+ class DisableLineDirective(DisableDirective):
+ def process_comment(self, comment):
+ comment = repr(comment)
+
+ if re.match(r'^# yamllint disable-line( rule:\S+)*\s*$', comment):
+ rules = [item[5:] for item in comment[23:].split(' ')][1:]
+ if len(rules) == 0:
+ self.rules = self.all_rules.copy()
+ else:
+ for id in rules:
+ if id in self.all_rules:
+ self.rules.add(id)
+
+ # Use a cache to store problems and flush it only when a end of line is
+ # found. This allows the use of yamllint directive to disable some rules on
+ # some lines.
+ cache = []
+ disabled = DisableDirective()
+ disabled_for_line = DisableLineDirective()
+ disabled_for_next_line = DisableLineDirective()
+
for elem in parser.token_or_comment_or_line_generator(buffer):
if isinstance(elem, parser.Token):
for rule in token_rules:
@@ -73,22 +124,45 @@ def get_costemic_problems(buffer, conf):
context[rule.ID]):
problem.rule = rule.ID
problem.level = rule_conf['level']
- yield problem
+ cache.append(problem)
elif isinstance(elem, parser.Comment):
for rule in comment_rules:
rule_conf = conf.rules[rule.ID]
for problem in rule.check(rule_conf, elem):
problem.rule = rule.ID
problem.level = rule_conf['level']
- yield problem
+ cache.append(problem)
+
+ disabled.process_comment(elem)
+ if elem.is_inline():
+ disabled_for_line.process_comment(elem)
+ else:
+ disabled_for_next_line.process_comment(elem)
elif isinstance(elem, parser.Line):
for rule in line_rules:
rule_conf = conf.rules[rule.ID]
for problem in rule.check(rule_conf, elem):
problem.rule = rule.ID
problem.level = rule_conf['level']
+ cache.append(problem)
+
+ # This is the last token/comment/line of this line, let's flush the
+ # problems found (but filter them according to the directives)
+ for problem in cache:
+ if not (disabled_for_line.is_disabled_by_directive(problem) or
+ disabled.is_disabled_by_directive(problem)):
yield problem
+ disabled_for_line = disabled_for_next_line
+ disabled_for_next_line = DisableLineDirective()
+ cache = []
+
+ # If no new line at the end of file, the cache is not empty
+ for problem in cache:
+ if not (disabled_for_line.is_disabled_by_directive(problem) or
+ disabled.is_disabled_by_directive(problem)):
+ yield problem
+
def get_syntax_error(buffer):
try: