Compare commits

...

21 Commits

Author SHA1 Message Date
Adrien Vergé
d174f9e3e3 yamllint version 1.3.1 2016-06-28 10:10:34 +02:00
Adrien Vergé
c8ba8f7e99 linter: Fix UnicodeError when parsing comments
And add tests when reading non-ASCII strings and comments (both from
Python strings and from files).

Fixes: #10
2016-06-28 09:58:23 +02:00
Adrien Vergé
63dd8313f8 yamllint version 1.3.0 2016-06-27 21:54:29 +02:00
Adrien Vergé
7be5867675 linter: Remove dead code
There is *always* a `Line` element at the end of file, even if the
newline character (`\n`) is missing.
2016-06-27 21:45:21 +02:00
Adrien Vergé
6061a2c4cc Rules: common: Remove dead code 2016-06-27 21:37:31 +02:00
Adrien Vergé
09118e417c Doc: Add license information on README page 2016-06-27 21:32:55 +02:00
Adrien Vergé
71b90ae208 Doc: Add new features in README 2016-06-27 21:32:55 +02:00
Adrien Vergé
8844855353 Doc: Remove old Debian install commands from README 2016-06-27 21:15:55 +02:00
Adrien Vergé
0eb310e102 Allow disabling yamllint checks using comments
Implement problem report disabling with comments in YAML source, for
instance:

    # 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

or:

    # yamllint disable rule:colons
    - Lorem       : ipsum
      dolor       : sit amet,
      consectetur : adipiscing elit
    # yamllint enable rule:colons

Closes: #8
2016-06-27 17:53:23 +02:00
Adrien Vergé
cdd094220c parser: Add tests for Comment.is_inline() 2016-06-27 17:47:13 +02:00
Adrien Vergé
7a7d98c96a parser: Iterate over lines + tokens + comments
Instead of iterating over lines and tokens (and find comments between
tokens in the comment rules), add a new `Comment` type and set rules
with `type = 'comment'`.
2016-06-27 17:47:13 +02:00
Adrien Vergé
9f99f25db5 linter: Assert that _run() is called with a buffer 2016-06-25 13:50:24 +02:00
Adrien Vergé
8c839a20c2 Config: Detect user config using os.path.expanduser()
Instead of `$HOME`, since the former works when `$HOME` is not set.

[1]: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=828033#10
2016-06-24 18:41:45 +02:00
Adrien Vergé
8e6e851c5b yamllint version 1.2.2 2016-06-24 08:40:45 +02:00
Adrien Vergé
edd4cca02f Merge pull request #9 from michelebariani/master
Patch allow-non-breakable-words on '-'
2016-06-15 20:06:04 +02:00
Michele Bariani
867970258e Patch allow-non-breakable-words on '-' 2016-06-15 18:07:42 +02:00
Adrien Vergé
d0cb5998c4 Merge pull request #7 from jwilk/spelling
Fix typos
2016-05-13 16:19:58 +02:00
Jakub Wilk
a5c97220e7 Fix typos 2016-05-13 15:47:56 +02:00
Adrien Vergé
598e5e4370 Doc: Fix typo on configuration page intro 2016-04-21 22:39:46 +02:00
Adrien Vergé
03076ee214 Doc: Add a pointer to rules on configuration page intro 2016-04-21 22:37:48 +02:00
Adrien Vergé
eabd349902 Config: Allow a user-global configuration file
Instead of just looking for `.yamllint` in the current working
directory, also look for `~/.config/yamllint/config` (using
`$XDG_CONFIG_HOME` or `$HOME`, see [1] and [2] for information).

[1]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.6.html
[2]: https://wiki.archlinux.org/index.php/XDG_Base_Directory_support

Closes: #6
2016-04-21 22:24:24 +02:00
21 changed files with 831 additions and 193 deletions

View File

