Compare commits

..

1 Commits

Author SHA1 Message Date
Adrien Vergé
5cad95ba4b Prototype: yaml-fix-indentation 2016-04-18 16:34:18 +02:00
57 changed files with 466 additions and 2777 deletions

View File

@@ -1,11 +0,0 @@
---
# 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
types: [file, yaml]

View File

@@ -1,20 +1,17 @@
---
language: python
python:
- 2.6
- 2.7
- 3.3
- 3.4
- 3.5
- 3.6
- nightly
install:
- pip install pyyaml flake8 flake8-import-order coveralls
- if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install unittest2; fi
- pip install pyyaml flake8 coveralls
- pip install .
script:
- if [[ $TRAVIS_PYTHON_VERSION != 2.6 ]]; then flake8 .; fi
- yamllint --strict $(git ls-files '*.yaml' '*.yml')
- flake8 .
- yamllint $(git ls-files '*.yaml' '*.yml')
- coverage run --source=yamllint setup.py test
after_success:
coveralls

View File

@@ -1,28 +0,0 @@
Changelog
=========
1.9.0 (2017-10-16)
------------------
- Add a new `key-ordering` rule
- Fix indentation rule for key following empty list
1.8.2 (2017-10-10)
------------------
- Be clearer about the `ignore` conf type
- Update pre-commit hook file
- Add documentation for pre-commit
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

View File

@@ -16,7 +16,7 @@ indentation, etc.
:target: https://coveralls.io/github/adrienverge/yamllint?branch=master
:alt: Code coverage status
.. image:: https://readthedocs.org/projects/yamllint/badge/?version=latest
:target: https://yamllint.readthedocs.io/en/latest/?badge=latest
:target: http://yamllint.readthedocs.org/en/latest/?badge=latest
:alt: Documentation status
Written in Python (compatible with Python 2 & 3).
@@ -24,10 +24,10 @@ Written in Python (compatible with Python 2 & 3).
Documentation
-------------
https://yamllint.readthedocs.io/
http://yamllint.readthedocs.org/
Overview
--------
Short overview
--------------
Screenshot
^^^^^^^^^^
@@ -44,12 +44,19 @@ On Fedora / CentOS:
sudo dnf install yamllint
On Debian 8+ / Ubuntu 16.04+:
On Debian 9+ / Ubuntu 16.04+:
.. code:: bash
sudo apt-get install yamllint
On older Debian / Ubuntu versions:
.. code:: bash
sudo add-apt-repository -y ppa:adrienverge/ppa && sudo apt-get update
sudo apt-get install yamllint
Alternatively using pip, the Python package manager:
.. code:: bash
@@ -75,19 +82,15 @@ Usage
yamllint -d relaxed file.yaml
# Use a custom lint configuration
yamllint -c /path/to/myconfig file-to-lint.yaml
yamllint -c ~/myconfig file.yml
.. code:: bash
# Output a parsable format (for syntax checking in editors like Vim, emacs...)
yamllint -f parsable file.yaml
`Read more in the complete documentation! <https://yamllint.readthedocs.io/>`_
Features
^^^^^^^^
Here is a yamllint configuration file example:
Configuration example
^^^^^^^^^^^^^^^^^^^^^
.. code:: yaml
@@ -101,48 +104,3 @@ Here is a yamllint configuration file example:
# don't bother me with this rule
indentation: disable
Within a YAML file, special comments can be used to disable checks for a single
line:
.. code:: yaml
This line is waaaaaaaaaay too long # yamllint disable-line
or for a whole block:
.. code:: yaml
# yamllint disable rule:colons
- Lorem : ipsum
dolor : sit amet,
consectetur : adipiscing elit
# yamllint enable
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
-------
`GPL version 3 <LICENSE>`_

View File

@@ -4,7 +4,6 @@
import sys
import os
from unittest.mock import MagicMock
sys.path.insert(0, os.path.abspath('..')) # noqa
@@ -41,15 +40,3 @@ htmlhelp_basename = 'yamllintdoc'
man_pages = [
('index', 'yamllint', '', [u'Adrien Vergé'], 1)
]
# -- Build with sphinx automodule without needing to install third-party libs
class Mock(MagicMock):
@classmethod
def __getattr__(cls, name):
return MagicMock()
MOCK_MODULES = ['pathspec', 'yaml']
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)

View File

