Compare commits

..

57 Commits

Author SHA1 Message Date
Adrien Vergé
42eda54014 yamllint version 1.6.0 2016-11-30 09:37:52 +01:00
Adrien Vergé
e909692f88 docs(truthy): Fix typo 2016-11-18 14:28:46 +01:00
Adrien Vergé
3bc72d4c40 feat(CI): Enforce strict checking of YAML files
Use the `--strict` flag to check all rules on local YAML files, to
prevent all problems (including warnings). This includes the newly added
`truthy` rule.
2016-11-18 12:02:30 +01:00
Adrien Vergé
21e81b6435 fix(rules): Use true/false, not yes/no
Although `yes` and `no` are recognized as booleans by the pyyaml parser,
the correct keywords are `true` and `false` (as highlighted by the newly
added `truthy` rule).

This commit replaces the use of `yes`/`no` by `true`/`false` and
advertise it in the docs, but also makes sure this change is
backward-compatible (so that `yes` and `no` still work).
2016-11-18 12:02:02 +01:00
Adrien Vergé
b97b6ad19b style(tests): Fix new flake8 errors
This change fixes new errors detected by the last version of pycodestyle
(2.2.0), which is a dependency of flake8:

    ./tests/test_spec_examples.py:51:1: E305 expected 2 blank lines
    after class or function definition, found 1
    ./tests/test_spec_examples.py:139:1: E305 expected 2 blank lines
    after class or function definition, found 1

See pycodestyle changelog at 2.2.0 and
https://github.com/PyCQA/pycodestyle/pull/593.
2016-11-17 12:24:38 +01:00
Adrien Vergé
2b7f5c5e72 docs(install): Update Debian version
yamllint is now backported in Debian 8 (Jessie):
https://tracker.debian.org/pkg/yamllint
https://packages.debian.org/source/jessie-backports/yamllint
2016-11-16 18:50:13 +01:00
Adrien Vergé
64369db9a2 docs(configuration): Fix typo 2016-11-07 18:11:33 +01:00
Adrien Vergé
2428f6eeaf Merge pull request #27 from jsok/strict-mode
Strict mode
2016-11-07 18:10:57 +01:00
Jonathan Sokolowski
bf386b3c90 docs: Explain strict mode return codes 2016-10-24 14:15:23 +11:00
Jonathan Sokolowski
03e0f5aa6b Add strict mode argument to CLI 2016-10-24 14:08:46 +11:00
Adrien Vergé
3b2a73d224 Merge pull request #25 from adamchainz/universal_wheels
Release as a universal wheel
2016-10-14 09:38:10 +02:00
Adam Chainz
3c525ab743 Release as a universal wheel
By releasing as a [Python wheel](http://pythonwheels.com/) as well as a
source distribution, you can speed up end user’s installs. After merging
this command, to release you just need to run `python setup.py clean
sdist bdist_wheel upload`.
2016-10-14 09:37:38 +02:00
Adrien Vergé
559ad5574b yamllint version 1.5.0 2016-10-08 11:56:46 +02:00
Adrien Vergé
adcb2d2953 Merge pull request #24 from adamchainz/dont_package_tests
setup.py - don't distribute tests
2016-10-08 11:54:46 +02:00
Adam Chainz
e948509fe5 setup.py - don't distribute tests
Found them installed in my `site-packages`, importable as `import tests` 😱

Tested with:

```
In [2]: find_packages()
Out[2]: ['tests', 'yamllint', 'tests.rules', 'yamllint.rules']

In [3]: find_packages(exclude=['tests', 'tests.*'])
Out[3]: ['yamllint', 'yamllint.rules']
```
2016-10-07 10:52:48 +01:00
Adrien Vergé
6dae8f5b6e feat(truthy): Allow explicit types
With this change, we don't require quotes for truthy values that are
explicitly typed. For instance, the following examples are all
considered valid:

    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
2016-10-02 08:15:53 +02:00
Adrien Vergé
073462a87d docs(rules): Fix missing truthy rule in index 2016-10-01 10:00:23 +02:00
Adrien Vergé
4b9ba9e201 docs(truthy): Enhance rule documentation 2016-10-01 10:00:23 +02:00
Peter Ericson
5294ff5552 truthy: Add tests for explicit booleans
From @adrienverge
2016-10-01 09:04:37 +02:00
Peter Ericson
1f472bc144 Add rule: truthy, to forbid truthy values that are not quoted 2016-10-01 09:03:59 +02:00
Adrien Vergé
c163135ee5 yamllint version 1.4.1 2016-09-27 09:46:40 +02:00
Adrien Vergé
f656cf42d2 fix(line-length): Wrap token scanning securely
With `allow-non-breakable-inline-mappings` enabled, every long line is
passed through `loader.peek_token()`. Even lines that are not valid
YAML. For this reason, this code must be wrapped in a `try`/`except`
block.

Closes: #21
2016-09-27 09:27:43 +02:00
Adrien Vergé
9b72a2d29a Merge branch 'adamchainz-readthedocs.io' 2016-09-21 10:47:01 +02:00
Adam Chainz
d7c17c7e7c Doc: Convert readthedocs links from .org to .io
As per [their blog post of the 27th
April](https://blog.readthedocs.com/securing-subdomains/) ‘Securing
subdomains’:

> Starting today, Read the Docs will start hosting projects from
> subdomains on the domain readthedocs.io, instead of on
> readthedocs.org. This change addresses some security concerns around
> site cookies while hosting user generated data on the same domain as
> our dashboard.

Test Plan: Manually visited all the links I’ve modified.
2016-09-21 10:36:00 +02:00
Adrien Vergé
60b72daad4 yamllint version 1.4.0 2016-09-19 13:53:03 +02:00
Adrien Vergé
773bb8a648 Merge pull request #17 from allanlewis/improve-unbreakable
line_length: Allow mapping values with long unbreakable lines
2016-09-19 13:51:44 +02:00
Adrien Vergé
d3cd8ba332 line-length: Generalize ...-inline-mappings for corner cases
This commit refactors the `allow-non-breakable-inline-mappings` logic to
use YAML tokens and avoid crashes or erroneous reports on cases like:

```yaml
- {a: "http://localhost/very/very/very/very/very/very/long/url"
   }
```

```yaml
dict:
  {a: long long long long long long long, b: nospace}
```

```yaml
- long_line: http://localhost/very/very/long/url
```

```yaml
long_line: and+some+space+at+the+end       <-- extra spaces
```

For reference see:
https://github.com/adrienverge/yamllint/pull/17#issuecomment-247805799
2016-09-19 12:39:52 +01:00
Allan Lewis
e56a7c788c line_length: Extract inline logic to new config option
This commit extracts the inline mappings logic defined in the previous
commit to a separate config option, as suggested by @adrienverge. I'll
squash this into the previous commit if the change is accepted. (I named
the option slightly differently to what was suggested as I think my
proposal reads better without consulting the docs: I'd be happy to
reconsider this.)
2016-09-19 12:39:52 +01:00
Allan Lewis
d017631aff line_length: Allow mapping values with long unbreakable lines 2016-09-12 16:31:58 +01:00
Adrien Vergé
5b98cd2053 feat(comments): Allow comments with multiple hash chars
This change make the `comments` rule accept comments that start with
multiple pound signs, e.g.:

    ##############################
    ## This is some documentation

Closes: #12
2016-08-12 11:58:57 +02:00
Adrien Vergé
82dd7dbf16 Merge pull request #16 from adrienverge/coloured_output_on_tty
feat(cli): Colour output only on TTY
2016-08-12 11:58:00 +02:00
Adrien Vergé
4533b8ae49 doc(config): Show relaxed conf contents
Closes: #15
2016-08-12 11:17:18 +02:00
Adrien Vergé
a2c68fdf9b feat(cli): Colour output only on TTY
When piping yamllint output to a file, "coloured" characters aren't
interpreted and pollute text formatting with glyphs like:

  �[4m./global.yaml�[0m
    �[2m1439:52�[0m   �[31merror�[0m    no new line character...

With this commit, stdout is checked: if it's a TTY then output is
coloured, otherwise output is simple text.

Closes: #14
2016-08-12 11:03:41 +02:00
Adrien Vergé
82ed191bc9 yamllint version 1.3.2 2016-06-28 12:06:58 +02:00
Adrien Vergé
92ff315fb4 Tests: Set proper LC_ALL when decoding UTF-8 is needed
Make sure the default localization conditions on the "test system"
support UTF-8 encoding.
2016-06-28 12:06:55 +02:00
Adrien Vergé
f4cebdc054 Tests: Run with LC_ALL=C for uniform tests
Use default (C) locale in all tests to make sure the localization
conditions are the same wherever tests are run.
2016-06-28 11:04:50 +02:00
Adrien Vergé
d174f9e3e3 yamllint version 1.3.1 2016-06-28 10:10:34 +02:00
Adrien Vergé
c8ba8f7e99 linter: Fix UnicodeError when parsing comments
And add tests when reading non-ASCII strings and comments (both from
Python strings and from files).

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

    # The following mapping contains the same key twice,
    # but I know what I'm doing:
    key: value 1
    key: value 2  # yamllint disable-line rule:key-duplicates

or:

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

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

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

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

Closes: #6
2016-04-21 22:24:24 +02:00
42 changed files with 1395 additions and 368 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

2
setup.cfg Normal file
View File

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

View File

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

View File

@@ -0,0 +1,20 @@
# -*- 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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.common import RuleTestCase
from yamllint.parser import token_generator
from yamllint.parser import token_or_comment_generator, Comment
from yamllint.rules.indentation import check
@@ -38,7 +38,8 @@ class IndentationStackTestCase(RuleTestCase):
'check-multi-line-strings': False}
context = {}
output = ''
for elem in token_generator(source):
for elem in [t for t in token_or_comment_generator(source)
if not isinstance(t, Comment)]:
list(check(conf, elem.curr, elem.prev, elem.next, elem.nextnext,
context))
@@ -548,7 +549,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf)
def test_one_space(self):
conf = 'indentation: {spaces: 1, indent-sequences: no}'
conf = 'indentation: {spaces: 1, indent-sequences: false}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -561,7 +562,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n'
' date: 1991\n'
'...\n', conf)
conf = 'indentation: {spaces: 1, indent-sequences: yes}'
conf = 'indentation: {spaces: 1, indent-sequences: true}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -576,7 +577,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf)
def test_two_spaces(self):
conf = 'indentation: {spaces: 2, indent-sequences: no}'
conf = 'indentation: {spaces: 2, indent-sequences: false}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -589,7 +590,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n'
' date: 1991\n'
'...\n', conf)
conf = 'indentation: {spaces: 2, indent-sequences: yes}'
conf = 'indentation: {spaces: 2, indent-sequences: true}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -604,7 +605,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf)
def test_three_spaces(self):
conf = 'indentation: {spaces: 3, indent-sequences: no}'
conf = 'indentation: {spaces: 3, indent-sequences: false}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -617,7 +618,7 @@ class IndentationTestCase(RuleTestCase):
' - name: Linux\n'
' date: 1991\n'
'...\n', conf)
conf = 'indentation: {spaces: 3, indent-sequences: yes}'
conf = 'indentation: {spaces: 3, indent-sequences: true}'
self.check('---\n'
'object:\n'
' k1:\n'
@@ -1129,7 +1130,7 @@ class IndentationTestCase(RuleTestCase):
problem=(2, 3))
def test_multi_lines(self):
conf = 'indentation: {spaces: consistent, indent-sequences: yes}'
conf = 'indentation: {spaces: consistent, indent-sequences: true}'
self.check('---\n'
'long_string: >\n'
' bla bla blah\n'
@@ -1437,7 +1438,7 @@ class IndentationTestCase(RuleTestCase):
'- !!map # Block collection\n'
' foo: bar\n', conf)
conf = 'indentation: {spaces: consistent, indent-sequences: no}'
conf = 'indentation: {spaces: consistent, indent-sequences: false}'
self.check('---\n'
'sequence: !!seq\n'
'- entry\n'
@@ -1504,7 +1505,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_plain(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: no}\n'
' check-multi-line-strings: false}\n'
'document-start: disable\n')
self.check('multi\n'
'line\n', conf)
@@ -1533,7 +1534,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_plain(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: yes}\n'
' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('multi\n'
' line\n', conf, problem=(2, 2))
@@ -1556,7 +1557,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: no}\n'
' check-multi-line-strings: false}\n'
'document-start: disable\n')
self.check('"multi\n'
' line"\n', conf)
@@ -1587,7 +1588,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: yes}\n'
' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('"multi\n'
'line"\n', conf, problem=(2, 1))
@@ -1643,7 +1644,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_folded_style(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: no}\n'
' check-multi-line-strings: false}\n'
'document-start: disable\n')
self.check('>\n'
' multi\n'
@@ -1681,7 +1682,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_folded_style(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: yes}\n'
' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('>\n'
' multi\n'
@@ -1722,7 +1723,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_basics_literal_style(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: no}\n'
' check-multi-line-strings: false}\n'
'document-start: disable\n')
self.check('|\n'
' multi\n'
@@ -1760,7 +1761,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_check_multi_line_literal_style(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: yes}\n'
' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('|\n'
' multi\n'
@@ -1804,7 +1805,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_plain(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: yes}\n'
' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('- long text: very "long"\n'
' \'string\' with\n'
@@ -1826,7 +1827,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_double_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: yes}\n'
' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('- long text: "very \\"long\\"\n'
' \'string\' with\n'
@@ -1854,7 +1855,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_single_quoted(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: yes}\n'
' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('- long text: \'very "long"\n'
' \'\'string\'\' with\n'
@@ -1882,7 +1883,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_folded(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: yes}\n'
' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('- long text: >\n'
' very "long"\n'
@@ -1900,7 +1901,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_paragraph_literal(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: yes}\n'
' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('- long text: |\n'
' very "long"\n'
@@ -1918,7 +1919,7 @@ class ScalarIndentationTestCase(RuleTestCase):
def test_consistent(self):
conf = ('indentation: {spaces: consistent,\n'
' check-multi-line-strings: yes}\n'
' check-multi-line-strings: true}\n'
'document-start: disable\n')
self.check('multi\n'
'line\n', conf)

View File

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

View File

@@ -0,0 +1,68 @@
# -*- 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,7 +18,10 @@ try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import fcntl
import locale
import os
import pty
import shutil
import tempfile
import unittest
@@ -37,6 +40,10 @@ class CommandLineTestCase(unittest.TestCase):
'- 1 \n'
'- 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
open(os.path.join(self.wd, 'empty.yml'), 'w').close()
@@ -64,6 +71,15 @@ class CommandLineTestCase(unittest.TestCase):
f.write('---\n'
'key: value\n')
# non-ASCII chars
os.mkdir(os.path.join(self.wd, 'non-ascii'))
with open(os.path.join(self.wd, 'non-ascii', 'utf-8'), 'wb') as f:
f.write((u'---\n'
u'- hétérogénéité\n'
u'# 19.99 €\n'
u'- お早う御座います。\n'
u'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'))
def tearDown(self):
shutil.rmtree(self.wd)
@@ -73,7 +89,8 @@ class CommandLineTestCase(unittest.TestCase):
[os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'),
os.path.join(self.wd, 'sub/ok.yaml')],
os.path.join(self.wd, 'sub/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')],
)
items = [os.path.join(self.wd, 'sub/ok.yaml'),
@@ -153,6 +170,49 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(out, '')
self.assertRegexpMatches(err, r'^invalid config: not a dict')
def test_run_with_config_file(self):
with open(os.path.join(self.wd, 'config'), 'w') as f:
f.write('rules: {trailing-spaces: disable}')
with self.assertRaises(SystemExit) as ctx:
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.exception.code, 0)
with open(os.path.join(self.wd, 'config'), 'w') as f:
f.write('rules: {trailing-spaces: enable}')
with self.assertRaises(SystemExit) as ctx:
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.exception.code, 1)
def test_run_with_user_global_config_file(self):
home = os.path.join(self.wd, 'fake-home')
os.mkdir(home)
dir = os.path.join(home, '.config')
os.mkdir(dir)
dir = os.path.join(dir, 'yamllint')
os.mkdir(dir)
config = os.path.join(dir, 'config')
temp = os.environ['HOME']
os.environ['HOME'] = home
with open(config, 'w') as f:
f.write('rules: {trailing-spaces: disable}')
with self.assertRaises(SystemExit) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.exception.code, 0)
with open(config, 'w') as f:
f.write('rules: {trailing-spaces: enable}')
with self.assertRaises(SystemExit) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.exception.code, 1)
os.environ['HOME'] = temp
def test_run_version(self):
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
@@ -192,6 +252,24 @@ class CommandLineTestCase(unittest.TestCase):
'(new-line-at-end-of-file)\n') % (file, file))
self.assertEqual(err, '')
def test_run_one_warning(self):
file = os.path.join(self.wd, 'warn.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', file))
self.assertEqual(ctx.exception.code, 0)
def test_run_warning_in_strict_mode(self):
file = os.path.join(self.wd, 'warn.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', '--strict', file))
self.assertEqual(ctx.exception.code, 2)
def test_run_one_ok_file(self):
file = os.path.join(self.wd, 'sub', 'ok.yaml')
@@ -218,6 +296,26 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(out, '')
self.assertEqual(err, '')
def test_run_non_ascii_file(self):
file = os.path.join(self.wd, 'non-ascii', 'utf-8')
# Make sure the default localization conditions on this "system"
# support UTF-8 encoding.
loc = locale.getlocale()
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', file))
locale.setlocale(locale.LC_ALL, loc)
self.assertEqual(ctx.exception.code, 0)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertEqual(err, '')
def test_run_multiple_files(self):
items = [os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 's')]
@@ -235,7 +333,7 @@ class CommandLineTestCase(unittest.TestCase):
'(key-duplicates)\n') % file)
self.assertEqual(err, '')
def test_run_colored_output(self):
def test_run_piped_output_nocolor(self):
file = os.path.join(self.wd, 'a.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
@@ -245,6 +343,38 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(ctx.exception.code, 1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, (
'%s\n'
' 2:4 error trailing spaces (trailing-spaces)\n'
' 3:4 error no new line character at the end of file '
'(new-line-at-end-of-file)\n'
'\n' % file))
self.assertEqual(err, '')
def test_run_colored_output(self):
file = os.path.join(self.wd, 'a.yaml')
# Create a pseudo-TTY and redirect stdout to it
master, slave = pty.openpty()
sys.stdout = sys.stderr = os.fdopen(slave, 'w')
with self.assertRaises(SystemExit) as ctx:
cli.run((file, ))
sys.stdout.flush()
self.assertEqual(ctx.exception.code, 1)
# Read output from TTY
output = os.fdopen(master, 'r')
flag = fcntl.fcntl(master, fcntl.F_GETFD)
fcntl.fcntl(master, fcntl.F_SETFL, flag | os.O_NONBLOCK)
out = output.read().replace('\r\n', '\n')
sys.stdout.close()
sys.stderr.close()
output.close()
self.assertEqual(out, (
'\033[4m%s\033[0m\n'
' \033[2m2:4\033[0m \033[31merror\033[0m '
@@ -253,4 +383,3 @@ class CommandLineTestCase(unittest.TestCase):
'no new line character at the end of file '
'\033[2m(new-line-at-end-of-file)\033[0m\n'
'\n' % file))
self.assertEqual(err, '')

View File

@@ -62,6 +62,45 @@ class SimpleConfigTestCase(unittest.TestCase):
' max-spaces-after: 1\n'
' abcdef: yes\n')
def test_yes_no_for_booleans(self):
c = config.YamlLintConfig('rules:\n'
' indentation:\n'
' spaces: 2\n'
' indent-sequences: true\n'
' check-multi-line-strings: false\n')
self.assertEqual(c.rules['indentation']['indent-sequences'], True)
self.assertEqual(c.rules['indentation']['check-multi-line-strings'],
False)
c = config.YamlLintConfig('rules:\n'
' indentation:\n'
' spaces: 2\n'
' indent-sequences: yes\n'
' check-multi-line-strings: false\n')
self.assertEqual(c.rules['indentation']['indent-sequences'], True)
self.assertEqual(c.rules['indentation']['check-multi-line-strings'],
False)
c = config.YamlLintConfig('rules:\n'
' indentation:\n'
' spaces: 2\n'
' indent-sequences: whatever\n'
' check-multi-line-strings: false\n')
self.assertEqual(c.rules['indentation']['indent-sequences'],
'whatever')
self.assertEqual(c.rules['indentation']['check-multi-line-strings'],
False)
with self.assertRaisesRegexp(
config.YamlLintConfigError,
'invalid config: option "indent-sequences" of "indentation" '
'should be in '):
c = config.YamlLintConfig('rules:\n'
' indentation:\n'
' spaces: 2\n'
' indent-sequences: YES!\n'
' check-multi-line-strings: false\n')
def test_validate_rule_conf(self):
class Rule(object):
ID = 'fake'

View File

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

View File

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

View File

@@ -48,6 +48,7 @@ from tests.common import RuleTestCase
class SpecificationTestCase(RuleTestCase):
rule_id = None
conf_general = ('document-start: disable\n'
'comments: {min-spaces-from-content: 1}\n'
'braces: {min-spaces-inside: 1, max-spaces-inside: 1}\n'
@@ -66,7 +67,7 @@ conf_overrides = {
'example-2.18': ('empty-lines: {max-end: 1}\n'),
'example-2.19': ('empty-lines: {max-end: 1}\n'),
'example-2.28': ('empty-lines: {max-end: 3}\n'),
'example-5.3': ('indentation: {indent-sequences: no}\n'
'example-5.3': ('indentation: {indent-sequences: false}\n'
'colons: {max-spaces-before: 1}\n'),
'example-6.4': ('trailing-spaces: disable\n'),
'example-6.5': ('trailing-spaces: disable\n'),
@@ -114,11 +115,13 @@ conf_overrides = {
'example-8.14': ('colons: {max-spaces-before: 1}\n'),
'example-8.16': ('indentation: {spaces: 1}\n'),
'example-8.17': ('indentation: disable\n'),
'example-8.20': ('indentation: {indent-sequences: no}\n'
'example-8.20': ('indentation: {indent-sequences: false}\n'
'colons: {max-spaces-before: 1}\n'),
'example-8.22': ('indentation: disable\n'),
'example-10.1': ('colons: {max-spaces-before: 2}\n'),
'example-10.2': ('indentation: {indent-sequences: no}\n'),
'example-10.2': ('indentation: {indent-sequences: false}\n'),
'example-10.8': ('truthy: disable\n'),
'example-10.9': ('truthy: disable\n'),
}
files = os.listdir(os.path.join(os.path.dirname(os.path.realpath(__file__)),
@@ -131,6 +134,7 @@ def _gen_test(buffer, conf):
self.check(buffer, conf)
return test
# The following tests are blacklisted (i.e. will not be checked against
# yamllint), because pyyaml is currently not able to parse the contents
# (using yaml.parse()).

View File

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

View File

@@ -1,50 +0,0 @@
#!/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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -31,6 +31,7 @@ from yamllint.rules import (
new_line_at_end_of_file,
new_lines,
trailing_spaces,
truthy,
)
_RULES = {
@@ -50,6 +51,7 @@ _RULES = {
new_line_at_end_of_file.ID: new_line_at_end_of_file,
new_lines.ID: new_lines,
trailing_spaces.ID: trailing_spaces,
truthy.ID: truthy,
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,12 +25,12 @@ Use this rule to control the indentation.
same within the file.
* ``indent-sequences`` defines whether block sequences should be indented or
not (when in a mapping, this indentation is not mandatory -- some people
perceive the ``-`` as part of the indentation). Possible values: ``yes``,
``no``, ``whatever`` and ``consistent``. ``consistent`` requires either all
block sequences to be indented, or none to be. ``whatever`` means either
perceive the ``-`` as part of the indentation). Possible values: ``true``,
``false``, ``whatever`` and ``consistent``. ``consistent`` requires either
all block sequences to be indented, or none to be. ``whatever`` means either
indenting or not indenting individual block sequences is OK.
* ``check-multi-line-strings`` defines whether to lint indentation in
multi-line strings. Set to ``yes`` to enable, ``no`` to disable.
multi-line strings. Set to ``true`` to enable, ``false`` to disable.
.. rubric:: Examples
@@ -99,7 +99,7 @@ Use this rule to control the indentation.
Russian:
dolls
#. With ``indentation: {spaces: 2, indent-sequences: no}``
#. With ``indentation: {spaces: 2, indent-sequences: false}``
the following code snippet would **PASS**:
::
@@ -152,7 +152,7 @@ Use this rule to control the indentation.
- spaghetti
- sauce
#. With ``indentation: {spaces: 4, check-multi-line-strings: yes}``
#. With ``indentation: {spaces: 4, check-multi-line-strings: true}``
the following code snippet would **PASS**:
::

View File

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

93
yamllint/rules/truthy.py Normal file
View File

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