Compare commits

..

39 Commits

Author SHA1 Message Date
Adrien Vergé
c037d3e586 yamllint version 1.6.1 2017-02-25 22:43:55 +01:00
Adrien Vergé
228c47ab77 fix(indentation): Fix seq indent detection with consistent spaces
In the case when the conf is as follows:

    indentation:
      spaces: consistent
      indent-sequences: true

and there is no indented block before the first block sequence, and this
block sequence is not indented, then the spaces number is computed as
zero (while it obviously shouldn't be).

This causes such a document to fail on 4th line, instead of 2nd:

    a:
    - b
    c:
      - d

This commit fixes that, and adds corresponding tests.

Fixes: #39
2017-02-25 22:43:55 +01:00
Adrien Vergé
413d7a8e4e Merge pull request #32 from mtnbikenc/typo-fix
Minor cosmetic typo
2017-01-11 21:04:02 +01:00
Russell Teague
c332c8e3d4 Minor cosmetic typo 2017-01-11 13:52:19 -05:00
Adrien Vergé
ea67ba3394 Merge pull request #31 from bootswithdefer/master
support for pre-commit from yelp
2016-12-28 19:13:39 +01:00
bootswithdefer
a7dbfb08b3 support for pre-commit from yelp 2016-12-28 19:11:32 +01:00
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
30 changed files with 710 additions and 103 deletions

View File

@@ -11,7 +11,7 @@ install:
- pip install . - pip install .
script: script:
- flake8 . - flake8 .
- yamllint $(git ls-files '*.yaml' '*.yml') - yamllint --strict $(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: http://yamllint.readthedocs.org/en/latest/?badge=latest :target: https://yamllint.readthedocs.io/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,7 +24,7 @@ Written in Python (compatible with Python 2 & 3).
Documentation Documentation
------------- -------------
http://yamllint.readthedocs.io/ https://yamllint.readthedocs.io/
Overview Overview
-------- --------
@@ -44,7 +44,7 @@ On Fedora / CentOS:
sudo dnf install yamllint sudo dnf install yamllint
On Debian 9+ / Ubuntu 16.04+: On Debian 8+ / Ubuntu 16.04+:
.. code:: bash .. code:: bash
@@ -82,7 +82,7 @@ Usage
# 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! <http://yamllint.readthedocs.io/>`_ `Read more in the complete documentation! <https://yamllint.readthedocs.io/>`_
Features Features
^^^^^^^^ ^^^^^^^^
@@ -119,7 +119,7 @@ or for a whole block:
consectetur : adipiscing elit consectetur : adipiscing elit
# yamllint enable # yamllint enable
`Read more in the complete documentation! <http://yamllint.readthedocs.io/>`_ `Read more in the complete documentation! <https://yamllint.readthedocs.io/>`_
License License
------- -------

View File

@@ -31,7 +31,10 @@ 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:
@@ -99,8 +102,15 @@ 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).
In both cases, the script will output them (with different colors when using the By default the script will exit with a return code ``1`` *only when* there is one or
``standard`` output format), but the exit code can be different. More precisely, more error(s).
the script will exit will a failure code *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

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

View File

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

11
hooks.yaml Normal file
View File

@@ -0,0 +1,11 @@
---
# For use with pre-commit.
# See usage instructions at http://pre-commit.com
- id: yamllint
name: yamllint
description: This hook runs yamllint.
entry: yamllint
language: python
files: \.(yaml|yml)$

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', 'Topic :: Software Development :: Testing',
], ],
packages=find_packages(), packages=find_packages(exclude=['tests', 'tests.*']),
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

@@ -35,11 +35,15 @@ 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: yes\n' ' require-starting-space: true\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'
@@ -52,7 +56,11 @@ 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', conf) ' # comment 3 ter\n'
'\n'
'################################\n'
'## comment 4\n'
'## comment 5\n', conf)
self.check('---\n' self.check('---\n'
'#comment\n' '#comment\n'
'\n' '\n'
@@ -63,13 +71,18 @@ 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', conf, ' # comment 3 ter\n'
'\n'
'################################\n'
'## comment 4\n'
'##comment 5\n', conf,
problem1=(2, 2), problem2=(6, 13), 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): def test_spaces_from_content(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: no\n' ' require-starting-space: false\n'
' min-spaces-from-content: 2\n') ' min-spaces-from-content: 2\n')
self.check('---\n' self.check('---\n'
'# comment\n' '# comment\n'
@@ -91,7 +104,7 @@ class CommentsTestCase(RuleTestCase):
def test_both(self): def test_both(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: yes\n' ' require-starting-space: true\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'
@@ -106,17 +119,22 @@ 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=(13, 30)) problem7=(15, 3),
problem8=(17, 30))
def test_empty_comment(self): def test_empty_comment(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: yes\n' ' require-starting-space: true\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'
@@ -128,13 +146,21 @@ class CommentsTestCase(RuleTestCase):
def test_first_line(self): def test_first_line(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: yes\n' ' require-starting-space: true\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: yes\n' ' require-starting-space: true\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

@@ -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'

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: yes}' conf = 'document-end: {present: true}'
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: no}' conf = 'document-end: {present: false}'
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: yes}\n' conf = ('document-end: {present: true}\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: yes}\n' conf = ('document-start: {present: true}\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: no}\n' conf = ('document-start: {present: false}\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: yes}' conf = 'document-start: {present: true}'
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: yes}' conf = 'document-start: {present: true}'
self.check('%YAML 1.2\n' self.check('%YAML 1.2\n'
'---\n' '---\n'
'doc: ument\n' 'doc: ument\n'

View File

@@ -549,7 +549,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf) '...\n', conf)
def test_one_space(self): def test_one_space(self):
conf = 'indentation: {spaces: 1, indent-sequences: no}' conf = 'indentation: {spaces: 1, indent-sequences: false}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -562,7 +562,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: yes}' conf = 'indentation: {spaces: 1, indent-sequences: true}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -577,7 +577,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf) '...\n', conf)
def test_two_spaces(self): def test_two_spaces(self):
conf = 'indentation: {spaces: 2, indent-sequences: no}' conf = 'indentation: {spaces: 2, indent-sequences: false}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -590,7 +590,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: yes}' conf = 'indentation: {spaces: 2, indent-sequences: true}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -605,7 +605,7 @@ class IndentationTestCase(RuleTestCase):
'...\n', conf) '...\n', conf)
def test_three_spaces(self): def test_three_spaces(self):
conf = 'indentation: {spaces: 3, indent-sequences: no}' conf = 'indentation: {spaces: 3, indent-sequences: false}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -618,7 +618,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: yes}' conf = 'indentation: {spaces: 3, indent-sequences: true}'
self.check('---\n' self.check('---\n'
'object:\n' 'object:\n'
' k1:\n' ' k1:\n'
@@ -632,7 +632,7 @@ class IndentationTestCase(RuleTestCase):
' date: 1991\n' ' date: 1991\n'
'...\n', conf) '...\n', conf)
def test_consistent(self): def test_consistent_spaces(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,6 +713,142 @@ 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'
@@ -1130,7 +1266,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: yes}' conf = 'indentation: {spaces: consistent, indent-sequences: true}'
self.check('---\n' self.check('---\n'
'long_string: >\n' 'long_string: >\n'
' bla bla blah\n' ' bla bla blah\n'
@@ -1438,7 +1574,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: no}' conf = 'indentation: {spaces: consistent, indent-sequences: false}'
self.check('---\n' self.check('---\n'
'sequence: !!seq\n' 'sequence: !!seq\n'
'- entry\n' '- entry\n'
@@ -1505,7 +1641,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: no}\n' ' check-multi-line-strings: false}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('multi\n' self.check('multi\n'
'line\n', conf) 'line\n', conf)
@@ -1534,7 +1670,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: yes}\n' ' check-multi-line-strings: true}\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))
@@ -1557,7 +1693,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: no}\n' ' check-multi-line-strings: false}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('"multi\n' self.check('"multi\n'
' line"\n', conf) ' line"\n', conf)
@@ -1588,7 +1724,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: yes}\n' ' check-multi-line-strings: true}\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))
@@ -1644,7 +1780,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: no}\n' ' check-multi-line-strings: false}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('>\n' self.check('>\n'
' multi\n' ' multi\n'
@@ -1682,7 +1818,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: yes}\n' ' check-multi-line-strings: true}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('>\n' self.check('>\n'
' multi\n' ' multi\n'
@@ -1723,7 +1859,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: no}\n' ' check-multi-line-strings: false}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('|\n' self.check('|\n'
' multi\n' ' multi\n'
@@ -1761,7 +1897,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: yes}\n' ' check-multi-line-strings: true}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('|\n' self.check('|\n'
' multi\n' ' multi\n'
@@ -1805,7 +1941,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: yes}\n' ' check-multi-line-strings: true}\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'
@@ -1827,7 +1963,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: yes}\n' ' check-multi-line-strings: true}\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'
@@ -1855,7 +1991,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: yes}\n' ' check-multi-line-strings: true}\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'
@@ -1883,7 +2019,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: yes}\n' ' check-multi-line-strings: true}\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'
@@ -1901,7 +2037,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: yes}\n' ' check-multi-line-strings: true}\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'
@@ -1919,7 +2055,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: yes}\n' ' check-multi-line-strings: true}\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,6 +32,9 @@ 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'
@@ -63,7 +66,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: yes}' conf = 'line-length: {max: 20, allow-non-breakable-words: true}'
self.check('---\n' + 30 * 'A' + '\n', conf) self.check('---\n' + 30 * 'A' + '\n', conf)
self.check('---\n' self.check('---\n'
'this:\n' 'this:\n'
@@ -84,8 +87,11 @@ class LineLengthTestCase(RuleTestCase):
'another:\n' 'another:\n'
' - https://localhost/very/very/long/url\n' ' - https://localhost/very/very/long/url\n'
'...\n', conf) '...\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' + 30 * 'A' + '\n', conf, problem=(2, 21))
self.check('---\n' self.check('---\n'
'this:\n' 'this:\n'
@@ -106,3 +112,46 @@ class LineLengthTestCase(RuleTestCase):
'another:\n' 'another:\n'
' - https://localhost/very/very/long/url\n' ' - https://localhost/very/very/long/url\n'
'...\n', conf, problem=(5, 21)) '...\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,8 +18,10 @@ 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 locale
import os import os
import pty
import shutil import shutil
import tempfile import tempfile
import unittest import unittest
@@ -38,6 +40,10 @@ 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()
@@ -83,7 +89,8 @@ 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'),
@@ -245,6 +252,24 @@ 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')
@@ -308,7 +333,7 @@ class CommandLineTestCase(unittest.TestCase):
'(key-duplicates)\n') % file) '(key-duplicates)\n') % file)
self.assertEqual(err, '') self.assertEqual(err, '')
def test_run_colored_output(self): def test_run_piped_output_nocolor(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()
@@ -318,6 +343,38 @@ 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 '
@@ -326,4 +383,3 @@ 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,6 +62,45 @@ 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

@@ -48,6 +48,7 @@ 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'
@@ -66,7 +67,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: no}\n' 'example-5.3': ('indentation: {indent-sequences: false}\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'),
@@ -114,11 +115,13 @@ 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: no}\n' 'example-8.20': ('indentation: {indent-sequences: false}\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: 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__)), 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) 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

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

View File

@@ -22,6 +22,7 @@ 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
@@ -48,6 +49,17 @@ 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':
@@ -74,6 +86,10 @@ 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))
@@ -110,7 +126,7 @@ def run(argv=None):
print(e, file=sys.stderr) print(e, file=sys.stderr)
sys.exit(-1) sys.exit(-1)
return_code = 0 max_level = 0
for file in find_files_recursively(args.files): for file in find_files_recursively(args.files):
try: try:
@@ -119,20 +135,32 @@ 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))
else: elif sys.stdout.isatty():
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))
if return_code == 0 and problem.level == 'error': max_level = max(max_level, PROBLEM_LEVELS[problem.level])
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)
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) sys.exit(return_code)

View File

@@ -16,14 +16,14 @@ rules:
max-spaces-after: 1 max-spaces-after: 1
comments: comments:
level: warning level: warning
require-starting-space: yes require-starting-space: true
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: yes present: true
empty-lines: empty-lines:
max: 2 max: 2
max-start: 0 max-start: 0
@@ -32,13 +32,16 @@ rules:
max-spaces-after: 1 max-spaces-after: 1
indentation: indentation:
spaces: consistent spaces: consistent
indent-sequences: yes indent-sequences: true
check-multi-line-strings: no check-multi-line-strings: false
key-duplicates: enable key-duplicates: enable
line-length: line-length:
max: 80 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-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,3 +25,5 @@ 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

@@ -21,6 +21,16 @@ 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):
@@ -53,7 +63,7 @@ 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_costemic_problems(buffer, conf): def get_cosmetic_problems(buffer, conf):
rules = conf.enabled_rules() rules = conf.enabled_rules()
# Split token rules from line rules # Split token rules from line rules
@@ -183,7 +193,7 @@ def _run(buffer, conf):
# right line # right line
syntax_error = get_syntax_error(buffer) syntax_error = get_syntax_error(buffer)
for problem in get_costemic_problems(buffer, conf): for problem in get_cosmetic_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

@@ -31,6 +31,7 @@ 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 = {
@@ -50,6 +51,7 @@ _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

@@ -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 ``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 * ``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: yes}`` #. With ``comments: {require-starting-space: true}``
the following code snippet would **PASS**: the following code snippet would **PASS**:
:: ::
@@ -35,6 +35,12 @@ 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**:
:: ::
@@ -71,9 +77,13 @@ def check(conf, comment):
yield LintProblem(comment.line_no, comment.column_no, yield LintProblem(comment.line_no, comment.column_no,
'too few spaces before comment') 'too few spaces before comment')
if (conf['require-starting-space'] and if conf['require-starting-space']:
comment.pointer + 1 < len(comment.buffer) and text_start = comment.pointer + 1
comment.buffer[comment.pointer + 1] != ' ' and while (comment.buffer[text_start] == '#' and
comment.buffer[comment.pointer + 1] != '\n'): text_start < len(comment.buffer)):
yield LintProblem(comment.line_no, comment.column_no + 1, text_start += 1
'missing starting space in comment') 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

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

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: ``yes``, perceive the ``-`` as part of the indentation). Possible values: ``true``,
``no``, ``whatever`` and ``consistent``. ``consistent`` requires either all ``false``, ``whatever`` and ``consistent``. ``consistent`` requires either
block sequences to be indented, or none to be. ``whatever`` means either all 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 ``yes`` to enable, ``no`` to disable. multi-line strings. Set to ``true`` to enable, ``false`` 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: no}`` #. With ``indentation: {spaces: 2, indent-sequences: false}``
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: yes}`` #. With ``indentation: {spaces: 4, check-multi-line-strings: true}``
the following code snippet would **PASS**: the following code snippet would **PASS**:
:: ::
@@ -469,7 +469,19 @@ 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:
indent = detect_indent(context['stack'][-1].indent, next) if (context['spaces'] == 'consistent' and
next.start_mark.column -
context['stack'][-1].indent == 0):
# In this case, the block sequence item is not indented
# (while it should be), but we don't know yet the
# indentation it should have (because `spaces` is
# `consistent` and its value has not been computed yet
# -- this is probably the beginning of the document).
# So we choose an arbitrary value (2).
indent = 2
else:
indent = detect_indent(context['stack'][-1].indent,
next)
else: # 'whatever' or 'consistent' 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,7 +22,9 @@ 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 ``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 .. 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 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: yes}`` #. With ``line-length: {max: 60, allow-non-breakable-words: true}``
the following code snippet would **PASS**: the following code snippet would **PASS**:
:: ::
@@ -61,7 +63,20 @@ Use this rule to set a limit to lines length.
- this line is waaaaaaaaaaaaaay too long but could be easily split... - 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**: the following code snippet would **FAIL**:
:: ::
@@ -73,17 +88,39 @@ 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] == ' ':
@@ -96,6 +133,10 @@ def check(conf, line):
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']))

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")