Compare commits
1 Commits
v1.3.0
...
proto/fix-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cad95ba4b |
47
README.rst
47
README.rst
@@ -24,10 +24,10 @@ Written in Python (compatible with Python 2 & 3).
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
http://yamllint.readthedocs.io/
|
||||
http://yamllint.readthedocs.org/
|
||||
|
||||
Overview
|
||||
--------
|
||||
Short overview
|
||||
--------------
|
||||
|
||||
Screenshot
|
||||
^^^^^^^^^^
|
||||
@@ -50,6 +50,13 @@ On Debian 9+ / Ubuntu 16.04+:
|
||||
|
||||
sudo apt-get install yamllint
|
||||
|
||||
On older Debian / Ubuntu versions:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
sudo add-apt-repository -y ppa:adrienverge/ppa && sudo apt-get update
|
||||
sudo apt-get install yamllint
|
||||
|
||||
Alternatively using pip, the Python package manager:
|
||||
|
||||
.. code:: bash
|
||||
@@ -75,19 +82,15 @@ Usage
|
||||
yamllint -d relaxed file.yaml
|
||||
|
||||
# Use a custom lint configuration
|
||||
yamllint -c /path/to/myconfig file-to-lint.yaml
|
||||
yamllint -c ~/myconfig file.yml
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# Output a parsable format (for syntax checking in editors like Vim, emacs...)
|
||||
yamllint -f parsable file.yaml
|
||||
|
||||
`Read more in the complete documentation! <http://yamllint.readthedocs.io/>`_
|
||||
|
||||
Features
|
||||
^^^^^^^^
|
||||
|
||||
Here is a yamllint configuration file example:
|
||||
Configuration example
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
@@ -101,27 +104,3 @@ Here is a yamllint configuration file example:
|
||||
|
||||
# don't bother me with this rule
|
||||
indentation: disable
|
||||
|
||||
Within a YAML file, special comments can be used to disable checks for a single
|
||||
line:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
This line is waaaaaaaaaay too long # yamllint disable-line
|
||||
|
||||
or for a whole block:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
# yamllint disable rule:colons
|
||||
- Lorem : ipsum
|
||||
dolor : sit amet,
|
||||
consectetur : adipiscing elit
|
||||
# yamllint enable
|
||||
|
||||
`Read more in the complete documentation! <http://yamllint.readthedocs.io/>`_
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
`GPL version 3 <LICENSE>`_
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
Configuration
|
||||
=============
|
||||
|
||||
yamllint uses a set of :doc:`rules <rules>` to check source files for problems.
|
||||
Each rule is independent from the others, and can be enabled, disabled or
|
||||
tweaked. All these settings can be gathered in a configuration file.
|
||||
yamllint uses a set of *rules* to check sources files for problems. Each rule is
|
||||
independent from the others, and can be enabled, disabled or tweaked. All these
|
||||
settings can be gathered in a configuration file.
|
||||
|
||||
To use a custom configuration file, use the ``-c`` option:
|
||||
To use a custom configuration file, either name it ``.yamllint`` in your working
|
||||
directory, or use the ``-c`` option:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
yamllint -c /path/to/myconfig file-to-lint.yaml
|
||||
|
||||
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
|
||||
- ``$XDG_CONFIG_HOME/yamllint/config``
|
||||
- ``~/.config/yamllint/config``
|
||||
|
||||
Finally if no config file is found, the default configuration is applied.
|
||||
yamllint -c ~/myconfig file.yaml
|
||||
|
||||
Default configuration
|
||||
---------------------
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
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``.
|
||||
@@ -23,6 +23,5 @@ Table of contents
|
||||
quickstart
|
||||
configuration
|
||||
rules
|
||||
disable_with_comments
|
||||
development
|
||||
text_editors
|
||||
|
||||
@@ -58,7 +58,7 @@ class CommentsIndentationTestCase(RuleTestCase):
|
||||
'# line 2\n', conf, problem=(2, 2))
|
||||
self.check('---\n'
|
||||
' # line 1\n'
|
||||
' # line 2\n', conf, problem1=(2, 3))
|
||||
' # line 2\n', conf, problem1=(2, 3), problem2=(3, 3))
|
||||
self.check('---\n'
|
||||
'obj:\n'
|
||||
' # normal\n'
|
||||
@@ -143,15 +143,3 @@ class CommentsIndentationTestCase(RuleTestCase):
|
||||
'# hey\n'
|
||||
'# normal\n'
|
||||
' #\n', conf, problem=(4, 2))
|
||||
|
||||
def test_inline_comment(self):
|
||||
conf = 'comments-indentation: enable'
|
||||
self.check('---\n'
|
||||
'- a # inline\n'
|
||||
'# ok\n', conf)
|
||||
self.check('---\n'
|
||||
'- a # inline\n'
|
||||
' # not ok\n', conf, problem=(3, 2))
|
||||
self.check('---\n'
|
||||
' # not ok\n'
|
||||
'- a # inline\n', conf, problem=(2, 2))
|
||||
|
||||
@@ -18,7 +18,8 @@ import unittest
|
||||
|
||||
import yaml
|
||||
|
||||
from yamllint.rules.common import get_line_indent
|
||||
from yamllint.rules.common import (Comment, get_line_indent,
|
||||
get_comments_between_tokens)
|
||||
|
||||
|
||||
class CommonTestCase(unittest.TestCase):
|
||||
@@ -42,3 +43,54 @@ class CommonTestCase(unittest.TestCase):
|
||||
self.assertEqual(get_line_indent(tokens[i]), 0)
|
||||
for i in (13, 16, 18, 22, 24):
|
||||
self.assertEqual(get_line_indent(tokens[i]), 2)
|
||||
|
||||
def check_comments(self, buffer, *expected):
|
||||
yaml_loader = yaml.BaseLoader(buffer)
|
||||
|
||||
comments = []
|
||||
|
||||
next = yaml_loader.peek_token()
|
||||
while next is not None:
|
||||
curr = yaml_loader.get_token()
|
||||
next = yaml_loader.peek_token()
|
||||
for comment in get_comments_between_tokens(curr, next):
|
||||
comments.append(comment)
|
||||
|
||||
self.assertEqual(comments, list(expected))
|
||||
|
||||
def test_get_comments_between_tokens(self):
|
||||
self.check_comments('# comment\n',
|
||||
Comment(1, 1, '# comment', 0))
|
||||
self.check_comments('---\n'
|
||||
'# comment\n'
|
||||
'...\n',
|
||||
Comment(2, 1, '# comment', 0))
|
||||
self.check_comments('---\n'
|
||||
'# no newline char',
|
||||
Comment(2, 1, '# no newline char', 0))
|
||||
self.check_comments('# just comment',
|
||||
Comment(1, 1, '# just comment', 0))
|
||||
self.check_comments('\n'
|
||||
' # indented comment\n',
|
||||
Comment(2, 4, '# indented comment', 0))
|
||||
self.check_comments('\n'
|
||||
'# trailing spaces \n',
|
||||
Comment(2, 1, '# trailing spaces ', 0))
|
||||
self.check_comments('# comment one\n'
|
||||
'\n'
|
||||
'key: val # key=val\n'
|
||||
'\n'
|
||||
'# this is\n'
|
||||
'# a block \n'
|
||||
'# comment\n'
|
||||
'\n'
|
||||
'other:\n'
|
||||
' - foo # equals\n'
|
||||
' # bar\n',
|
||||
Comment(1, 1, '# comment one', 0),
|
||||
Comment(3, 11, '# key=val', 0),
|
||||
Comment(5, 1, '# this is', 0),
|
||||
Comment(6, 1, '# a block ', 0),
|
||||
Comment(7, 1, '# comment', 0),
|
||||
Comment(10, 10, '# equals', 0),
|
||||
Comment(11, 10, '# bar', 0))
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from tests.common import RuleTestCase
|
||||
from yamllint.parser import token_or_comment_generator, Comment
|
||||
from yamllint.parser import token_generator
|
||||
from yamllint.rules.indentation import check
|
||||
|
||||
|
||||
@@ -38,8 +38,7 @@ class IndentationStackTestCase(RuleTestCase):
|
||||
'check-multi-line-strings': False}
|
||||
context = {}
|
||||
output = ''
|
||||
for elem in [t for t in token_or_comment_generator(source)
|
||||
if not isinstance(t, Comment)]:
|
||||
for elem in token_generator(source):
|
||||
list(check(conf, elem.curr, elem.prev, elem.next, elem.nextnext,
|
||||
context))
|
||||
|
||||
|
||||
@@ -78,12 +78,6 @@ class LineLengthTestCase(RuleTestCase):
|
||||
' # http://localhost/very/long/url\n'
|
||||
' comment\n'
|
||||
'...\n', conf)
|
||||
self.check('---\n'
|
||||
'this:\n'
|
||||
'is:\n'
|
||||
'another:\n'
|
||||
' - https://localhost/very/very/long/url\n'
|
||||
'...\n', conf)
|
||||
|
||||
conf = 'line-length: {max: 20, allow-non-breakable-words: no}'
|
||||
self.check('---\n' + 30 * 'A' + '\n', conf, problem=(2, 21))
|
||||
@@ -100,9 +94,3 @@ class LineLengthTestCase(RuleTestCase):
|
||||
' # http://localhost/very/long/url\n'
|
||||
' comment\n'
|
||||
'...\n', conf, problem=(5, 21))
|
||||
self.check('---\n'
|
||||
'this:\n'
|
||||
'is:\n'
|
||||
'another:\n'
|
||||
' - https://localhost/very/very/long/url\n'
|
||||
'...\n', conf, problem=(5, 21))
|
||||
|
||||
@@ -153,49 +153,6 @@ class CommandLineTestCase(unittest.TestCase):
|
||||
self.assertEqual(out, '')
|
||||
self.assertRegexpMatches(err, r'^invalid config: not a dict')
|
||||
|
||||
def test_run_with_config_file(self):
|
||||
with open(os.path.join(self.wd, 'config'), 'w') as f:
|
||||
f.write('rules: {trailing-spaces: disable}')
|
||||
|
||||
with self.assertRaises(SystemExit) as ctx:
|
||||
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
|
||||
self.assertEqual(ctx.exception.code, 0)
|
||||
|
||||
with open(os.path.join(self.wd, 'config'), 'w') as f:
|
||||
f.write('rules: {trailing-spaces: enable}')
|
||||
|
||||
with self.assertRaises(SystemExit) as ctx:
|
||||
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
|
||||
self.assertEqual(ctx.exception.code, 1)
|
||||
|
||||
def test_run_with_user_global_config_file(self):
|
||||
home = os.path.join(self.wd, 'fake-home')
|
||||
os.mkdir(home)
|
||||
dir = os.path.join(home, '.config')
|
||||
os.mkdir(dir)
|
||||
dir = os.path.join(dir, 'yamllint')
|
||||
os.mkdir(dir)
|
||||
config = os.path.join(dir, 'config')
|
||||
|
||||
temp = os.environ['HOME']
|
||||
os.environ['HOME'] = home
|
||||
|
||||
with open(config, 'w') as f:
|
||||
f.write('rules: {trailing-spaces: disable}')
|
||||
|
||||
with self.assertRaises(SystemExit) as ctx:
|
||||
cli.run((os.path.join(self.wd, 'a.yaml'), ))
|
||||
self.assertEqual(ctx.exception.code, 0)
|
||||
|
||||
with open(config, 'w') as f:
|
||||
f.write('rules: {trailing-spaces: enable}')
|
||||
|
||||
with self.assertRaises(SystemExit) as ctx:
|
||||
cli.run((os.path.join(self.wd, 'a.yaml'), ))
|
||||
self.assertEqual(ctx.exception.code, 1)
|
||||
|
||||
os.environ['HOME'] = temp
|
||||
|
||||
def test_run_version(self):
|
||||
sys.stdout, sys.stderr = StringIO(), StringIO()
|
||||
with self.assertRaises(SystemExit) as ctx:
|
||||
|
||||
@@ -18,9 +18,8 @@ import unittest
|
||||
|
||||
import yaml
|
||||
|
||||
from yamllint.parser import (line_generator, token_or_comment_generator,
|
||||
token_or_comment_or_line_generator,
|
||||
Line, Token, Comment)
|
||||
from yamllint.parser import (line_generator, token_generator,
|
||||
token_or_line_generator, Line, Token)
|
||||
|
||||
|
||||
class ParserTestCase(unittest.TestCase):
|
||||
@@ -62,8 +61,8 @@ class ParserTestCase(unittest.TestCase):
|
||||
self.assertEqual(e[2].line_no, 3)
|
||||
self.assertEqual(e[2].content, 'at the end')
|
||||
|
||||
def test_token_or_comment_generator(self):
|
||||
e = list(token_or_comment_generator(''))
|
||||
def test_token_generator(self):
|
||||
e = list(token_generator(''))
|
||||
self.assertEqual(len(e), 2)
|
||||
self.assertEqual(e[0].prev, None)
|
||||
self.assertIsInstance(e[0].curr, yaml.Token)
|
||||
@@ -72,74 +71,16 @@ class ParserTestCase(unittest.TestCase):
|
||||
self.assertEqual(e[1].curr, e[0].next)
|
||||
self.assertEqual(e[1].next, None)
|
||||
|
||||
e = list(token_or_comment_generator('---\n'
|
||||
'k: v\n'))
|
||||
e = list(token_generator('---\n'
|
||||
'k: v\n'))
|
||||
self.assertEqual(len(e), 9)
|
||||
self.assertIsInstance(e[3].curr, yaml.KeyToken)
|
||||
self.assertIsInstance(e[5].curr, yaml.ValueToken)
|
||||
|
||||
e = list(token_or_comment_generator('# start comment\n'
|
||||
'- a\n'
|
||||
'- key: val # key=val\n'
|
||||
'# this is\n'
|
||||
'# a block \n'
|
||||
'# comment\n'
|
||||
'- c\n'
|
||||
'# end comment\n'))
|
||||
self.assertEqual(len(e), 21)
|
||||
self.assertIsInstance(e[1], Comment)
|
||||
self.assertEqual(e[1], Comment(1, 1, '# start comment', 0))
|
||||
self.assertEqual(e[11], Comment(3, 13, '# key=val', 0))
|
||||
self.assertEqual(e[12], Comment(4, 1, '# this is', 0))
|
||||
self.assertEqual(e[13], Comment(5, 1, '# a block ', 0))
|
||||
self.assertEqual(e[14], Comment(6, 1, '# comment', 0))
|
||||
self.assertEqual(e[18], Comment(8, 1, '# end comment', 0))
|
||||
|
||||
e = list(token_or_comment_generator('---\n'
|
||||
'# no newline char'))
|
||||
self.assertEqual(e[2], Comment(2, 1, '# no newline char', 0))
|
||||
|
||||
e = list(token_or_comment_generator('# just comment'))
|
||||
self.assertEqual(e[1], Comment(1, 1, '# just comment', 0))
|
||||
|
||||
e = list(token_or_comment_generator('\n'
|
||||
' # indented comment\n'))
|
||||
self.assertEqual(e[1], Comment(2, 4, '# indented comment', 0))
|
||||
|
||||
e = list(token_or_comment_generator('\n'
|
||||
'# trailing spaces \n'))
|
||||
self.assertEqual(e[1], Comment(2, 1, '# trailing spaces ', 0))
|
||||
|
||||
e = [c for c in
|
||||
token_or_comment_generator('# block\n'
|
||||
'# comment\n'
|
||||
'- data # inline comment\n'
|
||||
'# block\n'
|
||||
'# comment\n'
|
||||
'- k: v # inline comment\n'
|
||||
'- [ l, ist\n'
|
||||
'] # inline comment\n'
|
||||
'- { m: ap\n'
|
||||
'} # inline comment\n'
|
||||
'# block comment\n'
|
||||
'- data # inline comment\n')
|
||||
if isinstance(c, Comment)]
|
||||
self.assertEqual(len(e), 10)
|
||||
self.assertFalse(e[0].is_inline())
|
||||
self.assertFalse(e[1].is_inline())
|
||||
self.assertTrue(e[2].is_inline())
|
||||
self.assertFalse(e[3].is_inline())
|
||||
self.assertFalse(e[4].is_inline())
|
||||
self.assertTrue(e[5].is_inline())
|
||||
self.assertTrue(e[6].is_inline())
|
||||
self.assertTrue(e[7].is_inline())
|
||||
self.assertFalse(e[8].is_inline())
|
||||
self.assertTrue(e[9].is_inline())
|
||||
|
||||
def test_token_or_comment_or_line_generator(self):
|
||||
e = list(token_or_comment_or_line_generator('---\n'
|
||||
'k: v # k=v\n'))
|
||||
self.assertEqual(len(e), 13)
|
||||
def test_token_or_line_generator(self):
|
||||
e = list(token_or_line_generator('---\n'
|
||||
'k: v\n'))
|
||||
self.assertEqual(len(e), 12)
|
||||
self.assertIsInstance(e[0], Token)
|
||||
self.assertIsInstance(e[0].curr, yaml.StreamStartToken)
|
||||
self.assertIsInstance(e[1], Token)
|
||||
@@ -148,6 +89,5 @@ class ParserTestCase(unittest.TestCase):
|
||||
self.assertIsInstance(e[3].curr, yaml.BlockMappingStartToken)
|
||||
self.assertIsInstance(e[4].curr, yaml.KeyToken)
|
||||
self.assertIsInstance(e[6].curr, yaml.ValueToken)
|
||||
self.assertIsInstance(e[8], Comment)
|
||||
self.assertIsInstance(e[9], Line)
|
||||
self.assertIsInstance(e[12], Line)
|
||||
self.assertIsInstance(e[8], Line)
|
||||
self.assertIsInstance(e[11], Line)
|
||||
|
||||
@@ -1,304 +0,0 @@
|
||||
# -*- 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/>.
|
||||
|
||||
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'))
|
||||
50
yaml-fix-indentation
Executable file
50
yaml-fix-indentation
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euf
|
||||
|
||||
DIR=$(dirname "$(readlink -f $0)")
|
||||
|
||||
fix_one_problem_in_file() {
|
||||
local filename=$1
|
||||
local error
|
||||
error=$(yamllint -f parsable "$filename" | grep 'wrong indentation: expected' \
|
||||
| head -n 1)
|
||||
if [ -z "$error" ]; then
|
||||
return 1
|
||||
fi
|
||||
local line=$(echo $error | cut -d: -f2)
|
||||
local expected=$(echo $error | cut -d: -f5 | sed 's/.* expected //;s/ but found.*//')
|
||||
local found=$(echo $error | cut -d: -f5 | sed 's/.*but found //;s/(inde.*//')
|
||||
"$DIR/yaml-remove-indentation" "$filename" $line $expected $found
|
||||
return 0
|
||||
}
|
||||
|
||||
reformat_yaml() {
|
||||
local in=$1
|
||||
local out=$2
|
||||
python -c 'import sys, yaml; yaml.dump(yaml.load(sys.stdin), sys.stdout)' <"$in" >"$out"
|
||||
}
|
||||
|
||||
fix_one_file() {
|
||||
local filename=$1
|
||||
local backup=$(mktemp originalXXXXX)
|
||||
cp "$filename" "$backup"
|
||||
echo "FIXING $file"
|
||||
while fix_one_problem_in_file "$filename"; do continue; done
|
||||
echo "CHECKING $file"
|
||||
local tmp_old=$(mktemp oldXXXXX)
|
||||
local tmp_new=$(mktemp newXXXXX)
|
||||
reformat_yaml "$backup" "$tmp_old"
|
||||
reformat_yaml "$filename" "$tmp_new"
|
||||
if ! diff -q "$tmp_old" "$tmp_new" &>/dev/null; then
|
||||
echo "error: after reformating, the file contents is detected different."
|
||||
echo "diff $backup $filename"
|
||||
echo "diff $tmp_old $tmp_new"
|
||||
exit 1
|
||||
fi
|
||||
rm "$backup" "$tmp_old" "$tmp_new"
|
||||
}
|
||||
|
||||
for file in "$@"; do
|
||||
fix_one_file "$file"
|
||||
done
|
||||
37
yaml-remove-indentation
Executable file
37
yaml-remove-indentation
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
|
||||
file = sys.argv[1]
|
||||
line = int(sys.argv[2]) - 1
|
||||
indent_expected = int(sys.argv[3])
|
||||
indent_found = int(sys.argv[4])
|
||||
|
||||
with open(file) as f:
|
||||
lines = f.readlines()
|
||||
|
||||
before = lines[:line]
|
||||
|
||||
is_a_list = lines[line].strip()[0] == '-'
|
||||
|
||||
i = line
|
||||
while (i < len(lines) and
|
||||
(lines[i].strip() == '' or
|
||||
(not is_a_list and lines[i].startswith(indent_found * ' ')) or
|
||||
(is_a_list and (lines[i].startswith(indent_found * ' ' + '-') or
|
||||
lines[i].startswith(indent_found * ' ' + ' '))))):
|
||||
i += 1
|
||||
|
||||
contents = lines[line:i]
|
||||
after = lines[i:]
|
||||
|
||||
new_contents = []
|
||||
for line in contents:
|
||||
if line.strip() != '':
|
||||
line = (indent_expected * ' ') + line[indent_found:]
|
||||
new_contents.append(line)
|
||||
|
||||
with open(file, 'w') as f:
|
||||
f.write(''.join(before))
|
||||
f.write(''.join(new_contents))
|
||||
f.write(''.join(after))
|
||||
@@ -22,7 +22,7 @@ indentation, etc."""
|
||||
|
||||
|
||||
APP_NAME = 'yamllint'
|
||||
APP_VERSION = '1.3.0'
|
||||
APP_VERSION = '1.2.1'
|
||||
APP_DESCRIPTION = __doc__
|
||||
|
||||
__author__ = u'Adrien Vergé'
|
||||
|
||||
@@ -86,13 +86,6 @@ def run(argv=None):
|
||||
'simultaneously.', file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
|
||||
# User-global config is supposed to be in ~/.config/yamllint/config
|
||||
if 'XDG_CONFIG_HOME' in os.environ:
|
||||
user_global_config = os.path.join(
|
||||
os.environ['XDG_CONFIG_HOME'], 'yamllint', 'config')
|
||||
else:
|
||||
user_global_config = os.path.expanduser('~/.config/yamllint/config')
|
||||
|
||||
try:
|
||||
if args.config_data is not None:
|
||||
if args.config_data != '' and ':' not in args.config_data:
|
||||
@@ -102,8 +95,6 @@ def run(argv=None):
|
||||
conf = YamlLintConfig(file=args.config_file)
|
||||
elif os.path.isfile('.yamllint'):
|
||||
conf = YamlLintConfig(file='.yamllint')
|
||||
elif os.path.isfile(user_global_config):
|
||||
conf = YamlLintConfig(file=user_global_config)
|
||||
else:
|
||||
conf = YamlLintConfig('extends: default')
|
||||
except YamlLintConfigError as e:
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
# 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 re
|
||||
|
||||
import yaml
|
||||
|
||||
from yamllint import parser
|
||||
@@ -58,63 +56,13 @@ def get_costemic_problems(buffer, conf):
|
||||
|
||||
# Split token rules from line rules
|
||||
token_rules = [r for r in rules if r.TYPE == 'token']
|
||||
comment_rules = [r for r in rules if r.TYPE == 'comment']
|
||||
line_rules = [r for r in rules if r.TYPE == 'line']
|
||||
|
||||
context = {}
|
||||
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):
|
||||
for elem in parser.token_or_line_generator(buffer):
|
||||
if isinstance(elem, parser.Token):
|
||||
for rule in token_rules:
|
||||
rule_conf = conf.rules[rule.ID]
|
||||
@@ -124,39 +72,15 @@ def get_costemic_problems(buffer, conf):
|
||||
context[rule.ID]):
|
||||
problem.rule = rule.ID
|
||||
problem.level = rule_conf['level']
|
||||
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']
|
||||
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)
|
||||
yield problem
|
||||
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 = []
|
||||
|
||||
|
||||
def get_syntax_error(buffer):
|
||||
try:
|
||||
@@ -170,9 +94,6 @@ def get_syntax_error(buffer):
|
||||
|
||||
|
||||
def _run(buffer, conf):
|
||||
assert hasattr(buffer, '__getitem__'), \
|
||||
'_run() argument must be a buffer, not a stream'
|
||||
|
||||
# If the document contains a syntax error, save it and yield it at the
|
||||
# right line
|
||||
syntax_error = get_syntax_error(buffer)
|
||||
|
||||
@@ -38,40 +38,6 @@ class Token(object):
|
||||
self.nextnext = nextnext
|
||||
|
||||
|
||||
class Comment(object):
|
||||
def __init__(self, line_no, column_no, buffer, pointer,
|
||||
token_before=None, token_after=None, comment_before=None):
|
||||
self.line_no = line_no
|
||||
self.column_no = column_no
|
||||
self.buffer = buffer
|
||||
self.pointer = pointer
|
||||
self.token_before = token_before
|
||||
self.token_after = token_after
|
||||
self.comment_before = comment_before
|
||||
|
||||
def __repr__(self):
|
||||
end = self.buffer.find('\n', self.pointer)
|
||||
if end == -1:
|
||||
end = self.buffer.find('\0', self.pointer)
|
||||
if end != -1:
|
||||
return self.buffer[self.pointer:end]
|
||||
return self.buffer[self.pointer:]
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, Comment) and
|
||||
self.line_no == other.line_no and
|
||||
self.column_no == other.column_no and
|
||||
str(self) == str(other))
|
||||
|
||||
def is_inline(self):
|
||||
return (
|
||||
not isinstance(self.token_before, yaml.StreamStartToken) and
|
||||
self.line_no == self.token_before.end_mark.line + 1 and
|
||||
# sometimes token end marks are on the next line
|
||||
self.buffer[self.token_before.end_mark.pointer - 1] != '\n'
|
||||
)
|
||||
|
||||
|
||||
def line_generator(buffer):
|
||||
line_no = 1
|
||||
cur = 0
|
||||
@@ -85,39 +51,7 @@ def line_generator(buffer):
|
||||
yield Line(line_no, buffer, start=cur, end=len(buffer))
|
||||
|
||||
|
||||
def comments_between_tokens(token1, token2):
|
||||
"""Find all comments between two tokens"""
|
||||
if token2 is None:
|
||||
buf = token1.end_mark.buffer[token1.end_mark.pointer:]
|
||||
elif (token1.end_mark.line == token2.start_mark.line and
|
||||
not isinstance(token1, yaml.StreamStartToken) and
|
||||
not isinstance(token2, yaml.StreamEndToken)):
|
||||
return
|
||||
else:
|
||||
buf = token1.end_mark.buffer[token1.end_mark.pointer:
|
||||
token2.start_mark.pointer]
|
||||
|
||||
line_no = token1.end_mark.line + 1
|
||||
column_no = token1.end_mark.column + 1
|
||||
pointer = token1.end_mark.pointer
|
||||
|
||||
comment_before = None
|
||||
for line in buf.split('\n'):
|
||||
pos = line.find('#')
|
||||
if pos != -1:
|
||||
comment = Comment(line_no, column_no + pos,
|
||||
token1.end_mark.buffer, pointer + pos,
|
||||
token1, token2, comment_before)
|
||||
yield comment
|
||||
|
||||
comment_before = comment
|
||||
|
||||
pointer += len(line) + 1
|
||||
line_no += 1
|
||||
column_no = 1
|
||||
|
||||
|
||||
def token_or_comment_generator(buffer):
|
||||
def token_generator(buffer):
|
||||
yaml_loader = yaml.BaseLoader(buffer)
|
||||
|
||||
try:
|
||||
@@ -129,9 +63,6 @@ def token_or_comment_generator(buffer):
|
||||
|
||||
yield Token(curr.start_mark.line + 1, curr, prev, next, nextnext)
|
||||
|
||||
for comment in comments_between_tokens(curr, next):
|
||||
yield comment
|
||||
|
||||
prev = curr
|
||||
curr = next
|
||||
|
||||
@@ -139,19 +70,19 @@ def token_or_comment_generator(buffer):
|
||||
pass
|
||||
|
||||
|
||||
def token_or_comment_or_line_generator(buffer):
|
||||
def token_or_line_generator(buffer):
|
||||
"""Generator that mixes tokens and lines, ordering them by line number"""
|
||||
tok_or_com_gen = token_or_comment_generator(buffer)
|
||||
token_gen = token_generator(buffer)
|
||||
line_gen = line_generator(buffer)
|
||||
|
||||
tok_or_com = next(tok_or_com_gen, None)
|
||||
token = next(token_gen, None)
|
||||
line = next(line_gen, None)
|
||||
|
||||
while tok_or_com is not None or line is not None:
|
||||
if tok_or_com is None or (line is not None and
|
||||
tok_or_com.line_no > line.line_no):
|
||||
while token is not None or line is not None:
|
||||
if token is None or (line is not None and
|
||||
token.line_no > line.line_no):
|
||||
yield line
|
||||
line = next(line_gen, None)
|
||||
else:
|
||||
yield tok_or_com
|
||||
tok_or_com = next(tok_or_com_gen, None)
|
||||
yield token
|
||||
token = next(token_gen, None)
|
||||
|
||||
@@ -55,25 +55,33 @@ Use this rule to control the position and formatting of comments.
|
||||
"""
|
||||
|
||||
|
||||
import yaml
|
||||
|
||||
from yamllint.linter import LintProblem
|
||||
from yamllint.rules.common import get_comments_between_tokens
|
||||
|
||||
|
||||
ID = 'comments'
|
||||
TYPE = 'comment'
|
||||
TYPE = 'token'
|
||||
CONF = {'require-starting-space': bool,
|
||||
'min-spaces-from-content': int}
|
||||
|
||||
|
||||
def check(conf, comment):
|
||||
if (conf['min-spaces-from-content'] != -1 and comment.is_inline() and
|
||||
comment.pointer - comment.token_before.end_mark.pointer <
|
||||
conf['min-spaces-from-content']):
|
||||
yield LintProblem(comment.line_no, comment.column_no,
|
||||
'too few spaces before comment')
|
||||
def check(conf, token, prev, next, nextnext, context):
|
||||
for comment in get_comments_between_tokens(token, next):
|
||||
if (conf['min-spaces-from-content'] != -1 and
|
||||
not isinstance(token, yaml.StreamStartToken) and
|
||||
comment.line == token.end_mark.line + 1):
|
||||
# Sometimes token end marks are on the next line
|
||||
if token.end_mark.buffer[token.end_mark.pointer - 1] != '\n':
|
||||
if (comment.pointer - token.end_mark.pointer <
|
||||
conf['min-spaces-from-content']):
|
||||
yield LintProblem(comment.line, comment.column,
|
||||
'too few spaces before comment')
|
||||
|
||||
if (conf['require-starting-space'] and
|
||||
comment.pointer + 1 < len(comment.buffer) and
|
||||
comment.buffer[comment.pointer + 1] != ' ' and
|
||||
comment.buffer[comment.pointer + 1] != '\n'):
|
||||
yield LintProblem(comment.line_no, comment.column_no + 1,
|
||||
'missing starting space in comment')
|
||||
if (conf['require-starting-space'] and
|
||||
comment.pointer + 1 < len(comment.buffer) and
|
||||
comment.buffer[comment.pointer + 1] != ' ' and
|
||||
comment.buffer[comment.pointer + 1] != '\n'):
|
||||
yield LintProblem(comment.line, comment.column + 1,
|
||||
'missing starting space in comment')
|
||||
|
||||
@@ -78,11 +78,11 @@ Use this rule to force comments to be indented like content.
|
||||
import yaml
|
||||
|
||||
from yamllint.linter import LintProblem
|
||||
from yamllint.rules.common import get_line_indent
|
||||
from yamllint.rules.common import get_line_indent, get_comments_between_tokens
|
||||
|
||||
|
||||
ID = 'comments-indentation'
|
||||
TYPE = 'comment'
|
||||
TYPE = 'token'
|
||||
|
||||
|
||||
# Case A:
|
||||
@@ -98,42 +98,28 @@ TYPE = 'comment'
|
||||
# # commented line 2
|
||||
# current: line
|
||||
|
||||
def check(conf, comment):
|
||||
# Only check block comments
|
||||
if (not isinstance(comment.token_before, yaml.StreamStartToken) and
|
||||
comment.token_before.end_mark.line + 1 == comment.line_no):
|
||||
def check(conf, token, prev, next, nextnext, context):
|
||||
if prev is None:
|
||||
return
|
||||
|
||||
next_line_indent = comment.token_after.start_mark.column
|
||||
if isinstance(comment.token_after, yaml.StreamEndToken):
|
||||
next_line_indent = 0
|
||||
curr_line_indent = token.start_mark.column
|
||||
if isinstance(token, yaml.StreamEndToken):
|
||||
curr_line_indent = 0
|
||||
|
||||
if isinstance(comment.token_before, yaml.StreamStartToken):
|
||||
skip_first_line = True
|
||||
if isinstance(prev, yaml.StreamStartToken):
|
||||
skip_first_line = False
|
||||
prev_line_indent = 0
|
||||
else:
|
||||
prev_line_indent = get_line_indent(comment.token_before)
|
||||
prev_line_indent = get_line_indent(prev)
|
||||
|
||||
# In the following case only the next line indent is valid:
|
||||
# list:
|
||||
# # comment
|
||||
# - 1
|
||||
# - 2
|
||||
if prev_line_indent <= next_line_indent:
|
||||
prev_line_indent = next_line_indent
|
||||
if prev_line_indent <= curr_line_indent:
|
||||
prev_line_indent = -1 # disable it
|
||||
|
||||
# If two indents are valid but a previous comment went back to normal
|
||||
# indent, for the next ones to do the same. In other words, avoid this:
|
||||
# list:
|
||||
# - 1
|
||||
# # comment on valid indent (0)
|
||||
# # comment on valid indent (4)
|
||||
# other-list:
|
||||
# - 2
|
||||
if (comment.comment_before is not None and
|
||||
not comment.comment_before.is_inline()):
|
||||
prev_line_indent = comment.comment_before.column_no - 1
|
||||
|
||||
if (comment.column_no - 1 != prev_line_indent and
|
||||
comment.column_no - 1 != next_line_indent):
|
||||
yield LintProblem(comment.line_no, comment.column_no,
|
||||
'comment not indented like content')
|
||||
for comment in get_comments_between_tokens(
|
||||
prev, token, skip_first_line=skip_first_line):
|
||||
if comment.column - 1 == curr_line_indent:
|
||||
prev_line_indent = -1 # disable it
|
||||
elif comment.column - 1 != prev_line_indent:
|
||||
yield LintProblem(comment.line, comment.column,
|
||||
'comment not indented like content')
|
||||
|
||||
@@ -48,6 +48,27 @@ def spaces_before(token, prev, next, min=-1, max=-1,
|
||||
token.start_mark.column + 1, min_desc)
|
||||
|
||||
|
||||
class Comment(object):
|
||||
def __init__(self, line, column, buffer, pointer):
|
||||
self.line = line
|
||||
self.column = column
|
||||
self.buffer = buffer
|
||||
self.pointer = pointer
|
||||
|
||||
def __repr__(self):
|
||||
end = self.buffer.find('\n', self.pointer)
|
||||
if end == -1:
|
||||
end = self.buffer.find('\0', self.pointer)
|
||||
if end != -1:
|
||||
return self.buffer[self.pointer:end]
|
||||
return self.buffer[self.pointer:]
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.line == other.line and
|
||||
self.column == other.column and
|
||||
str(self) == str(other))
|
||||
|
||||
|
||||
def get_line_indent(token):
|
||||
"""Finds the indent of the line the token starts in."""
|
||||
start = token.start_mark.buffer.rfind('\n', 0,
|
||||
@@ -77,6 +98,35 @@ def get_real_end_line(token):
|
||||
return end_line
|
||||
|
||||
|
||||
def get_comments_between_tokens(token1, token2, skip_first_line=False):
|
||||
if token2 is None:
|
||||
buf = token1.end_mark.buffer[token1.end_mark.pointer:]
|
||||
elif (token1.end_mark.line == token2.start_mark.line and
|
||||
not isinstance(token1, yaml.StreamStartToken) and
|
||||
not isinstance(token2, yaml.StreamEndToken)):
|
||||
return
|
||||
else:
|
||||
buf = token1.end_mark.buffer[token1.end_mark.pointer:
|
||||
token2.start_mark.pointer]
|
||||
|
||||
line_no = token1.end_mark.line + 1
|
||||
column_no = token1.end_mark.column + 1
|
||||
pointer = token1.end_mark.pointer
|
||||
|
||||
for line in buf.split('\n'):
|
||||
if skip_first_line:
|
||||
skip_first_line = False
|
||||
else:
|
||||
pos = line.find('#')
|
||||
if pos != -1:
|
||||
yield Comment(line_no, column_no + pos,
|
||||
token1.end_mark.buffer, pointer + pos)
|
||||
|
||||
pointer += len(line) + 1
|
||||
line_no += 1
|
||||
column_no = 1
|
||||
|
||||
|
||||
def is_explicit_key(token):
|
||||
# explicit key:
|
||||
# ? key
|
||||
|
||||
@@ -62,7 +62,7 @@ CONF = {'max': int,
|
||||
|
||||
def check(conf, line):
|
||||
if line.start == line.end and line.end < len(line.buffer):
|
||||
# Only alert on the last blank line of a series
|
||||
# Only alert on the last blank line of a serie
|
||||
if (line.end < len(line.buffer) - 1 and
|
||||
line.buffer[line.end + 1] == '\n'):
|
||||
return
|
||||
|
||||
@@ -59,7 +59,7 @@ Use this rule to set a limit to lines length.
|
||||
the following code snippet would **FAIL**:
|
||||
::
|
||||
|
||||
- this line is waaaaaaaaaaaaaay too long but could be easily split...
|
||||
- this line is waaaaaaaaaaaaaay too long but could be easily splitted...
|
||||
|
||||
#. With ``line-length: {max: 60, allow-non-breakable-words: no}``
|
||||
|
||||
@@ -90,7 +90,7 @@ def check(conf, line):
|
||||
start += 1
|
||||
|
||||
if start != line.end:
|
||||
if line.buffer[start] in ('#', '-'):
|
||||
if line.buffer[start] == '#':
|
||||
start += 2
|
||||
|
||||
if line.buffer.find(' ', start, line.end) == -1:
|
||||
|
||||
Reference in New Issue
Block a user