@@ -1,24 +1,16 @@
Configuration
=============
yamllint uses a set of :doc:`rules <rules>` to check source files for problems.
Each rule is independent from the others, and can be enabled, disabled or
tweaked. All these settings can be gathered in a configuration file.
yamllint uses a set of *rules* to check sources files for problems. Each rule is
independent from the others, and can be enabled, disabled or tweaked. All these
settings can be gathered in a configuration file.
To use a custom configuration file, use the ``-c`` option:
To use a custom configuration file, either name it ``.yamllint`` in your working
directory, or use the ``-c`` option:
.. code:: bash
yamllint -c /path/to/myconfig file-to-lint.yaml
If ``-c`` is not provided, yamllint will look for a configuration file in the
following locations (by order of preference):
- ``.yamllint`` in the current working directory
- ``$XDG_CONFIG_HOME/yamllint/config``
- ``~/.config/yamllint/config``
Finally if no config file is found, the default configuration is applied.
yamllint -c ~/myconfig file.yaml
Default configuration
---------------------
@@ -31,10 +23,7 @@ Unless told otherwise, yamllint uses its ``default`` configuration:
Details on rules can be found on :doc:`the rules page <rules>`.
There is another pre-defined configuration named ``relaxed``. As its name
suggests, it is more tolerant:
.. literalinclude:: ../yamllint/conf/relaxed.yaml
:language: yaml
suggests, it is more tolerant.
It can be chosen using:
@@ -102,69 +91,8 @@ 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).
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
<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/*
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).

View File

@@ -1,75 +0,0 @@
Disable with comments
=====================
Disabling checks for a specific line
------------------------------------
To prevent yamllint from reporting problems for a specific line, add a directive
comment (``# yamllint disable-line ...``) on that line, or on the line above.
For instance:
.. code-block:: yaml
# The following mapping contains the same key twice,
# but I know what I'm doing:
key: value 1
key: value 2 # yamllint disable-line rule:key-duplicates
- This line is waaaaaaaaaay too long but yamllint will not report anything about it. # yamllint disable-line rule:line-length
This line will be checked by yamllint.
or:
.. code-block:: yaml
# The following mapping contains the same key twice,
# but I know what I'm doing:
key: value 1
# yamllint disable-line rule:key-duplicates
key: value 2
# yamllint disable-line rule:line-length
- This line is waaaaaaaaaay too long but yamllint will not report anything about it.
This line will be checked by yamllint.
It is possible, although not recommend, to disabled **all** rules for a
specific line:
.. code-block:: yaml
# yamllint disable-line
- { all : rules ,are disabled for this line}
If you need to disable multiple rules, it is allowed to chain rules like this:
``# yamllint disable-line rule:hyphens rule:commas rule:indentation``.
Disabling checks for all (or part of) the file
----------------------------------------------
To prevent yamllint from reporting problems for the whole file, or for a block of
lines within the file, use ``# yamllint disable ...`` and ``# yamllint enable
...`` directive comments. For instance:
.. code-block:: yaml
# yamllint disable rule:colons
- Lorem : ipsum
dolor : sit amet,
consectetur : adipiscing elit
# yamllint enable rule:colons
- rest of the document...
It is possible, although not recommend, to disabled **all** rules:
.. code-block:: yaml
# yamllint disable
- Lorem :
ipsum:
dolor : [ sit,amet]
- consectetur : adipiscing elit
# yamllint enable
If you need to disable multiple rules, it is allowed to chain rules like this:
``# yamllint disable rule:hyphens rule:commas rule:indentation``.

View File

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

View File

@@ -1,17 +0,0 @@
Integration with other software
===============================
Integration with pre-commit
---------------------------
You can integrate yamllint in `pre-commit <http://pre-commit.com/>`_ tool.
Here is an example, to add in your .pre-commit-config.yaml
.. code:: yaml
---
# Update the sha variable with the release version that you want, from the yamllint repo
- repo: https://github.com/adrienverge/yamllint.git
sha: v1.8.1
hooks:
- id: yamllint

View File

@@ -10,7 +10,7 @@ On Fedora / CentOS:
sudo dnf install yamllint
On Debian 8+ / Ubuntu 16.04+:
On Debian 9+ / Ubuntu 16.04+:
.. code:: bash

View File

@@ -74,11 +74,6 @@ key-duplicates
.. automodule:: yamllint.rules.key_duplicates
key-ordering
--------------
.. automodule:: yamllint.rules.key_ordering
line-length
-----------
@@ -98,8 +93,3 @@ trailing-spaces
---------------
.. automodule:: yamllint.rules.trailing_spaces
truthy
---------------
.. automodule:: yamllint.rules.truthy

View File

@@ -9,12 +9,8 @@ text editor.
Vim
---
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``:
Assuming that the `syntastic <https://github.com/scrooloose/syntastic>`_ plugin
is installed, add to your ``.vimrc``:
::
@@ -27,12 +23,6 @@ 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
------------------

View File

@@ -1,5 +0,0 @@
[bdist_wheel]
universal = 1
[flake8]
import-order-style = pep8

View File

@@ -42,10 +42,11 @@ setup(
'Topic :: Software Development :: Testing',
],
packages=find_packages(exclude=['tests', 'tests.*']),
packages=find_packages(),
entry_points={'console_scripts': ['yamllint=yamllint.cli:run']},
package_data={'yamllint': ['conf/*.yaml'],
'tests': ['yaml-1.2-spec-examples/*']},
install_requires=['pathspec >=0.5.3', 'pyyaml'],
test_suite='tests',
install_requires=['pyyaml'],
tests_require=['nose'],
test_suite='nose.collector',
)

View File

@@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import locale
locale.setlocale(locale.LC_ALL, 'C')

View File

@@ -14,14 +14,7 @@
# 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 sys
try:
assert sys.version_info >= (2, 7)
import unittest
except:
import unittest2 as unittest
import unittest
import yaml
@@ -56,21 +49,3 @@ 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

View File

@@ -32,19 +32,11 @@ class ColonTestCase(RuleTestCase):
'dict7: { a: 1, b, c: 3 }\n', conf)
def test_min_spaces(self):
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')
conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 0}'
self.check('---\n'
'dict: {}\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')
conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 1}'
self.check('---\n'
'dict: {}\n', conf, problem=(2, 8))
self.check('---\n'
@@ -60,11 +52,7 @@ class ColonTestCase(RuleTestCase):
' b\n'
'}\n', conf)
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')
conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 3}'
self.check('---\n'
'dict: { a: 1, b }\n', conf,
problem1=(2, 9), problem2=(2, 17))
@@ -72,11 +60,7 @@ class ColonTestCase(RuleTestCase):
'dict: { a: 1, b }\n', conf)
def test_max_spaces(self):
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')
conf = 'braces: {max-spaces-inside: 0, min-spaces-inside: -1}'
self.check('---\n'
'dict: {}\n', conf)
self.check('---\n'
@@ -95,11 +79,7 @@ class ColonTestCase(RuleTestCase):
' b\n'
'}\n', conf)
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')
conf = 'braces: {max-spaces-inside: 3, min-spaces-inside: -1}'
self.check('---\n'
'dict: { a: 1, b }\n', conf)
self.check('---\n'
@@ -107,11 +87,7 @@ class ColonTestCase(RuleTestCase):
problem1=(2, 11), problem2=(2, 23))
def test_min_and_max_spaces(self):
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')
conf = 'braces: {max-spaces-inside: 0, min-spaces-inside: 0}'
self.check('---\n'
'dict: {}\n', conf)
self.check('---\n'
@@ -119,169 +95,14 @@ class ColonTestCase(RuleTestCase):
self.check('---\n'
'dict: { a: 1, b}\n', conf, problem=(2, 10))
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')
conf = 'braces: {max-spaces-inside: 1, min-spaces-inside: 1}'
self.check('---\n'
'dict: {a: 1, b, c: 3 }\n', conf, problem=(2, 8))
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')
conf = 'braces: {max-spaces-inside: 2, min-spaces-inside: 0}'
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)

View File

@@ -32,19 +32,11 @@ class ColonTestCase(RuleTestCase):
'array7: [ a, b, c ]\n', conf)
def test_min_spaces(self):
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')
conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 0}'
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')
conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 1}'
self.check('---\n'
'array: []\n', conf, problem=(2, 9))
self.check('---\n'
@@ -59,11 +51,7 @@ class ColonTestCase(RuleTestCase):
' b\n'
']\n', conf)
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')
conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 3}'
self.check('---\n'
'array: [ a, b ]\n', conf,
problem1=(2, 10), problem2=(2, 15))
@@ -71,11 +59,7 @@ class ColonTestCase(RuleTestCase):
'array: [ a, b ]\n', conf)
def test_max_spaces(self):
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')
conf = 'brackets: {max-spaces-inside: 0, min-spaces-inside: -1}'
self.check('---\n'
'array: []\n', conf)
self.check('---\n'
@@ -94,11 +78,7 @@ class ColonTestCase(RuleTestCase):
' b\n'
']\n', conf)
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')
conf = 'brackets: {max-spaces-inside: 3, min-spaces-inside: -1}'
self.check('---\n'
'array: [ a, b ]\n', conf)
self.check('---\n'
@@ -106,11 +86,7 @@ class ColonTestCase(RuleTestCase):
problem1=(2, 12), problem2=(2, 21))
def test_min_and_max_spaces(self):
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')
conf = 'brackets: {max-spaces-inside: 0, min-spaces-inside: 0}'
self.check('---\n'
'array: []\n', conf)
self.check('---\n'
@@ -118,169 +94,14 @@ class ColonTestCase(RuleTestCase):
self.check('---\n'
'array: [ a, b]\n', conf, problem=(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')
conf = 'brackets: {max-spaces-inside: 1, min-spaces-inside: 1}'
self.check('---\n'
'array: [a, b, c ]\n', conf, problem=(2, 9))
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')
conf = 'brackets: {max-spaces-inside: 2, min-spaces-inside: 0}'
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)

View File

@@ -35,15 +35,11 @@ class CommentsTestCase(RuleTestCase):
' #comment 3 bis\n'
' # comment 3 ter\n'
'\n'
'################################\n'
'## comment 4\n'
'##comment 5\n'
'\n'
'string: "Une longue phrase." # this is French\n', conf)
def test_starting_space(self):
conf = ('comments:\n'
' require-starting-space: true\n'
' require-starting-space: yes\n'
' min-spaces-from-content: -1\n'
'comments-indentation: disable\n')
self.check('---\n'
@@ -56,11 +52,7 @@ class CommentsTestCase(RuleTestCase):
'# comment 2\n'
'# comment 3\n'
' # comment 3 bis\n'
' # comment 3 ter\n'
'\n'
'################################\n'
'## comment 4\n'
'## comment 5\n', conf)
' # comment 3 ter\n', conf)
self.check('---\n'
'#comment\n'
'\n'
@@ -71,18 +63,13 @@ class CommentsTestCase(RuleTestCase):
'# comment 2\n'
'#comment 3\n'
' #comment 3 bis\n'
' # comment 3 ter\n'
'\n'
'################################\n'
'## comment 4\n'
'##comment 5\n', conf,
' # comment 3 ter\n', conf,
problem1=(2, 2), problem2=(6, 13),
problem3=(9, 2), problem4=(10, 4),
problem5=(15, 3))
problem4=(9, 2), problem5=(10, 4))
def test_spaces_from_content(self):
conf = ('comments:\n'
' require-starting-space: false\n'
' require-starting-space: no\n'
' min-spaces-from-content: 2\n')
self.check('---\n'
'# comment\n'
@@ -104,7 +91,7 @@ class CommentsTestCase(RuleTestCase):
def test_both(self):
conf = ('comments:\n'
' require-starting-space: true\n'
' require-starting-space: yes\n'
' min-spaces-from-content: 2\n'
'comments-indentation: disable\n')
self.check('---\n'
@@ -119,22 +106,17 @@ class CommentsTestCase(RuleTestCase):
' #comment 3 bis\n'
' # comment 3 ter\n'
'\n'
'################################\n'
'## comment 4\n'
'##comment 5\n'
'\n'
'string: "Une longue phrase." # this is French\n', conf,
problem1=(2, 2),
problem2=(4, 7),
problem3=(6, 11), problem4=(6, 12),
problem5=(9, 2),
problem6=(10, 4),
problem7=(15, 3),
problem8=(17, 30))
problem7=(13, 30))
def test_empty_comment(self):
conf = ('comments:\n'
' require-starting-space: true\n'
' require-starting-space: yes\n'
' min-spaces-from-content: 2\n')
self.check('---\n'
'# This is paragraph 1.\n'
@@ -146,21 +128,13 @@ class CommentsTestCase(RuleTestCase):
def test_first_line(self):
conf = ('comments:\n'
' require-starting-space: true\n'
' require-starting-space: yes\n'
' min-spaces-from-content: 2\n')
self.check('# comment\n', conf)
def test_last_line(self):
conf = ('comments:\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'
'#', conf)
def test_multi_line_scalar(self):
conf = ('comments:\n'
' require-starting-space: true\n'
' require-starting-space: yes\n'
' min-spaces-from-content: 2\n'
'trailing-spaces: disable\n')
self.check('---\n'

View File

@@ -58,7 +58,7 @@ class CommentsIndentationTestCase(RuleTestCase):
'# line 2\n', conf, problem=(2, 2))
self.check('---\n'
' # line 1\n'
' # line 2\n', conf, problem1=(2, 3))
' # line 2\n', conf, problem1=(2, 3), problem2=(3, 3))
self.check('---\n'
'obj:\n'
' # normal\n'
@@ -102,13 +102,13 @@ class CommentsIndentationTestCase(RuleTestCase):
' a: 1\n'
' # b: 2\n'
'# this object is useless\n'
'obj2: "no"\n', conf)
'obj2: no\n', conf)
self.check('---\n'
'obj1:\n'
' a: 1\n'
'# this object is useless\n'
' # b: 2\n'
'obj2: "no"\n', conf, problem=(5, 3))
'obj2: no\n', conf, problem=(5, 3))
self.check('---\n'
'obj1:\n'
' a: 1\n'
@@ -143,15 +143,3 @@ class CommentsIndentationTestCase(RuleTestCase):
'# hey\n'
'# normal\n'
' #\n', conf, problem=(4, 2))
def test_inline_comment(self):
conf = 'comments-indentation: enable'
self.check('---\n'
'- a # inline\n'
'# ok\n', conf)
self.check('---\n'
'- a # inline\n'
' # not ok\n', conf, problem=(3, 2))
self.check('---\n'
' # not ok\n'
'- a # inline\n', conf, problem=(2, 2))

View File

@@ -18,7 +18,8 @@ import unittest
import yaml
from yamllint.rules.common import get_line_indent
from yamllint.rules.common import (Comment, get_line_indent,
get_comments_between_tokens)
class CommonTestCase(unittest.TestCase):
@@ -42,3 +43,54 @@ class CommonTestCase(unittest.TestCase):
self.assertEqual(get_line_indent(tokens[i]), 0)
for i in (13, 16, 18, 22, 24):
self.assertEqual(get_line_indent(tokens[i]), 2)
def check_comments(self, buffer, *expected):
yaml_loader = yaml.BaseLoader(buffer)
comments = []
next = yaml_loader.peek_token()
while next is not None:
curr = yaml_loader.get_token()
next = yaml_loader.peek_token()
for comment in get_comments_between_tokens(curr, next):
comments.append(comment)
self.assertEqual(comments, list(expected))
def test_get_comments_between_tokens(self):
self.check_comments('# comment\n',
Comment(1, 1, '# comment', 0))
self.check_comments('---\n'
'# comment\n'
'...\n',
Comment(2, 1, '# comment', 0))
self.check_comments('---\n'
'# no newline char',
Comment(2, 1, '# no newline char', 0))
self.check_comments('# just comment',
Comment(1, 1, '# just comment', 0))
self.check_comments('\n'
' # indented comment\n',
Comment(2, 4, '# indented comment', 0))
self.check_comments('\n'
'# trailing spaces \n',
Comment(2, 1, '# trailing spaces ', 0))
self.check_comments('# comment one\n'
'\n'
'key: val # key=val\n'
'\n'
'# this is\n'
'# a block \n'
'# comment\n'
'\n'
'other:\n'
' - foo # equals\n'
' # bar\n',
Comment(1, 1, '# comment one', 0),
Comment(3, 11, '# key=val', 0),
Comment(5, 1, '# this is', 0),
Comment(6, 1, '# a block ', 0),
Comment(7, 1, '# comment', 0),
Comment(10, 10, '# equals', 0),
Comment(11, 10, '# bar', 0))

View File

@@ -31,7 +31,7 @@ class DocumentEndTestCase(RuleTestCase):
' document: end\n', conf)
def test_required(self):
conf = 'document-end: {present: true}'
conf = 'document-end: {present: yes}'
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: false}'
conf = 'document-end: {present: no}'
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: true}\n'
conf = ('document-end: {present: yes}\n'
'document-start: disable\n')
self.check('---\n'
'first: document\n'

View File

@@ -28,7 +28,7 @@ class DocumentStartTestCase(RuleTestCase):
'key: val\n', conf)
def test_required(self):
conf = ('document-start: {present: true}\n'
conf = ('document-start: {present: yes}\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: false}\n'
conf = ('document-start: {present: no}\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: true}'
conf = 'document-start: {present: yes}'
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: true}'
conf = 'document-start: {present: yes}'
self.check('%YAML 1.2\n'
'---\n'
'doc: ument\n'

View File

@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.common import RuleTestCase
from yamllint.parser import token_or_comment_generator, Comment
from yamllint.parser import token_generator
from yamllint.rules.indentation import check
@@ -38,8 +38,7 @@ class IndentationStackTestCase(RuleTestCase):
'check-multi-line-strings': False}
context = {}
output = ''
for elem in [t for t in token_or_comment_generator(source)
if not isinstance(t, Comment)]:
for elem in token_generator(source):
list(check(conf, elem.curr, elem.prev, elem.next, elem.nextnext,
context))
@@ -549,7 +548,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf)
def test_one_space(self):
conf = 'indentation: {spaces: 1, indent-sequences: false}'
conf = 'indentation: {spaces: 1, indent-sequences: no}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -562,7 +561,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n'
' date: 1991\n'
'...\n', conf)
conf = 'indentation: {spaces: 1, indent-sequences: true}'
conf = 'indentation: {spaces: 1, indent-sequences: yes}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -577,7 +576,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf)
def test_two_spaces(self):
conf = 'indentation: {spaces: 2, indent-sequences: false}'
conf = 'indentation: {spaces: 2, indent-sequences: no}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -589,11 +588,8 @@ class IndentationTestCase(RuleTestCase):
' date: 1969\n'
' - name: Linux\n'
' date: 1991\n'
' k4:\n'
' -\n'
' k5: v3\n'
'...\n', conf)
conf = 'indentation: {spaces: 2, indent-sequences: true}'
conf = 'indentation: {spaces: 2, indent-sequences: yes}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -608,7 +604,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf)
def test_three_spaces(self):
conf = 'indentation: {spaces: 3, indent-sequences: false}'
conf = 'indentation: {spaces: 3, indent-sequences: no}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -621,7 +617,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n'
' date: 1991\n'
'...\n', conf)
conf = 'indentation: {spaces: 3, indent-sequences: true}'
conf = 'indentation: {spaces: 3, indent-sequences: yes}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -635,7 +631,7 @@ class IndentationTestCase(RuleTestCase):
' date: 1991\n'
'...\n', conf)
def test_consistent_spaces(self):
def test_consistent(self):
conf = ('indentation: {spaces: consistent,\n'
' indent-sequences: whatever}\n'
'document-start: disable\n')
@@ -716,142 +712,6 @@ 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'
@@ -1211,20 +1071,6 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n'
' date: 1991\n'
'...\n', conf, problem=(5, 10, 'syntax'))
conf = 'indentation: {spaces: 2, indent-sequences: true}'
self.check('---\n'
'a:\n'
'-\n' # empty list
'b: c\n'
'...\n', conf, problem=(3, 1))
conf = 'indentation: {spaces: 2, indent-sequences: consistent}'
self.check('---\n'
'a:\n'
' -\n' # empty list
'b:\n'
'-\n'
'c: d\n'
'...\n', conf, problem=(5, 1))
def test_over_indented(self):
conf = 'indentation: {spaces: 2, indent-sequences: consistent}'
@@ -1281,23 +1127,9 @@ class IndentationTestCase(RuleTestCase):
' - subel\n'
'...\n', conf,
problem=(2, 3))
conf = 'indentation: {spaces: 2, indent-sequences: false}'
self.check('---\n'
'a:\n'
' -\n' # empty list
'b: c\n'
'...\n', conf, problem=(3, 3))
conf = 'indentation: {spaces: 2, indent-sequences: consistent}'
self.check('---\n'
'a:\n'
'-\n' # empty list
'b:\n'
' -\n'
'c: d\n'
'...\n', conf, problem=(5, 3))
def test_multi_lines(self):
conf = 'indentation: {spaces: consistent, indent-sequences: true}'
conf = 'indentation: {spaces: consistent, indent-sequences: yes}'
self.check('---\n'
'long_string: >\n'
' bla bla blah\n'
@@ -1605,7 +1437,7 @@ class IndentationTestCase(RuleTestCase):
'- !!map # Block collection\n'
' foo: bar\n', conf)
conf = 'indentation: {spaces: consistent, indent-sequences: false}'
conf = 'indentation: {spaces: consistent, indent-sequences: no}'
self.check('---\n'
'sequence: !!seq\n'
'- entry\n'
@@ -1672,7 +1504,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_plain(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: false}\n'
' check-multi-line-strings: no}\n'
'document-start: disable\n')
self.check('multi\n'
'line\n', conf)
@@ -1701,7 +1533,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_plain(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n'
' check-multi-line-strings: yes}\n'
'document-start: disable\n')
self.check('multi\n'
' line\n', conf, problem=(2, 2))
@@ -1724,7 +1556,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: false}\n'
' check-multi-line-strings: no}\n'
'document-start: disable\n')
self.check('"multi\n'
' line"\n', conf)
@@ -1755,7 +1587,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n'
' check-multi-line-strings: yes}\n'
'document-start: disable\n')
self.check('"multi\n'
'line"\n', conf, problem=(2, 1))
@@ -1811,7 +1643,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_folded_style(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: false}\n'
' check-multi-line-strings: no}\n'
'document-start: disable\n')
self.check('>\n'
' multi\n'
@@ -1849,7 +1681,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_folded_style(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n'
' check-multi-line-strings: yes}\n'
'document-start: disable\n')
self.check('>\n'
' multi\n'
@@ -1890,7 +1722,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_literal_style(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: false}\n'
' check-multi-line-strings: no}\n'
'document-start: disable\n')
self.check('|\n'
' multi\n'
@@ -1928,7 +1760,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_literal_style(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n'
' check-multi-line-strings: yes}\n'
'document-start: disable\n')
self.check('|\n'
' multi\n'
@@ -1972,7 +1804,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_plain(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n'
' check-multi-line-strings: yes}\n'
'document-start: disable\n')
self.check('- long text: very "long"\n'
' \'string\' with\n'
@@ -1994,7 +1826,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_double_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n'
' check-multi-line-strings: yes}\n'
'document-start: disable\n')
self.check('- long text: "very \\"long\\"\n'
' \'string\' with\n'
@@ -2022,7 +1854,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_single_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n'
' check-multi-line-strings: yes}\n'
'document-start: disable\n')
self.check('- long text: \'very "long"\n'
' \'\'string\'\' with\n'
@@ -2050,7 +1882,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_folded(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n'
' check-multi-line-strings: yes}\n'
'document-start: disable\n')
self.check('- long text: >\n'
' very "long"\n'
@@ -2068,7 +1900,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_literal(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n'
' check-multi-line-strings: yes}\n'
'document-start: disable\n')
self.check('- long text: |\n'
' very "long"\n'
@@ -2086,7 +1918,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_consistent(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n'
' check-multi-line-strings: yes}\n'
'document-start: disable\n')
self.check('multi\n'
'line\n', conf)

View File

@@ -1,116 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 Johannes F. Knauf
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.common import RuleTestCase
class KeyOrderingTestCase(RuleTestCase):
rule_id = 'key-ordering'
def test_disabled(self):
conf = 'key-ordering: disable'
self.check('---\n'
'block mapping:\n'
' secondkey: a\n'
' firstkey: b\n', conf)
self.check('---\n'
'flow mapping:\n'
' {secondkey: a, firstkey: b}\n', conf)
self.check('---\n'
'second: before_first\n'
'at: root\n', conf)
self.check('---\n'
'nested but OK:\n'
' second: {first: 1}\n'
' third:\n'
' second: 2\n', conf)
def test_enabled(self):
conf = 'key-ordering: enable'
self.check('---\n'
'block mapping:\n'
' secondkey: a\n'
' firstkey: b\n', conf,
problem=(4, 3))
self.check('---\n'
'flow mapping:\n'
' {secondkey: a, firstkey: b}\n', conf,
problem=(3, 18))
self.check('---\n'
'second: before_first\n'
'at: root\n', conf,
problem=(3, 1))
self.check('---\n'
'nested but OK:\n'
' second: {first: 1}\n'
' third:\n'
' second: 2\n', conf)
def test_word_length(self):
conf = 'key-ordering: enable'
self.check('---\n'
'a: 1\n'
'ab: 1\n'
'abc: 1\n', conf)
self.check('---\n'
'a: 1\n'
'abc: 1\n'
'ab: 1\n', conf,
problem=(4, 1))
def test_key_duplicates(self):
conf = ('key-duplicates: disable\n'
'key-ordering: enable')
self.check('---\n'
'key: 1\n'
'key: 2\n', conf)
def test_case(self):
conf = 'key-ordering: enable'
self.check('---\n'
'T-shirt: 1\n'
'T-shirts: 2\n'
't-shirt: 3\n'
't-shirts: 4\n', conf)
self.check('---\n'
'T-shirt: 1\n'
't-shirt: 2\n'
'T-shirts: 3\n'
't-shirts: 4\n', conf,
problem=(4, 1))
def test_accents(self):
conf = 'key-ordering: enable'
self.check('---\n'
'hair: true\n'
'hais: true\n'
'haïr: true\n'
'haïssable: true\n', conf)
self.check('---\n'
'haïr: true\n'
'hais: true\n', conf,
problem=(3, 1))
self.check('---\n'
'haïr: true\n'
'hais: true\n', conf,
problem=(3, 1))
def test_key_tokens_in_flow_sequences(self):
conf = 'key-ordering: enable'
self.check('---\n'
'[\n'
' key: value, mappings, in, flow: sequence\n'
']\n', conf)

View File

@@ -32,9 +32,6 @@ class LineLengthTestCase(RuleTestCase):
self.check('---\n' + 81 * 'a' + '\n', conf)
self.check(1000 * 'b', conf)
self.check('---\n' + 1000 * 'b' + '\n', conf)
self.check('content: |\n'
' {% this line is' + 99 * ' really' + ' long %}\n',
conf)
def test_default(self):
conf = ('line-length: {max: 80}\n'
@@ -66,7 +63,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: true}'
conf = 'line-length: {max: 20, allow-non-breakable-words: yes}'
self.check('---\n' + 30 * 'A' + '\n', conf)
self.check('---\n'
'this:\n'
@@ -81,17 +78,8 @@ class LineLengthTestCase(RuleTestCase):
' # http://localhost/very/long/url\n'
' comment\n'
'...\n', conf)
self.check('---\n'
'this:\n'
'is:\n'
'another:\n'
' - https://localhost/very/very/long/url\n'
'...\n', conf)
self.check('---\n'
'long_line: http://localhost/very/very/long/url\n', conf,
problem=(2, 21))
conf = 'line-length: {max: 20, allow-non-breakable-words: false}'
conf = 'line-length: {max: 20, allow-non-breakable-words: no}'
self.check('---\n' + 30 * 'A' + '\n', conf, problem=(2, 21))
self.check('---\n'
'this:\n'
@@ -106,52 +94,3 @@ class LineLengthTestCase(RuleTestCase):
' # http://localhost/very/long/url\n'
' comment\n'
'...\n', conf, problem=(5, 21))
self.check('---\n'
'this:\n'
'is:\n'
'another:\n'
' - https://localhost/very/very/long/url\n'
'...\n', conf, problem=(5, 21))
self.check('---\n'
'long_line: http://localhost/very/very/long/url\n'
'...\n', conf, problem=(2, 21))
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',
conf, problem=(2, 21))
def test_non_breakable_inline_mappings(self):
conf = 'line-length: {max: 20, ' \
'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)
self.check('---\n'
'- long line: http://localhost/very/very/long/url\n', conf)
self.check('---\n'
'long_line: http://localhost/short/url + word\n'
'long line: http://localhost/short/url + word\n',
conf, problem1=(2, 21), problem2=(3, 21))
conf = ('line-length: {max: 20,'
' allow-non-breakable-inline-mappings: true}\n'
'trailing-spaces: disable')
self.check('---\n'
'long_line: and+some+space+at+the+end \n',
conf, problem=(2, 21))
self.check('---\n'
'long line: and+some+space+at+the+end \n',
conf, problem=(2, 21))
self.check('---\n'
'- long line: and+some+space+at+the+end \n',
conf, problem=(2, 21))
# See https://github.com/adrienverge/yamllint/issues/21
conf = 'line-length: {allow-non-breakable-inline-mappings: true}'
self.check('---\n'
'content: |\n'
' {% this line is' + 99 * ' really' + ' long %}\n',
conf, problem=(3, 81))

View File

@@ -1,68 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Peter Ericson
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.common import RuleTestCase
class TruthyTestCase(RuleTestCase):
rule_id = 'truthy'
def test_disabled(self):
conf = 'truthy: disable'
self.check('---\n'
'1: True\n', conf)
self.check('---\n'
'True: 1\n', conf)
def test_enabled(self):
conf = 'truthy: enable\n'
self.check('---\n'
'1: True\n'
'True: 1\n',
conf, problem1=(2, 4), problem2=(3, 1))
self.check('---\n'
'1: "True"\n'
'"True": 1\n', conf)
self.check('---\n'
'[\n'
' true, false,\n'
' "false", "FALSE",\n'
' "true", "True",\n'
' True, FALSE,\n'
' on, OFF,\n'
' NO, Yes\n'
']\n', conf,
problem1=(6, 3), problem2=(6, 9),
problem3=(7, 3), problem4=(7, 7),
problem5=(8, 3), problem6=(8, 7))
def test_explicit_types(self):
conf = 'truthy: enable\n'
self.check('---\n'
'string1: !!str True\n'
'string2: !!str yes\n'
'string3: !!str off\n'
'encoded: !!binary |\n'
' True\n'
' OFF\n'
' pad==\n' # this decodes as 'N\xbb\x9e8Qii'
'boolean1: !!bool true\n'
'boolean2: !!bool "false"\n'
'boolean3: !!bool FALSE\n'
'boolean4: !!bool True\n'
'boolean5: !!bool off\n'
'boolean6: !!bool NO\n',
conf)

View File

@@ -18,64 +18,54 @@ try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import fcntl
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):
@classmethod
def setUpClass(cls):
super(CommandLineTestCase, cls).setUpClass()
def setUp(self):
self.wd = tempfile.mkdtemp(prefix='yamllint-tests-')
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'),
})
# .yaml file at root
with open(os.path.join(self.wd, 'a.yaml'), 'w') as f:
f.write('---\n'
'- 1 \n'
'- 2')
@classmethod
def tearDownClass(cls):
super(CommandLineTestCase, cls).tearDownClass()
# .yml file at root
open(os.path.join(self.wd, 'empty.yml'), 'w').close()
shutil.rmtree(cls.wd)
# 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')
def tearDown(self):
shutil.rmtree(self.wd)
def test_find_files_recursively(self):
self.assertEqual(
@@ -83,8 +73,7 @@ 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, 'warn.yaml')],
os.path.join(self.wd, 'sub/ok.yaml')],
)
items = [os.path.join(self.wd, 'sub/ok.yaml'),
@@ -139,11 +128,8 @@ class CommandLineTestCase(unittest.TestCase):
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertRegexpMatches(
err.splitlines()[-1],
r'^yamllint: error: argument -d\/--config-data: '
r'not allowed with argument -c\/--config-file$'
)
self.assertRegexpMatches(err, r'^Options --config-file and '
r'--config-data cannot be used')
def test_run_with_bad_config(self):
sys.stdout, sys.stderr = StringIO(), StringIO()
@@ -167,49 +153,6 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(out, '')
self.assertRegexpMatches(err, r'^invalid config: not a dict')
def test_run_with_config_file(self):
with open(os.path.join(self.wd, 'config'), 'w') as f:
f.write('rules: {trailing-spaces: disable}')
with self.assertRaises(SystemExit) as ctx:
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.exception.code, 0)
with open(os.path.join(self.wd, 'config'), 'w') as f:
f.write('rules: {trailing-spaces: enable}')
with self.assertRaises(SystemExit) as ctx:
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.exception.code, 1)
def test_run_with_user_global_config_file(self):
home = os.path.join(self.wd, 'fake-home')
os.mkdir(home)
dir = os.path.join(home, '.config')
os.mkdir(dir)
dir = os.path.join(dir, 'yamllint')
os.mkdir(dir)
config = os.path.join(dir, 'config')
temp = os.environ['HOME']
os.environ['HOME'] = home
with open(config, 'w') as f:
f.write('rules: {trailing-spaces: disable}')
with self.assertRaises(SystemExit) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.exception.code, 0)
with open(config, 'w') as f:
f.write('rules: {trailing-spaces: enable}')
with self.assertRaises(SystemExit) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.exception.code, 1)
os.environ['HOME'] = temp
def test_run_version(self):
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
@@ -249,24 +192,6 @@ 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')
@@ -293,26 +218,6 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(out, '')
self.assertEqual(err, '')
def test_run_non_ascii_file(self):
file = os.path.join(self.wd, 'non-ascii', 'utf-8')
# Make sure the default localization conditions on this "system"
# support UTF-8 encoding.
loc = locale.getlocale()
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', file))
locale.setlocale(locale.LC_ALL, loc)
self.assertEqual(ctx.exception.code, 0)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertEqual(err, '')
def test_run_multiple_files(self):
items = [os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 's')]
@@ -330,7 +235,7 @@ class CommandLineTestCase(unittest.TestCase):
'(key-duplicates)\n') % file)
self.assertEqual(err, '')
def test_run_piped_output_nocolor(self):
def test_run_colored_output(self):
file = os.path.join(self.wd, 'a.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
@@ -340,38 +245,6 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(ctx.exception.code, 1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, (
'%s\n'
' 2:4 error trailing spaces (trailing-spaces)\n'
' 3:4 error no new line character at the end of file '
'(new-line-at-end-of-file)\n'
'\n' % file))
self.assertEqual(err, '')
def test_run_colored_output(self):
file = os.path.join(self.wd, 'a.yaml')
# Create a pseudo-TTY and redirect stdout to it
master, slave = pty.openpty()
sys.stdout = sys.stderr = os.fdopen(slave, 'w')
with self.assertRaises(SystemExit) as ctx:
cli.run((file, ))
sys.stdout.flush()
self.assertEqual(ctx.exception.code, 1)
# Read output from TTY
output = os.fdopen(master, 'r')
flag = fcntl.fcntl(master, fcntl.F_GETFD)
fcntl.fcntl(master, fcntl.F_SETFL, flag | os.O_NONBLOCK)
out = output.read().replace('\r\n', '\n')
sys.stdout.close()
sys.stderr.close()
output.close()
self.assertEqual(out, (
'\033[4m%s\033[0m\n'
' \033[2m2:4\033[0m \033[31merror\033[0m '
@@ -380,3 +253,4 @@ class CommandLineTestCase(unittest.TestCase):
'no new line character at the end of file '
'\033[2m(new-line-at-end-of-file)\033[0m\n'
'\n' % file))
self.assertEqual(err, '')

View File

@@ -14,24 +14,10 @@
# 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
try:
assert sys.version_info >= (2, 7)
import unittest
except:
import unittest2 as unittest
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):
@@ -44,7 +30,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(None)), 1)
self.assertEqual(len(new.enabled_rules()), 1)
def test_invalid_conf(self):
with self.assertRaises(config.YamlLintConfigError):
@@ -76,45 +62,6 @@ 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'
@@ -184,7 +131,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(None)), 2)
self.assertEqual(len(new.enabled_rules()), 2)
def test_extend_remove_rule(self):
old = config.YamlLintConfig('rules:\n'
@@ -201,7 +148,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(None)), 1)
self.assertEqual(len(new.enabled_rules()), 1)
def test_extend_edit_rule(self):
old = config.YamlLintConfig('rules:\n'
@@ -221,7 +168,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(None)), 2)
self.assertEqual(len(new.enabled_rules()), 2)
def test_extend_reenable_rule(self):
old = config.YamlLintConfig('rules:\n'
@@ -239,7 +186,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(None)), 2)
self.assertEqual(len(new.enabled_rules()), 2)
class ExtendedLibraryConfigTestCase(unittest.TestCase):
@@ -284,94 +231,3 @@ 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,
)))

View File

@@ -15,12 +15,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import io
import sys
try:
assert sys.version_info >= (2, 7)
import unittest
except:
import unittest2 as unittest
import unittest
from yamllint.config import YamlLintConfig
from yamllint import linter
@@ -48,15 +44,3 @@ class LinterTestCase(unittest.TestCase):
def test_run_on_list(self):
self.assertRaises(TypeError, linter.run,
['h', 'e', 'l', 'l', 'o'], self.fake_config())
def test_run_on_non_ascii_chars(self):
s = (u'- hétérogénéité\n'
u'# 19.99 €\n')
linter.run(s, self.fake_config())
linter.run(s.encode('utf-8'), self.fake_config())
linter.run(s.encode('iso-8859-15'), self.fake_config())
s = (u'- お早う御座います。\n'
u'# الأَبْجَدِيَّة العَرَبِيَّة\n')
linter.run(s, self.fake_config())
linter.run(s.encode('utf-8'), self.fake_config())

View File

@@ -1,88 +0,0 @@
# -*- 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 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])

View File

@@ -14,18 +14,12 @@
# 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 sys
try:
assert sys.version_info >= (2, 7)
import unittest
except:
import unittest2 as unittest
import unittest
import yaml
from yamllint.parser import (line_generator, token_or_comment_generator,
token_or_comment_or_line_generator,
Line, Token, Comment)
from yamllint.parser import (line_generator, token_generator,
token_or_line_generator, Line, Token)
class ParserTestCase(unittest.TestCase):
@@ -67,8 +61,8 @@ class ParserTestCase(unittest.TestCase):
self.assertEqual(e[2].line_no, 3)
self.assertEqual(e[2].content, 'at the end')
def test_token_or_comment_generator(self):
e = list(token_or_comment_generator(''))
def test_token_generator(self):
e = list(token_generator(''))
self.assertEqual(len(e), 2)
self.assertEqual(e[0].prev, None)
self.assertIsInstance(e[0].curr, yaml.Token)
@@ -77,74 +71,16 @@ class ParserTestCase(unittest.TestCase):
self.assertEqual(e[1].curr, e[0].next)
self.assertEqual(e[1].next, None)
e = list(token_or_comment_generator('---\n'
'k: v\n'))
e = list(token_generator('---\n'
'k: v\n'))
self.assertEqual(len(e), 9)
self.assertIsInstance(e[3].curr, yaml.KeyToken)
self.assertIsInstance(e[5].curr, yaml.ValueToken)
e = list(token_or_comment_generator('# start comment\n'
'- a\n'
'- key: val # key=val\n'
'# this is\n'
'# a block \n'
'# comment\n'
'- c\n'
'# end comment\n'))
self.assertEqual(len(e), 21)
self.assertIsInstance(e[1], Comment)
self.assertEqual(e[1], Comment(1, 1, '# start comment', 0))
self.assertEqual(e[11], Comment(3, 13, '# key=val', 0))
self.assertEqual(e[12], Comment(4, 1, '# this is', 0))
self.assertEqual(e[13], Comment(5, 1, '# a block ', 0))
self.assertEqual(e[14], Comment(6, 1, '# comment', 0))
self.assertEqual(e[18], Comment(8, 1, '# end comment', 0))
e = list(token_or_comment_generator('---\n'
'# no newline char'))
self.assertEqual(e[2], Comment(2, 1, '# no newline char', 0))
e = list(token_or_comment_generator('# just comment'))
self.assertEqual(e[1], Comment(1, 1, '# just comment', 0))
e = list(token_or_comment_generator('\n'
' # indented comment\n'))
self.assertEqual(e[1], Comment(2, 4, '# indented comment', 0))
e = list(token_or_comment_generator('\n'
'# trailing spaces \n'))
self.assertEqual(e[1], Comment(2, 1, '# trailing spaces ', 0))
e = [c for c in
token_or_comment_generator('# block\n'
'# comment\n'
'- data # inline comment\n'
'# block\n'
'# comment\n'
'- k: v # inline comment\n'
'- [ l, ist\n'
'] # inline comment\n'
'- { m: ap\n'
'} # inline comment\n'
'# block comment\n'
'- data # inline comment\n')
if isinstance(c, Comment)]
self.assertEqual(len(e), 10)
self.assertFalse(e[0].is_inline())
self.assertFalse(e[1].is_inline())
self.assertTrue(e[2].is_inline())
self.assertFalse(e[3].is_inline())
self.assertFalse(e[4].is_inline())
self.assertTrue(e[5].is_inline())
self.assertTrue(e[6].is_inline())
self.assertTrue(e[7].is_inline())
self.assertFalse(e[8].is_inline())
self.assertTrue(e[9].is_inline())
def test_token_or_comment_or_line_generator(self):
e = list(token_or_comment_or_line_generator('---\n'
'k: v # k=v\n'))
self.assertEqual(len(e), 13)
def test_token_or_line_generator(self):
e = list(token_or_line_generator('---\n'
'k: v\n'))
self.assertEqual(len(e), 12)
self.assertIsInstance(e[0], Token)
self.assertIsInstance(e[0].curr, yaml.StreamStartToken)
self.assertIsInstance(e[1], Token)
@@ -153,6 +89,5 @@ class ParserTestCase(unittest.TestCase):
self.assertIsInstance(e[3].curr, yaml.BlockMappingStartToken)
self.assertIsInstance(e[4].curr, yaml.KeyToken)
self.assertIsInstance(e[6].curr, yaml.ValueToken)
self.assertIsInstance(e[8], Comment)
self.assertIsInstance(e[9], Line)
self.assertIsInstance(e[12], Line)
self.assertIsInstance(e[8], Line)
self.assertIsInstance(e[11], Line)

View File

@@ -48,7 +48,6 @@ 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'
@@ -67,7 +66,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: false}\n'
'example-5.3': ('indentation: {indent-sequences: no}\n'
'colons: {max-spaces-before: 1}\n'),
'example-6.4': ('trailing-spaces: disable\n'),
'example-6.5': ('trailing-spaces: disable\n'),
@@ -115,13 +114,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: false}\n'
'example-8.20': ('indentation: {indent-sequences: no}\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: false}\n'),
'example-10.8': ('truthy: disable\n'),
'example-10.9': ('truthy: disable\n'),
'example-10.2': ('indentation: {indent-sequences: no}\n'),
}
files = os.listdir(os.path.join(os.path.dirname(os.path.realpath(__file__)),
@@ -134,7 +131,6 @@ 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()).

View File

@@ -1,304 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.common import RuleTestCase
class YamllintDirectivesTestCase(RuleTestCase):
conf = ('commas: disable\n'
'trailing-spaces: {}\n'
'colons: {max-spaces-before: 1}\n')
def test_disable_directive(self):
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces \n'
'- bad : colon\n'
'- [valid , YAML]\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(4, 8, 'colons'),
problem3=(6, 7, 'colons'),
problem4=(6, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces \n'
'# yamllint disable\n'
'- bad : colon\n'
'- [valid , YAML]\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem=(3, 18, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'# yamllint disable\n'
'- trailing spaces \n'
'- bad : colon\n'
'- [valid , YAML]\n'
'# yamllint enable\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(8, 7, 'colons'),
problem2=(8, 26, 'trailing-spaces'))
def test_disable_directive_with_rules(self):
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces \n'
'# yamllint disable rule:trailing-spaces\n'
'- bad : colon\n'
'- [valid , YAML]\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(5, 8, 'colons'),
problem3=(7, 7, 'colons'))
self.check('---\n'
'- [valid , YAML]\n'
'# yamllint disable rule:trailing-spaces\n'
'- trailing spaces \n'
'- bad : colon\n'
'- [valid , YAML]\n'
'# yamllint enable rule:trailing-spaces\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(5, 8, 'colons'),
problem2=(8, 7, 'colons'),
problem3=(8, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'# yamllint disable rule:trailing-spaces\n'
'- trailing spaces \n'
'- bad : colon\n'
'- [valid , YAML]\n'
'# yamllint enable\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(5, 8, 'colons'),
problem2=(8, 7, 'colons'),
problem3=(8, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'# yamllint disable\n'
'- trailing spaces \n'
'- bad : colon\n'
'- [valid , YAML]\n'
'# yamllint enable rule:trailing-spaces\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem=(8, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'# yamllint disable rule:colons\n'
'- trailing spaces \n'
'# yamllint disable rule:trailing-spaces\n'
'- bad : colon\n'
'- [valid , YAML]\n'
'# yamllint enable rule:colons\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(4, 18, 'trailing-spaces'),
problem2=(9, 7, 'colons'))
def test_disable_line_directive(self):
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces \n'
'# yamllint disable-line\n'
'- bad : colon\n'
'- [valid , YAML]\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(7, 7, 'colons'),
problem3=(7, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces \n'
'- bad : colon # yamllint disable-line\n'
'- [valid , YAML]\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(6, 7, 'colons'),
problem3=(6, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces \n'
'- bad : colon\n'
'- [valid , YAML] # yamllint disable-line\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(4, 8, 'colons'),
problem3=(6, 7, 'colons'),
problem4=(6, 26, 'trailing-spaces'))
def test_disable_line_directive_with_rules(self):
self.check('---\n'
'- [valid , YAML]\n'
'# yamllint disable-line rule:colons\n'
'- trailing spaces \n'
'- bad : colon\n'
'- [valid , YAML]\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(4, 18, 'trailing-spaces'),
problem2=(5, 8, 'colons'),
problem3=(7, 7, 'colons'),
problem4=(7, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces # yamllint disable-line rule:colons \n'
'- bad : colon\n'
'- [valid , YAML]\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(3, 55, 'trailing-spaces'),
problem2=(4, 8, 'colons'),
problem3=(6, 7, 'colons'),
problem4=(6, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces \n'
'# yamllint disable-line rule:colons\n'
'- bad : colon\n'
'- [valid , YAML]\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(7, 7, 'colons'),
problem3=(7, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces \n'
'- bad : colon # yamllint disable-line rule:colons\n'
'- [valid , YAML]\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(6, 7, 'colons'),
problem3=(6, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces \n'
'- bad : colon\n'
'- [valid , YAML]\n'
'# yamllint disable-line rule:colons\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(4, 8, 'colons'),
problem3=(7, 26, 'trailing-spaces'))
self.check('---\n'
'- [valid , YAML]\n'
'- trailing spaces \n'
'- bad : colon\n'
'- [valid , YAML]\n'
'# yamllint disable-line rule:colons rule:trailing-spaces\n'
'- bad : colon and spaces \n'
'- [valid , YAML]\n',
self.conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(4, 8, 'colons'))
def test_directive_on_last_line(self):
conf = 'new-line-at-end-of-file: {}'
self.check('---\n'
'no new line',
conf,
problem=(2, 12, 'new-line-at-end-of-file'))
self.check('---\n'
'# yamllint disable\n'
'no new line',
conf)
self.check('---\n'
'no new line # yamllint disable',
conf)
def test_indented_directive(self):
conf = 'brackets: {min-spaces-inside: 0, max-spaces-inside: 0}'
self.check('---\n'
'- a: 1\n'
' b:\n'
' c: [ x]\n',
conf,
problem=(4, 12, 'brackets'))
self.check('---\n'
'- a: 1\n'
' b:\n'
' # yamllint disable-line rule:brackets\n'
' c: [ x]\n',
conf)
def test_directive_on_itself(self):
conf = ('comments: {min-spaces-from-content: 2}\n'
'comments-indentation: {}\n')
self.check('---\n'
'- a: 1 # comment too close\n'
' b:\n'
' # wrong indentation\n'
' c: [x]\n',
conf,
problem1=(2, 8, 'comments'),
problem2=(4, 2, 'comments-indentation'))
self.check('---\n'
'# yamllint disable\n'
'- a: 1 # comment too close\n'
' b:\n'
' # wrong indentation\n'
' c: [x]\n',
conf)
self.check('---\n'
'- a: 1 # yamllint disable-line\n'
' b:\n'
' # yamllint disable-line\n'
' # wrong indentation\n'
' c: [x]\n',
conf)
self.check('---\n'
'- a: 1 # yamllint disable-line rule:comments\n'
' b:\n'
' # yamllint disable-line rule:comments-indentation\n'
' # wrong indentation\n'
' c: [x]\n',
conf)
self.check('---\n'
'# yamllint disable\n'
'- a: 1 # comment too close\n'
' # yamllint enable rule:comments-indentation\n'
' b:\n'
' # wrong indentation\n'
' c: [x]\n',
conf,
problem=(6, 2, 'comments-indentation'))

50
yaml-fix-indentation Executable file
View File

@@ -0,0 +1,50 @@
#!/bin/bash
set -euf
DIR=$(dirname "$(readlink -f $0)")
fix_one_problem_in_file() {
local filename=$1
local error
error=$(yamllint -f parsable "$filename" | grep 'wrong indentation: expected' \
| head -n 1)
if [ -z "$error" ]; then
return 1
fi
local line=$(echo $error | cut -d: -f2)
local expected=$(echo $error | cut -d: -f5 | sed 's/.* expected //;s/ but found.*//')
local found=$(echo $error | cut -d: -f5 | sed 's/.*but found //;s/(inde.*//')
"$DIR/yaml-remove-indentation" "$filename" $line $expected $found
return 0
}
reformat_yaml() {
local in=$1
local out=$2
python -c 'import sys, yaml; yaml.dump(yaml.load(sys.stdin), sys.stdout)' <"$in" >"$out"
}
fix_one_file() {
local filename=$1
local backup=$(mktemp originalXXXXX)
cp "$filename" "$backup"
echo "FIXING $file"
while fix_one_problem_in_file "$filename"; do continue; done
echo "CHECKING $file"
local tmp_old=$(mktemp oldXXXXX)
local tmp_new=$(mktemp newXXXXX)
reformat_yaml "$backup" "$tmp_old"
reformat_yaml "$filename" "$tmp_new"
if ! diff -q "$tmp_old" "$tmp_new" &>/dev/null; then
echo "error: after reformating, the file contents is detected different."
echo "diff $backup $filename"
echo "diff $tmp_old $tmp_new"
exit 1
fi
rm "$backup" "$tmp_old" "$tmp_new"
}
for file in "$@"; do
fix_one_file "$file"
done

