Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9709bc6e6 | ||
|
|
5060917e40 | ||
|
|
a052cf7dba | ||
|
|
ae33716529 | ||
|
|
df26cc0438 | ||
|
|
342d7b49dd | ||
|
|
7d638d47b9 | ||
|
|
db116eaaaf | ||
|
|
30dfa78923 | ||
|
|
4ae829c062 | ||
|
|
400aa084da | ||
|
|
a825645cbe | ||
|
|
1764e32def | ||
|
|
d6a81f1b23 | ||
|
|
38d14c7314 | ||
|
|
ff1c9ad221 | ||
|
|
4b2b57aa32 | ||
|
|
51b6d8377f | ||
|
|
f507319419 | ||
|
|
c0c8534501 | ||
|
|
2b26cbc56b | ||
|
|
c037d3e586 | ||
|
|
228c47ab77 | ||
|
|
413d7a8e4e | ||
|
|
c332c8e3d4 | ||
|
|
ea67ba3394 | ||
|
|
a7dbfb08b3 |
@@ -5,9 +5,10 @@ python:
|
||||
- 3.3
|
||||
- 3.4
|
||||
- 3.5
|
||||
- 3.6
|
||||
- nightly
|
||||
install:
|
||||
- pip install pyyaml flake8 coveralls
|
||||
- pip install pyyaml flake8 flake8-import-order coveralls
|
||||
- pip install .
|
||||
script:
|
||||
- flake8 .
|
||||
|
||||
21
README.rst
21
README.rst
@@ -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! <https://yamllint.readthedocs.io/>`_
|
||||
|
||||
License
|
||||
|
||||
@@ -114,3 +114,57 @@ 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
|
||||
<https://pypi.python.org/pypi/pathspec>`_ 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/*
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -9,8 +9,12 @@ text editor.
|
||||
Vim
|
||||
---
|
||||
|
||||
Assuming that the `syntastic <https://github.com/scrooloose/syntastic>`_ plugin
|
||||
is installed, add to your ``.vimrc``:
|
||||
Assuming that the `ALE <https://github.com/w0rp/ale>`_ plugin is
|
||||
installed, yamllint is supported by default. It is automatically enabled when
|
||||
editing YAML files.
|
||||
|
||||
If you instead use the `syntastic <https://github.com/scrooloose/syntastic>`_
|
||||
plugin, add this to your ``.vimrc``:
|
||||
|
||||
::
|
||||
|
||||
@@ -23,6 +27,12 @@ Assuming that the `neomake <https://github.com/benekastah/neomake>`_ plugin is
|
||||
installed, yamllint is supported by default. It is automatically enabled when
|
||||
editing YAML files.
|
||||
|
||||
Emacs
|
||||
-----
|
||||
|
||||
If you are `flycheck <https://github.com/flycheck/flycheck>`_ user, you can use
|
||||
`flycheck-yamllint <https://github.com/krzysztof-magosa/flycheck-yamllint>`_ integration.
|
||||
|
||||
Other text editors
|
||||
------------------
|
||||
|
||||
|
||||
11
hooks.yaml
Normal file
11
hooks.yaml
Normal file
@@ -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)$
|
||||
@@ -1,2 +1,5 @@
|
||||
[bdist_wheel]
|
||||
universal = 1
|
||||
|
||||
[flake8]
|
||||
import-order-style = pep8
|
||||
|
||||
2
setup.py
2
setup.py
@@ -46,7 +46,7 @@ setup(
|
||||
entry_points={'console_scripts': ['yamllint=yamllint.cli:run']},
|
||||
package_data={'yamllint': ['conf/*.yaml'],
|
||||
'tests': ['yaml-1.2-spec-examples/*']},
|
||||
install_requires=['pyyaml'],
|
||||
install_requires=['pathspec', 'pyyaml'],
|
||||
tests_require=['nose'],
|
||||
test_suite='nose.collector',
|
||||
)
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import yaml
|
||||
@@ -49,3 +51,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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -23,65 +23,54 @@ import locale
|
||||
import os
|
||||
import pty
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from yamllint import cli
|
||||
|
||||
from tests.common import build_temp_workspace
|
||||
|
||||
|
||||
class CommandLineTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.wd = tempfile.mkdtemp(prefix='yamllint-tests-')
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(CommandLineTestCase, cls).setUpClass()
|
||||
|
||||
# .yaml file at root
|
||||
with open(os.path.join(self.wd, 'a.yaml'), 'w') as f:
|
||||
f.write('---\n'
|
||||
'- 1 \n'
|
||||
'- 2')
|
||||
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'),
|
||||
})
|
||||
|
||||
# file with only one warning
|
||||
with open(os.path.join(self.wd, 'warn.yaml'), 'w') as f:
|
||||
f.write('key: value\n')
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(CommandLineTestCase, cls).tearDownClass()
|
||||
|
||||
# .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)
|
||||
shutil.rmtree(cls.wd)
|
||||
|
||||
def test_find_files_recursively(self):
|
||||
self.assertEqual(
|
||||
@@ -145,8 +134,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()
|
||||
|
||||
@@ -14,10 +14,20 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import 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 +40,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):
|
||||
@@ -170,7 +180,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'
|
||||
@@ -187,7 +197,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'
|
||||
@@ -207,7 +217,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'
|
||||
@@ -225,7 +235,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):
|
||||
@@ -270,3 +280,93 @@ 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)
|
||||
|
||||
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,
|
||||
)))
|
||||
|
||||
82
tests/test_module.py
Normal file
82
tests/test_module.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# -*- 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
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])
|
||||
@@ -22,7 +22,7 @@ indentation, etc."""
|
||||
|
||||
|
||||
APP_NAME = 'yamllint'
|
||||
APP_VERSION = '1.6.0'
|
||||
APP_VERSION = '1.8.0'
|
||||
APP_DESCRIPTION = __doc__
|
||||
|
||||
__author__ = u'Adrien Vergé'
|
||||
|
||||
4
yamllint/__main__.py
Normal file
4
yamllint/__main__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from yamllint.cli import run
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -15,6 +15,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
@@ -78,11 +79,13 @@ 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')
|
||||
@@ -97,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(
|
||||
@@ -129,10 +127,11 @@ def run(argv=None):
|
||||
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():
|
||||
|
||||
@@ -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,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(
|
||||
|
||||
@@ -63,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']
|
||||
@@ -185,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'
|
||||
|
||||
@@ -193,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):
|
||||
@@ -215,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.
|
||||
@@ -223,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')
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user