Compare commits

..

1 Commits

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

View File

@@ -11,7 +11,7 @@ install:
- pip install . - pip install .
script: script:
- flake8 . - flake8 .
- yamllint --strict $(git ls-files '*.yaml' '*.yml') - yamllint $(git ls-files '*.yaml' '*.yml')
- coverage run --source=yamllint setup.py test - coverage run --source=yamllint setup.py test
after_success: after_success:
coveralls coveralls

View File

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

View File

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

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,6 +23,5 @@ Table of contents
quickstart quickstart
configuration configuration
rules rules
disable_with_comments
development development
text_editors text_editors

View File

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

View File

@@ -93,8 +93,3 @@ trailing-spaces
--------------- ---------------
.. automodule:: yamllint.rules.trailing_spaces .. automodule:: yamllint.rules.trailing_spaces
truthy
---------------
.. automodule:: yamllint.rules.truthy

View File

@@ -9,12 +9,8 @@ text editor.
Vim Vim
--- ---
Assuming that the `ALE <https://github.com/w0rp/ale>`_ plugin is Assuming that the `syntastic <https://github.com/scrooloose/syntastic>`_ plugin
installed, yamllint is supported by default. It is automatically enabled when is installed, add to your ``.vimrc``:
editing YAML files.
If you instead use the `syntastic <https://github.com/scrooloose/syntastic>`_
plugin, add this 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 installed, yamllint is supported by default. It is automatically enabled when
editing YAML files. 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 Other text editors
------------------ ------------------

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
files: \.(yaml|yml)$

View File

@@ -1,2 +0,0 @@
[bdist_wheel]
universal = 1

View File

@@ -42,7 +42,7 @@ setup(
'Topic :: Software Development :: Testing', 'Topic :: Software Development :: Testing',
], ],
packages=find_packages(exclude=['tests', 'tests.*']), packages=find_packages(),
entry_points={'console_scripts': ['yamllint=yamllint.cli:run']}, entry_points={'console_scripts': ['yamllint=yamllint.cli:run']},
package_data={'yamllint': ['conf/*.yaml'], package_data={'yamllint': ['conf/*.yaml'],
'tests': ['yaml-1.2-spec-examples/*']}, 'tests': ['yaml-1.2-spec-examples/*']},

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

@@ -32,19 +32,11 @@ class ColonTestCase(RuleTestCase):
'dict7: { a: 1, b, c: 3 }\n', conf) 'dict7: { a: 1, b, c: 3 }\n', conf)
def test_min_spaces(self): def test_min_spaces(self):
conf = ('braces:\n' conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 0}'
' max-spaces-inside: -1\n'
' min-spaces-inside: 0\n'
' max-spaces-inside-empty: -1\n'
' min-spaces-inside-empty: -1\n')
self.check('---\n' self.check('---\n'
'dict: {}\n', conf) 'dict: {}\n', conf)
conf = ('braces:\n' conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 1}'
' 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' self.check('---\n'
'dict: {}\n', conf, problem=(2, 8)) 'dict: {}\n', conf, problem=(2, 8))
self.check('---\n' self.check('---\n'
@@ -60,11 +52,7 @@ class ColonTestCase(RuleTestCase):
' b\n' ' b\n'
'}\n', conf) '}\n', conf)
conf = ('braces:\n' conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 3}'
' max-spaces-inside: -1\n'
' min-spaces-inside: 3\n'
' max-spaces-inside-empty: -1\n'
' min-spaces-inside-empty: -1\n')
self.check('---\n' self.check('---\n'
'dict: { a: 1, b }\n', conf, 'dict: { a: 1, b }\n', conf,
problem1=(2, 9), problem2=(2, 17)) problem1=(2, 9), problem2=(2, 17))
@@ -72,11 +60,7 @@ class ColonTestCase(RuleTestCase):
'dict: { a: 1, b }\n', conf) 'dict: { a: 1, b }\n', conf)
def test_max_spaces(self): def test_max_spaces(self):
conf = ('braces:\n' conf = 'braces: {max-spaces-inside: 0, min-spaces-inside: -1}'
' 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' self.check('---\n'
'dict: {}\n', conf) 'dict: {}\n', conf)
self.check('---\n' self.check('---\n'
@@ -95,11 +79,7 @@ class ColonTestCase(RuleTestCase):
' b\n' ' b\n'
'}\n', conf) '}\n', conf)
conf = ('braces:\n' conf = 'braces: {max-spaces-inside: 3, min-spaces-inside: -1}'
' max-spaces-inside: 3\n'
' min-spaces-inside: -1\n'
' max-spaces-inside-empty: -1\n'
' min-spaces-inside-empty: -1\n')
self.check('---\n' self.check('---\n'
'dict: { a: 1, b }\n', conf) 'dict: { a: 1, b }\n', conf)
self.check('---\n' self.check('---\n'
@@ -107,11 +87,7 @@ class ColonTestCase(RuleTestCase):
problem1=(2, 11), problem2=(2, 23)) problem1=(2, 11), problem2=(2, 23))
def test_min_and_max_spaces(self): def test_min_and_max_spaces(self):
conf = ('braces:\n' conf = 'braces: {max-spaces-inside: 0, min-spaces-inside: 0}'
' max-spaces-inside: 0\n'
' min-spaces-inside: 0\n'
' max-spaces-inside-empty: -1\n'
' min-spaces-inside-empty: -1\n')
self.check('---\n' self.check('---\n'
'dict: {}\n', conf) 'dict: {}\n', conf)
self.check('---\n' self.check('---\n'
@@ -119,169 +95,14 @@ class ColonTestCase(RuleTestCase):
self.check('---\n' self.check('---\n'
'dict: { a: 1, b}\n', conf, problem=(2, 10)) 'dict: { a: 1, b}\n', conf, problem=(2, 10))
conf = ('braces:\n' conf = 'braces: {max-spaces-inside: 1, min-spaces-inside: 1}'
' 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' self.check('---\n'
'dict: {a: 1, b, c: 3 }\n', conf, problem=(2, 8)) 'dict: {a: 1, b, c: 3 }\n', conf, problem=(2, 8))
conf = ('braces:\n' conf = 'braces: {max-spaces-inside: 2, min-spaces-inside: 0}'
' max-spaces-inside: 2\n'
' min-spaces-inside: 0\n'
' max-spaces-inside-empty: -1\n'
' min-spaces-inside-empty: -1\n')
self.check('---\n' self.check('---\n'
'dict: {a: 1, b, c: 3 }\n', conf) 'dict: {a: 1, b, c: 3 }\n', conf)
self.check('---\n' self.check('---\n'
'dict: { a: 1, b, c: 3 }\n', conf) 'dict: { a: 1, b, c: 3 }\n', conf)
self.check('---\n' self.check('---\n'
'dict: { a: 1, b, c: 3 }\n', conf, problem=(2, 10)) '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) 'array7: [ a, b, c ]\n', conf)
def test_min_spaces(self): def test_min_spaces(self):
conf = ('brackets:\n' conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 0}'
' max-spaces-inside: -1\n'
' min-spaces-inside: 0\n'
' max-spaces-inside-empty: -1\n'
' min-spaces-inside-empty: -1\n')
self.check('---\n' self.check('---\n'
'array: []\n', conf) 'array: []\n', conf)
conf = ('brackets:\n' conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 1}'
' 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' self.check('---\n'
'array: []\n', conf, problem=(2, 9)) 'array: []\n', conf, problem=(2, 9))
self.check('---\n' self.check('---\n'
@@ -59,11 +51,7 @@ class ColonTestCase(RuleTestCase):
' b\n' ' b\n'
']\n', conf) ']\n', conf)
conf = ('brackets:\n' conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 3}'
' max-spaces-inside: -1\n'
' min-spaces-inside: 3\n'
' max-spaces-inside-empty: -1\n'
' min-spaces-inside-empty: -1\n')
self.check('---\n' self.check('---\n'
'array: [ a, b ]\n', conf, 'array: [ a, b ]\n', conf,
problem1=(2, 10), problem2=(2, 15)) problem1=(2, 10), problem2=(2, 15))
@@ -71,11 +59,7 @@ class ColonTestCase(RuleTestCase):
'array: [ a, b ]\n', conf) 'array: [ a, b ]\n', conf)
def test_max_spaces(self): def test_max_spaces(self):
conf = ('brackets:\n' conf = 'brackets: {max-spaces-inside: 0, min-spaces-inside: -1}'
' 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' self.check('---\n'
'array: []\n', conf) 'array: []\n', conf)
self.check('---\n' self.check('---\n'
@@ -94,11 +78,7 @@ class ColonTestCase(RuleTestCase):
' b\n' ' b\n'
']\n', conf) ']\n', conf)
conf = ('brackets:\n' conf = 'brackets: {max-spaces-inside: 3, min-spaces-inside: -1}'
' max-spaces-inside: 3\n'
' min-spaces-inside: -1\n'
' max-spaces-inside-empty: -1\n'
' min-spaces-inside-empty: -1\n')
self.check('---\n' self.check('---\n'
'array: [ a, b ]\n', conf) 'array: [ a, b ]\n', conf)
self.check('---\n' self.check('---\n'
@@ -106,11 +86,7 @@ class ColonTestCase(RuleTestCase):
problem1=(2, 12), problem2=(2, 21)) problem1=(2, 12), problem2=(2, 21))
def test_min_and_max_spaces(self): def test_min_and_max_spaces(self):
conf = ('brackets:\n' conf = 'brackets: {max-spaces-inside: 0, min-spaces-inside: 0}'
' max-spaces-inside: 0\n'
' min-spaces-inside: 0\n'
' max-spaces-inside-empty: -1\n'
' min-spaces-inside-empty: -1\n')
self.check('---\n' self.check('---\n'
'array: []\n', conf) 'array: []\n', conf)
self.check('---\n' self.check('---\n'
@@ -118,169 +94,14 @@ class ColonTestCase(RuleTestCase):
self.check('---\n' self.check('---\n'
'array: [ a, b]\n', conf, problem=(2, 11)) 'array: [ a, b]\n', conf, problem=(2, 11))
conf = ('brackets:\n' conf = 'brackets: {max-spaces-inside: 1, min-spaces-inside: 1}'
' 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' self.check('---\n'
'array: [a, b, c ]\n', conf, problem=(2, 9)) 'array: [a, b, c ]\n', conf, problem=(2, 9))
conf = ('brackets:\n' conf = 'brackets: {max-spaces-inside: 2, min-spaces-inside: 0}'
' max-spaces-inside: 2\n'
' min-spaces-inside: 0\n'
' max-spaces-inside-empty: -1\n'
' min-spaces-inside-empty: -1\n')
self.check('---\n' self.check('---\n'
'array: [a, b, c ]\n', conf) 'array: [a, b, c ]\n', conf)
self.check('---\n' self.check('---\n'
'array: [ a, b, c ]\n', conf) 'array: [ a, b, c ]\n', conf)
self.check('---\n' self.check('---\n'
'array: [ a, b, c ]\n', conf, problem=(2, 11)) '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 bis\n'
' # comment 3 ter\n' ' # comment 3 ter\n'
'\n' '\n'
'################################\n'
'## comment 4\n'
'##comment 5\n'
'\n'
'string: "Une longue phrase." # this is French\n', conf) 'string: "Une longue phrase." # this is French\n', conf)
def test_starting_space(self): def test_starting_space(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: true\n' ' require-starting-space: yes\n'
' min-spaces-from-content: -1\n' ' min-spaces-from-content: -1\n'
'comments-indentation: disable\n') 'comments-indentation: disable\n')
self.check('---\n' self.check('---\n'
@@ -56,11 +52,7 @@ class CommentsTestCase(RuleTestCase):
'# comment 2\n' '# comment 2\n'
'# comment 3\n' '# comment 3\n'
' # comment 3 bis\n' ' # comment 3 bis\n'
' # comment 3 ter\n' ' # comment 3 ter\n', conf)
'\n'
'################################\n'
'## comment 4\n'
'## comment 5\n', conf)
self.check('---\n' self.check('---\n'
'#comment\n' '#comment\n'
'\n' '\n'
@@ -71,18 +63,13 @@ class CommentsTestCase(RuleTestCase):
'# comment 2\n' '# comment 2\n'
'#comment 3\n' '#comment 3\n'
' #comment 3 bis\n' ' #comment 3 bis\n'
' # comment 3 ter\n' ' # comment 3 ter\n', conf,
'\n'
'################################\n'
'## comment 4\n'
'##comment 5\n', conf,
problem1=(2, 2), problem2=(6, 13), problem1=(2, 2), problem2=(6, 13),
problem3=(9, 2), problem4=(10, 4), problem4=(9, 2), problem5=(10, 4))
problem5=(15, 3))
def test_spaces_from_content(self): def test_spaces_from_content(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: false\n' ' require-starting-space: no\n'
' min-spaces-from-content: 2\n') ' min-spaces-from-content: 2\n')
self.check('---\n' self.check('---\n'
'# comment\n' '# comment\n'
@@ -104,7 +91,7 @@ class CommentsTestCase(RuleTestCase):
def test_both(self): def test_both(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: true\n' ' require-starting-space: yes\n'
' min-spaces-from-content: 2\n' ' min-spaces-from-content: 2\n'
'comments-indentation: disable\n') 'comments-indentation: disable\n')
self.check('---\n' self.check('---\n'
@@ -119,22 +106,17 @@ class CommentsTestCase(RuleTestCase):
' #comment 3 bis\n' ' #comment 3 bis\n'
' # comment 3 ter\n' ' # comment 3 ter\n'
'\n' '\n'
'################################\n'
'## comment 4\n'
'##comment 5\n'
'\n'
'string: "Une longue phrase." # this is French\n', conf, 'string: "Une longue phrase." # this is French\n', conf,
problem1=(2, 2), problem1=(2, 2),
problem2=(4, 7), problem2=(4, 7),
problem3=(6, 11), problem4=(6, 12), problem3=(6, 11), problem4=(6, 12),
problem5=(9, 2), problem5=(9, 2),
problem6=(10, 4), problem6=(10, 4),
problem7=(15, 3), problem7=(13, 30))
problem8=(17, 30))
def test_empty_comment(self): def test_empty_comment(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: true\n' ' require-starting-space: yes\n'
' min-spaces-from-content: 2\n') ' min-spaces-from-content: 2\n')
self.check('---\n' self.check('---\n'
'# This is paragraph 1.\n' '# This is paragraph 1.\n'
@@ -146,21 +128,13 @@ class CommentsTestCase(RuleTestCase):
def test_first_line(self): def test_first_line(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: true\n' ' require-starting-space: yes\n'
' min-spaces-from-content: 2\n') ' min-spaces-from-content: 2\n')
self.check('# comment\n', conf) 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): def test_multi_line_scalar(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: true\n' ' require-starting-space: yes\n'
' min-spaces-from-content: 2\n' ' min-spaces-from-content: 2\n'
'trailing-spaces: disable\n') 'trailing-spaces: disable\n')
self.check('---\n' self.check('---\n'

View File

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

View File

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

View File

@@ -31,7 +31,7 @@ class DocumentEndTestCase(RuleTestCase):
' document: end\n', conf) ' document: end\n', conf)
def test_required(self): def test_required(self):
conf = 'document-end: {present: true}' conf = 'document-end: {present: yes}'
self.check('', conf) self.check('', conf)
self.check('\n', conf) self.check('\n', conf)
self.check('---\n' self.check('---\n'
@@ -43,7 +43,7 @@ class DocumentEndTestCase(RuleTestCase):
' document: end\n', conf, problem=(3, 1)) ' document: end\n', conf, problem=(3, 1))
def test_forbidden(self): def test_forbidden(self):
conf = 'document-end: {present: false}' conf = 'document-end: {present: no}'
self.check('---\n' self.check('---\n'
'with:\n' 'with:\n'
' document: end\n' ' document: end\n'
@@ -53,7 +53,7 @@ class DocumentEndTestCase(RuleTestCase):
' document: end\n', conf) ' document: end\n', conf)
def test_multiple_documents(self): def test_multiple_documents(self):
conf = ('document-end: {present: true}\n' conf = ('document-end: {present: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('---\n' self.check('---\n'
'first: document\n' 'first: document\n'

View File

@@ -28,7 +28,7 @@ class DocumentStartTestCase(RuleTestCase):
'key: val\n', conf) 'key: val\n', conf)
def test_required(self): def test_required(self):
conf = ('document-start: {present: true}\n' conf = ('document-start: {present: yes}\n'
'empty-lines: disable\n') 'empty-lines: disable\n')
self.check('', conf) self.check('', conf)
self.check('\n', conf) self.check('\n', conf)
@@ -44,7 +44,7 @@ class DocumentStartTestCase(RuleTestCase):
'key: val\n', conf) 'key: val\n', conf)
def test_forbidden(self): def test_forbidden(self):
conf = ('document-start: {present: false}\n' conf = ('document-start: {present: no}\n'
'empty-lines: disable\n') 'empty-lines: disable\n')
self.check('', conf) self.check('', conf)
self.check('key: val\n', conf) self.check('key: val\n', conf)
@@ -62,7 +62,7 @@ class DocumentStartTestCase(RuleTestCase):
'key: val\n', conf, problem=(2, 1)) 'key: val\n', conf, problem=(2, 1))
def test_multiple_documents(self): def test_multiple_documents(self):
conf = 'document-start: {present: true}' conf = 'document-start: {present: yes}'
self.check('---\n' self.check('---\n'
'first: document\n' 'first: document\n'
'...\n' '...\n'
@@ -85,7 +85,7 @@ class DocumentStartTestCase(RuleTestCase):
'third: document\n', conf, problem=(4, 1, 'syntax')) 'third: document\n', conf, problem=(4, 1, 'syntax'))
def test_directives(self): def test_directives(self):
conf = 'document-start: {present: true}' conf = 'document-start: {present: yes}'
self.check('%YAML 1.2\n' self.check('%YAML 1.2\n'
'---\n' '---\n'
'doc: ument\n' 'doc: ument\n'

View File

@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.common import RuleTestCase from tests.common import RuleTestCase
from yamllint.parser import token_or_comment_generator, Comment from yamllint.parser import token_generator
from yamllint.rules.indentation import check from yamllint.rules.indentation import check
@@ -38,8 +38,7 @@ class IndentationStackTestCase(RuleTestCase):
'check-multi-line-strings': False} 'check-multi-line-strings': False}
context = {} context = {}
output = '' output = ''
for elem in [t for t in token_or_comment_generator(source) for elem in token_generator(source):
if not isinstance(t, Comment)]:
list(check(conf, elem.curr, elem.prev, elem.next, elem.nextnext, list(check(conf, elem.curr, elem.prev, elem.next, elem.nextnext,
context)) context))
@@ -549,7 +548,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf) '...\n', conf)
def test_one_space(self): def test_one_space(self):
conf = 'indentation: {spaces: 1, indent-sequences: false}' conf = 'indentation: {spaces: 1, indent-sequences: no}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -562,7 +561,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n' ' - name: Linux\n'
' date: 1991\n' ' date: 1991\n'
'...\n', conf) '...\n', conf)
conf = 'indentation: {spaces: 1, indent-sequences: true}' conf = 'indentation: {spaces: 1, indent-sequences: yes}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -577,7 +576,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf) '...\n', conf)
def test_two_spaces(self): def test_two_spaces(self):
conf = 'indentation: {spaces: 2, indent-sequences: false}' conf = 'indentation: {spaces: 2, indent-sequences: no}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -590,7 +589,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n' ' - name: Linux\n'
' date: 1991\n' ' date: 1991\n'
'...\n', conf) '...\n', conf)
conf = 'indentation: {spaces: 2, indent-sequences: true}' conf = 'indentation: {spaces: 2, indent-sequences: yes}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -605,7 +604,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf) '...\n', conf)
def test_three_spaces(self): def test_three_spaces(self):
conf = 'indentation: {spaces: 3, indent-sequences: false}' conf = 'indentation: {spaces: 3, indent-sequences: no}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -618,7 +617,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n' ' - name: Linux\n'
' date: 1991\n' ' date: 1991\n'
'...\n', conf) '...\n', conf)
conf = 'indentation: {spaces: 3, indent-sequences: true}' conf = 'indentation: {spaces: 3, indent-sequences: yes}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -632,7 +631,7 @@ class IndentationTestCase(RuleTestCase):
' date: 1991\n' ' date: 1991\n'
'...\n', conf) '...\n', conf)
def test_consistent_spaces(self): def test_consistent(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' indent-sequences: whatever}\n' ' indent-sequences: whatever}\n'
'document-start: disable\n') 'document-start: disable\n')
@@ -713,142 +712,6 @@ class IndentationTestCase(RuleTestCase):
'- b\n' '- b\n'
'- c\n', conf) '- 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): def test_indent_sequences_whatever(self):
conf = 'indentation: {spaces: 4, indent-sequences: whatever}' conf = 'indentation: {spaces: 4, indent-sequences: whatever}'
self.check('---\n' self.check('---\n'
@@ -1266,7 +1129,7 @@ class IndentationTestCase(RuleTestCase):
problem=(2, 3)) problem=(2, 3))
def test_multi_lines(self): def test_multi_lines(self):
conf = 'indentation: {spaces: consistent, indent-sequences: true}' conf = 'indentation: {spaces: consistent, indent-sequences: yes}'
self.check('---\n' self.check('---\n'
'long_string: >\n' 'long_string: >\n'
' bla bla blah\n' ' bla bla blah\n'
@@ -1574,7 +1437,7 @@ class IndentationTestCase(RuleTestCase):
'- !!map # Block collection\n' '- !!map # Block collection\n'
' foo: bar\n', conf) ' foo: bar\n', conf)
conf = 'indentation: {spaces: consistent, indent-sequences: false}' conf = 'indentation: {spaces: consistent, indent-sequences: no}'
self.check('---\n' self.check('---\n'
'sequence: !!seq\n' 'sequence: !!seq\n'
'- entry\n' '- entry\n'
@@ -1641,7 +1504,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_plain(self): def test_basics_plain(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: false}\n' ' check-multi-line-strings: no}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('multi\n' self.check('multi\n'
'line\n', conf) 'line\n', conf)
@@ -1670,7 +1533,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_plain(self): def test_check_multi_line_plain(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n' ' check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('multi\n' self.check('multi\n'
' line\n', conf, problem=(2, 2)) ' line\n', conf, problem=(2, 2))
@@ -1693,7 +1556,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_quoted(self): def test_basics_quoted(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: false}\n' ' check-multi-line-strings: no}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('"multi\n' self.check('"multi\n'
' line"\n', conf) ' line"\n', conf)
@@ -1724,7 +1587,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_quoted(self): def test_check_multi_line_quoted(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n' ' check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('"multi\n' self.check('"multi\n'
'line"\n', conf, problem=(2, 1)) 'line"\n', conf, problem=(2, 1))
@@ -1780,7 +1643,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_folded_style(self): def test_basics_folded_style(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: false}\n' ' check-multi-line-strings: no}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('>\n' self.check('>\n'
' multi\n' ' multi\n'
@@ -1818,7 +1681,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_folded_style(self): def test_check_multi_line_folded_style(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n' ' check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('>\n' self.check('>\n'
' multi\n' ' multi\n'
@@ -1859,7 +1722,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_literal_style(self): def test_basics_literal_style(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: false}\n' ' check-multi-line-strings: no}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('|\n' self.check('|\n'
' multi\n' ' multi\n'
@@ -1897,7 +1760,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_literal_style(self): def test_check_multi_line_literal_style(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n' ' check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('|\n' self.check('|\n'
' multi\n' ' multi\n'
@@ -1941,7 +1804,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_plain(self): def test_paragraph_plain(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n' ' check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('- long text: very "long"\n' self.check('- long text: very "long"\n'
' \'string\' with\n' ' \'string\' with\n'
@@ -1963,7 +1826,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_double_quoted(self): def test_paragraph_double_quoted(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n' ' check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('- long text: "very \\"long\\"\n' self.check('- long text: "very \\"long\\"\n'
' \'string\' with\n' ' \'string\' with\n'
@@ -1991,7 +1854,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_single_quoted(self): def test_paragraph_single_quoted(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n' ' check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('- long text: \'very "long"\n' self.check('- long text: \'very "long"\n'
' \'\'string\'\' with\n' ' \'\'string\'\' with\n'
@@ -2019,7 +1882,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_folded(self): def test_paragraph_folded(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n' ' check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('- long text: >\n' self.check('- long text: >\n'
' very "long"\n' ' very "long"\n'
@@ -2037,7 +1900,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_literal(self): def test_paragraph_literal(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n' ' check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('- long text: |\n' self.check('- long text: |\n'
' very "long"\n' ' very "long"\n'
@@ -2055,7 +1918,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_consistent(self): def test_consistent(self):
conf = ('indentation: {spaces: consistent,\n' conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: true}\n' ' check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('multi\n' self.check('multi\n'
'line\n', conf) 'line\n', conf)

View File

@@ -32,9 +32,6 @@ class LineLengthTestCase(RuleTestCase):
self.check('---\n' + 81 * 'a' + '\n', conf) self.check('---\n' + 81 * 'a' + '\n', conf)
self.check(1000 * 'b', conf) self.check(1000 * 'b', conf)
self.check('---\n' + 1000 * 'b' + '\n', conf) self.check('---\n' + 1000 * 'b' + '\n', conf)
self.check('content: |\n'
' {% this line is' + 99 * ' really' + ' long %}\n',
conf)
def test_default(self): def test_default(self):
conf = ('line-length: {max: 80}\n' conf = ('line-length: {max: 80}\n'
@@ -66,7 +63,7 @@ class LineLengthTestCase(RuleTestCase):
self.check('---\n' + 81 * ' ' + '\n', conf, problem=(2, 81)) self.check('---\n' + 81 * ' ' + '\n', conf, problem=(2, 81))
def test_non_breakable_word(self): 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' + 30 * 'A' + '\n', conf)
self.check('---\n' self.check('---\n'
'this:\n' 'this:\n'
@@ -81,17 +78,8 @@ class LineLengthTestCase(RuleTestCase):
' # http://localhost/very/long/url\n' ' # http://localhost/very/long/url\n'
' comment\n' ' comment\n'
'...\n', conf) '...\n', conf)
self.check('---\n'
'this:\n'
'is:\n'
'another:\n'
' - https://localhost/very/very/long/url\n'
'...\n', conf)
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' + 30 * 'A' + '\n', conf, problem=(2, 21))
self.check('---\n' self.check('---\n'
'this:\n' 'this:\n'
@@ -106,52 +94,3 @@ class LineLengthTestCase(RuleTestCase):
' # http://localhost/very/long/url\n' ' # http://localhost/very/long/url\n'
' comment\n' ' comment\n'
'...\n', conf, problem=(5, 21)) '...\n', conf, problem=(5, 21))
self.check('---\n'
'this:\n'
'is:\n'
'another:\n'
' - https://localhost/very/very/long/url\n'
'...\n', conf, problem=(5, 21))
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,10 +18,7 @@ try:
from cStringIO import StringIO from cStringIO import StringIO
except ImportError: except ImportError:
from io import StringIO from io import StringIO
import fcntl
import locale
import os import os
import pty
import shutil import shutil
import tempfile import tempfile
import unittest import unittest
@@ -40,10 +37,6 @@ class CommandLineTestCase(unittest.TestCase):
'- 1 \n' '- 1 \n'
'- 2') '- 2')
# file with only one warning
with open(os.path.join(self.wd, 'warn.yaml'), 'w') as f:
f.write('key: value\n')
# .yml file at root # .yml file at root
open(os.path.join(self.wd, 'empty.yml'), 'w').close() open(os.path.join(self.wd, 'empty.yml'), 'w').close()
@@ -71,15 +64,6 @@ class CommandLineTestCase(unittest.TestCase):
f.write('---\n' f.write('---\n'
'key: value\n') 'key: value\n')
# non-ASCII chars
os.mkdir(os.path.join(self.wd, 'non-ascii'))
with open(os.path.join(self.wd, 'non-ascii', 'utf-8'), 'wb') as f:
f.write((u'---\n'
u'- hétérogénéité\n'
u'# 19.99 €\n'
u'- お早う御座います。\n'
u'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'))
def tearDown(self): def tearDown(self):
shutil.rmtree(self.wd) shutil.rmtree(self.wd)
@@ -89,8 +73,7 @@ class CommandLineTestCase(unittest.TestCase):
[os.path.join(self.wd, 'a.yaml'), [os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'empty.yml'), 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, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'),
os.path.join(self.wd, 'sub/ok.yaml'), os.path.join(self.wd, 'sub/ok.yaml')],
os.path.join(self.wd, 'warn.yaml')],
) )
items = [os.path.join(self.wd, 'sub/ok.yaml'), items = [os.path.join(self.wd, 'sub/ok.yaml'),
@@ -170,49 +153,6 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(out, '') self.assertEqual(out, '')
self.assertRegexpMatches(err, r'^invalid config: not a dict') self.assertRegexpMatches(err, r'^invalid config: not a dict')
def test_run_with_config_file(self):
with open(os.path.join(self.wd, 'config'), 'w') as f:
f.write('rules: {trailing-spaces: disable}')
with self.assertRaises(SystemExit) as ctx:
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.exception.code, 0)
with open(os.path.join(self.wd, 'config'), 'w') as f:
f.write('rules: {trailing-spaces: enable}')
with self.assertRaises(SystemExit) as ctx:
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.exception.code, 1)
def test_run_with_user_global_config_file(self):
home = os.path.join(self.wd, 'fake-home')
os.mkdir(home)
dir = os.path.join(home, '.config')
os.mkdir(dir)
dir = os.path.join(dir, 'yamllint')
os.mkdir(dir)
config = os.path.join(dir, 'config')
temp = os.environ['HOME']
os.environ['HOME'] = home
with open(config, 'w') as f:
f.write('rules: {trailing-spaces: disable}')
with self.assertRaises(SystemExit) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.exception.code, 0)
with open(config, 'w') as f:
f.write('rules: {trailing-spaces: enable}')
with self.assertRaises(SystemExit) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.exception.code, 1)
os.environ['HOME'] = temp
def test_run_version(self): def test_run_version(self):
sys.stdout, sys.stderr = StringIO(), StringIO() sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx: with self.assertRaises(SystemExit) as ctx:
@@ -252,24 +192,6 @@ class CommandLineTestCase(unittest.TestCase):
'(new-line-at-end-of-file)\n') % (file, file)) '(new-line-at-end-of-file)\n') % (file, file))
self.assertEqual(err, '') 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): def test_run_one_ok_file(self):
file = os.path.join(self.wd, 'sub', 'ok.yaml') file = os.path.join(self.wd, 'sub', 'ok.yaml')
@@ -296,26 +218,6 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(out, '') self.assertEqual(out, '')
self.assertEqual(err, '') self.assertEqual(err, '')
def test_run_non_ascii_file(self):
file = os.path.join(self.wd, 'non-ascii', 'utf-8')
# 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): def test_run_multiple_files(self):
items = [os.path.join(self.wd, 'empty.yml'), items = [os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 's')] os.path.join(self.wd, 's')]
@@ -333,7 +235,7 @@ class CommandLineTestCase(unittest.TestCase):
'(key-duplicates)\n') % file) '(key-duplicates)\n') % file)
self.assertEqual(err, '') self.assertEqual(err, '')
def test_run_piped_output_nocolor(self): def test_run_colored_output(self):
file = os.path.join(self.wd, 'a.yaml') file = os.path.join(self.wd, 'a.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO() sys.stdout, sys.stderr = StringIO(), StringIO()
@@ -343,38 +245,6 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(ctx.exception.code, 1) self.assertEqual(ctx.exception.code, 1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue() 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, ( self.assertEqual(out, (
'\033[4m%s\033[0m\n' '\033[4m%s\033[0m\n'
' \033[2m2:4\033[0m \033[31merror\033[0m ' ' \033[2m2:4\033[0m \033[31merror\033[0m '
@@ -383,3 +253,4 @@ class CommandLineTestCase(unittest.TestCase):
'no new line character at the end of file ' 'no new line character at the end of file '
'\033[2m(new-line-at-end-of-file)\033[0m\n' '\033[2m(new-line-at-end-of-file)\033[0m\n'
'\n' % file)) '\n' % file))
self.assertEqual(err, '')

View File

@@ -62,45 +62,6 @@ class SimpleConfigTestCase(unittest.TestCase):
' max-spaces-after: 1\n' ' max-spaces-after: 1\n'
' abcdef: yes\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): def test_validate_rule_conf(self):
class Rule(object): class Rule(object):
ID = 'fake' ID = 'fake'

View File

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

View File

@@ -1,82 +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 unittest
class ModuleTestCase(unittest.TestCase):
def setUp(self):
self.wd = tempfile.mkdtemp(prefix='yamllint-tests-')
# file with only one warning
with open(os.path.join(self.wd, 'warn.yaml'), 'w') as f:
f.write('key: value\n')
# file in dir
os.mkdir(os.path.join(self.wd, 'sub'))
with open(os.path.join(self.wd, 'sub', 'nok.yaml'), 'w') as f:
f.write('---\n'
'list: [ 1, 1, 2, 3, 5, 8] \n')
def tearDown(self):
shutil.rmtree(self.wd)
def test_run_module_no_args(self):
with self.assertRaises(subprocess.CalledProcessError) as ctx:
subprocess.check_output(['python', '-m', 'yamllint'],
stderr=subprocess.STDOUT)
self.assertEqual(ctx.exception.returncode, 2)
self.assertRegexpMatches(ctx.exception.output.decode(),
r'^usage: yamllint')
def test_run_module_on_bad_dir(self):
with self.assertRaises(subprocess.CalledProcessError) as ctx:
subprocess.check_output(['python', '-m', 'yamllint',
'/does/not/exist'],
stderr=subprocess.STDOUT)
self.assertRegexpMatches(ctx.exception.output.decode(),
r'No such file or directory')
def test_run_module_on_file(self):
out = subprocess.check_output(
['python', '-m', 'yamllint', os.path.join(self.wd, 'warn.yaml')])
lines = out.decode().splitlines()
self.assertIn('/warn.yaml', lines[0])
self.assertEqual('\n'.join(lines[1:]),
' 1:1 warning missing document start "---"'
' (document-start)\n')
def test_run_module_on_dir(self):
with self.assertRaises(subprocess.CalledProcessError) as ctx:
subprocess.check_output(['python', '-m', 'yamllint', self.wd])
self.assertEqual(ctx.exception.returncode, 1)
files = ctx.exception.output.decode().split('\n\n')
self.assertIn(
'/warn.yaml\n'
' 1:1 warning missing document start "---"'
' (document-start)',
files[0])
self.assertIn(
'/sub/nok.yaml\n'
' 2:9 error too many spaces inside brackets'
' (brackets)\n'
' 2:27 error trailing spaces (trailing-spaces)',
files[1])

View File

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

View File

@@ -48,7 +48,6 @@ from tests.common import RuleTestCase
class SpecificationTestCase(RuleTestCase): class SpecificationTestCase(RuleTestCase):
rule_id = None rule_id = None
conf_general = ('document-start: disable\n' conf_general = ('document-start: disable\n'
'comments: {min-spaces-from-content: 1}\n' 'comments: {min-spaces-from-content: 1}\n'
'braces: {min-spaces-inside: 1, max-spaces-inside: 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.18': ('empty-lines: {max-end: 1}\n'),
'example-2.19': ('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-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'), 'colons: {max-spaces-before: 1}\n'),
'example-6.4': ('trailing-spaces: disable\n'), 'example-6.4': ('trailing-spaces: disable\n'),
'example-6.5': ('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.14': ('colons: {max-spaces-before: 1}\n'),
'example-8.16': ('indentation: {spaces: 1}\n'), 'example-8.16': ('indentation: {spaces: 1}\n'),
'example-8.17': ('indentation: disable\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'), 'colons: {max-spaces-before: 1}\n'),
'example-8.22': ('indentation: disable\n'), 'example-8.22': ('indentation: disable\n'),
'example-10.1': ('colons: {max-spaces-before: 2}\n'), 'example-10.1': ('colons: {max-spaces-before: 2}\n'),
'example-10.2': ('indentation: {indent-sequences: false}\n'), 'example-10.2': ('indentation: {indent-sequences: no}\n'),
'example-10.8': ('truthy: disable\n'),
'example-10.9': ('truthy: disable\n'),
} }
files = os.listdir(os.path.join(os.path.dirname(os.path.realpath(__file__)), 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) self.check(buffer, conf)
return test return test
# The following tests are blacklisted (i.e. will not be checked against # The following tests are blacklisted (i.e. will not be checked against
# yamllint), because pyyaml is currently not able to parse the contents # yamllint), because pyyaml is currently not able to parse the contents
# (using yaml.parse()). # (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_NAME = 'yamllint'
APP_VERSION = '1.7.0' APP_VERSION = '1.2.1'
APP_DESCRIPTION = __doc__ APP_DESCRIPTION = __doc__
__author__ = u'Adrien Vergé' __author__ = u'Adrien Vergé'

View File

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

View File

@@ -22,7 +22,6 @@ import argparse
from yamllint import APP_DESCRIPTION, APP_NAME, APP_VERSION from yamllint import APP_DESCRIPTION, APP_NAME, APP_VERSION
from yamllint.config import YamlLintConfig, YamlLintConfigError from yamllint.config import YamlLintConfig, YamlLintConfigError
from yamllint.linter import PROBLEM_LEVELS
from yamllint import linter from yamllint import linter
@@ -49,17 +48,6 @@ class Format(object):
@staticmethod @staticmethod
def standard(problem, filename): 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 = ' \033[2m%d:%d\033[0m' % (problem.line, problem.column)
line += max(20 - len(line), 0) * ' ' line += max(20 - len(line), 0) * ' '
if problem.level == 'warning': if problem.level == 'warning':
@@ -86,10 +74,6 @@ def run(argv=None):
parser.add_argument('-f', '--format', parser.add_argument('-f', '--format',
choices=('parsable', 'standard'), default='standard', choices=('parsable', 'standard'), default='standard',
help='format for parsing output') 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', parser.add_argument('-v', '--version', action='version',
version='%s %s' % (APP_NAME, APP_VERSION)) version='%s %s' % (APP_NAME, APP_VERSION))
@@ -102,13 +86,6 @@ def run(argv=None):
'simultaneously.', file=sys.stderr) 'simultaneously.', file=sys.stderr)
sys.exit(-1) sys.exit(-1)
# User-global config is supposed to be in ~/.config/yamllint/config
if 'XDG_CONFIG_HOME' in os.environ:
user_global_config = os.path.join(
os.environ['XDG_CONFIG_HOME'], 'yamllint', 'config')
else:
user_global_config = os.path.expanduser('~/.config/yamllint/config')
try: try:
if args.config_data is not None: if args.config_data is not None:
if args.config_data != '' and ':' not in args.config_data: if args.config_data != '' and ':' not in args.config_data:
@@ -118,15 +95,13 @@ def run(argv=None):
conf = YamlLintConfig(file=args.config_file) conf = YamlLintConfig(file=args.config_file)
elif os.path.isfile('.yamllint'): elif os.path.isfile('.yamllint'):
conf = YamlLintConfig(file='.yamllint') conf = YamlLintConfig(file='.yamllint')
elif os.path.isfile(user_global_config):
conf = YamlLintConfig(file=user_global_config)
else: else:
conf = YamlLintConfig('extends: default') conf = YamlLintConfig('extends: default')
except YamlLintConfigError as e: except YamlLintConfigError as e:
print(e, file=sys.stderr) print(e, file=sys.stderr)
sys.exit(-1) sys.exit(-1)
max_level = 0 return_code = 0
for file in find_files_recursively(args.files): for file in find_files_recursively(args.files):
try: try:
@@ -135,32 +110,20 @@ def run(argv=None):
for problem in linter.run(f, conf): for problem in linter.run(f, conf):
if args.format == 'parsable': if args.format == 'parsable':
print(Format.parsable(problem, file)) print(Format.parsable(problem, file))
elif sys.stdout.isatty(): else:
if first: if first:
print('\033[4m%s\033[0m' % file) print('\033[4m%s\033[0m' % file)
first = False first = False
print(Format.standard_color(problem, file))
else:
if first:
print(file)
first = False
print(Format.standard(problem, file)) 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': if not first and args.format != 'parsable':
print('') print('')
except EnvironmentError as e: except EnvironmentError as e:
print(e, file=sys.stderr) print(e, file=sys.stderr)
sys.exit(-1) return_code = -1
if max_level == PROBLEM_LEVELS['error']:
return_code = 1
elif max_level == PROBLEM_LEVELS['warning']:
return_code = 2 if args.strict else 0
else:
return_code = 0
sys.exit(return_code) sys.exit(return_code)

View File

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

View File

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

View File

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

View File

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

View File

@@ -31,7 +31,6 @@ from yamllint.rules import (
new_line_at_end_of_file, new_line_at_end_of_file,
new_lines, new_lines,
trailing_spaces, trailing_spaces,
truthy,
) )
_RULES = { _RULES = {
@@ -51,7 +50,6 @@ _RULES = {
new_line_at_end_of_file.ID: new_line_at_end_of_file, new_line_at_end_of_file.ID: new_line_at_end_of_file,
new_lines.ID: new_lines, new_lines.ID: new_lines,
trailing_spaces.ID: trailing_spaces, 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. braces.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside * ``max-spaces-inside`` defines the maximal number of spaces allowed inside
braces. 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 .. rubric:: Examples
@@ -63,30 +59,6 @@ Use this rule to control the number of spaces inside braces (``{`` and ``}``).
:: ::
object: {key1: 4, key2: 8 } 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' ID = 'braces'
TYPE = 'token' TYPE = 'token'
CONF = {'min-spaces-inside': int, CONF = {'min-spaces-inside': int,
'max-spaces-inside': int, 'max-spaces-inside': int}
'min-spaces-inside-empty': int,
'max-spaces-inside-empty': int}
def check(conf, token, prev, next, nextnext, context): def check(conf, token, prev, next, nextnext, context):
if (isinstance(token, yaml.FlowMappingStartToken) and if isinstance(token, yaml.FlowMappingStartToken):
isinstance(next, yaml.FlowMappingEndToken)):
problem = spaces_after(token, prev, next,
min=(conf['min-spaces-inside-empty']
if conf['min-spaces-inside-empty'] != -1
else conf['min-spaces-inside']),
max=(conf['max-spaces-inside-empty']
if conf['max-spaces-inside-empty'] != -1
else conf['max-spaces-inside']),
min_desc='too few spaces inside empty braces',
max_desc='too many spaces inside empty braces')
if problem is not None:
yield problem
elif isinstance(token, yaml.FlowMappingStartToken):
problem = spaces_after(token, prev, next, problem = spaces_after(token, prev, next,
min=conf['min-spaces-inside'], min=conf['min-spaces-inside'],
max=conf['max-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. brackets.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside * ``max-spaces-inside`` defines the maximal number of spaces allowed inside
brackets. 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 .. rubric:: Examples
@@ -64,30 +60,6 @@ Use this rule to control the number of spaces inside brackets (``[`` and
:: ::
object: [1, 2, abc ] 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' ID = 'brackets'
TYPE = 'token' TYPE = 'token'
CONF = {'min-spaces-inside': int, CONF = {'min-spaces-inside': int,
'max-spaces-inside': int, 'max-spaces-inside': int}
'min-spaces-inside-empty': int,
'max-spaces-inside-empty': int}
def check(conf, token, prev, next, nextnext, context): def check(conf, token, prev, next, nextnext, context):
if (isinstance(token, yaml.FlowSequenceStartToken) and if isinstance(token, yaml.FlowSequenceStartToken):
isinstance(next, yaml.FlowSequenceEndToken)):
problem = spaces_after(token, prev, next,
min=(conf['min-spaces-inside-empty']
if conf['min-spaces-inside-empty'] != -1
else conf['min-spaces-inside']),
max=(conf['max-spaces-inside-empty']
if conf['max-spaces-inside-empty'] != -1
else conf['max-spaces-inside']),
min_desc='too few spaces inside empty brackets',
max_desc=('too many spaces inside empty '
'brackets'))
if problem is not None:
yield problem
elif isinstance(token, yaml.FlowSequenceStartToken):
problem = spaces_after(token, prev, next, problem = spaces_after(token, prev, next,
min=conf['min-spaces-inside'], min=conf['min-spaces-inside'],
max=conf['max-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 .. rubric:: Options
* Use ``require-starting-space`` to require a space character right after the * 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 * ``min-spaces-from-content`` is used to visually separate inline comments from
content. It defines the minimal required number of spaces between a comment content. It defines the minimal required number of spaces between a comment
and its preceding content. and its preceding content.
.. rubric:: Examples .. rubric:: Examples
#. With ``comments: {require-starting-space: true}`` #. With ``comments: {require-starting-space: yes}``
the following code snippet would **PASS**: the following code snippet would **PASS**:
:: ::
@@ -35,12 +35,6 @@ Use this rule to control the position and formatting of comments.
# This sentence # This sentence
# is a block comment # is a block comment
the following code snippet would **PASS**:
::
##############################
## This is some documentation
the following code snippet would **FAIL**: 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.linter import LintProblem
from yamllint.rules.common import get_comments_between_tokens
ID = 'comments' ID = 'comments'
TYPE = 'comment' TYPE = 'token'
CONF = {'require-starting-space': bool, CONF = {'require-starting-space': bool,
'min-spaces-from-content': int} 'min-spaces-from-content': int}
def check(conf, comment): def check(conf, token, prev, next, nextnext, context):
if (conf['min-spaces-from-content'] != -1 and comment.is_inline() and for comment in get_comments_between_tokens(token, next):
comment.pointer - comment.token_before.end_mark.pointer < 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']): conf['min-spaces-from-content']):
yield LintProblem(comment.line_no, comment.column_no, yield LintProblem(comment.line, comment.column,
'too few spaces before comment') 'too few spaces before comment')
if conf['require-starting-space']: if (conf['require-starting-space'] and
text_start = comment.pointer + 1 comment.pointer + 1 < len(comment.buffer) and
while (comment.buffer[text_start] == '#' and comment.buffer[comment.pointer + 1] != ' ' and
text_start < len(comment.buffer)): comment.buffer[comment.pointer + 1] != '\n'):
text_start += 1 yield LintProblem(comment.line, comment.column + 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,
'missing starting space in comment') 'missing starting space in comment')

View File

@@ -78,11 +78,11 @@ Use this rule to force comments to be indented like content.
import yaml import yaml
from yamllint.linter import LintProblem from yamllint.linter import LintProblem
from yamllint.rules.common import get_line_indent from yamllint.rules.common import get_line_indent, get_comments_between_tokens
ID = 'comments-indentation' ID = 'comments-indentation'
TYPE = 'comment' TYPE = 'token'
# Case A: # Case A:
@@ -98,42 +98,28 @@ TYPE = 'comment'
# # commented line 2 # # commented line 2
# current: line # current: line
def check(conf, comment): def check(conf, token, prev, next, nextnext, context):
# Only check block comments if prev is None:
if (not isinstance(comment.token_before, yaml.StreamStartToken) and
comment.token_before.end_mark.line + 1 == comment.line_no):
return return
next_line_indent = comment.token_after.start_mark.column curr_line_indent = token.start_mark.column
if isinstance(comment.token_after, yaml.StreamEndToken): if isinstance(token, yaml.StreamEndToken):
next_line_indent = 0 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 prev_line_indent = 0
else: 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: if prev_line_indent <= curr_line_indent:
# list: prev_line_indent = -1 # disable it
# # comment
# - 1
# - 2
if prev_line_indent <= next_line_indent:
prev_line_indent = next_line_indent
# If two indents are valid but a previous comment went back to normal for comment in get_comments_between_tokens(
# indent, for the next ones to do the same. In other words, avoid this: prev, token, skip_first_line=skip_first_line):
# list: if comment.column - 1 == curr_line_indent:
# - 1 prev_line_indent = -1 # disable it
# # comment on valid indent (0) elif comment.column - 1 != prev_line_indent:
# # comment on valid indent (4) yield LintProblem(comment.line, comment.column,
# 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') '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) token.start_mark.column + 1, min_desc)
class Comment(object):
def __init__(self, line, column, buffer, pointer):
self.line = line
self.column = column
self.buffer = buffer
self.pointer = pointer
def __repr__(self):
end = self.buffer.find('\n', self.pointer)
if end == -1:
end = self.buffer.find('\0', self.pointer)
if end != -1:
return self.buffer[self.pointer:end]
return self.buffer[self.pointer:]
def __eq__(self, other):
return (self.line == other.line and
self.column == other.column and
str(self) == str(other))
def get_line_indent(token): def get_line_indent(token):
"""Finds the indent of the line the token starts in.""" """Finds the indent of the line the token starts in."""
start = token.start_mark.buffer.rfind('\n', 0, start = token.start_mark.buffer.rfind('\n', 0,
@@ -77,6 +98,35 @@ def get_real_end_line(token):
return end_line return end_line
def get_comments_between_tokens(token1, token2, skip_first_line=False):
if token2 is None:
buf = token1.end_mark.buffer[token1.end_mark.pointer:]
elif (token1.end_mark.line == token2.start_mark.line and
not isinstance(token1, yaml.StreamStartToken) and
not isinstance(token2, yaml.StreamEndToken)):
return
else:
buf = token1.end_mark.buffer[token1.end_mark.pointer:
token2.start_mark.pointer]
line_no = token1.end_mark.line + 1
column_no = token1.end_mark.column + 1
pointer = token1.end_mark.pointer
for line in buf.split('\n'):
if skip_first_line:
skip_first_line = False
else:
pos = line.find('#')
if pos != -1:
yield Comment(line_no, column_no + pos,
token1.end_mark.buffer, pointer + pos)
pointer += len(line) + 1
line_no += 1
column_no = 1
def is_explicit_key(token): def is_explicit_key(token):
# explicit key: # explicit key:
# ? key # ? key

View File

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

View File

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

View File

@@ -25,12 +25,12 @@ Use this rule to control the indentation.
same within the file. same within the file.
* ``indent-sequences`` defines whether block sequences should be indented or * ``indent-sequences`` defines whether block sequences should be indented or
not (when in a mapping, this indentation is not mandatory -- some people not (when in a mapping, this indentation is not mandatory -- some people
perceive the ``-`` as part of the indentation). Possible values: ``true``, perceive the ``-`` as part of the indentation). Possible values: ``yes``,
``false``, ``whatever`` and ``consistent``. ``consistent`` requires either ``no``, ``whatever`` and ``consistent``. ``consistent`` requires either all
all block sequences to be indented, or none to be. ``whatever`` means either block sequences to be indented, or none to be. ``whatever`` means either
indenting or not indenting individual block sequences is OK. indenting or not indenting individual block sequences is OK.
* ``check-multi-line-strings`` defines whether to lint indentation in * ``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 .. rubric:: Examples
@@ -99,7 +99,7 @@ Use this rule to control the indentation.
Russian: Russian:
dolls dolls
#. With ``indentation: {spaces: 2, indent-sequences: false}`` #. With ``indentation: {spaces: 2, indent-sequences: no}``
the following code snippet would **PASS**: the following code snippet would **PASS**:
:: ::
@@ -152,7 +152,7 @@ Use this rule to control the indentation.
- spaghetti - spaghetti
- sauce - 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**: the following code snippet would **PASS**:
:: ::
@@ -469,19 +469,7 @@ def _check(conf, token, prev, next, nextnext, context):
if context['indent-sequences'] is False: if context['indent-sequences'] is False:
indent = context['stack'][-1].indent indent = context['stack'][-1].indent
elif context['indent-sequences'] is True: elif context['indent-sequences'] is True:
if (context['spaces'] == 'consistent' and indent = detect_indent(context['stack'][-1].indent, next)
next.start_mark.column -
context['stack'][-1].indent == 0):
# In this case, the block sequence item is not indented
# (while it should be), but we don't know yet the
# indentation it should have (because `spaces` is
# `consistent` and its value has not been computed yet
# -- this is probably the beginning of the document).
# So we choose an arbitrary value (2).
indent = 2
else:
indent = detect_indent(context['stack'][-1].indent,
next)
else: # 'whatever' or 'consistent' else: # 'whatever' or 'consistent'
if next.start_mark.column == context['stack'][-1].indent: if next.start_mark.column == context['stack'][-1].indent:
# key: # key:

View File

@@ -22,9 +22,7 @@ Use this rule to set a limit to lines length.
* ``max`` defines the maximal (inclusive) length of lines. * ``max`` defines the maximal (inclusive) length of lines.
* ``allow-non-breakable-words`` is used to allow non breakable words (without * ``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 spaces inside) to overflow the limit. This is useful for long URLs, for
instance. Use ``true`` to allow, ``false`` to forbid. instance. Use ``yes`` to allow, ``no`` to forbid.
* ``allow-non-breakable-inline-mappings`` implies ``allow-non-breakable-words``
and extends it to also allow non-breakable words in inline mappings.
.. rubric:: Examples .. 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 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. 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**: 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**: 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**: #. With ``line-length: {max: 60, allow-non-breakable-words: no}``
::
- 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}``
the following code snippet would **FAIL**: 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 from yamllint.linter import LintProblem
ID = 'line-length' ID = 'line-length'
TYPE = 'line' TYPE = 'line'
CONF = {'max': int, CONF = {'max': int,
'allow-non-breakable-words': bool, '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
def check(conf, line): def check(conf, line):
if line.end - line.start > conf['max']: if line.end - line.start > conf['max']:
conf['allow-non-breakable-words'] |= \
conf['allow-non-breakable-inline-mappings']
if conf['allow-non-breakable-words']: if conf['allow-non-breakable-words']:
start = line.start start = line.start
while start < line.end and line.buffer[start] == ' ': while start < line.end and line.buffer[start] == ' ':
start += 1 start += 1
if start != line.end: if start != line.end:
if line.buffer[start] in ('#', '-'): if line.buffer[start] == '#':
start += 2 start += 2
if line.buffer.find(' ', start, line.end) == -1: if line.buffer.find(' ', start, line.end) == -1:
return return
if (conf['allow-non-breakable-inline-mappings'] and
check_inline_mapping(line)):
return
yield LintProblem(line.line_no, conf['max'] + 1, yield LintProblem(line.line_no, conf['max'] + 1,
'line too long (%d > %d characters)' % 'line too long (%d > %d characters)' %
(line.end - line.start, conf['max'])) (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")