37
yaml-remove-indentation Executable file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env python3
import sys
file = sys.argv[1]
line = int(sys.argv[2]) - 1
indent_expected = int(sys.argv[3])
indent_found = int(sys.argv[4])
with open(file) as f:
lines = f.readlines()
before = lines[:line]
is_a_list = lines[line].strip()[0] == '-'
i = line
while (i < len(lines) and
(lines[i].strip() == '' or
(not is_a_list and lines[i].startswith(indent_found * ' ')) or
(is_a_list and (lines[i].startswith(indent_found * ' ' + '-') or
lines[i].startswith(indent_found * ' ' + ' '))))):
i += 1
contents = lines[line:i]
after = lines[i:]
new_contents = []
for line in contents:
if line.strip() != '':
line = (indent_expected * ' ') + line[indent_found:]
new_contents.append(line)
with open(file, 'w') as f:
f.write(''.join(before))
f.write(''.join(new_contents))
f.write(''.join(after))

View File

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

View File

@@ -1,4 +0,0 @@
from yamllint.cli import run
if __name__ == '__main__':
run()

View File

@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
import os.path
import sys
@@ -23,7 +22,6 @@ 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
@@ -50,17 +48,6 @@ class Format(object):
@staticmethod
def standard(problem, filename):
line = ' %d:%d' % (problem.line, problem.column)
line += max(12 - len(line), 0) * ' '
line += problem.level
line += max(21 - len(line), 0) * ' '
line += problem.desc
if problem.rule:
line += ' (%s)' % problem.rule
return line
@staticmethod
def standard_color(problem, filename):
line = ' \033[2m%d:%d\033[0m' % (problem.line, problem.column)
line += max(20 - len(line), 0) * ' '
if problem.level == 'warning':
@@ -79,20 +66,14 @@ def run(argv=None):
description=APP_DESCRIPTION)
parser.add_argument('files', metavar='FILE_OR_DIR', nargs='+',
help='files to check')
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('-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)')
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))
@@ -100,12 +81,10 @@ def run(argv=None):
args = parser.parse_args(argv)
# User-global config is supposed to be in ~/.config/yamllint/config
if 'XDG_CONFIG_HOME' in os.environ:
user_global_config = os.path.join(
os.environ['XDG_CONFIG_HOME'], 'yamllint', 'config')
else:
user_global_config = os.path.expanduser('~/.config/yamllint/config')
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)
try:
if args.config_data is not None:
@@ -116,50 +95,35 @@ def run(argv=None):
conf = YamlLintConfig(file=args.config_file)
elif os.path.isfile('.yamllint'):
conf = YamlLintConfig(file='.yamllint')
elif os.path.isfile(user_global_config):
conf = YamlLintConfig(file=user_global_config)
else:
conf = YamlLintConfig('extends: default')
except YamlLintConfigError as e:
print(e, file=sys.stderr)
sys.exit(-1)
max_level = 0
return_code = 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, filepath):
for problem in linter.run(f, conf):
if args.format == 'parsable':
print(Format.parsable(problem, file))
elif sys.stdout.isatty():
else:
if first:
print('\033[4m%s\033[0m' % file)
first = False
print(Format.standard_color(problem, file))
else:
if first:
print(file)
first = False
print(Format.standard(problem, file))
max_level = max(max_level, PROBLEM_LEVELS[problem.level])
if return_code == 0 and problem.level == 'error':
return_code = 1
if not first and args.format != 'parsable':
print('')
except EnvironmentError as e:
print(e, file=sys.stderr)
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
return_code = -1
sys.exit(return_code)

View File

@@ -4,13 +4,9 @@ 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
@@ -20,14 +16,14 @@ rules:
max-spaces-after: 1
comments:
level: warning
require-starting-space: true
require-starting-space: yes
min-spaces-from-content: 2
comments-indentation:
level: warning
document-end: disable
document-start:
level: warning
present: true
present: yes
empty-lines:
max: 2
max-start: 0
@@ -36,17 +32,13 @@ rules:
max-spaces-after: 1
indentation:
spaces: consistent
indent-sequences: true
check-multi-line-strings: false
indent-sequences: yes
check-multi-line-strings: no
key-duplicates: enable
key-ordering: disable
line-length:
max: 80
allow-non-breakable-words: true
allow-non-breakable-inline-mappings: false
allow-non-breakable-words: yes
new-line-at-end-of-file: enable
new-lines:
type: unix
trailing-spaces: enable
truthy:
level: warning

View File

@@ -25,5 +25,3 @@ rules:
indent-sequences: consistent
line-length:
level: warning
allow-non-breakable-inline-mappings: true
truthy: disable

View File

@@ -16,7 +16,6 @@
import os.path
import pathspec
import yaml
import yamllint.rules
@@ -30,8 +29,6 @@ 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()
@@ -39,14 +36,9 @@ class YamlLintConfig(object):
self.parse(content)
self.validate()
def is_file_ignored(self, filepath):
return self.ignore and self.ignore.match_file(filepath)
def enabled_rules(self, filepath):
def enabled_rules(self):
return [yamllint.rules.get(id) for id, val in self.rules.items()
if val is not False and (
filepath is None or 'ignore' not in val or
not val['ignore'].match_file(filepath))]
if val is not False]
def extend(self, base_config):
assert isinstance(base_config, YamlLintConfig)
@@ -61,9 +53,6 @@ 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)
@@ -84,13 +73,6 @@ 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 contain file patterns')
self.ignore = pathspec.PathSpec.from_lines(
'gitwildmatch', conf['ignore'].splitlines())
def validate(self):
for id in self.rules:
try:
@@ -108,14 +90,6 @@ 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 contain file 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'):
@@ -124,7 +98,7 @@ def validate_rule_conf(rule, conf):
options = getattr(rule, 'CONF', {})
for optkey in conf:
if optkey in ('ignore', 'level'):
if optkey == 'level':
continue
if optkey not in options:
raise YamlLintConfigError(

View File

@@ -14,23 +14,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import yaml
from yamllint import parser
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='<no description>', rule=None):
@@ -63,74 +51,18 @@ class LintProblem(object):
return '%d:%d: %s' % (self.line, self.column, self.message)
def get_cosmetic_problems(buffer, conf, filepath):
rules = conf.enabled_rules(filepath)
def get_costemic_problems(buffer, conf):
rules = conf.enabled_rules()
# Split token rules from line rules
token_rules = [r for r in rules if r.TYPE == 'token']
comment_rules = [r for r in rules if r.TYPE == 'comment']
line_rules = [r for r in rules if r.TYPE == 'line']
context = {}
for rule in token_rules:
context[rule.ID] = {}
class DisableDirective():
def __init__(self):
self.rules = set()
self.all_rules = set([r.ID for r in rules])
def process_comment(self, comment):
try:
comment = str(comment)
except UnicodeError:
return # this certainly wasn't a yamllint directive comment
if re.match(r'^# yamllint disable( rule:\S+)*\s*$', comment):
rules = [item[5:] for item in comment[18:].split(' ')][1:]
if len(rules) == 0:
self.rules = self.all_rules.copy()
else:
for id in rules:
if id in self.all_rules:
self.rules.add(id)
elif re.match(r'^# yamllint enable( rule:\S+)*\s*$', comment):
rules = [item[5:] for item in comment[17:].split(' ')][1:]
if len(rules) == 0:
self.rules.clear()
else:
for id in rules:
self.rules.discard(id)
def is_disabled_by_directive(self, problem):
return problem.rule in self.rules
class DisableLineDirective(DisableDirective):
def process_comment(self, comment):
try:
comment = str(comment)
except UnicodeError:
return # this certainly wasn't a yamllint directive comment
if re.match(r'^# yamllint disable-line( rule:\S+)*\s*$', comment):
rules = [item[5:] for item in comment[23:].split(' ')][1:]
if len(rules) == 0:
self.rules = self.all_rules.copy()
else:
for id in rules:
if id in self.all_rules:
self.rules.add(id)
# Use a cache to store problems and flush it only when a end of line is
# found. This allows the use of yamllint directive to disable some rules on
# some lines.
cache = []
disabled = DisableDirective()
disabled_for_line = DisableLineDirective()
disabled_for_next_line = DisableLineDirective()
for elem in parser.token_or_comment_or_line_generator(buffer):
for elem in parser.token_or_line_generator(buffer):
if isinstance(elem, parser.Token):
for rule in token_rules:
rule_conf = conf.rules[rule.ID]
@@ -140,39 +72,15 @@ def get_cosmetic_problems(buffer, conf, filepath):
context[rule.ID]):
problem.rule = rule.ID
problem.level = rule_conf['level']
cache.append(problem)
elif isinstance(elem, parser.Comment):
for rule in comment_rules:
rule_conf = conf.rules[rule.ID]
for problem in rule.check(rule_conf, elem):
problem.rule = rule.ID
problem.level = rule_conf['level']
cache.append(problem)
disabled.process_comment(elem)
if elem.is_inline():
disabled_for_line.process_comment(elem)
else:
disabled_for_next_line.process_comment(elem)
yield problem
elif isinstance(elem, parser.Line):
for rule in line_rules:
rule_conf = conf.rules[rule.ID]
for problem in rule.check(rule_conf, elem):
problem.rule = rule.ID
problem.level = rule_conf['level']
cache.append(problem)
# This is the last token/comment/line of this line, let's flush the
# problems found (but filter them according to the directives)
for problem in cache:
if not (disabled_for_line.is_disabled_by_directive(problem) or
disabled.is_disabled_by_directive(problem)):
yield problem
disabled_for_line = disabled_for_next_line
disabled_for_next_line = DisableLineDirective()
cache = []
def get_syntax_error(buffer):
try:
@@ -185,15 +93,12 @@ def get_syntax_error(buffer):
return problem
def _run(buffer, conf, filepath):
assert hasattr(buffer, '__getitem__'), \
'_run() argument must be a buffer, not a stream'
def _run(buffer, conf):
# If the document contains a syntax error, save it and yield it at the
# right line
syntax_error = get_syntax_error(buffer)
for problem in get_cosmetic_problems(buffer, conf, filepath):
for problem in get_costemic_problems(buffer, conf):
# 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 +120,7 @@ def _run(buffer, conf, filepath):
yield syntax_error
def run(input, conf, filepath=None):
def run(input, conf):
"""Lints a YAML source.
Returns a generator of LintProblem objects.
@@ -223,14 +128,11 @@ def run(input, conf, filepath=None):
: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, filepath)
return _run(input, conf)
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, filepath)
return _run(content, conf)
else:
raise TypeError('input should be a string or a stream')

View File

@@ -38,40 +38,6 @@ class Token(object):
self.nextnext = nextnext
class Comment(object):
def __init__(self, line_no, column_no, buffer, pointer,
token_before=None, token_after=None, comment_before=None):
self.line_no = line_no
self.column_no = column_no
self.buffer = buffer
self.pointer = pointer
self.token_before = token_before
self.token_after = token_after
self.comment_before = comment_before
def __str__(self):
end = self.buffer.find('\n', self.pointer)
if end == -1:
end = self.buffer.find('\0', self.pointer)
if end != -1:
return self.buffer[self.pointer:end]
return self.buffer[self.pointer:]
def __eq__(self, other):
return (isinstance(other, Comment) and
self.line_no == other.line_no and
self.column_no == other.column_no and
str(self) == str(other))
def is_inline(self):
return (
not isinstance(self.token_before, yaml.StreamStartToken) and
self.line_no == self.token_before.end_mark.line + 1 and
# sometimes token end marks are on the next line
self.buffer[self.token_before.end_mark.pointer - 1] != '\n'
)
def line_generator(buffer):
line_no = 1
cur = 0
@@ -85,39 +51,7 @@ def line_generator(buffer):
yield Line(line_no, buffer, start=cur, end=len(buffer))
def comments_between_tokens(token1, token2):
"""Find all comments between two tokens"""
if token2 is None:
buf = token1.end_mark.buffer[token1.end_mark.pointer:]
elif (token1.end_mark.line == token2.start_mark.line and
not isinstance(token1, yaml.StreamStartToken) and
not isinstance(token2, yaml.StreamEndToken)):
return
else:
buf = token1.end_mark.buffer[token1.end_mark.pointer:
token2.start_mark.pointer]
line_no = token1.end_mark.line + 1
column_no = token1.end_mark.column + 1
pointer = token1.end_mark.pointer
comment_before = None
for line in buf.split('\n'):
pos = line.find('#')
if pos != -1:
comment = Comment(line_no, column_no + pos,
token1.end_mark.buffer, pointer + pos,
token1, token2, comment_before)
yield comment
comment_before = comment
pointer += len(line) + 1
line_no += 1
column_no = 1
def token_or_comment_generator(buffer):
def token_generator(buffer):
yaml_loader = yaml.BaseLoader(buffer)
try:
@@ -129,9 +63,6 @@ def token_or_comment_generator(buffer):
yield Token(curr.start_mark.line + 1, curr, prev, next, nextnext)
for comment in comments_between_tokens(curr, next):
yield comment
prev = curr
curr = next
@@ -139,19 +70,19 @@ def token_or_comment_generator(buffer):
pass
def token_or_comment_or_line_generator(buffer):
def token_or_line_generator(buffer):
"""Generator that mixes tokens and lines, ordering them by line number"""
tok_or_com_gen = token_or_comment_generator(buffer)
token_gen = token_generator(buffer)
line_gen = line_generator(buffer)
tok_or_com = next(tok_or_com_gen, None)
token = next(token_gen, None)
line = next(line_gen, None)
while tok_or_com is not None or line is not None:
if tok_or_com is None or (line is not None and
tok_or_com.line_no > line.line_no):
while token is not None or line is not None:
if token is None or (line is not None and
token.line_no > line.line_no):
yield line
line = next(line_gen, None)
else:
yield tok_or_com
tok_or_com = next(tok_or_com_gen, None)
yield token
token = next(token_gen, None)

View File

@@ -27,12 +27,10 @@ from yamllint.rules import (
hyphens,
indentation,
key_duplicates,
key_ordering,
line_length,
new_line_at_end_of_file,
new_lines,
trailing_spaces,
truthy,
)
_RULES = {
@@ -48,12 +46,10 @@ _RULES = {
hyphens.ID: hyphens,
indentation.ID: indentation,
key_duplicates.ID: key_duplicates,
key_ordering.ID: key_ordering,
line_length.ID: line_length,
new_line_at_end_of_file.ID: new_line_at_end_of_file,
new_lines.ID: new_lines,
trailing_spaces.ID: trailing_spaces,
truthy.ID: truthy,
}

View File

@@ -23,10 +23,6 @@ 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
@@ -63,30 +59,6 @@ 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: {}
"""
@@ -98,27 +70,11 @@ from yamllint.rules.common import spaces_after, spaces_before
ID = 'braces'
TYPE = 'token'
CONF = {'min-spaces-inside': int,
'max-spaces-inside': int,
'min-spaces-inside-empty': int,
'max-spaces-inside-empty': int}
'max-spaces-inside': int}
def check(conf, token, prev, next, nextnext, context):
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):
if isinstance(token, yaml.FlowMappingStartToken):
problem = spaces_after(token, prev, next,
min=conf['min-spaces-inside'],
max=conf['max-spaces-inside'],

View File

@@ -24,10 +24,6 @@ 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
@@ -64,30 +60,6 @@ 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: []
"""
@@ -99,28 +71,11 @@ from yamllint.rules.common import spaces_after, spaces_before
ID = 'brackets'
TYPE = 'token'
CONF = {'min-spaces-inside': int,
'max-spaces-inside': int,
'min-spaces-inside-empty': int,
'max-spaces-inside-empty': int}
'max-spaces-inside': int}
def check(conf, token, prev, next, nextnext, context):
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):
if isinstance(token, yaml.FlowSequenceStartToken):
problem = spaces_after(token, prev, next,
min=conf['min-spaces-inside'],
max=conf['max-spaces-inside'],

View File

@@ -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 ``true`` to enable, ``false`` to disable.
``#``. Set to ``yes`` to enable, ``no`` 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: true}``
#. With ``comments: {require-starting-space: yes}``
the following code snippet would **PASS**:
::
@@ -35,12 +35,6 @@ Use this rule to control the position and formatting of comments.
# This sentence
# is a block comment
the following code snippet would **PASS**:
::
##############################
## This is some documentation
the following code snippet would **FAIL**:
::
@@ -61,29 +55,33 @@ Use this rule to control the position and formatting of comments.
"""
import yaml
from yamllint.linter import LintProblem
from yamllint.rules.common import get_comments_between_tokens
ID = 'comments'
TYPE = 'comment'
TYPE = 'token'
CONF = {'require-starting-space': bool,
'min-spaces-from-content': int}
def check(conf, comment):
if (conf['min-spaces-from-content'] != -1 and comment.is_inline() and
comment.pointer - comment.token_before.end_mark.pointer <
conf['min-spaces-from-content']):
yield LintProblem(comment.line_no, comment.column_no,
'too few spaces before comment')
def check(conf, token, prev, next, nextnext, context):
for comment in get_comments_between_tokens(token, next):
if (conf['min-spaces-from-content'] != -1 and
not isinstance(token, yaml.StreamStartToken) and
comment.line == token.end_mark.line + 1):
# Sometimes token end marks are on the next line
if token.end_mark.buffer[token.end_mark.pointer - 1] != '\n':
if (comment.pointer - token.end_mark.pointer <
conf['min-spaces-from-content']):
yield LintProblem(comment.line, comment.column,
'too few spaces before comment')
if conf['require-starting-space']:
text_start = comment.pointer + 1
while (comment.buffer[text_start] == '#' and
text_start < len(comment.buffer)):
text_start += 1
if (text_start < len(comment.buffer) and
comment.buffer[text_start] not in (' ', '\n', '\0')):
yield LintProblem(comment.line_no,
comment.column_no + text_start - comment.pointer,
if (conf['require-starting-space'] and
comment.pointer + 1 < len(comment.buffer) and
comment.buffer[comment.pointer + 1] != ' ' and
comment.buffer[comment.pointer + 1] != '\n'):
yield LintProblem(comment.line, comment.column + 1,
'missing starting space in comment')

View File

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

View File

@@ -48,6 +48,27 @@ def spaces_before(token, prev, next, min=-1, max=-1,
token.start_mark.column + 1, min_desc)
class Comment(object):
def __init__(self, line, column, buffer, pointer):
self.line = line
self.column = column
self.buffer = buffer
self.pointer = pointer
def __repr__(self):
end = self.buffer.find('\n', self.pointer)
if end == -1:
end = self.buffer.find('\0', self.pointer)
if end != -1:
return self.buffer[self.pointer:end]
return self.buffer[self.pointer:]
def __eq__(self, other):
return (self.line == other.line and
self.column == other.column and
str(self) == str(other))
def get_line_indent(token):
"""Finds the indent of the line the token starts in."""
start = token.start_mark.buffer.rfind('\n', 0,
@@ -77,6 +98,35 @@ def get_real_end_line(token):
return end_line
def get_comments_between_tokens(token1, token2, skip_first_line=False):
if token2 is None:
buf = token1.end_mark.buffer[token1.end_mark.pointer:]
elif (token1.end_mark.line == token2.start_mark.line and
not isinstance(token1, yaml.StreamStartToken) and
not isinstance(token2, yaml.StreamEndToken)):
return
else:
buf = token1.end_mark.buffer[token1.end_mark.pointer:
token2.start_mark.pointer]
line_no = token1.end_mark.line + 1
column_no = token1.end_mark.column + 1
pointer = token1.end_mark.pointer
for line in buf.split('\n'):
if skip_first_line:
skip_first_line = False
else:
pos = line.find('#')
if pos != -1:
yield Comment(line_no, column_no + pos,
token1.end_mark.buffer, pointer + pos)
pointer += len(line) + 1
line_no += 1
column_no = 1
def is_explicit_key(token):
# explicit key:
# ? key

View File

@@ -19,12 +19,12 @@ Use this rule to require or forbid the use of document end marker (``...``).
.. rubric:: Options
* Set ``present`` to ``true`` when the document end marker is required, or to
``false`` when it is forbidden.
* Set ``present`` to ``yes`` when the document end marker is required, or to
``no`` when it is forbidden.
.. rubric:: Examples
#. With ``document-end: {present: true}``
#. With ``document-end: {present: yes}``
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: false}``
#. With ``document-end: {present: no}``
the following code snippet would **PASS**:
::

View File

@@ -19,12 +19,12 @@ Use this rule to require or forbid the use of document start marker (``---``).
.. rubric:: Options
* Set ``present`` to ``true`` when the document start marker is required, or to
``false`` when it is forbidden.
* Set ``present`` to ``yes`` when the document start marker is required, or to
``no`` when it is forbidden.
.. rubric:: Examples
#. With ``document-start: {present: true}``
#. With ``document-start: {present: yes}``
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: false}``
#. With ``document-start: {present: no}``
the following code snippet would **PASS**:
::

View File

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

View File

@@ -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: ``true``,
``false``, ``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: ``yes``,
``no``, ``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 ``true`` to enable, ``false`` to disable.
multi-line strings. Set to ``yes`` to enable, ``no`` to disable.
.. rubric:: Examples
@@ -99,7 +99,7 @@ Use this rule to control the indentation.
Russian:
dolls
#. With ``indentation: {spaces: 2, indent-sequences: false}``
#. With ``indentation: {spaces: 2, indent-sequences: no}``
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: true}``
#. With ``indentation: {spaces: 4, check-multi-line-strings: yes}``
the following code snippet would **PASS**:
::
@@ -399,10 +399,6 @@ def _check(conf, token, prev, next, nextnext, context):
# - item 1
# - item 2
indent = next.start_mark.column
elif next.start_mark.column == token.start_mark.column:
# -
# key: value
indent = next.start_mark.column
else:
# -
# item 1
@@ -473,19 +469,7 @@ 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:
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)
indent = detect_indent(context['stack'][-1].indent, next)
else: # 'whatever' or 'consistent'
if next.start_mark.column == context['stack'][-1].indent:
# key:

View File

@@ -1,110 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 Johannes F. Knauf
#
# 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/>.
"""
Use this rule to enforce alphabetical ordering of keys in mappings. The sorting
order uses the Unicode code point number. As a result, the ordering is
case-sensitive and not accent-friendly (see examples below).
.. rubric:: Examples
#. With ``key-ordering: {}``
the following code snippet would **PASS**:
::
- key 1: v
key 2: val
key 3: value
- {a: 1, b: 2, c: 3}
- T-shirt: 1
T-shirts: 2
t-shirt: 3
t-shirts: 4
- hair: true
hais: true
haïr: true
haïssable: true
the following code snippet would **FAIL**:
::
- key 2: v
key 1: val
the following code snippet would **FAIL**:
::
- {b: 1, a: 2}
the following code snippet would **FAIL**:
::
- T-shirt: 1
t-shirt: 2
T-shirts: 3
t-shirts: 4
the following code snippet would **FAIL**:
::
- haïr: true
hais: true
"""
import yaml
from yamllint.linter import LintProblem
ID = 'key-ordering'
TYPE = 'token'
CONF = {}
MAP, SEQ = range(2)
class Parent(object):
def __init__(self, type):
self.type = type
self.keys = []
def check(conf, token, prev, next, nextnext, context):
if 'stack' not in context:
context['stack'] = []
if isinstance(token, (yaml.BlockMappingStartToken,
yaml.FlowMappingStartToken)):
context['stack'].append(Parent(MAP))
elif isinstance(token, (yaml.BlockSequenceStartToken,
yaml.FlowSequenceStartToken)):
context['stack'].append(Parent(SEQ))
elif isinstance(token, (yaml.BlockEndToken,
yaml.FlowMappingEndToken,
yaml.FlowSequenceEndToken)):
context['stack'].pop()
elif (isinstance(token, yaml.KeyToken) and
isinstance(next, yaml.ScalarToken)):
# This check is done because KeyTokens can be found inside flow
# sequences... strange, but allowed.
if len(context['stack']) > 0 and context['stack'][-1].type == MAP:
if any(next.value < key for key in context['stack'][-1].keys):
yield LintProblem(
next.start_mark.line + 1, next.start_mark.column + 1,
'wrong ordering of key "%s" in mapping' % next.value)
else:
context['stack'][-1].keys.append(next.value)

View File

@@ -22,9 +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 ``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.
instance. Use ``yes`` to allow, ``no`` to forbid.
.. rubric:: Examples
@@ -44,7 +42,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: true}``
#. With ``line-length: {max: 60, allow-non-breakable-words: yes}``
the following code snippet would **PASS**:
::
@@ -61,22 +59,9 @@ Use this rule to set a limit to lines length.
the following code snippet would **FAIL**:
::
- this line is waaaaaaaaaaaaaay too long but could be easily split...
- this line is waaaaaaaaaaaaaay too long but could be easily splitted...
and the following code snippet would also **FAIL**:
::
- foobar: http://localhost/very/very/very/very/very/very/very/very/long/url
#. 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: false}``
#. With ``line-length: {max: 60, allow-non-breakable-words: no}``
the following code snippet would **FAIL**:
::
@@ -88,55 +73,29 @@ Use this rule to set a limit to lines length.
"""
import yaml
from yamllint.linter import LintProblem
ID = 'line-length'
TYPE = 'line'
CONF = {'max': int,
'allow-non-breakable-words': bool,
'allow-non-breakable-inline-mappings': bool}
def check_inline_mapping(line):
loader = yaml.SafeLoader(line.content)
try:
while loader.peek_token():
if isinstance(loader.get_token(), yaml.BlockMappingStartToken):
while loader.peek_token():
if isinstance(loader.get_token(), yaml.ValueToken):
t = loader.get_token()
if isinstance(t, yaml.ScalarToken):
return (
' ' not in line.content[t.start_mark.column:])
except yaml.scanner.ScannerError:
pass
return False
'allow-non-breakable-words': bool}
def check(conf, line):
if line.end - line.start > conf['max']:
conf['allow-non-breakable-words'] |= \
conf['allow-non-breakable-inline-mappings']
if conf['allow-non-breakable-words']:
start = line.start
while start < line.end and line.buffer[start] == ' ':
start += 1
if start != line.end:
if line.buffer[start] in ('#', '-'):
if line.buffer[start] == '#':
start += 2
if line.buffer.find(' ', start, line.end) == -1:
return
if (conf['allow-non-breakable-inline-mappings'] and
check_inline_mapping(line)):
return
yield LintProblem(line.line_no, conf['max'] + 1,
'line too long (%d > %d characters)' %
(line.end - line.start, conf['max']))

View File

@@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Peter Ericson
#
# 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/>.
"""
Use this rule to forbid truthy values that are not quoted nor explicitly typed.
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
#. With ``truthy: {}``
the following code snippet would **PASS**:
::
boolean: true
object: {"True": 1, 1: "True"}
"yes": 1
"on": 2
"true": 3
"True": 4
explicit:
string1: !!str True
string2: !!str yes
string3: !!str off
encoded: !!binary |
True
OFF
pad== # this decodes as 'N\xbb\x9e8Qii'
boolean1: !!bool true
boolean2: !!bool "false"
boolean3: !!bool FALSE
boolean4: !!bool True
boolean5: !!bool off
boolean6: !!bool NO
the following code snippet would **FAIL**:
::
object: {True: 1, 1: True}
the following code snippet would **FAIL**:
::
yes: 1
on: 2
true: 3
True: 4
"""
import yaml
from yamllint.linter import LintProblem
ID = 'truthy'
TYPE = 'token'
CONF = {}
TRUTHY = ['YES', 'Yes', 'yes',
'NO', 'No', 'no',
'TRUE', 'True', # 'true' is a boolean
'FALSE', 'False', # 'false' is a boolean
'ON', 'On', 'on',
'OFF', 'Off', 'off']
def check(conf, token, prev, next, nextnext, context):
if prev and isinstance(prev, yaml.tokens.TagToken):
return
if isinstance(token, yaml.tokens.ScalarToken):
if token.value in TRUTHY and token.style is None:
yield LintProblem(token.start_mark.line + 1,
token.start_mark.column + 1,
"truthy value is not quoted")