@@ -24,10 +24,10 @@ Written in Python (compatible with Python 2 & 3).
Documentation Documentation
------------- -------------
http://yamllint.readthedocs.org/ http://yamllint.readthedocs.io/
Short overview Overview
-------------- --------
Screenshot Screenshot
^^^^^^^^^^ ^^^^^^^^^^
@@ -50,13 +50,6 @@ On Debian 9+ / Ubuntu 16.04+:
sudo apt-get install yamllint 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: Alternatively using pip, the Python package manager:
.. code:: bash .. code:: bash
@@ -82,15 +75,19 @@ Usage
yamllint -d relaxed file.yaml yamllint -d relaxed file.yaml
# Use a custom lint configuration # Use a custom lint configuration
yamllint -c ~/myconfig file.yml yamllint -c /path/to/myconfig file-to-lint.yaml
.. code:: bash .. code:: bash
# Output a parsable format (for syntax checking in editors like Vim, emacs...) # Output a parsable format (for syntax checking in editors like Vim, emacs...)
yamllint -f parsable file.yaml yamllint -f parsable file.yaml
Configuration example `Read more in the complete documentation! <http://yamllint.readthedocs.io/>`_
^^^^^^^^^^^^^^^^^^^^^
Features
^^^^^^^^
Here is a yamllint configuration file example:
.. code:: yaml .. code:: yaml
@@ -104,3 +101,27 @@ Configuration example
# don't bother me with this rule # don't bother me with this rule
indentation: disable 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>`_

View File

@@ -1,16 +1,24 @@
Configuration Configuration
============= =============
yamllint uses a set of *rules* to check sources files for problems. Each rule is yamllint uses a set of :doc:`rules <rules>` to check source files for problems.
independent from the others, and can be enabled, disabled or tweaked. All these Each rule is independent from the others, and can be enabled, disabled or
settings can be gathered in a configuration file. tweaked. All these settings can be gathered in a configuration file.
To use a custom configuration file, either name it ``.yamllint`` in your working To use a custom configuration file, use the ``-c`` option:
directory, or use the ``-c`` option:
.. code:: bash .. code:: bash
yamllint -c ~/myconfig file.yaml 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.
Default configuration Default configuration
--------------------- ---------------------

View File

@@ -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``.

View File

@@ -23,5 +23,6 @@ Table of contents
quickstart quickstart
configuration configuration
rules rules
disable_with_comments
development development
text_editors text_editors

View File

@@ -58,7 +58,7 @@ class CommentsIndentationTestCase(RuleTestCase):
'# line 2\n', conf, problem=(2, 2)) '# line 2\n', conf, problem=(2, 2))
self.check('---\n' self.check('---\n'
' # line 1\n' ' # line 1\n'
' # line 2\n', conf, problem1=(2, 3), problem2=(3, 3)) ' # line 2\n', conf, problem1=(2, 3))
self.check('---\n' self.check('---\n'
'obj:\n' 'obj:\n'
' # normal\n' ' # normal\n'
@@ -143,3 +143,15 @@ class CommentsIndentationTestCase(RuleTestCase):
'# hey\n' '# hey\n'
'# normal\n' '# normal\n'
' #\n', conf, problem=(4, 2)) ' #\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))

View File

@@ -18,8 +18,7 @@ import unittest
import yaml import yaml
from yamllint.rules.common import (Comment, get_line_indent, from yamllint.rules.common import get_line_indent
get_comments_between_tokens)
class CommonTestCase(unittest.TestCase): class CommonTestCase(unittest.TestCase):
@@ -43,54 +42,3 @@ class CommonTestCase(unittest.TestCase):
self.assertEqual(get_line_indent(tokens[i]), 0) self.assertEqual(get_line_indent(tokens[i]), 0)
for i in (13, 16, 18, 22, 24): for i in (13, 16, 18, 22, 24):
self.assertEqual(get_line_indent(tokens[i]), 2) 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))

View File

@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.common import RuleTestCase from tests.common import RuleTestCase
from yamllint.parser import token_generator from yamllint.parser import token_or_comment_generator, Comment
from yamllint.rules.indentation import check from yamllint.rules.indentation import check
@@ -38,7 +38,8 @@ class IndentationStackTestCase(RuleTestCase):
'check-multi-line-strings': False} 'check-multi-line-strings': False}
context = {} context = {}
output = '' output = ''
for elem in token_generator(source): for elem in [t for t in token_or_comment_generator(source)
if not isinstance(t, Comment)]:
list(check(conf, elem.curr, elem.prev, elem.next, elem.nextnext, list(check(conf, elem.curr, elem.prev, elem.next, elem.nextnext,
context)) context))

View File

@@ -78,6 +78,12 @@ class LineLengthTestCase(RuleTestCase):
' # http://localhost/very/long/url\n' ' # http://localhost/very/long/url\n'
' comment\n' ' comment\n'
'...\n', conf) '...\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}' conf = 'line-length: {max: 20, allow-non-breakable-words: no}'
self.check('---\n' + 30 * 'A' + '\n', conf, problem=(2, 21)) self.check('---\n' + 30 * 'A' + '\n', conf, problem=(2, 21))
@@ -94,3 +100,9 @@ class LineLengthTestCase(RuleTestCase):
' # http://localhost/very/long/url\n' ' # http://localhost/very/long/url\n'
' comment\n' ' comment\n'
'...\n', conf, problem=(5, 21)) '...\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))

View File

@@ -64,6 +64,15 @@ class CommandLineTestCase(unittest.TestCase):
f.write('---\n' f.write('---\n'
'key: value\n') 'key: value\n')
# non-ASCII chars
os.mkdir(os.path.join(self.wd, 'non-ascii'))
with open(os.path.join(self.wd, 'non-ascii', 'utf-8'), 'wb') as f:
f.write((u'---\n'
u'- hétérogénéité\n'
u'# 19.99 €\n'
u'- お早う御座います。\n'
u'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'))
def tearDown(self): def tearDown(self):
shutil.rmtree(self.wd) shutil.rmtree(self.wd)
@@ -153,6 +162,49 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(out, '') self.assertEqual(out, '')
self.assertRegexpMatches(err, r'^invalid config: not a dict') 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): def test_run_version(self):
sys.stdout, sys.stderr = StringIO(), StringIO() sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx: with self.assertRaises(SystemExit) as ctx:
@@ -218,6 +270,19 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(out, '') self.assertEqual(out, '')
self.assertEqual(err, '') self.assertEqual(err, '')
def test_run_non_ascii_file(self):
file = os.path.join(self.wd, 'non-ascii', 'utf-8')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', file))
self.assertEqual(ctx.exception.code, 0)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertEqual(err, '')
def test_run_multiple_files(self): def test_run_multiple_files(self):
items = [os.path.join(self.wd, 'empty.yml'), items = [os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 's')] os.path.join(self.wd, 's')]

View File

@@ -44,3 +44,15 @@ class LinterTestCase(unittest.TestCase):
def test_run_on_list(self): def test_run_on_list(self):
self.assertRaises(TypeError, linter.run, self.assertRaises(TypeError, linter.run,
['h', 'e', 'l', 'l', 'o'], self.fake_config()) ['h', 'e', 'l', 'l', 'o'], self.fake_config())
def test_run_on_non_ascii_chars(self):
s = (u'- hétérogénéité\n'
u'# 19.99 €\n')
linter.run(s, self.fake_config())
linter.run(s.encode('utf-8'), self.fake_config())
linter.run(s.encode('iso-8859-15'), self.fake_config())
s = (u'- お早う御座います。\n'
u'# الأَبْجَدِيَّة العَرَبِيَّة\n')
linter.run(s, self.fake_config())
linter.run(s.encode('utf-8'), self.fake_config())

View File

@@ -18,8 +18,9 @@ import unittest
import yaml import yaml
from yamllint.parser import (line_generator, token_generator, from yamllint.parser import (line_generator, token_or_comment_generator,
token_or_line_generator, Line, Token) token_or_comment_or_line_generator,
Line, Token, Comment)
class ParserTestCase(unittest.TestCase): class ParserTestCase(unittest.TestCase):
@@ -61,8 +62,8 @@ class ParserTestCase(unittest.TestCase):
self.assertEqual(e[2].line_no, 3) self.assertEqual(e[2].line_no, 3)
self.assertEqual(e[2].content, 'at the end') self.assertEqual(e[2].content, 'at the end')
def test_token_generator(self): def test_token_or_comment_generator(self):
e = list(token_generator('')) e = list(token_or_comment_generator(''))
self.assertEqual(len(e), 2) self.assertEqual(len(e), 2)
self.assertEqual(e[0].prev, None) self.assertEqual(e[0].prev, None)
self.assertIsInstance(e[0].curr, yaml.Token) self.assertIsInstance(e[0].curr, yaml.Token)
@@ -71,16 +72,74 @@ class ParserTestCase(unittest.TestCase):
self.assertEqual(e[1].curr, e[0].next) self.assertEqual(e[1].curr, e[0].next)
self.assertEqual(e[1].next, None) self.assertEqual(e[1].next, None)
e = list(token_generator('---\n' e = list(token_or_comment_generator('---\n'
'k: v\n')) 'k: v\n'))
self.assertEqual(len(e), 9) self.assertEqual(len(e), 9)
self.assertIsInstance(e[3].curr, yaml.KeyToken) self.assertIsInstance(e[3].curr, yaml.KeyToken)
self.assertIsInstance(e[5].curr, yaml.ValueToken) self.assertIsInstance(e[5].curr, yaml.ValueToken)
def test_token_or_line_generator(self): e = list(token_or_comment_generator('# start comment\n'
e = list(token_or_line_generator('---\n' '- a\n'
'k: v\n')) '- key: val # key=val\n'
self.assertEqual(len(e), 12) '# 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)
self.assertIsInstance(e[0], Token) self.assertIsInstance(e[0], Token)
self.assertIsInstance(e[0].curr, yaml.StreamStartToken) self.assertIsInstance(e[0].curr, yaml.StreamStartToken)
self.assertIsInstance(e[1], Token) self.assertIsInstance(e[1], Token)
@@ -89,5 +148,6 @@ class ParserTestCase(unittest.TestCase):
self.assertIsInstance(e[3].curr, yaml.BlockMappingStartToken) self.assertIsInstance(e[3].curr, yaml.BlockMappingStartToken)
self.assertIsInstance(e[4].curr, yaml.KeyToken) self.assertIsInstance(e[4].curr, yaml.KeyToken)
self.assertIsInstance(e[6].curr, yaml.ValueToken) self.assertIsInstance(e[6].curr, yaml.ValueToken)
self.assertIsInstance(e[8], Line) self.assertIsInstance(e[8], Comment)
self.assertIsInstance(e[11], Line) self.assertIsInstance(e[9], Line)
self.assertIsInstance(e[12], Line)

View File

@@ -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 <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'))

View File

@@ -22,7 +22,7 @@ indentation, etc."""
APP_NAME = 'yamllint' APP_NAME = 'yamllint'
APP_VERSION = '1.2.1' APP_VERSION = '1.3.1'
APP_DESCRIPTION = __doc__ APP_DESCRIPTION = __doc__
__author__ = u'Adrien Vergé' __author__ = u'Adrien Vergé'

View File

@@ -86,6 +86,13 @@ def run(argv=None):
'simultaneously.', file=sys.stderr) 'simultaneously.', file=sys.stderr)
sys.exit(-1) 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: try:
if args.config_data is not None: if args.config_data is not None:
if args.config_data != '' and ':' not in args.config_data: if args.config_data != '' and ':' not in args.config_data:
@@ -95,6 +102,8 @@ def run(argv=None):
conf = YamlLintConfig(file=args.config_file) conf = YamlLintConfig(file=args.config_file)
elif os.path.isfile('.yamllint'): elif os.path.isfile('.yamllint'):
conf = YamlLintConfig(file='.yamllint') conf = YamlLintConfig(file='.yamllint')
elif os.path.isfile(user_global_config):
conf = YamlLintConfig(file=user_global_config)
else: else:
conf = YamlLintConfig('extends: default') conf = YamlLintConfig('extends: default')
except YamlLintConfigError as e: except YamlLintConfigError as e:

View File

@@ -14,6 +14,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import yaml import yaml
from yamllint import parser from yamllint import parser
@@ -56,13 +58,69 @@ def get_costemic_problems(buffer, conf):
# Split token rules from line rules # Split token rules from line rules
token_rules = [r for r in rules if r.TYPE == 'token'] 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'] line_rules = [r for r in rules if r.TYPE == 'line']
context = {} context = {}
for rule in token_rules: for rule in token_rules:
context[rule.ID] = {} context[rule.ID] = {}
for elem in parser.token_or_line_generator(buffer): class DisableDirective():
def __init__(self):
self.rules = set()
self.all_rules = set([r.ID for r in rules])
def process_comment(self, comment):
try:
comment = str(comment)
except UnicodeError:
return # this certainly wasn't a yamllint directive 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):
try:
comment = str(comment)
except UnicodeError:
return # this certainly wasn't a yamllint directive 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): if isinstance(elem, parser.Token):
for rule in token_rules: for rule in token_rules:
rule_conf = conf.rules[rule.ID] rule_conf = conf.rules[rule.ID]
@@ -72,15 +130,39 @@ def get_costemic_problems(buffer, conf):
context[rule.ID]): context[rule.ID]):
problem.rule = rule.ID problem.rule = rule.ID
problem.level = rule_conf['level'] 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']
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): elif isinstance(elem, parser.Line):
for rule in line_rules: for rule in line_rules:
rule_conf = conf.rules[rule.ID] rule_conf = conf.rules[rule.ID]
for problem in rule.check(rule_conf, elem): for problem in rule.check(rule_conf, elem):
problem.rule = rule.ID problem.rule = rule.ID
problem.level = rule_conf['level'] 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 yield problem
disabled_for_line = disabled_for_next_line
disabled_for_next_line = DisableLineDirective()
cache = []
def get_syntax_error(buffer): def get_syntax_error(buffer):
try: try:
@@ -94,6 +176,9 @@ def get_syntax_error(buffer):
def _run(buffer, conf): 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 # If the document contains a syntax error, save it and yield it at the
# right line # right line
syntax_error = get_syntax_error(buffer) syntax_error = get_syntax_error(buffer)

View File

@@ -38,6 +38,40 @@ class Token(object):
self.nextnext = nextnext 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 __str__(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): def line_generator(buffer):
line_no = 1 line_no = 1
cur = 0 cur = 0
@@ -51,7 +85,39 @@ def line_generator(buffer):
yield Line(line_no, buffer, start=cur, end=len(buffer)) yield Line(line_no, buffer, start=cur, end=len(buffer))
def token_generator(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):
yaml_loader = yaml.BaseLoader(buffer) yaml_loader = yaml.BaseLoader(buffer)
try: try:
@@ -63,6 +129,9 @@ def token_generator(buffer):
yield Token(curr.start_mark.line + 1, curr, prev, next, nextnext) yield Token(curr.start_mark.line + 1, curr, prev, next, nextnext)
for comment in comments_between_tokens(curr, next):
yield comment
prev = curr prev = curr
curr = next curr = next
@@ -70,19 +139,19 @@ def token_generator(buffer):
pass pass
def token_or_line_generator(buffer): def token_or_comment_or_line_generator(buffer):
"""Generator that mixes tokens and lines, ordering them by line number""" """Generator that mixes tokens and lines, ordering them by line number"""
token_gen = token_generator(buffer) tok_or_com_gen = token_or_comment_generator(buffer)
line_gen = line_generator(buffer) line_gen = line_generator(buffer)
token = next(token_gen, None) tok_or_com = next(tok_or_com_gen, None)
line = next(line_gen, None) line = next(line_gen, None)
while token is not None or line is not None: while tok_or_com is not None or line is not None:
if token is None or (line is not None and if tok_or_com is None or (line is not None and
token.line_no > line.line_no): tok_or_com.line_no > line.line_no):
yield line yield line
line = next(line_gen, None) line = next(line_gen, None)
else: else:
yield token yield tok_or_com
token = next(token_gen, None) tok_or_com = next(tok_or_com_gen, None)

View File

@@ -55,33 +55,25 @@ Use this rule to control the position and formatting of comments.
""" """
import yaml
from yamllint.linter import LintProblem from yamllint.linter import LintProblem
from yamllint.rules.common import get_comments_between_tokens
ID = 'comments' ID = 'comments'
TYPE = 'token' TYPE = 'comment'
CONF = {'require-starting-space': bool, CONF = {'require-starting-space': bool,
'min-spaces-from-content': int} 'min-spaces-from-content': int}
def check(conf, token, prev, next, nextnext, context): def check(conf, comment):
for comment in get_comments_between_tokens(token, next): if (conf['min-spaces-from-content'] != -1 and comment.is_inline() and
if (conf['min-spaces-from-content'] != -1 and comment.pointer - comment.token_before.end_mark.pointer <
not isinstance(token, yaml.StreamStartToken) and conf['min-spaces-from-content']):
comment.line == token.end_mark.line + 1): yield LintProblem(comment.line_no, comment.column_no,
# Sometimes token end marks are on the next line 'too few spaces before comment')
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 if (conf['require-starting-space'] and
comment.pointer + 1 < len(comment.buffer) and comment.pointer + 1 < len(comment.buffer) and
comment.buffer[comment.pointer + 1] != ' ' and comment.buffer[comment.pointer + 1] != ' ' and
comment.buffer[comment.pointer + 1] != '\n'): comment.buffer[comment.pointer + 1] != '\n'):
yield LintProblem(comment.line, comment.column + 1, yield LintProblem(comment.line_no, comment.column_no + 1,
'missing starting space in comment') 'missing starting space in comment')

View File

@@ -78,11 +78,11 @@ Use this rule to force comments to be indented like content.
import yaml import yaml
from yamllint.linter import LintProblem from yamllint.linter import LintProblem
from yamllint.rules.common import get_line_indent, get_comments_between_tokens from yamllint.rules.common import get_line_indent
ID = 'comments-indentation' ID = 'comments-indentation'
TYPE = 'token' TYPE = 'comment'
# Case A: # Case A:
@@ -98,28 +98,42 @@ TYPE = 'token'
# # commented line 2 # # commented line 2
# current: line # current: line
def check(conf, token, prev, next, nextnext, context): def check(conf, comment):
if prev is None: # Only check block comments
if (not isinstance(comment.token_before, yaml.StreamStartToken) and
comment.token_before.end_mark.line + 1 == comment.line_no):
return return
curr_line_indent = token.start_mark.column next_line_indent = comment.token_after.start_mark.column
if isinstance(token, yaml.StreamEndToken): if isinstance(comment.token_after, yaml.StreamEndToken):
curr_line_indent = 0 next_line_indent = 0
skip_first_line = True if isinstance(comment.token_before, yaml.StreamStartToken):
if isinstance(prev, yaml.StreamStartToken):
skip_first_line = False
prev_line_indent = 0 prev_line_indent = 0
else: else:
prev_line_indent = get_line_indent(prev) prev_line_indent = get_line_indent(comment.token_before)
if prev_line_indent <= curr_line_indent: # In the following case only the next line indent is valid:
prev_line_indent = -1 # disable it # list:
# # comment
# - 1
# - 2
if prev_line_indent <= next_line_indent:
prev_line_indent = next_line_indent
for comment in get_comments_between_tokens( # If two indents are valid but a previous comment went back to normal
prev, token, skip_first_line=skip_first_line): # indent, for the next ones to do the same. In other words, avoid this:
if comment.column - 1 == curr_line_indent: # list:
prev_line_indent = -1 # disable it # - 1
elif comment.column - 1 != prev_line_indent: # # comment on valid indent (0)
yield LintProblem(comment.line, comment.column, # # comment on valid indent (4)
'comment not indented like content') # 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')

View File

@@ -48,27 +48,6 @@ def spaces_before(token, prev, next, min=-1, max=-1,
token.start_mark.column + 1, min_desc) 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): def get_line_indent(token):
"""Finds the indent of the line the token starts in.""" """Finds the indent of the line the token starts in."""
start = token.start_mark.buffer.rfind('\n', 0, start = token.start_mark.buffer.rfind('\n', 0,
@@ -98,35 +77,6 @@ def get_real_end_line(token):
return end_line 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): def is_explicit_key(token):
# explicit key: # explicit key:
# ? key # ? key

View File

@@ -62,7 +62,7 @@ CONF = {'max': int,
def check(conf, line): def check(conf, line):
if line.start == line.end and line.end < len(line.buffer): if line.start == line.end and line.end < len(line.buffer):
# Only alert on the last blank line of a serie # Only alert on the last blank line of a series
if (line.end < len(line.buffer) - 1 and if (line.end < len(line.buffer) - 1 and
line.buffer[line.end + 1] == '\n'): line.buffer[line.end + 1] == '\n'):
return return

View File

@@ -59,7 +59,7 @@ Use this rule to set a limit to lines length.
the following code snippet would **FAIL**: the following code snippet would **FAIL**:
:: ::
- this line is waaaaaaaaaaaaaay too long but could be easily splitted... - this line is waaaaaaaaaaaaaay too long but could be easily split...
#. With ``line-length: {max: 60, allow-non-breakable-words: no}`` #. With ``line-length: {max: 60, allow-non-breakable-words: no}``
@@ -90,7 +90,7 @@ def check(conf, line):
start += 1 start += 1
if start != line.end: if start != line.end:
if line.buffer[start] == '#': if line.buffer[start] in ('#', '-'):
start += 2 start += 2
if line.buffer.find(' ', start, line.end) == -1: if line.buffer.find(' ', start, line.end) == -1: