diff --git a/.travis.yml b/.travis.yml
index 0ff3297..bd365c1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,17 +1,20 @@
---
language: python
python:
+ - 2.6
- 2.7
- 3.3
- 3.4
- 3.5
+ - 3.6
- nightly
install:
- - pip install pyyaml flake8 coveralls
+ - pip install pyyaml flake8 flake8-import-order coveralls
+ - if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install unittest2; fi
- pip install .
script:
- - flake8 .
- - yamllint $(git ls-files '*.yaml' '*.yml')
+ - if [[ $TRAVIS_PYTHON_VERSION != 2.6 ]]; then flake8 .; fi
+ - yamllint --strict $(git ls-files '*.yaml' '*.yml')
- coverage run --source=yamllint setup.py test
after_success:
coveralls
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
new file mode 100644
index 0000000..0b2f9ff
--- /dev/null
+++ b/CHANGELOG.rst
@@ -0,0 +1,15 @@
+Changelog
+=========
+
+1.8.1 (2017-07-04)
+------------------
+
+- Require pathspec >= 0.5.3
+- Support Python 2.6
+- Add a changelog
+
+1.8.0 (2017-06-28)
+------------------
+
+- Refactor argparse with mutually_exclusive_group
+- Add support to ignore paths in configuration
diff --git a/README.rst b/README.rst
index 1fb9541..1f5dac1 100644
--- a/README.rst
+++ b/README.rst
@@ -44,7 +44,7 @@ On Fedora / CentOS:
sudo dnf install yamllint
-On Debian 9+ / Ubuntu 16.04+:
+On Debian 8+ / Ubuntu 16.04+:
.. code:: bash
@@ -119,6 +119,27 @@ or for a whole block:
consectetur : adipiscing elit
# yamllint enable
+Specific files can be ignored (totally or for some rules only) using a
+``.gitignore``-style pattern:
+
+.. code:: yaml
+
+ # For all rules
+ ignore: |
+ *.dont-lint-me.yaml
+ /bin/
+ !/bin/*.lint-me-anyway.yaml
+
+ rules:
+ key-duplicates:
+ ignore: |
+ generated
+ *.template.yaml
+ trailing-spaces:
+ ignore: |
+ *.ignore-trailing-spaces.yaml
+ /ascii-art/*
+
`Read more in the complete documentation! `_
License
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 661e506..0815789 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -102,8 +102,69 @@ Errors and warnings
-------------------
Problems detected by yamllint can be raised either as errors or as warnings.
+The CLI will output them (with different colors when using the ``standard``
+output format).
-In both cases, the script will output them (with different colors when using the
-``standard`` output format), but the exit code can be different. More precisely,
-the script will exit will a failure code *only when* there is one or more
-error(s).
+By default the script will exit with a return code ``1`` *only when* there is one or
+more error(s).
+
+However if strict mode is enabled with the ``-s`` (or ``--strict``) option, the
+return code will be:
+
+ * ``0`` if no errors or warnings occur
+ * ``1`` if one or more errors occur
+ * ``2`` if no errors occur, but one or more warnings occur
+
+Ignoring paths
+--------------
+
+It is possible to exclude specific files or directories, so that the linter
+doesn't process them.
+
+You can either totally ignore files (they won't be looked at):
+
+.. code-block:: yaml
+
+ extends: default
+
+ ignore: |
+ /this/specific/file.yaml
+ /all/this/directory/
+ *.template.yaml
+
+or ignore paths only for specific rules:
+
+.. code-block:: yaml
+
+ extends: default
+
+ rules:
+ trailing-spaces:
+ ignore: |
+ /this-file-has-trailing-spaces-but-it-is-OK.yaml
+ /generated/*.yaml
+
+Note that this ``.gitignore``-style path pattern allows complex path
+exclusion/inclusion, see the `pathspec README file
+`_ for more details.
+Here is a more complex example:
+
+.. code-block:: yaml
+
+ # For all rules
+ ignore: |
+ *.dont-lint-me.yaml
+ /bin/
+ !/bin/*.lint-me-anyway.yaml
+
+ extends: default
+
+ rules:
+ key-duplicates:
+ ignore: |
+ generated
+ *.template.yaml
+ trailing-spaces:
+ ignore: |
+ *.ignore-trailing-spaces.yaml
+ /ascii-art/*
diff --git a/docs/disable_with_comments.rst b/docs/disable_with_comments.rst
index 10477fb..34d6987 100644
--- a/docs/disable_with_comments.rst
+++ b/docs/disable_with_comments.rst
@@ -32,7 +32,7 @@ or:
- 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
+It is possible, although not recommend, to disabled **all** rules for a
specific line:
.. code-block:: yaml
@@ -46,7 +46,7 @@ If you need to disable multiple rules, it is allowed to chain rules like this:
Disabling checks for all (or part of) the file
----------------------------------------------
-To prevent yamllint from reporting problems for the whoe file, or for a block of
+To prevent yamllint from reporting problems for the whole file, or for a block of
lines within the file, use ``# yamllint disable ...`` and ``# yamllint enable
...`` directive comments. For instance:
@@ -60,7 +60,7 @@ lines within the file, use ``# yamllint disable ...`` and ``# yamllint enable
- rest of the document...
-It it possible, although not recommend, to disabled **all** rules:
+It is possible, although not recommend, to disabled **all** rules:
.. code-block:: yaml
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 9da607a..3365877 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -10,7 +10,7 @@ On Fedora / CentOS:
sudo dnf install yamllint
-On Debian 9+ / Ubuntu 16.04+:
+On Debian 8+ / Ubuntu 16.04+:
.. code:: bash
diff --git a/docs/text_editors.rst b/docs/text_editors.rst
index 8909aa9..da3e96f 100644
--- a/docs/text_editors.rst
+++ b/docs/text_editors.rst
@@ -9,8 +9,12 @@ text editor.
Vim
---
-Assuming that the `syntastic `_ plugin
-is installed, add to your ``.vimrc``:
+Assuming that the `ALE `_ plugin is
+installed, yamllint is supported by default. It is automatically enabled when
+editing YAML files.
+
+If you instead use the `syntastic `_
+plugin, add this to your ``.vimrc``:
::
@@ -23,6 +27,12 @@ Assuming that the `neomake `_ plugin is
installed, yamllint is supported by default. It is automatically enabled when
editing YAML files.
+Emacs
+-----
+
+If you are `flycheck `_ user, you can use
+`flycheck-yamllint `_ integration.
+
Other text editors
------------------
diff --git a/hooks.yaml b/hooks.yaml
new file mode 100644
index 0000000..482f3fb
--- /dev/null
+++ b/hooks.yaml
@@ -0,0 +1,11 @@
+---
+
+# For use with pre-commit.
+# See usage instructions at http://pre-commit.com
+
+- id: yamllint
+ name: yamllint
+ description: This hook runs yamllint.
+ entry: yamllint
+ language: python
+ files: \.(yaml|yml)$
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..4d5142d
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[bdist_wheel]
+universal = 1
+
+[flake8]
+import-order-style = pep8
diff --git a/setup.py b/setup.py
index 8a865f4..e87980f 100644
--- a/setup.py
+++ b/setup.py
@@ -46,7 +46,6 @@ setup(
entry_points={'console_scripts': ['yamllint=yamllint.cli:run']},
package_data={'yamllint': ['conf/*.yaml'],
'tests': ['yaml-1.2-spec-examples/*']},
- install_requires=['pyyaml'],
- tests_require=['nose'],
- test_suite='nose.collector',
+ install_requires=['pathspec >=0.5.3', 'pyyaml'],
+ test_suite='tests',
)
diff --git a/tests/common.py b/tests/common.py
index ed626d0..11dd235 100644
--- a/tests/common.py
+++ b/tests/common.py
@@ -14,7 +14,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import unittest
+import os
+import tempfile
+import sys
+try:
+ assert sys.version_info >= (2, 7)
+ import unittest
+except:
+ import unittest2 as unittest
import yaml
@@ -49,3 +56,21 @@ class RuleTestCase(unittest.TestCase):
real_problems = list(linter.run(source, self.build_fake_config(conf)))
self.assertEqual(real_problems, expected_problems)
+
+
+def build_temp_workspace(files):
+ tempdir = tempfile.mkdtemp(prefix='yamllint-tests-')
+
+ for path, content in files.items():
+ path = os.path.join(tempdir, path)
+ if not os.path.exists(os.path.dirname(path)):
+ os.makedirs(os.path.dirname(path))
+
+ if type(content) is list:
+ os.mkdir(path)
+ else:
+ mode = 'wb' if isinstance(content, bytes) else 'w'
+ with open(path, mode) as f:
+ f.write(content)
+
+ return tempdir
diff --git a/tests/rules/test_braces.py b/tests/rules/test_braces.py
index 0262ae0..308715b 100644
--- a/tests/rules/test_braces.py
+++ b/tests/rules/test_braces.py
@@ -32,11 +32,19 @@ class ColonTestCase(RuleTestCase):
'dict7: { a: 1, b, c: 3 }\n', conf)
def test_min_spaces(self):
- conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 0}'
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: 0\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'dict: {}\n', conf)
- conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 1}'
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: 1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'dict: {}\n', conf, problem=(2, 8))
self.check('---\n'
@@ -52,7 +60,11 @@ class ColonTestCase(RuleTestCase):
' b\n'
'}\n', conf)
- conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 3}'
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: 3\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'dict: { a: 1, b }\n', conf,
problem1=(2, 9), problem2=(2, 17))
@@ -60,7 +72,11 @@ class ColonTestCase(RuleTestCase):
'dict: { a: 1, b }\n', conf)
def test_max_spaces(self):
- conf = 'braces: {max-spaces-inside: 0, min-spaces-inside: -1}'
+ conf = ('braces:\n'
+ ' max-spaces-inside: 0\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'dict: {}\n', conf)
self.check('---\n'
@@ -79,7 +95,11 @@ class ColonTestCase(RuleTestCase):
' b\n'
'}\n', conf)
- conf = 'braces: {max-spaces-inside: 3, min-spaces-inside: -1}'
+ conf = ('braces:\n'
+ ' max-spaces-inside: 3\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'dict: { a: 1, b }\n', conf)
self.check('---\n'
@@ -87,7 +107,11 @@ class ColonTestCase(RuleTestCase):
problem1=(2, 11), problem2=(2, 23))
def test_min_and_max_spaces(self):
- conf = 'braces: {max-spaces-inside: 0, min-spaces-inside: 0}'
+ conf = ('braces:\n'
+ ' max-spaces-inside: 0\n'
+ ' min-spaces-inside: 0\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'dict: {}\n', conf)
self.check('---\n'
@@ -95,14 +119,169 @@ class ColonTestCase(RuleTestCase):
self.check('---\n'
'dict: { a: 1, b}\n', conf, problem=(2, 10))
- conf = 'braces: {max-spaces-inside: 1, min-spaces-inside: 1}'
+ conf = ('braces:\n'
+ ' max-spaces-inside: 1\n'
+ ' min-spaces-inside: 1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'dict: {a: 1, b, c: 3 }\n', conf, problem=(2, 8))
- conf = 'braces: {max-spaces-inside: 2, min-spaces-inside: 0}'
+ conf = ('braces:\n'
+ ' max-spaces-inside: 2\n'
+ ' min-spaces-inside: 0\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'dict: {a: 1, b, c: 3 }\n', conf)
self.check('---\n'
'dict: { a: 1, b, c: 3 }\n', conf)
self.check('---\n'
'dict: { a: 1, b, c: 3 }\n', conf, problem=(2, 10))
+
+ def test_min_spaces_empty(self):
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 0\n'
+ ' min-spaces-inside-empty: 0\n')
+ self.check('---\n'
+ 'array: {}\n', conf)
+
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: 1\n')
+ self.check('---\n'
+ 'array: {}\n', conf, problem=(2, 9))
+ self.check('---\n'
+ 'array: { }\n', conf)
+
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: 3\n')
+ self.check('---\n'
+ 'array: {}\n', conf, problem=(2, 9))
+ self.check('---\n'
+ 'array: { }\n', conf)
+
+ def test_max_spaces_empty(self):
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 0\n'
+ ' min-spaces-inside-empty: -1\n')
+ self.check('---\n'
+ 'array: {}\n', conf)
+ self.check('---\n'
+ 'array: { }\n', conf, problem=(2, 9))
+
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 1\n'
+ ' min-spaces-inside-empty: -1\n')
+ self.check('---\n'
+ 'array: {}\n', conf)
+ self.check('---\n'
+ 'array: { }\n', conf)
+ self.check('---\n'
+ 'array: { }\n', conf, problem=(2, 10))
+
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 3\n'
+ ' min-spaces-inside-empty: -1\n')
+ self.check('---\n'
+ 'array: {}\n', conf)
+ self.check('---\n'
+ 'array: { }\n', conf)
+ self.check('---\n'
+ 'array: { }\n', conf, problem=(2, 12))
+
+ def test_min_and_max_spaces_empty(self):
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 2\n'
+ ' min-spaces-inside-empty: 1\n')
+ self.check('---\n'
+ 'array: {}\n', conf, problem=(2, 9))
+ self.check('---\n'
+ 'array: { }\n', conf)
+ self.check('---\n'
+ 'array: { }\n', conf)
+ self.check('---\n'
+ 'array: { }\n', conf, problem=(2, 11))
+
+ def test_mixed_empty_nonempty(self):
+ conf = ('braces:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: 1\n'
+ ' max-spaces-inside-empty: 0\n'
+ ' min-spaces-inside-empty: 0\n')
+ self.check('---\n'
+ 'array: { a: 1, b }\n', conf)
+ self.check('---\n'
+ 'array: {a: 1, b}\n', conf,
+ problem1=(2, 9), problem2=(2, 16))
+ self.check('---\n'
+ 'array: {}\n', conf)
+ self.check('---\n'
+ 'array: { }\n', conf,
+ problem1=(2, 9))
+
+ conf = ('braces:\n'
+ ' max-spaces-inside: 0\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 1\n'
+ ' min-spaces-inside-empty: 1\n')
+ self.check('---\n'
+ 'array: { a: 1, b }\n', conf,
+ problem1=(2, 9), problem2=(2, 17))
+ self.check('---\n'
+ 'array: {a: 1, b}\n', conf)
+ self.check('---\n'
+ 'array: {}\n', conf,
+ problem1=(2, 9))
+ self.check('---\n'
+ 'array: { }\n', conf)
+
+ conf = ('braces:\n'
+ ' max-spaces-inside: 2\n'
+ ' min-spaces-inside: 1\n'
+ ' max-spaces-inside-empty: 1\n'
+ ' min-spaces-inside-empty: 1\n')
+ self.check('---\n'
+ 'array: { a: 1, b }\n', conf)
+ self.check('---\n'
+ 'array: {a: 1, b }\n', conf,
+ problem1=(2, 9), problem2=(2, 18))
+ self.check('---\n'
+ 'array: {}\n', conf,
+ problem1=(2, 9))
+ self.check('---\n'
+ 'array: { }\n', conf)
+ self.check('---\n'
+ 'array: { }\n', conf,
+ problem1=(2, 11))
+
+ conf = ('braces:\n'
+ ' max-spaces-inside: 1\n'
+ ' min-spaces-inside: 1\n'
+ ' max-spaces-inside-empty: 1\n'
+ ' min-spaces-inside-empty: 1\n')
+ self.check('---\n'
+ 'array: { a: 1, b }\n', conf)
+ self.check('---\n'
+ 'array: {a: 1, b}\n', conf,
+ problem1=(2, 9), problem2=(2, 16))
+ self.check('---\n'
+ 'array: {}\n', conf,
+ problem1=(2, 9))
+ self.check('---\n'
+ 'array: { }\n', conf)
diff --git a/tests/rules/test_brackets.py b/tests/rules/test_brackets.py
index 8fb8006..273f1a7 100644
--- a/tests/rules/test_brackets.py
+++ b/tests/rules/test_brackets.py
@@ -32,11 +32,19 @@ class ColonTestCase(RuleTestCase):
'array7: [ a, b, c ]\n', conf)
def test_min_spaces(self):
- conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 0}'
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: 0\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'array: []\n', conf)
- conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 1}'
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: 1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'array: []\n', conf, problem=(2, 9))
self.check('---\n'
@@ -51,7 +59,11 @@ class ColonTestCase(RuleTestCase):
' b\n'
']\n', conf)
- conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 3}'
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: 3\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'array: [ a, b ]\n', conf,
problem1=(2, 10), problem2=(2, 15))
@@ -59,7 +71,11 @@ class ColonTestCase(RuleTestCase):
'array: [ a, b ]\n', conf)
def test_max_spaces(self):
- conf = 'brackets: {max-spaces-inside: 0, min-spaces-inside: -1}'
+ conf = ('brackets:\n'
+ ' max-spaces-inside: 0\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'array: []\n', conf)
self.check('---\n'
@@ -78,7 +94,11 @@ class ColonTestCase(RuleTestCase):
' b\n'
']\n', conf)
- conf = 'brackets: {max-spaces-inside: 3, min-spaces-inside: -1}'
+ conf = ('brackets:\n'
+ ' max-spaces-inside: 3\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'array: [ a, b ]\n', conf)
self.check('---\n'
@@ -86,7 +106,11 @@ class ColonTestCase(RuleTestCase):
problem1=(2, 12), problem2=(2, 21))
def test_min_and_max_spaces(self):
- conf = 'brackets: {max-spaces-inside: 0, min-spaces-inside: 0}'
+ conf = ('brackets:\n'
+ ' max-spaces-inside: 0\n'
+ ' min-spaces-inside: 0\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'array: []\n', conf)
self.check('---\n'
@@ -94,14 +118,169 @@ class ColonTestCase(RuleTestCase):
self.check('---\n'
'array: [ a, b]\n', conf, problem=(2, 11))
- conf = 'brackets: {max-spaces-inside: 1, min-spaces-inside: 1}'
+ conf = ('brackets:\n'
+ ' max-spaces-inside: 1\n'
+ ' min-spaces-inside: 1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'array: [a, b, c ]\n', conf, problem=(2, 9))
- conf = 'brackets: {max-spaces-inside: 2, min-spaces-inside: 0}'
+ conf = ('brackets:\n'
+ ' max-spaces-inside: 2\n'
+ ' min-spaces-inside: 0\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: -1\n')
self.check('---\n'
'array: [a, b, c ]\n', conf)
self.check('---\n'
'array: [ a, b, c ]\n', conf)
self.check('---\n'
'array: [ a, b, c ]\n', conf, problem=(2, 11))
+
+ def test_min_spaces_empty(self):
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 0\n'
+ ' min-spaces-inside-empty: 0\n')
+ self.check('---\n'
+ 'array: []\n', conf)
+
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: 1\n')
+ self.check('---\n'
+ 'array: []\n', conf, problem=(2, 9))
+ self.check('---\n'
+ 'array: [ ]\n', conf)
+
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: -1\n'
+ ' min-spaces-inside-empty: 3\n')
+ self.check('---\n'
+ 'array: []\n', conf, problem=(2, 9))
+ self.check('---\n'
+ 'array: [ ]\n', conf)
+
+ def test_max_spaces_empty(self):
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 0\n'
+ ' min-spaces-inside-empty: -1\n')
+ self.check('---\n'
+ 'array: []\n', conf)
+ self.check('---\n'
+ 'array: [ ]\n', conf, problem=(2, 9))
+
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 1\n'
+ ' min-spaces-inside-empty: -1\n')
+ self.check('---\n'
+ 'array: []\n', conf)
+ self.check('---\n'
+ 'array: [ ]\n', conf)
+ self.check('---\n'
+ 'array: [ ]\n', conf, problem=(2, 10))
+
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 3\n'
+ ' min-spaces-inside-empty: -1\n')
+ self.check('---\n'
+ 'array: []\n', conf)
+ self.check('---\n'
+ 'array: [ ]\n', conf)
+ self.check('---\n'
+ 'array: [ ]\n', conf, problem=(2, 12))
+
+ def test_min_and_max_spaces_empty(self):
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 2\n'
+ ' min-spaces-inside-empty: 1\n')
+ self.check('---\n'
+ 'array: []\n', conf, problem=(2, 9))
+ self.check('---\n'
+ 'array: [ ]\n', conf)
+ self.check('---\n'
+ 'array: [ ]\n', conf)
+ self.check('---\n'
+ 'array: [ ]\n', conf, problem=(2, 11))
+
+ def test_mixed_empty_nonempty(self):
+ conf = ('brackets:\n'
+ ' max-spaces-inside: -1\n'
+ ' min-spaces-inside: 1\n'
+ ' max-spaces-inside-empty: 0\n'
+ ' min-spaces-inside-empty: 0\n')
+ self.check('---\n'
+ 'array: [ a, b ]\n', conf)
+ self.check('---\n'
+ 'array: [a, b]\n', conf,
+ problem1=(2, 9), problem2=(2, 13))
+ self.check('---\n'
+ 'array: []\n', conf)
+ self.check('---\n'
+ 'array: [ ]\n', conf,
+ problem1=(2, 9))
+
+ conf = ('brackets:\n'
+ ' max-spaces-inside: 0\n'
+ ' min-spaces-inside: -1\n'
+ ' max-spaces-inside-empty: 1\n'
+ ' min-spaces-inside-empty: 1\n')
+ self.check('---\n'
+ 'array: [ a, b ]\n', conf,
+ problem1=(2, 9), problem2=(2, 14))
+ self.check('---\n'
+ 'array: [a, b]\n', conf)
+ self.check('---\n'
+ 'array: []\n', conf,
+ problem1=(2, 9))
+ self.check('---\n'
+ 'array: [ ]\n', conf)
+
+ conf = ('brackets:\n'
+ ' max-spaces-inside: 2\n'
+ ' min-spaces-inside: 1\n'
+ ' max-spaces-inside-empty: 1\n'
+ ' min-spaces-inside-empty: 1\n')
+ self.check('---\n'
+ 'array: [ a, b ]\n', conf)
+ self.check('---\n'
+ 'array: [a, b ]\n', conf,
+ problem1=(2, 9), problem2=(2, 15))
+ self.check('---\n'
+ 'array: []\n', conf,
+ problem1=(2, 9))
+ self.check('---\n'
+ 'array: [ ]\n', conf)
+ self.check('---\n'
+ 'array: [ ]\n', conf,
+ problem1=(2, 11))
+
+ conf = ('brackets:\n'
+ ' max-spaces-inside: 1\n'
+ ' min-spaces-inside: 1\n'
+ ' max-spaces-inside-empty: 1\n'
+ ' min-spaces-inside-empty: 1\n')
+ self.check('---\n'
+ 'array: [ a, b ]\n', conf)
+ self.check('---\n'
+ 'array: [a, b]\n', conf,
+ problem1=(2, 9), problem2=(2, 13))
+ self.check('---\n'
+ 'array: []\n', conf,
+ problem1=(2, 9))
+ self.check('---\n'
+ 'array: [ ]\n', conf)
diff --git a/tests/rules/test_comments.py b/tests/rules/test_comments.py
index ee8a62c..ded31fa 100644
--- a/tests/rules/test_comments.py
+++ b/tests/rules/test_comments.py
@@ -43,7 +43,7 @@ class CommentsTestCase(RuleTestCase):
def test_starting_space(self):
conf = ('comments:\n'
- ' require-starting-space: yes\n'
+ ' require-starting-space: true\n'
' min-spaces-from-content: -1\n'
'comments-indentation: disable\n')
self.check('---\n'
@@ -82,7 +82,7 @@ class CommentsTestCase(RuleTestCase):
def test_spaces_from_content(self):
conf = ('comments:\n'
- ' require-starting-space: no\n'
+ ' require-starting-space: false\n'
' min-spaces-from-content: 2\n')
self.check('---\n'
'# comment\n'
@@ -104,7 +104,7 @@ class CommentsTestCase(RuleTestCase):
def test_both(self):
conf = ('comments:\n'
- ' require-starting-space: yes\n'
+ ' require-starting-space: true\n'
' min-spaces-from-content: 2\n'
'comments-indentation: disable\n')
self.check('---\n'
@@ -134,7 +134,7 @@ class CommentsTestCase(RuleTestCase):
def test_empty_comment(self):
conf = ('comments:\n'
- ' require-starting-space: yes\n'
+ ' require-starting-space: true\n'
' min-spaces-from-content: 2\n')
self.check('---\n'
'# This is paragraph 1.\n'
@@ -146,13 +146,13 @@ class CommentsTestCase(RuleTestCase):
def test_first_line(self):
conf = ('comments:\n'
- ' require-starting-space: yes\n'
+ ' require-starting-space: true\n'
' min-spaces-from-content: 2\n')
self.check('# comment\n', conf)
def test_last_line(self):
conf = ('comments:\n'
- ' require-starting-space: yes\n'
+ ' require-starting-space: true\n'
' min-spaces-from-content: 2\n'
'new-line-at-end-of-file: disable\n')
self.check('# comment with no newline char:\n'
@@ -160,7 +160,7 @@ class CommentsTestCase(RuleTestCase):
def test_multi_line_scalar(self):
conf = ('comments:\n'
- ' require-starting-space: yes\n'
+ ' require-starting-space: true\n'
' min-spaces-from-content: 2\n'
'trailing-spaces: disable\n')
self.check('---\n'
diff --git a/tests/rules/test_document_end.py b/tests/rules/test_document_end.py
index 82d5772..d62a7e6 100644
--- a/tests/rules/test_document_end.py
+++ b/tests/rules/test_document_end.py
@@ -31,7 +31,7 @@ class DocumentEndTestCase(RuleTestCase):
' document: end\n', conf)
def test_required(self):
- conf = 'document-end: {present: yes}'
+ conf = 'document-end: {present: true}'
self.check('', conf)
self.check('\n', conf)
self.check('---\n'
@@ -43,7 +43,7 @@ class DocumentEndTestCase(RuleTestCase):
' document: end\n', conf, problem=(3, 1))
def test_forbidden(self):
- conf = 'document-end: {present: no}'
+ conf = 'document-end: {present: false}'
self.check('---\n'
'with:\n'
' document: end\n'
@@ -53,7 +53,7 @@ class DocumentEndTestCase(RuleTestCase):
' document: end\n', conf)
def test_multiple_documents(self):
- conf = ('document-end: {present: yes}\n'
+ conf = ('document-end: {present: true}\n'
'document-start: disable\n')
self.check('---\n'
'first: document\n'
diff --git a/tests/rules/test_document_start.py b/tests/rules/test_document_start.py
index 15fe2b7..20de9ad 100644
--- a/tests/rules/test_document_start.py
+++ b/tests/rules/test_document_start.py
@@ -28,7 +28,7 @@ class DocumentStartTestCase(RuleTestCase):
'key: val\n', conf)
def test_required(self):
- conf = ('document-start: {present: yes}\n'
+ conf = ('document-start: {present: true}\n'
'empty-lines: disable\n')
self.check('', conf)
self.check('\n', conf)
@@ -44,7 +44,7 @@ class DocumentStartTestCase(RuleTestCase):
'key: val\n', conf)
def test_forbidden(self):
- conf = ('document-start: {present: no}\n'
+ conf = ('document-start: {present: false}\n'
'empty-lines: disable\n')
self.check('', conf)
self.check('key: val\n', conf)
@@ -62,7 +62,7 @@ class DocumentStartTestCase(RuleTestCase):
'key: val\n', conf, problem=(2, 1))
def test_multiple_documents(self):
- conf = 'document-start: {present: yes}'
+ conf = 'document-start: {present: true}'
self.check('---\n'
'first: document\n'
'...\n'
@@ -85,7 +85,7 @@ class DocumentStartTestCase(RuleTestCase):
'third: document\n', conf, problem=(4, 1, 'syntax'))
def test_directives(self):
- conf = 'document-start: {present: yes}'
+ conf = 'document-start: {present: true}'
self.check('%YAML 1.2\n'
'---\n'
'doc: ument\n'
diff --git a/tests/rules/test_indentation.py b/tests/rules/test_indentation.py
index cbbc979..2733ea7 100644
--- a/tests/rules/test_indentation.py
+++ b/tests/rules/test_indentation.py
@@ -549,7 +549,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf)
def test_one_space(self):
- conf = 'indentation: {spaces: 1, indent-sequences: no}'
+ conf = 'indentation: {spaces: 1, indent-sequences: false}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -562,7 +562,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n'
' date: 1991\n'
'...\n', conf)
- conf = 'indentation: {spaces: 1, indent-sequences: yes}'
+ conf = 'indentation: {spaces: 1, indent-sequences: true}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -577,7 +577,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf)
def test_two_spaces(self):
- conf = 'indentation: {spaces: 2, indent-sequences: no}'
+ conf = 'indentation: {spaces: 2, indent-sequences: false}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -590,7 +590,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n'
' date: 1991\n'
'...\n', conf)
- conf = 'indentation: {spaces: 2, indent-sequences: yes}'
+ conf = 'indentation: {spaces: 2, indent-sequences: true}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -605,7 +605,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf)
def test_three_spaces(self):
- conf = 'indentation: {spaces: 3, indent-sequences: no}'
+ conf = 'indentation: {spaces: 3, indent-sequences: false}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -618,7 +618,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n'
' date: 1991\n'
'...\n', conf)
- conf = 'indentation: {spaces: 3, indent-sequences: yes}'
+ conf = 'indentation: {spaces: 3, indent-sequences: true}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -632,7 +632,7 @@ class IndentationTestCase(RuleTestCase):
' date: 1991\n'
'...\n', conf)
- def test_consistent(self):
+ def test_consistent_spaces(self):
conf = ('indentation: {spaces: consistent,\n'
' indent-sequences: whatever}\n'
'document-start: disable\n')
@@ -713,6 +713,142 @@ class IndentationTestCase(RuleTestCase):
'- b\n'
'- c\n', conf)
+ def test_consistent_spaces_and_indent_sequences(self):
+ conf = 'indentation: {spaces: consistent, indent-sequences: true}'
+ self.check('---\n'
+ 'list one:\n'
+ '- 1\n'
+ '- 2\n'
+ '- 3\n'
+ 'list two:\n'
+ ' - a\n'
+ ' - b\n'
+ ' - c\n', conf, problem1=(3, 1))
+ self.check('---\n'
+ 'list one:\n'
+ ' - 1\n'
+ ' - 2\n'
+ ' - 3\n'
+ 'list two:\n'
+ ' - a\n'
+ ' - b\n'
+ ' - c\n', conf, problem1=(7, 5))
+ self.check('---\n'
+ 'list one:\n'
+ ' - 1\n'
+ ' - 2\n'
+ ' - 3\n'
+ 'list two:\n'
+ '- a\n'
+ '- b\n'
+ '- c\n', conf, problem1=(7, 1))
+
+ conf = 'indentation: {spaces: consistent, indent-sequences: false}'
+ self.check('---\n'
+ 'list one:\n'
+ '- 1\n'
+ '- 2\n'
+ '- 3\n'
+ 'list two:\n'
+ ' - a\n'
+ ' - b\n'
+ ' - c\n', conf, problem1=(7, 5))
+ self.check('---\n'
+ 'list one:\n'
+ '- 1\n'
+ '- 2\n'
+ '- 3\n'
+ 'list two:\n'
+ ' - a\n'
+ ' - b\n'
+ ' - c\n', conf, problem1=(7, 3))
+ self.check('---\n'
+ 'list one:\n'
+ ' - 1\n'
+ ' - 2\n'
+ ' - 3\n'
+ 'list two:\n'
+ '- a\n'
+ '- b\n'
+ '- c\n', conf, problem1=(3, 3))
+
+ conf = ('indentation: {spaces: consistent,\n'
+ ' indent-sequences: consistent}')
+ self.check('---\n'
+ 'list one:\n'
+ '- 1\n'
+ '- 2\n'
+ '- 3\n'
+ 'list two:\n'
+ ' - a\n'
+ ' - b\n'
+ ' - c\n', conf, problem1=(7, 5))
+ self.check('---\n'
+ 'list one:\n'
+ ' - 1\n'
+ ' - 2\n'
+ ' - 3\n'
+ 'list two:\n'
+ '- a\n'
+ '- b\n'
+ '- c\n', conf, problem1=(7, 1))
+ self.check('---\n'
+ 'list one:\n'
+ '- 1\n'
+ '- 2\n'
+ '- 3\n'
+ 'list two:\n'
+ '- a\n'
+ '- b\n'
+ '- c\n', conf)
+ self.check('---\n'
+ 'list one:\n'
+ ' - 1\n'
+ ' - 2\n'
+ ' - 3\n'
+ 'list two:\n'
+ ' - a\n'
+ ' - b\n'
+ ' - c\n', conf, problem1=(7, 5))
+
+ conf = 'indentation: {spaces: consistent, indent-sequences: whatever}'
+ self.check('---\n'
+ 'list one:\n'
+ '- 1\n'
+ '- 2\n'
+ '- 3\n'
+ 'list two:\n'
+ ' - a\n'
+ ' - b\n'
+ ' - c\n', conf)
+ self.check('---\n'
+ 'list one:\n'
+ ' - 1\n'
+ ' - 2\n'
+ ' - 3\n'
+ 'list two:\n'
+ '- a\n'
+ '- b\n'
+ '- c\n', conf)
+ self.check('---\n'
+ 'list one:\n'
+ '- 1\n'
+ '- 2\n'
+ '- 3\n'
+ 'list two:\n'
+ '- a\n'
+ '- b\n'
+ '- c\n', conf)
+ self.check('---\n'
+ 'list one:\n'
+ ' - 1\n'
+ ' - 2\n'
+ ' - 3\n'
+ 'list two:\n'
+ ' - a\n'
+ ' - b\n'
+ ' - c\n', conf, problem1=(7, 5))
+
def test_indent_sequences_whatever(self):
conf = 'indentation: {spaces: 4, indent-sequences: whatever}'
self.check('---\n'
@@ -1130,7 +1266,7 @@ class IndentationTestCase(RuleTestCase):
problem=(2, 3))
def test_multi_lines(self):
- conf = 'indentation: {spaces: consistent, indent-sequences: yes}'
+ conf = 'indentation: {spaces: consistent, indent-sequences: true}'
self.check('---\n'
'long_string: >\n'
' bla bla blah\n'
@@ -1438,7 +1574,7 @@ class IndentationTestCase(RuleTestCase):
'- !!map # Block collection\n'
' foo: bar\n', conf)
- conf = 'indentation: {spaces: consistent, indent-sequences: no}'
+ conf = 'indentation: {spaces: consistent, indent-sequences: false}'
self.check('---\n'
'sequence: !!seq\n'
'- entry\n'
@@ -1505,7 +1641,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_plain(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: no}\n'
+ ' check-multi-line-strings: false}\n'
'document-start: disable\n')
self.check('multi\n'
'line\n', conf)
@@ -1534,7 +1670,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_plain(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: yes}\n'
+ ' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('multi\n'
' line\n', conf, problem=(2, 2))
@@ -1557,7 +1693,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: no}\n'
+ ' check-multi-line-strings: false}\n'
'document-start: disable\n')
self.check('"multi\n'
' line"\n', conf)
@@ -1588,7 +1724,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: yes}\n'
+ ' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('"multi\n'
'line"\n', conf, problem=(2, 1))
@@ -1644,7 +1780,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_folded_style(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: no}\n'
+ ' check-multi-line-strings: false}\n'
'document-start: disable\n')
self.check('>\n'
' multi\n'
@@ -1682,7 +1818,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_folded_style(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: yes}\n'
+ ' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('>\n'
' multi\n'
@@ -1723,7 +1859,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_literal_style(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: no}\n'
+ ' check-multi-line-strings: false}\n'
'document-start: disable\n')
self.check('|\n'
' multi\n'
@@ -1761,7 +1897,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_literal_style(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: yes}\n'
+ ' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('|\n'
' multi\n'
@@ -1805,7 +1941,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_plain(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: yes}\n'
+ ' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('- long text: very "long"\n'
' \'string\' with\n'
@@ -1827,7 +1963,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_double_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: yes}\n'
+ ' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('- long text: "very \\"long\\"\n'
' \'string\' with\n'
@@ -1855,7 +1991,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_single_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: yes}\n'
+ ' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('- long text: \'very "long"\n'
' \'\'string\'\' with\n'
@@ -1883,7 +2019,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_folded(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: yes}\n'
+ ' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('- long text: >\n'
' very "long"\n'
@@ -1901,7 +2037,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_literal(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: yes}\n'
+ ' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('- long text: |\n'
' very "long"\n'
@@ -1919,7 +2055,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_consistent(self):
conf = ('indentation: {spaces: consistent,\n'
- ' check-multi-line-strings: yes}\n'
+ ' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('multi\n'
'line\n', conf)
diff --git a/tests/rules/test_line_length.py b/tests/rules/test_line_length.py
index be1115e..cab3e18 100644
--- a/tests/rules/test_line_length.py
+++ b/tests/rules/test_line_length.py
@@ -66,7 +66,7 @@ class LineLengthTestCase(RuleTestCase):
self.check('---\n' + 81 * ' ' + '\n', conf, problem=(2, 81))
def test_non_breakable_word(self):
- conf = 'line-length: {max: 20, allow-non-breakable-words: yes}'
+ conf = 'line-length: {max: 20, allow-non-breakable-words: true}'
self.check('---\n' + 30 * 'A' + '\n', conf)
self.check('---\n'
'this:\n'
@@ -91,7 +91,7 @@ class LineLengthTestCase(RuleTestCase):
'long_line: http://localhost/very/very/long/url\n', conf,
problem=(2, 21))
- conf = 'line-length: {max: 20, allow-non-breakable-words: no}'
+ conf = 'line-length: {max: 20, allow-non-breakable-words: false}'
self.check('---\n' + 30 * 'A' + '\n', conf, problem=(2, 21))
self.check('---\n'
'this:\n'
@@ -116,7 +116,7 @@ class LineLengthTestCase(RuleTestCase):
'long_line: http://localhost/very/very/long/url\n'
'...\n', conf, problem=(2, 21))
- conf = ('line-length: {max: 20, allow-non-breakable-words: yes}\n'
+ conf = ('line-length: {max: 20, allow-non-breakable-words: true}\n'
'trailing-spaces: disable')
self.check('---\n'
'loooooooooong+word+and+some+space+at+the+end \n',
@@ -124,7 +124,7 @@ class LineLengthTestCase(RuleTestCase):
def test_non_breakable_inline_mappings(self):
conf = 'line-length: {max: 20, ' \
- 'allow-non-breakable-inline-mappings: yes}'
+ 'allow-non-breakable-inline-mappings: true}'
self.check('---\n'
'long_line: http://localhost/very/very/long/url\n'
'long line: http://localhost/very/very/long/url\n', conf)
@@ -137,7 +137,7 @@ class LineLengthTestCase(RuleTestCase):
conf, problem1=(2, 21), problem2=(3, 21))
conf = ('line-length: {max: 20,'
- ' allow-non-breakable-inline-mappings: yes}\n'
+ ' allow-non-breakable-inline-mappings: true}\n'
'trailing-spaces: disable')
self.check('---\n'
'long_line: and+some+space+at+the+end \n',
@@ -150,7 +150,7 @@ class LineLengthTestCase(RuleTestCase):
conf, problem=(2, 21))
# See https://github.com/adrienverge/yamllint/issues/21
- conf = 'line-length: {allow-non-breakable-inline-mappings: yes}'
+ conf = 'line-length: {allow-non-breakable-inline-mappings: true}'
self.check('---\n'
'content: |\n'
' {% this line is' + 99 * ' really' + ' long %}\n',
diff --git a/tests/test_cli.py b/tests/test_cli.py
index da139a8..bec7078 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -23,61 +23,59 @@ import locale
import os
import pty
import shutil
-import tempfile
-import unittest
import sys
+try:
+ assert sys.version_info >= (2, 7)
+ import unittest
+except:
+ import unittest2 as unittest
from yamllint import cli
+from tests.common import build_temp_workspace
+
+@unittest.skipIf(sys.version_info < (2, 7), 'Python 2.6 not supported')
class CommandLineTestCase(unittest.TestCase):
- def setUp(self):
- self.wd = tempfile.mkdtemp(prefix='yamllint-tests-')
-
- # .yaml file at root
- with open(os.path.join(self.wd, 'a.yaml'), 'w') as f:
- f.write('---\n'
- '- 1 \n'
- '- 2')
-
- # .yml file at root
- open(os.path.join(self.wd, 'empty.yml'), 'w').close()
-
- # file in dir
- os.mkdir(os.path.join(self.wd, 'sub'))
- with open(os.path.join(self.wd, 'sub', 'ok.yaml'), 'w') as f:
- f.write('---\n'
- 'key: value\n')
-
- # file in very nested dir
- dir = self.wd
- for i in range(15):
- dir = os.path.join(dir, 's')
- os.mkdir(dir)
- with open(os.path.join(dir, 'file.yaml'), 'w') as f:
- f.write('---\n'
- 'key: value\n'
- 'key: other value\n')
-
- # empty dir
- os.mkdir(os.path.join(self.wd, 'empty-dir'))
-
- # non-YAML file
- with open(os.path.join(self.wd, 'no-yaml.json'), 'w') as f:
- f.write('---\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):
- shutil.rmtree(self.wd)
+ @classmethod
+ def setUpClass(cls):
+ super(CommandLineTestCase, cls).setUpClass()
+
+ cls.wd = build_temp_workspace({
+ # .yaml file at root
+ 'a.yaml': '---\n'
+ '- 1 \n'
+ '- 2',
+ # file with only one warning
+ 'warn.yaml': 'key: value\n',
+ # .yml file at root
+ 'empty.yml': '',
+ # file in dir
+ 'sub/ok.yaml': '---\n'
+ 'key: value\n',
+ # file in very nested dir
+ 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml': '---\n'
+ 'key: value\n'
+ 'key: other value\n',
+ # empty dir
+ 'empty-dir': [],
+ # non-YAML file
+ 'no-yaml.json': '---\n'
+ 'key: value\n',
+ # non-ASCII chars
+ 'non-ascii/utf-8': (
+ u'---\n'
+ u'- hétérogénéité\n'
+ u'# 19.99 €\n'
+ u'- お早う御座います。\n'
+ u'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'),
+ })
+
+ @classmethod
+ def tearDownClass(cls):
+ super(CommandLineTestCase, cls).tearDownClass()
+
+ shutil.rmtree(cls.wd)
def test_find_files_recursively(self):
self.assertEqual(
@@ -85,7 +83,8 @@ class CommandLineTestCase(unittest.TestCase):
[os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'),
- os.path.join(self.wd, 'sub/ok.yaml')],
+ os.path.join(self.wd, 'sub/ok.yaml'),
+ os.path.join(self.wd, 'warn.yaml')],
)
items = [os.path.join(self.wd, 'sub/ok.yaml'),
@@ -140,8 +139,11 @@ class CommandLineTestCase(unittest.TestCase):
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
- self.assertRegexpMatches(err, r'^Options --config-file and '
- r'--config-data cannot be used')
+ self.assertRegexpMatches(
+ err.splitlines()[-1],
+ r'^yamllint: error: argument -d\/--config-data: '
+ r'not allowed with argument -c\/--config-file$'
+ )
def test_run_with_bad_config(self):
sys.stdout, sys.stderr = StringIO(), StringIO()
@@ -247,6 +249,24 @@ class CommandLineTestCase(unittest.TestCase):
'(new-line-at-end-of-file)\n') % (file, file))
self.assertEqual(err, '')
+ def test_run_one_warning(self):
+ file = os.path.join(self.wd, 'warn.yaml')
+
+ sys.stdout, sys.stderr = StringIO(), StringIO()
+ with self.assertRaises(SystemExit) as ctx:
+ cli.run(('-f', 'parsable', file))
+
+ self.assertEqual(ctx.exception.code, 0)
+
+ def test_run_warning_in_strict_mode(self):
+ file = os.path.join(self.wd, 'warn.yaml')
+
+ sys.stdout, sys.stderr = StringIO(), StringIO()
+ with self.assertRaises(SystemExit) as ctx:
+ cli.run(('-f', 'parsable', '--strict', file))
+
+ self.assertEqual(ctx.exception.code, 2)
+
def test_run_one_ok_file(self):
file = os.path.join(self.wd, 'sub', 'ok.yaml')
diff --git a/tests/test_config.py b/tests/test_config.py
index ef93ec7..a23da10 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -14,10 +14,24 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import unittest
-
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from io import StringIO
+import os
+import shutil
+import sys
+try:
+ assert sys.version_info >= (2, 7)
+ import unittest
+except:
+ import unittest2 as unittest
+
+from yamllint import cli
from yamllint import config
+from tests.common import build_temp_workspace
+
class SimpleConfigTestCase(unittest.TestCase):
def test_parse_config(self):
@@ -30,7 +44,7 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertEqual(new.rules['colons']['max-spaces-before'], 0)
self.assertEqual(new.rules['colons']['max-spaces-after'], 1)
- self.assertEqual(len(new.enabled_rules()), 1)
+ self.assertEqual(len(new.enabled_rules(None)), 1)
def test_invalid_conf(self):
with self.assertRaises(config.YamlLintConfigError):
@@ -62,6 +76,45 @@ class SimpleConfigTestCase(unittest.TestCase):
' max-spaces-after: 1\n'
' abcdef: yes\n')
+ def test_yes_no_for_booleans(self):
+ c = config.YamlLintConfig('rules:\n'
+ ' indentation:\n'
+ ' spaces: 2\n'
+ ' indent-sequences: true\n'
+ ' check-multi-line-strings: false\n')
+ self.assertEqual(c.rules['indentation']['indent-sequences'], True)
+ self.assertEqual(c.rules['indentation']['check-multi-line-strings'],
+ False)
+
+ c = config.YamlLintConfig('rules:\n'
+ ' indentation:\n'
+ ' spaces: 2\n'
+ ' indent-sequences: yes\n'
+ ' check-multi-line-strings: false\n')
+ self.assertEqual(c.rules['indentation']['indent-sequences'], True)
+ self.assertEqual(c.rules['indentation']['check-multi-line-strings'],
+ False)
+
+ c = config.YamlLintConfig('rules:\n'
+ ' indentation:\n'
+ ' spaces: 2\n'
+ ' indent-sequences: whatever\n'
+ ' check-multi-line-strings: false\n')
+ self.assertEqual(c.rules['indentation']['indent-sequences'],
+ 'whatever')
+ self.assertEqual(c.rules['indentation']['check-multi-line-strings'],
+ False)
+
+ with self.assertRaisesRegexp(
+ config.YamlLintConfigError,
+ 'invalid config: option "indent-sequences" of "indentation" '
+ 'should be in '):
+ c = config.YamlLintConfig('rules:\n'
+ ' indentation:\n'
+ ' spaces: 2\n'
+ ' indent-sequences: YES!\n'
+ ' check-multi-line-strings: false\n')
+
def test_validate_rule_conf(self):
class Rule(object):
ID = 'fake'
@@ -131,7 +184,7 @@ class ExtendedConfigTestCase(unittest.TestCase):
self.assertEqual(new.rules['colons']['max-spaces-after'], 1)
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
- self.assertEqual(len(new.enabled_rules()), 2)
+ self.assertEqual(len(new.enabled_rules(None)), 2)
def test_extend_remove_rule(self):
old = config.YamlLintConfig('rules:\n'
@@ -148,7 +201,7 @@ class ExtendedConfigTestCase(unittest.TestCase):
self.assertEqual(new.rules['colons'], False)
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
- self.assertEqual(len(new.enabled_rules()), 1)
+ self.assertEqual(len(new.enabled_rules(None)), 1)
def test_extend_edit_rule(self):
old = config.YamlLintConfig('rules:\n'
@@ -168,7 +221,7 @@ class ExtendedConfigTestCase(unittest.TestCase):
self.assertEqual(new.rules['colons']['max-spaces-after'], 4)
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
- self.assertEqual(len(new.enabled_rules()), 2)
+ self.assertEqual(len(new.enabled_rules(None)), 2)
def test_extend_reenable_rule(self):
old = config.YamlLintConfig('rules:\n'
@@ -186,7 +239,7 @@ class ExtendedConfigTestCase(unittest.TestCase):
self.assertEqual(new.rules['colons']['max-spaces-after'], 1)
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
- self.assertEqual(len(new.enabled_rules()), 2)
+ self.assertEqual(len(new.enabled_rules(None)), 2)
class ExtendedLibraryConfigTestCase(unittest.TestCase):
@@ -231,3 +284,94 @@ class ExtendedLibraryConfigTestCase(unittest.TestCase):
self.assertEqual(sorted(new.rules.keys()), sorted(old.rules.keys()))
for rule in new.rules:
self.assertEqual(new.rules[rule], old.rules[rule])
+
+
+class IgnorePathConfigTestCase(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(IgnorePathConfigTestCase, cls).setUpClass()
+
+ bad_yaml = ('---\n'
+ '- key: val1\n'
+ ' key: val2\n'
+ '- trailing space \n'
+ '- lonely hyphen\n')
+
+ cls.wd = build_temp_workspace({
+ 'bin/file.lint-me-anyway.yaml': bad_yaml,
+ 'bin/file.yaml': bad_yaml,
+ 'file-at-root.yaml': bad_yaml,
+ 'file.dont-lint-me.yaml': bad_yaml,
+ 'ign-dup/file.yaml': bad_yaml,
+ 'ign-dup/sub/dir/file.yaml': bad_yaml,
+ 'ign-trail/file.yaml': bad_yaml,
+ 'include/ign-dup/sub/dir/file.yaml': bad_yaml,
+ 's/s/ign-trail/file.yaml': bad_yaml,
+ 's/s/ign-trail/s/s/file.yaml': bad_yaml,
+ 's/s/ign-trail/s/s/file2.lint-me-anyway.yaml': bad_yaml,
+
+ '.yamllint': 'ignore: |\n'
+ ' *.dont-lint-me.yaml\n'
+ ' /bin/\n'
+ ' !/bin/*.lint-me-anyway.yaml\n'
+ '\n'
+ 'extends: default\n'
+ '\n'
+ 'rules:\n'
+ ' key-duplicates:\n'
+ ' ignore: |\n'
+ ' /ign-dup\n'
+ ' trailing-spaces:\n'
+ ' ignore: |\n'
+ ' ign-trail\n'
+ ' !*.lint-me-anyway.yaml\n',
+ })
+
+ cls.backup_wd = os.getcwd()
+ os.chdir(cls.wd)
+
+ @classmethod
+ def tearDownClass(cls):
+ super(IgnorePathConfigTestCase, cls).tearDownClass()
+
+ os.chdir(cls.backup_wd)
+
+ shutil.rmtree(cls.wd)
+
+ @unittest.skipIf(sys.version_info < (2, 7), 'Python 2.6 not supported')
+ def test_run_with_ignored_path(self):
+ sys.stdout = StringIO()
+ with self.assertRaises(SystemExit):
+ cli.run(('-f', 'parsable', '.'))
+
+ out = sys.stdout.getvalue()
+ out = '\n'.join(sorted(out.splitlines()))
+
+ keydup = '[error] duplication of key "key" in mapping (key-duplicates)'
+ trailing = '[error] trailing spaces (trailing-spaces)'
+ hyphen = '[error] too many spaces after hyphen (hyphens)'
+
+ self.assertEqual(out, '\n'.join((
+ './bin/file.lint-me-anyway.yaml:3:3: ' + keydup,
+ './bin/file.lint-me-anyway.yaml:4:17: ' + trailing,
+ './bin/file.lint-me-anyway.yaml:5:5: ' + hyphen,
+ './file-at-root.yaml:3:3: ' + keydup,
+ './file-at-root.yaml:4:17: ' + trailing,
+ './file-at-root.yaml:5:5: ' + hyphen,
+ './ign-dup/file.yaml:4:17: ' + trailing,
+ './ign-dup/file.yaml:5:5: ' + hyphen,
+ './ign-dup/sub/dir/file.yaml:4:17: ' + trailing,
+ './ign-dup/sub/dir/file.yaml:5:5: ' + hyphen,
+ './ign-trail/file.yaml:3:3: ' + keydup,
+ './ign-trail/file.yaml:5:5: ' + hyphen,
+ './include/ign-dup/sub/dir/file.yaml:3:3: ' + keydup,
+ './include/ign-dup/sub/dir/file.yaml:4:17: ' + trailing,
+ './include/ign-dup/sub/dir/file.yaml:5:5: ' + hyphen,
+ './s/s/ign-trail/file.yaml:3:3: ' + keydup,
+ './s/s/ign-trail/file.yaml:5:5: ' + hyphen,
+ './s/s/ign-trail/s/s/file.yaml:3:3: ' + keydup,
+ './s/s/ign-trail/s/s/file.yaml:5:5: ' + hyphen,
+ './s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:3:3: ' + keydup,
+ './s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:4:17: ' + trailing,
+ './s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:5:5: ' + hyphen,
+ )))
diff --git a/tests/test_linter.py b/tests/test_linter.py
index db126ad..edd803f 100644
--- a/tests/test_linter.py
+++ b/tests/test_linter.py
@@ -15,8 +15,12 @@
# along with this program. If not, see .
import io
-
-import unittest
+import sys
+try:
+ assert sys.version_info >= (2, 7)
+ import unittest
+except:
+ import unittest2 as unittest
from yamllint.config import YamlLintConfig
from yamllint import linter
diff --git a/tests/test_module.py b/tests/test_module.py
new file mode 100644
index 0000000..678f40c
--- /dev/null
+++ b/tests/test_module.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2017 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 .
+
+import os
+import shutil
+import subprocess
+import tempfile
+import sys
+try:
+ assert sys.version_info >= (2, 7)
+ import unittest
+except:
+ import unittest2 as unittest
+
+
+@unittest.skipIf(sys.version_info < (2, 7), 'Python 2.6 not supported')
+class ModuleTestCase(unittest.TestCase):
+ def setUp(self):
+ self.wd = tempfile.mkdtemp(prefix='yamllint-tests-')
+
+ # file with only one warning
+ with open(os.path.join(self.wd, 'warn.yaml'), 'w') as f:
+ f.write('key: value\n')
+
+ # file in dir
+ os.mkdir(os.path.join(self.wd, 'sub'))
+ with open(os.path.join(self.wd, 'sub', 'nok.yaml'), 'w') as f:
+ f.write('---\n'
+ 'list: [ 1, 1, 2, 3, 5, 8] \n')
+
+ def tearDown(self):
+ shutil.rmtree(self.wd)
+
+ def test_run_module_no_args(self):
+ with self.assertRaises(subprocess.CalledProcessError) as ctx:
+ subprocess.check_output(['python', '-m', 'yamllint'],
+ stderr=subprocess.STDOUT)
+ self.assertEqual(ctx.exception.returncode, 2)
+ self.assertRegexpMatches(ctx.exception.output.decode(),
+ r'^usage: yamllint')
+
+ def test_run_module_on_bad_dir(self):
+ with self.assertRaises(subprocess.CalledProcessError) as ctx:
+ subprocess.check_output(['python', '-m', 'yamllint',
+ '/does/not/exist'],
+ stderr=subprocess.STDOUT)
+ self.assertRegexpMatches(ctx.exception.output.decode(),
+ r'No such file or directory')
+
+ def test_run_module_on_file(self):
+ out = subprocess.check_output(
+ ['python', '-m', 'yamllint', os.path.join(self.wd, 'warn.yaml')])
+ lines = out.decode().splitlines()
+ self.assertIn('/warn.yaml', lines[0])
+ self.assertEqual('\n'.join(lines[1:]),
+ ' 1:1 warning missing document start "---"'
+ ' (document-start)\n')
+
+ def test_run_module_on_dir(self):
+ with self.assertRaises(subprocess.CalledProcessError) as ctx:
+ subprocess.check_output(['python', '-m', 'yamllint', self.wd])
+ self.assertEqual(ctx.exception.returncode, 1)
+
+ files = ctx.exception.output.decode().split('\n\n')
+ self.assertIn(
+ '/warn.yaml\n'
+ ' 1:1 warning missing document start "---"'
+ ' (document-start)',
+ files[0])
+ self.assertIn(
+ '/sub/nok.yaml\n'
+ ' 2:9 error too many spaces inside brackets'
+ ' (brackets)\n'
+ ' 2:27 error trailing spaces (trailing-spaces)',
+ files[1])
diff --git a/tests/test_parser.py b/tests/test_parser.py
index e40b9ac..c5c51d8 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -14,7 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import unittest
+import sys
+try:
+ assert sys.version_info >= (2, 7)
+ import unittest
+except:
+ import unittest2 as unittest
import yaml
diff --git a/tests/test_spec_examples.py b/tests/test_spec_examples.py
index 04d6ea7..f3e9239 100644
--- a/tests/test_spec_examples.py
+++ b/tests/test_spec_examples.py
@@ -48,6 +48,7 @@ from tests.common import RuleTestCase
class SpecificationTestCase(RuleTestCase):
rule_id = None
+
conf_general = ('document-start: disable\n'
'comments: {min-spaces-from-content: 1}\n'
'braces: {min-spaces-inside: 1, max-spaces-inside: 1}\n'
@@ -66,7 +67,7 @@ conf_overrides = {
'example-2.18': ('empty-lines: {max-end: 1}\n'),
'example-2.19': ('empty-lines: {max-end: 1}\n'),
'example-2.28': ('empty-lines: {max-end: 3}\n'),
- 'example-5.3': ('indentation: {indent-sequences: no}\n'
+ 'example-5.3': ('indentation: {indent-sequences: false}\n'
'colons: {max-spaces-before: 1}\n'),
'example-6.4': ('trailing-spaces: disable\n'),
'example-6.5': ('trailing-spaces: disable\n'),
@@ -114,11 +115,11 @@ conf_overrides = {
'example-8.14': ('colons: {max-spaces-before: 1}\n'),
'example-8.16': ('indentation: {spaces: 1}\n'),
'example-8.17': ('indentation: disable\n'),
- 'example-8.20': ('indentation: {indent-sequences: no}\n'
+ 'example-8.20': ('indentation: {indent-sequences: false}\n'
'colons: {max-spaces-before: 1}\n'),
'example-8.22': ('indentation: disable\n'),
'example-10.1': ('colons: {max-spaces-before: 2}\n'),
- 'example-10.2': ('indentation: {indent-sequences: no}\n'),
+ 'example-10.2': ('indentation: {indent-sequences: false}\n'),
'example-10.8': ('truthy: disable\n'),
'example-10.9': ('truthy: disable\n'),
}
@@ -133,6 +134,7 @@ def _gen_test(buffer, conf):
self.check(buffer, conf)
return test
+
# The following tests are blacklisted (i.e. will not be checked against
# yamllint), because pyyaml is currently not able to parse the contents
# (using yaml.parse()).
diff --git a/yamllint/__init__.py b/yamllint/__init__.py
index 1219cca..e53dce1 100644
--- a/yamllint/__init__.py
+++ b/yamllint/__init__.py
@@ -22,7 +22,7 @@ indentation, etc."""
APP_NAME = 'yamllint'
-APP_VERSION = '1.5.0'
+APP_VERSION = '1.8.1'
APP_DESCRIPTION = __doc__
__author__ = u'Adrien Vergé'
diff --git a/yamllint/__main__.py b/yamllint/__main__.py
new file mode 100644
index 0000000..bc16534
--- /dev/null
+++ b/yamllint/__main__.py
@@ -0,0 +1,4 @@
+from yamllint.cli import run
+
+if __name__ == '__main__':
+ run()
diff --git a/yamllint/cli.py b/yamllint/cli.py
index 9e3e5f3..41695a3 100644
--- a/yamllint/cli.py
+++ b/yamllint/cli.py
@@ -15,6 +15,7 @@
# along with this program. If not, see .
from __future__ import print_function
+
import os.path
import sys
@@ -22,6 +23,7 @@ import argparse
from yamllint import APP_DESCRIPTION, APP_NAME, APP_VERSION
from yamllint.config import YamlLintConfig, YamlLintConfigError
+from yamllint.linter import PROBLEM_LEVELS
from yamllint import linter
@@ -77,14 +79,20 @@ def run(argv=None):
description=APP_DESCRIPTION)
parser.add_argument('files', metavar='FILE_OR_DIR', nargs='+',
help='files to check')
- parser.add_argument('-c', '--config-file', dest='config_file',
- action='store', help='path to a custom configuration')
- parser.add_argument('-d', '--config-data', dest='config_data',
- action='store',
- help='custom configuration (as YAML source)')
+ config_group = parser.add_mutually_exclusive_group()
+ config_group.add_argument('-c', '--config-file', dest='config_file',
+ action='store',
+ help='path to a custom configuration')
+ config_group.add_argument('-d', '--config-data', dest='config_data',
+ action='store',
+ help='custom configuration (as YAML source)')
parser.add_argument('-f', '--format',
choices=('parsable', 'standard'), default='standard',
help='format for parsing output')
+ parser.add_argument('-s', '--strict',
+ action='store_true',
+ help='return non-zero exit code on warnings '
+ 'as well as errors')
parser.add_argument('-v', '--version', action='version',
version='%s %s' % (APP_NAME, APP_VERSION))
@@ -92,11 +100,6 @@ def run(argv=None):
args = parser.parse_args(argv)
- if args.config_file is not None and args.config_data is not None:
- print('Options --config-file and --config-data cannot be used '
- '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(
@@ -121,13 +124,14 @@ def run(argv=None):
print(e, file=sys.stderr)
sys.exit(-1)
- return_code = 0
+ max_level = 0
for file in find_files_recursively(args.files):
+ filepath = file[2:] if file.startswith('./') else file
try:
first = True
with open(file) as f:
- for problem in linter.run(f, conf):
+ for problem in linter.run(f, conf, filepath):
if args.format == 'parsable':
print(Format.parsable(problem, file))
elif sys.stdout.isatty():
@@ -143,13 +147,19 @@ def run(argv=None):
print(Format.standard(problem, file))
- if return_code == 0 and problem.level == 'error':
- return_code = 1
+ max_level = max(max_level, PROBLEM_LEVELS[problem.level])
if not first and args.format != 'parsable':
print('')
except EnvironmentError as e:
print(e, file=sys.stderr)
- return_code = -1
+ sys.exit(-1)
+
+ if max_level == PROBLEM_LEVELS['error']:
+ return_code = 1
+ elif max_level == PROBLEM_LEVELS['warning']:
+ return_code = 2 if args.strict else 0
+ else:
+ return_code = 0
sys.exit(return_code)
diff --git a/yamllint/conf/default.yaml b/yamllint/conf/default.yaml
index e4d720b..c7c4da4 100644
--- a/yamllint/conf/default.yaml
+++ b/yamllint/conf/default.yaml
@@ -4,9 +4,13 @@ rules:
braces:
min-spaces-inside: 0
max-spaces-inside: 0
+ min-spaces-inside-empty: -1
+ max-spaces-inside-empty: -1
brackets:
min-spaces-inside: 0
max-spaces-inside: 0
+ min-spaces-inside-empty: -1
+ max-spaces-inside-empty: -1
colons:
max-spaces-before: 0
max-spaces-after: 1
@@ -16,14 +20,14 @@ rules:
max-spaces-after: 1
comments:
level: warning
- require-starting-space: yes
+ require-starting-space: true
min-spaces-from-content: 2
comments-indentation:
level: warning
document-end: disable
document-start:
level: warning
- present: yes
+ present: true
empty-lines:
max: 2
max-start: 0
@@ -32,13 +36,13 @@ rules:
max-spaces-after: 1
indentation:
spaces: consistent
- indent-sequences: yes
- check-multi-line-strings: no
+ indent-sequences: true
+ check-multi-line-strings: false
key-duplicates: enable
line-length:
max: 80
- allow-non-breakable-words: yes
- allow-non-breakable-inline-mappings: no
+ allow-non-breakable-words: true
+ allow-non-breakable-inline-mappings: false
new-line-at-end-of-file: enable
new-lines:
type: unix
diff --git a/yamllint/conf/relaxed.yaml b/yamllint/conf/relaxed.yaml
index fb2cdaf..83f5340 100644
--- a/yamllint/conf/relaxed.yaml
+++ b/yamllint/conf/relaxed.yaml
@@ -25,5 +25,5 @@ rules:
indent-sequences: consistent
line-length:
level: warning
- allow-non-breakable-inline-mappings: yes
+ allow-non-breakable-inline-mappings: true
truthy: disable
diff --git a/yamllint/config.py b/yamllint/config.py
index 48ea380..fb5a161 100644
--- a/yamllint/config.py
+++ b/yamllint/config.py
@@ -16,6 +16,7 @@
import os.path
+import pathspec
import yaml
import yamllint.rules
@@ -29,6 +30,8 @@ class YamlLintConfig(object):
def __init__(self, content=None, file=None):
assert (content is None) ^ (file is None)
+ self.ignore = None
+
if file is not None:
with open(file) as f:
content = f.read()
@@ -36,9 +39,14 @@ class YamlLintConfig(object):
self.parse(content)
self.validate()
- def enabled_rules(self):
+ def is_file_ignored(self, filepath):
+ return self.ignore and self.ignore.match_file(filepath)
+
+ def enabled_rules(self, filepath):
return [yamllint.rules.get(id) for id, val in self.rules.items()
- if val is not False]
+ if val is not False and (
+ filepath is None or 'ignore' not in val or
+ not val['ignore'].match_file(filepath))]
def extend(self, base_config):
assert isinstance(base_config, YamlLintConfig)
@@ -53,6 +61,9 @@ class YamlLintConfig(object):
self.rules = base_config.rules
+ if base_config.ignore is not None:
+ self.ignore = base_config.ignore
+
def parse(self, raw_content):
try:
conf = yaml.safe_load(raw_content)
@@ -73,6 +84,13 @@ class YamlLintConfig(object):
except Exception as e:
raise YamlLintConfigError('invalid config: %s' % e)
+ if 'ignore' in conf:
+ if type(conf['ignore']) != str:
+ raise YamlLintConfigError(
+ 'invalid config: ignore should be a list of patterns')
+ self.ignore = pathspec.PathSpec.from_lines(
+ 'gitwildmatch', conf['ignore'].splitlines())
+
def validate(self):
for id in self.rules:
try:
@@ -90,6 +108,14 @@ def validate_rule_conf(rule, conf):
conf = {}
if type(conf) == dict:
+ if ('ignore' in conf and
+ type(conf['ignore']) != pathspec.pathspec.PathSpec):
+ if type(conf['ignore']) != str:
+ raise YamlLintConfigError(
+ 'invalid config: ignore should be a list of patterns')
+ conf['ignore'] = pathspec.PathSpec.from_lines(
+ 'gitwildmatch', conf['ignore'].splitlines())
+
if 'level' not in conf:
conf['level'] = 'error'
elif conf['level'] not in ('error', 'warning'):
@@ -98,7 +124,7 @@ def validate_rule_conf(rule, conf):
options = getattr(rule, 'CONF', {})
for optkey in conf:
- if optkey == 'level':
+ if optkey in ('ignore', 'level'):
continue
if optkey not in options:
raise YamlLintConfigError(
diff --git a/yamllint/linter.py b/yamllint/linter.py
index b546e8a..c8eff8d 100644
--- a/yamllint/linter.py
+++ b/yamllint/linter.py
@@ -21,6 +21,16 @@ import yaml
from yamllint import parser
+PROBLEM_LEVELS = {
+ 0: None,
+ 1: 'warning',
+ 2: 'error',
+ None: 0,
+ 'warning': 1,
+ 'error': 2,
+}
+
+
class LintProblem(object):
"""Represents a linting problem found by yamllint."""
def __init__(self, line, column, desc='', rule=None):
@@ -53,8 +63,8 @@ class LintProblem(object):
return '%d:%d: %s' % (self.line, self.column, self.message)
-def get_costemic_problems(buffer, conf):
- rules = conf.enabled_rules()
+def get_cosmetic_problems(buffer, conf, filepath):
+ rules = conf.enabled_rules(filepath)
# Split token rules from line rules
token_rules = [r for r in rules if r.TYPE == 'token']
@@ -175,7 +185,7 @@ def get_syntax_error(buffer):
return problem
-def _run(buffer, conf):
+def _run(buffer, conf, filepath):
assert hasattr(buffer, '__getitem__'), \
'_run() argument must be a buffer, not a stream'
@@ -183,7 +193,7 @@ def _run(buffer, conf):
# right line
syntax_error = get_syntax_error(buffer)
- for problem in get_costemic_problems(buffer, conf):
+ for problem in get_cosmetic_problems(buffer, conf, filepath):
# Insert the syntax error (if any) at the right place...
if (syntax_error and syntax_error.line <= problem.line and
syntax_error.column <= problem.column):
@@ -205,7 +215,7 @@ def _run(buffer, conf):
yield syntax_error
-def run(input, conf):
+def run(input, conf, filepath=None):
"""Lints a YAML source.
Returns a generator of LintProblem objects.
@@ -213,11 +223,14 @@ def run(input, conf):
:param input: buffer, string or stream to read from
:param conf: yamllint configuration object
"""
+ if conf.is_file_ignored(filepath):
+ return ()
+
if type(input) in (type(b''), type(u'')): # compat with Python 2 & 3
- return _run(input, conf)
+ return _run(input, conf, filepath)
elif hasattr(input, 'read'): # Python 2's file or Python 3's io.IOBase
# We need to have everything in memory to parse correctly
content = input.read()
- return _run(content, conf)
+ return _run(content, conf, filepath)
else:
raise TypeError('input should be a string or a stream')
diff --git a/yamllint/rules/braces.py b/yamllint/rules/braces.py
index 55f465e..59d9558 100644
--- a/yamllint/rules/braces.py
+++ b/yamllint/rules/braces.py
@@ -23,6 +23,10 @@ Use this rule to control the number of spaces inside braces (``{`` and ``}``).
braces.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside
braces.
+* ``min-spaces-inside-empty`` defines the minimal number of spaces required
+ inside empty braces.
+* ``max-spaces-inside-empty`` defines the maximal number of spaces allowed
+ inside empty braces.
.. rubric:: Examples
@@ -59,6 +63,30 @@ Use this rule to control the number of spaces inside braces (``{`` and ``}``).
::
object: {key1: 4, key2: 8 }
+
+#. With ``braces: {min-spaces-inside-empty: 0, max-spaces-inside-empty: 0}``
+
+ the following code snippet would **PASS**:
+ ::
+
+ object: {}
+
+ the following code snippet would **FAIL**:
+ ::
+
+ object: { }
+
+#. With ``braces: {min-spaces-inside-empty: 1, max-spaces-inside-empty: -1}``
+
+ the following code snippet would **PASS**:
+ ::
+
+ object: { }
+
+ the following code snippet would **FAIL**:
+ ::
+
+ object: {}
"""
@@ -70,11 +98,27 @@ from yamllint.rules.common import spaces_after, spaces_before
ID = 'braces'
TYPE = 'token'
CONF = {'min-spaces-inside': int,
- 'max-spaces-inside': int}
+ 'max-spaces-inside': int,
+ 'min-spaces-inside-empty': int,
+ 'max-spaces-inside-empty': int}
def check(conf, token, prev, next, nextnext, context):
- if isinstance(token, yaml.FlowMappingStartToken):
+ if (isinstance(token, yaml.FlowMappingStartToken) and
+ isinstance(next, yaml.FlowMappingEndToken)):
+ problem = spaces_after(token, prev, next,
+ min=(conf['min-spaces-inside-empty']
+ if conf['min-spaces-inside-empty'] != -1
+ else conf['min-spaces-inside']),
+ max=(conf['max-spaces-inside-empty']
+ if conf['max-spaces-inside-empty'] != -1
+ else conf['max-spaces-inside']),
+ min_desc='too few spaces inside empty braces',
+ max_desc='too many spaces inside empty braces')
+ if problem is not None:
+ yield problem
+
+ elif isinstance(token, yaml.FlowMappingStartToken):
problem = spaces_after(token, prev, next,
min=conf['min-spaces-inside'],
max=conf['max-spaces-inside'],
diff --git a/yamllint/rules/brackets.py b/yamllint/rules/brackets.py
index ae0e744..33bdaa9 100644
--- a/yamllint/rules/brackets.py
+++ b/yamllint/rules/brackets.py
@@ -24,6 +24,10 @@ Use this rule to control the number of spaces inside brackets (``[`` and
brackets.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside
brackets.
+* ``min-spaces-inside-empty`` defines the minimal number of spaces required
+ inside empty brackets.
+* ``max-spaces-inside-empty`` defines the maximal number of spaces allowed
+ inside empty brackets.
.. rubric:: Examples
@@ -60,6 +64,30 @@ Use this rule to control the number of spaces inside brackets (``[`` and
::
object: [1, 2, abc ]
+
+#. With ``brackets: {min-spaces-inside-empty: 0, max-spaces-inside-empty: 0}``
+
+ the following code snippet would **PASS**:
+ ::
+
+ object: []
+
+ the following code snippet would **FAIL**:
+ ::
+
+ object: [ ]
+
+#. With ``brackets: {min-spaces-inside-empty: 1, max-spaces-inside-empty: -1}``
+
+ the following code snippet would **PASS**:
+ ::
+
+ object: [ ]
+
+ the following code snippet would **FAIL**:
+ ::
+
+ object: []
"""
@@ -71,11 +99,28 @@ from yamllint.rules.common import spaces_after, spaces_before
ID = 'brackets'
TYPE = 'token'
CONF = {'min-spaces-inside': int,
- 'max-spaces-inside': int}
+ 'max-spaces-inside': int,
+ 'min-spaces-inside-empty': int,
+ 'max-spaces-inside-empty': int}
def check(conf, token, prev, next, nextnext, context):
- if isinstance(token, yaml.FlowSequenceStartToken):
+ if (isinstance(token, yaml.FlowSequenceStartToken) and
+ isinstance(next, yaml.FlowSequenceEndToken)):
+ problem = spaces_after(token, prev, next,
+ min=(conf['min-spaces-inside-empty']
+ if conf['min-spaces-inside-empty'] != -1
+ else conf['min-spaces-inside']),
+ max=(conf['max-spaces-inside-empty']
+ if conf['max-spaces-inside-empty'] != -1
+ else conf['max-spaces-inside']),
+ min_desc='too few spaces inside empty brackets',
+ max_desc=('too many spaces inside empty '
+ 'brackets'))
+ if problem is not None:
+ yield problem
+
+ elif isinstance(token, yaml.FlowSequenceStartToken):
problem = spaces_after(token, prev, next,
min=conf['min-spaces-inside'],
max=conf['max-spaces-inside'],
diff --git a/yamllint/rules/comments.py b/yamllint/rules/comments.py
index 33fbcbe..69da045 100644
--- a/yamllint/rules/comments.py
+++ b/yamllint/rules/comments.py
@@ -20,14 +20,14 @@ Use this rule to control the position and formatting of comments.
.. rubric:: Options
* Use ``require-starting-space`` to require a space character right after the
- ``#``. Set to ``yes`` to enable, ``no`` to disable.
+ ``#``. Set to ``true`` to enable, ``false`` to disable.
* ``min-spaces-from-content`` is used to visually separate inline comments from
content. It defines the minimal required number of spaces between a comment
and its preceding content.
.. rubric:: Examples
-#. With ``comments: {require-starting-space: yes}``
+#. With ``comments: {require-starting-space: true}``
the following code snippet would **PASS**:
::
diff --git a/yamllint/rules/document_end.py b/yamllint/rules/document_end.py
index c6e7356..34323e4 100644
--- a/yamllint/rules/document_end.py
+++ b/yamllint/rules/document_end.py
@@ -19,12 +19,12 @@ Use this rule to require or forbid the use of document end marker (``...``).
.. rubric:: Options
-* Set ``present`` to ``yes`` when the document end marker is required, or to
- ``no`` when it is forbidden.
+* Set ``present`` to ``true`` when the document end marker is required, or to
+ ``false`` when it is forbidden.
.. rubric:: Examples
-#. With ``document-end: {present: yes}``
+#. With ``document-end: {present: true}``
the following code snippet would **PASS**:
::
@@ -49,7 +49,7 @@ Use this rule to require or forbid the use of document end marker (``...``).
- is: another one
...
-#. With ``document-end: {present: no}``
+#. With ``document-end: {present: false}``
the following code snippet would **PASS**:
::
diff --git a/yamllint/rules/document_start.py b/yamllint/rules/document_start.py
index 5955a7f..090260a 100644
--- a/yamllint/rules/document_start.py
+++ b/yamllint/rules/document_start.py
@@ -19,12 +19,12 @@ Use this rule to require or forbid the use of document start marker (``---``).
.. rubric:: Options
-* Set ``present`` to ``yes`` when the document start marker is required, or to
- ``no`` when it is forbidden.
+* Set ``present`` to ``true`` when the document start marker is required, or to
+ ``false`` when it is forbidden.
.. rubric:: Examples
-#. With ``document-start: {present: yes}``
+#. With ``document-start: {present: true}``
the following code snippet would **PASS**:
::
@@ -45,7 +45,7 @@ Use this rule to require or forbid the use of document start marker (``---``).
- this
- is: another one
-#. With ``document-start: {present: no}``
+#. With ``document-start: {present: false}``
the following code snippet would **PASS**:
::
diff --git a/yamllint/rules/indentation.py b/yamllint/rules/indentation.py
index 9388e4f..432c23c 100644
--- a/yamllint/rules/indentation.py
+++ b/yamllint/rules/indentation.py
@@ -25,12 +25,12 @@ Use this rule to control the indentation.
same within the file.
* ``indent-sequences`` defines whether block sequences should be indented or
not (when in a mapping, this indentation is not mandatory -- some people
- perceive the ``-`` as part of the indentation). Possible values: ``yes``,
- ``no``, ``whatever`` and ``consistent``. ``consistent`` requires either all
- block sequences to be indented, or none to be. ``whatever`` means either
+ perceive the ``-`` as part of the indentation). Possible values: ``true``,
+ ``false``, ``whatever`` and ``consistent``. ``consistent`` requires either
+ all block sequences to be indented, or none to be. ``whatever`` means either
indenting or not indenting individual block sequences is OK.
* ``check-multi-line-strings`` defines whether to lint indentation in
- multi-line strings. Set to ``yes`` to enable, ``no`` to disable.
+ multi-line strings. Set to ``true`` to enable, ``false`` to disable.
.. rubric:: Examples
@@ -99,7 +99,7 @@ Use this rule to control the indentation.
Russian:
dolls
-#. With ``indentation: {spaces: 2, indent-sequences: no}``
+#. With ``indentation: {spaces: 2, indent-sequences: false}``
the following code snippet would **PASS**:
::
@@ -152,7 +152,7 @@ Use this rule to control the indentation.
- spaghetti
- sauce
-#. With ``indentation: {spaces: 4, check-multi-line-strings: yes}``
+#. With ``indentation: {spaces: 4, check-multi-line-strings: true}``
the following code snippet would **PASS**:
::
@@ -469,7 +469,19 @@ def _check(conf, token, prev, next, nextnext, context):
if context['indent-sequences'] is False:
indent = context['stack'][-1].indent
elif context['indent-sequences'] is True:
- indent = detect_indent(context['stack'][-1].indent, next)
+ if (context['spaces'] == 'consistent' and
+ next.start_mark.column -
+ context['stack'][-1].indent == 0):
+ # In this case, the block sequence item is not indented
+ # (while it should be), but we don't know yet the
+ # indentation it should have (because `spaces` is
+ # `consistent` and its value has not been computed yet
+ # -- this is probably the beginning of the document).
+ # So we choose an arbitrary value (2).
+ indent = 2
+ else:
+ indent = detect_indent(context['stack'][-1].indent,
+ next)
else: # 'whatever' or 'consistent'
if next.start_mark.column == context['stack'][-1].indent:
# key:
diff --git a/yamllint/rules/line_length.py b/yamllint/rules/line_length.py
index a0c0b37..e87972e 100644
--- a/yamllint/rules/line_length.py
+++ b/yamllint/rules/line_length.py
@@ -22,7 +22,7 @@ Use this rule to set a limit to lines length.
* ``max`` defines the maximal (inclusive) length of lines.
* ``allow-non-breakable-words`` is used to allow non breakable words (without
spaces inside) to overflow the limit. This is useful for long URLs, for
- instance. Use ``yes`` to allow, ``no`` to forbid.
+ instance. Use ``true`` to allow, ``false`` to forbid.
* ``allow-non-breakable-inline-mappings`` implies ``allow-non-breakable-words``
and extends it to also allow non-breakable words in inline mappings.
@@ -44,7 +44,7 @@ Use this rule to set a limit to lines length.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
-#. With ``line-length: {max: 60, allow-non-breakable-words: yes}``
+#. With ``line-length: {max: 60, allow-non-breakable-words: true}``
the following code snippet would **PASS**:
::
@@ -68,15 +68,15 @@ Use this rule to set a limit to lines length.
- foobar: http://localhost/very/very/very/very/very/very/very/very/long/url
-#. With ``line-length: {max: 60, allow-non-breakable-words: yes,
- allow-non-breakable-inline-mappings: yes}``
+#. With ``line-length: {max: 60, allow-non-breakable-words: true,
+ allow-non-breakable-inline-mappings: true}``
the following code snippet would **PASS**:
::
- foobar: http://localhost/very/very/very/very/very/very/very/very/long/url
-#. With ``line-length: {max: 60, allow-non-breakable-words: no}``
+#. With ``line-length: {max: 60, allow-non-breakable-words: false}``
the following code snippet would **FAIL**:
::
diff --git a/yamllint/rules/truthy.py b/yamllint/rules/truthy.py
index 8b541a7..c453c86 100644
--- a/yamllint/rules/truthy.py
+++ b/yamllint/rules/truthy.py
@@ -17,9 +17,9 @@
"""
Use this rule to forbid truthy values that are not quoted nor explicitly typed.
-This would prevent YAML parsers to tranform ``[yes, FALSE, Off]`` into ``[true,
-false, false]`` or ``{y: 1, yes: 2, on: 3, true: 4, True: 5}`` into ``{y: 1,
-true: 5}``.
+This would prevent YAML parsers from transforming ``[yes, FALSE, Off]`` into
+``[true, false, false]`` or ``{y: 1, yes: 2, on: 3, true: 4, True: 5}`` into
+``{y: 1, true: 5}``.
.. rubric:: Examples