Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
506e066410 | ||
|
|
54f21c0514 | ||
|
|
36b4776778 | ||
|
|
3bdc1b6e1b | ||
|
|
c16934117b | ||
|
|
8ab680635b | ||
|
|
503bde9e70 | ||
|
|
1b379628d7 | ||
|
|
6a842229fd | ||
|
|
8b9eab33bf | ||
|
|
22e792a433 | ||
|
|
f713dc8be2 | ||
|
|
a92743c8ca | ||
|
|
501def327d | ||
|
|
ed5d319df8 | ||
|
|
6ec1e7b54a | ||
|
|
c4475ece34 | ||
|
|
8537b0a164 | ||
|
|
83ea74e2f8 | ||
|
|
e43768f203 | ||
|
|
d422274563 | ||
|
|
2d931b5a81 | ||
|
|
773bfc0f3c | ||
|
|
1543d0e435 | ||
|
|
f82346dac7 | ||
|
|
ca540c113b | ||
|
|
c8fc170ff0 | ||
|
|
c4a3e15ff0 | ||
|
|
db57127971 | ||
|
|
c8e516be2f | ||
|
|
1c0dd48ccd | ||
|
|
f4edb85a04 | ||
|
|
d99bb9fec3 | ||
|
|
3c4013fda1 | ||
|
|
1a961bd4b0 | ||
|
|
7a8cfeed6d | ||
|
|
f9709bc6e6 | ||
|
|
5060917e40 | ||
|
|
a052cf7dba | ||
|
|
ae33716529 | ||
|
|
df26cc0438 | ||
|
|
342d7b49dd | ||
|
|
7d638d47b9 | ||
|
|
db116eaaaf | ||
|
|
30dfa78923 | ||
|
|
4ae829c062 | ||
|
|
400aa084da | ||
|
|
a825645cbe | ||
|
|
1764e32def | ||
|
|
d6a81f1b23 | ||
|
|
38d14c7314 | ||
|
|
ff1c9ad221 | ||
|
|
4b2b57aa32 | ||
|
|
51b6d8377f | ||
|
|
f507319419 | ||
|
|
c0c8534501 | ||
|
|
2b26cbc56b | ||
|
|
c037d3e586 | ||
|
|
228c47ab77 | ||
|
|
413d7a8e4e | ||
|
|
c332c8e3d4 | ||
|
|
ea67ba3394 | ||
|
|
a7dbfb08b3 | ||
|
|
42eda54014 | ||
|
|
e909692f88 | ||
|
|
3bc72d4c40 | ||
|
|
21e81b6435 | ||
|
|
b97b6ad19b | ||
|
|
2b7f5c5e72 | ||
|
|
64369db9a2 | ||
|
|
2428f6eeaf | ||
|
|
bf386b3c90 | ||
|
|
03e0f5aa6b | ||
|
|
3b2a73d224 | ||
|
|
3c525ab743 |
11
.pre-commit-hooks.yaml
Normal file
11
.pre-commit-hooks.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
|
||||
# For use with pre-commit.
|
||||
# See usage instructions at https://pre-commit.com
|
||||
|
||||
- id: yamllint
|
||||
name: yamllint
|
||||
description: This hook runs yamllint.
|
||||
entry: yamllint
|
||||
language: python
|
||||
types: [file, yaml]
|
||||
13
.travis.yml
13
.travis.yml
@@ -1,17 +1,24 @@
|
||||
---
|
||||
language: python
|
||||
python:
|
||||
- 2.6
|
||||
- 2.7
|
||||
- 3.3
|
||||
- 3.4
|
||||
- 3.5
|
||||
- 3.6
|
||||
- nightly
|
||||
install:
|
||||
- pip install pyyaml flake8 coveralls
|
||||
- pip install pyyaml flake8 flake8-import-order coveralls
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install unittest2; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then pip install sphinx; fi
|
||||
- pip install .
|
||||
script:
|
||||
- flake8 .
|
||||
- yamllint $(git ls-files '*.yaml' '*.yml')
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 2.6 ]]; then flake8 .; fi
|
||||
- yamllint --strict $(git ls-files '*.yaml' '*.yml')
|
||||
- coverage run --source=yamllint setup.py test
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then
|
||||
python setup.py build_sphinx;
|
||||
fi
|
||||
after_success:
|
||||
coveralls
|
||||
|
||||
52
CHANGELOG.rst
Normal file
52
CHANGELOG.rst
Normal file
@@ -0,0 +1,52 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
1.11.1 (2018-04-06)
|
||||
-------------------
|
||||
|
||||
- Handle merge keys (`<<`) in the `key-duplicates` rule
|
||||
- Update documentation about pre-commit
|
||||
- Make examples for `ignore` rule clearer
|
||||
- Clarify documentation on the 'truthy' rule
|
||||
- Fix crash in parser due to a change in PyYAML > 3.12
|
||||
|
||||
1.11.0 (2018-02-21)
|
||||
-------------------
|
||||
|
||||
- Add a new `octal-values` rule
|
||||
|
||||
1.10.0 (2017-11-05)
|
||||
-------------------
|
||||
|
||||
- Fix colored output on Windows
|
||||
- Check documentation compilation on continuous integration
|
||||
- Add a new `empty-values` rule
|
||||
- Make sure test files are included in dist bundle
|
||||
- Tests: Use en_US.UTF-8 locale when C.UTF-8 not available
|
||||
- Tests: Dynamically detect Python executable path
|
||||
|
||||
1.9.0 (2017-10-16)
|
||||
------------------
|
||||
|
||||
- Add a new `key-ordering` rule
|
||||
- Fix indentation rule for key following empty list
|
||||
|
||||
1.8.2 (2017-10-10)
|
||||
------------------
|
||||
|
||||
- Be clearer about the `ignore` conf type
|
||||
- Update pre-commit hook file
|
||||
- Add documentation for pre-commit
|
||||
|
||||
1.8.1 (2017-07-04)
|
||||
------------------
|
||||
|
||||
- Require pathspec >= 0.5.3
|
||||
- Support Python 2.6
|
||||
- Add a changelog
|
||||
|
||||
1.8.0 (2017-06-28)
|
||||
------------------
|
||||
|
||||
- Refactor argparse with mutually_exclusive_group
|
||||
- Add support to ignore paths in configuration
|
||||
@@ -1,3 +1,4 @@
|
||||
include LICENSE
|
||||
include README.rst
|
||||
include docs/*
|
||||
include tests/*.py tests/rules/*.py tests/yaml-1.2-spec-examples/*
|
||||
|
||||
23
README.rst
23
README.rst
@@ -44,7 +44,7 @@ On Fedora / CentOS:
|
||||
|
||||
sudo dnf install yamllint
|
||||
|
||||
On Debian 9+ / Ubuntu 16.04+:
|
||||
On Debian 8+ / Ubuntu 16.04+:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
@@ -119,6 +119,27 @@ or for a whole block:
|
||||
consectetur : adipiscing elit
|
||||
# yamllint enable
|
||||
|
||||
Specific files can be ignored (totally or for some rules only) using a
|
||||
``.gitignore``-style pattern:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
# For all rules
|
||||
ignore: |
|
||||
*.dont-lint-me.yaml
|
||||
/bin/
|
||||
!/bin/*.lint-me-anyway.yaml
|
||||
|
||||
rules:
|
||||
key-duplicates:
|
||||
ignore: |
|
||||
generated
|
||||
*.template.yaml
|
||||
trailing-spaces:
|
||||
ignore: |
|
||||
*.ignore-trailing-spaces.yaml
|
||||
/ascii-art/*
|
||||
|
||||
`Read more in the complete documentation! <https://yamllint.readthedocs.io/>`_
|
||||
|
||||
License
|
||||
|
||||
13
docs/conf.py
13
docs/conf.py
@@ -4,6 +4,7 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
sys.path.insert(0, os.path.abspath('..')) # noqa
|
||||
|
||||
@@ -40,3 +41,15 @@ htmlhelp_basename = 'yamllintdoc'
|
||||
man_pages = [
|
||||
('index', 'yamllint', '', [u'Adrien Vergé'], 1)
|
||||
]
|
||||
|
||||
# -- Build with sphinx automodule without needing to install third-party libs
|
||||
|
||||
|
||||
class Mock(MagicMock):
|
||||
@classmethod
|
||||
def __getattr__(cls, name):
|
||||
return MagicMock()
|
||||
|
||||
|
||||
MOCK_MODULES = ['pathspec', 'yaml']
|
||||
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
|
||||
|
||||
@@ -102,8 +102,69 @@ 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
|
||||
|
||||
Ignoring paths
|
||||
--------------
|
||||
|
||||
It is possible to exclude specific files or directories, so that the linter
|
||||
doesn't process them.
|
||||
|
||||
You can either totally ignore files (they won't be looked at):
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
extends: default
|
||||
|
||||
ignore: |
|
||||
/this/specific/file.yaml
|
||||
all/this/directory/
|
||||
*.template.yaml
|
||||
|
||||
or ignore paths only for specific rules:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
trailing-spaces:
|
||||
ignore: |
|
||||
/this-file-has-trailing-spaces-but-it-is-OK.yaml
|
||||
/generated/*.yaml
|
||||
|
||||
Note that this ``.gitignore``-style path pattern allows complex path
|
||||
exclusion/inclusion, see the `pathspec README file
|
||||
<https://pypi.python.org/pypi/pathspec>`_ for more details.
|
||||
Here is a more complex example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# For all rules
|
||||
ignore: |
|
||||
*.dont-lint-me.yaml
|
||||
/bin/
|
||||
!/bin/*.lint-me-anyway.yaml
|
||||
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
key-duplicates:
|
||||
ignore: |
|
||||
generated
|
||||
*.template.yaml
|
||||
trailing-spaces:
|
||||
ignore: |
|
||||
*.ignore-trailing-spaces.yaml
|
||||
ascii-art/*
|
||||
|
||||
@@ -32,7 +32,7 @@ or:
|
||||
- This line is waaaaaaaaaay too long but yamllint will not report anything about it.
|
||||
This line will be checked by yamllint.
|
||||
|
||||
It it possible, although not recommend, to disabled **all** rules for a
|
||||
It is possible, although not recommend, to disabled **all** rules for a
|
||||
specific line:
|
||||
|
||||
.. code-block:: yaml
|
||||
@@ -46,7 +46,7 @@ If you need to disable multiple rules, it is allowed to chain rules like this:
|
||||
Disabling checks for all (or part of) the file
|
||||
----------------------------------------------
|
||||
|
||||
To prevent yamllint from reporting problems for the whoe file, or for a block of
|
||||
To prevent yamllint from reporting problems for the whole file, or for a block of
|
||||
lines within the file, use ``# yamllint disable ...`` and ``# yamllint enable
|
||||
...`` directive comments. For instance:
|
||||
|
||||
@@ -60,7 +60,7 @@ lines within the file, use ``# yamllint disable ...`` and ``# yamllint enable
|
||||
|
||||
- rest of the document...
|
||||
|
||||
It it possible, although not recommend, to disabled **all** rules:
|
||||
It is possible, although not recommend, to disabled **all** rules:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
||||
@@ -26,3 +26,4 @@ Table of contents
|
||||
disable_with_comments
|
||||
development
|
||||
text_editors
|
||||
integration
|
||||
|
||||
17
docs/integration.rst
Normal file
17
docs/integration.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
Integration with other software
|
||||
===============================
|
||||
|
||||
Integration with pre-commit
|
||||
---------------------------
|
||||
|
||||
You can integrate yamllint in `pre-commit <http://pre-commit.com/>`_ tool.
|
||||
Here is an example, to add in your .pre-commit-config.yaml
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
---
|
||||
# Update the sha variable with the release version that you want, from the yamllint repo
|
||||
- repo: https://github.com/adrienverge/yamllint.git
|
||||
sha: v1.8.1
|
||||
hooks:
|
||||
- id: yamllint
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -59,6 +59,11 @@ empty-lines
|
||||
|
||||
.. automodule:: yamllint.rules.empty_lines
|
||||
|
||||
empty-values
|
||||
------------
|
||||
|
||||
.. automodule:: yamllint.rules.empty_values
|
||||
|
||||
hyphens
|
||||
-------
|
||||
|
||||
@@ -74,6 +79,11 @@ key-duplicates
|
||||
|
||||
.. automodule:: yamllint.rules.key_duplicates
|
||||
|
||||
key-ordering
|
||||
--------------
|
||||
|
||||
.. automodule:: yamllint.rules.key_ordering
|
||||
|
||||
line-length
|
||||
-----------
|
||||
|
||||
@@ -89,6 +99,11 @@ new-lines
|
||||
|
||||
.. automodule:: yamllint.rules.new_lines
|
||||
|
||||
octal-values
|
||||
------------
|
||||
|
||||
.. automodule:: yamllint.rules.octal_values
|
||||
|
||||
trailing-spaces
|
||||
---------------
|
||||
|
||||
|
||||
@@ -9,8 +9,12 @@ text editor.
|
||||
Vim
|
||||
---
|
||||
|
||||
Assuming that the `syntastic <https://github.com/scrooloose/syntastic>`_ plugin
|
||||
is installed, add to your ``.vimrc``:
|
||||
Assuming that the `ALE <https://github.com/w0rp/ale>`_ plugin is
|
||||
installed, yamllint is supported by default. It is automatically enabled when
|
||||
editing YAML files.
|
||||
|
||||
If you instead use the `syntastic <https://github.com/scrooloose/syntastic>`_
|
||||
plugin, add this to your ``.vimrc``:
|
||||
|
||||
::
|
||||
|
||||
@@ -23,6 +27,12 @@ Assuming that the `neomake <https://github.com/benekastah/neomake>`_ plugin is
|
||||
installed, yamllint is supported by default. It is automatically enabled when
|
||||
editing YAML files.
|
||||
|
||||
Emacs
|
||||
-----
|
||||
|
||||
If you are `flycheck <https://github.com/flycheck/flycheck>`_ user, you can use
|
||||
`flycheck-yamllint <https://github.com/krzysztof-magosa/flycheck-yamllint>`_ integration.
|
||||
|
||||
Other text editors
|
||||
------------------
|
||||
|
||||
|
||||
12
setup.cfg
Normal file
12
setup.cfg
Normal file
@@ -0,0 +1,12 @@
|
||||
[bdist_wheel]
|
||||
universal = 1
|
||||
|
||||
[flake8]
|
||||
import-order-style = pep8
|
||||
application-import-names = yamllint
|
||||
|
||||
[build_sphinx]
|
||||
all-files = 1
|
||||
source-dir = docs
|
||||
build-dir = docs/_build
|
||||
warning-is-error = 1
|
||||
8
setup.py
8
setup.py
@@ -44,9 +44,7 @@ setup(
|
||||
|
||||
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/*']},
|
||||
install_requires=['pyyaml'],
|
||||
tests_require=['nose'],
|
||||
test_suite='nose.collector',
|
||||
package_data={'yamllint': ['conf/*.yaml']},
|
||||
install_requires=['pathspec >=0.5.3', 'pyyaml'],
|
||||
test_suite='tests',
|
||||
)
|
||||
|
||||
@@ -14,7 +14,14 @@
|
||||
# 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 unittest
|
||||
import os
|
||||
import tempfile
|
||||
import sys
|
||||
try:
|
||||
assert sys.version_info >= (2, 7)
|
||||
import unittest
|
||||
except AssertionError:
|
||||
import unittest2 as unittest
|
||||
|
||||
import yaml
|
||||
|
||||
@@ -49,3 +56,21 @@ class RuleTestCase(unittest.TestCase):
|
||||
|
||||
real_problems = list(linter.run(source, self.build_fake_config(conf)))
|
||||
self.assertEqual(real_problems, expected_problems)
|
||||
|
||||
|
||||
def build_temp_workspace(files):
|
||||
tempdir = tempfile.mkdtemp(prefix='yamllint-tests-')
|
||||
|
||||
for path, content in files.items():
|
||||
path = os.path.join(tempdir, path)
|
||||
if not os.path.exists(os.path.dirname(path)):
|
||||
os.makedirs(os.path.dirname(path))
|
||||
|
||||
if type(content) is list:
|
||||
os.mkdir(path)
|
||||
else:
|
||||
mode = 'wb' if isinstance(content, bytes) else 'w'
|
||||
with open(path, mode) as f:
|
||||
f.write(content)
|
||||
|
||||
return tempdir
|
||||
|
||||
@@ -32,11 +32,19 @@ class ColonTestCase(RuleTestCase):
|
||||
'dict7: { a: 1, b, c: 3 }\n', conf)
|
||||
|
||||
def test_min_spaces(self):
|
||||
conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 0}'
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: 0\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'dict: {}\n', conf)
|
||||
|
||||
conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 1}'
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: 1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'dict: {}\n', conf, problem=(2, 8))
|
||||
self.check('---\n'
|
||||
@@ -52,7 +60,11 @@ class ColonTestCase(RuleTestCase):
|
||||
' b\n'
|
||||
'}\n', conf)
|
||||
|
||||
conf = 'braces: {max-spaces-inside: -1, min-spaces-inside: 3}'
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: 3\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'dict: { a: 1, b }\n', conf,
|
||||
problem1=(2, 9), problem2=(2, 17))
|
||||
@@ -60,7 +72,11 @@ class ColonTestCase(RuleTestCase):
|
||||
'dict: { a: 1, b }\n', conf)
|
||||
|
||||
def test_max_spaces(self):
|
||||
conf = 'braces: {max-spaces-inside: 0, min-spaces-inside: -1}'
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: 0\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'dict: {}\n', conf)
|
||||
self.check('---\n'
|
||||
@@ -79,7 +95,11 @@ class ColonTestCase(RuleTestCase):
|
||||
' b\n'
|
||||
'}\n', conf)
|
||||
|
||||
conf = 'braces: {max-spaces-inside: 3, min-spaces-inside: -1}'
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: 3\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'dict: { a: 1, b }\n', conf)
|
||||
self.check('---\n'
|
||||
@@ -87,7 +107,11 @@ class ColonTestCase(RuleTestCase):
|
||||
problem1=(2, 11), problem2=(2, 23))
|
||||
|
||||
def test_min_and_max_spaces(self):
|
||||
conf = 'braces: {max-spaces-inside: 0, min-spaces-inside: 0}'
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: 0\n'
|
||||
' min-spaces-inside: 0\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'dict: {}\n', conf)
|
||||
self.check('---\n'
|
||||
@@ -95,14 +119,169 @@ class ColonTestCase(RuleTestCase):
|
||||
self.check('---\n'
|
||||
'dict: { a: 1, b}\n', conf, problem=(2, 10))
|
||||
|
||||
conf = 'braces: {max-spaces-inside: 1, min-spaces-inside: 1}'
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: 1\n'
|
||||
' min-spaces-inside: 1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'dict: {a: 1, b, c: 3 }\n', conf, problem=(2, 8))
|
||||
|
||||
conf = 'braces: {max-spaces-inside: 2, min-spaces-inside: 0}'
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: 2\n'
|
||||
' min-spaces-inside: 0\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'dict: {a: 1, b, c: 3 }\n', conf)
|
||||
self.check('---\n'
|
||||
'dict: { a: 1, b, c: 3 }\n', conf)
|
||||
self.check('---\n'
|
||||
'dict: { a: 1, b, c: 3 }\n', conf, problem=(2, 10))
|
||||
|
||||
def test_min_spaces_empty(self):
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 0\n'
|
||||
' min-spaces-inside-empty: 0\n')
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf)
|
||||
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: 1\n')
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf, problem=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf)
|
||||
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: 3\n')
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf, problem=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf)
|
||||
|
||||
def test_max_spaces_empty(self):
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 0\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf)
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf, problem=(2, 9))
|
||||
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf)
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf)
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf, problem=(2, 10))
|
||||
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 3\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf)
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf)
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf, problem=(2, 12))
|
||||
|
||||
def test_min_and_max_spaces_empty(self):
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 2\n'
|
||||
' min-spaces-inside-empty: 1\n')
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf, problem=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf)
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf)
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf, problem=(2, 11))
|
||||
|
||||
def test_mixed_empty_nonempty(self):
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: 1\n'
|
||||
' max-spaces-inside-empty: 0\n'
|
||||
' min-spaces-inside-empty: 0\n')
|
||||
self.check('---\n'
|
||||
'array: { a: 1, b }\n', conf)
|
||||
self.check('---\n'
|
||||
'array: {a: 1, b}\n', conf,
|
||||
problem1=(2, 9), problem2=(2, 16))
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf)
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf,
|
||||
problem1=(2, 9))
|
||||
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: 0\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 1\n'
|
||||
' min-spaces-inside-empty: 1\n')
|
||||
self.check('---\n'
|
||||
'array: { a: 1, b }\n', conf,
|
||||
problem1=(2, 9), problem2=(2, 17))
|
||||
self.check('---\n'
|
||||
'array: {a: 1, b}\n', conf)
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf,
|
||||
problem1=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf)
|
||||
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: 2\n'
|
||||
' min-spaces-inside: 1\n'
|
||||
' max-spaces-inside-empty: 1\n'
|
||||
' min-spaces-inside-empty: 1\n')
|
||||
self.check('---\n'
|
||||
'array: { a: 1, b }\n', conf)
|
||||
self.check('---\n'
|
||||
'array: {a: 1, b }\n', conf,
|
||||
problem1=(2, 9), problem2=(2, 18))
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf,
|
||||
problem1=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf)
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf,
|
||||
problem1=(2, 11))
|
||||
|
||||
conf = ('braces:\n'
|
||||
' max-spaces-inside: 1\n'
|
||||
' min-spaces-inside: 1\n'
|
||||
' max-spaces-inside-empty: 1\n'
|
||||
' min-spaces-inside-empty: 1\n')
|
||||
self.check('---\n'
|
||||
'array: { a: 1, b }\n', conf)
|
||||
self.check('---\n'
|
||||
'array: {a: 1, b}\n', conf,
|
||||
problem1=(2, 9), problem2=(2, 16))
|
||||
self.check('---\n'
|
||||
'array: {}\n', conf,
|
||||
problem1=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: { }\n', conf)
|
||||
|
||||
@@ -32,11 +32,19 @@ class ColonTestCase(RuleTestCase):
|
||||
'array7: [ a, b, c ]\n', conf)
|
||||
|
||||
def test_min_spaces(self):
|
||||
conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 0}'
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: 0\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf)
|
||||
|
||||
conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 1}'
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: 1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf, problem=(2, 9))
|
||||
self.check('---\n'
|
||||
@@ -51,7 +59,11 @@ class ColonTestCase(RuleTestCase):
|
||||
' b\n'
|
||||
']\n', conf)
|
||||
|
||||
conf = 'brackets: {max-spaces-inside: -1, min-spaces-inside: 3}'
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: 3\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: [ a, b ]\n', conf,
|
||||
problem1=(2, 10), problem2=(2, 15))
|
||||
@@ -59,7 +71,11 @@ class ColonTestCase(RuleTestCase):
|
||||
'array: [ a, b ]\n', conf)
|
||||
|
||||
def test_max_spaces(self):
|
||||
conf = 'brackets: {max-spaces-inside: 0, min-spaces-inside: -1}'
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: 0\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf)
|
||||
self.check('---\n'
|
||||
@@ -78,7 +94,11 @@ class ColonTestCase(RuleTestCase):
|
||||
' b\n'
|
||||
']\n', conf)
|
||||
|
||||
conf = 'brackets: {max-spaces-inside: 3, min-spaces-inside: -1}'
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: 3\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: [ a, b ]\n', conf)
|
||||
self.check('---\n'
|
||||
@@ -86,7 +106,11 @@ class ColonTestCase(RuleTestCase):
|
||||
problem1=(2, 12), problem2=(2, 21))
|
||||
|
||||
def test_min_and_max_spaces(self):
|
||||
conf = 'brackets: {max-spaces-inside: 0, min-spaces-inside: 0}'
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: 0\n'
|
||||
' min-spaces-inside: 0\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf)
|
||||
self.check('---\n'
|
||||
@@ -94,14 +118,169 @@ class ColonTestCase(RuleTestCase):
|
||||
self.check('---\n'
|
||||
'array: [ a, b]\n', conf, problem=(2, 11))
|
||||
|
||||
conf = 'brackets: {max-spaces-inside: 1, min-spaces-inside: 1}'
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: 1\n'
|
||||
' min-spaces-inside: 1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: [a, b, c ]\n', conf, problem=(2, 9))
|
||||
|
||||
conf = 'brackets: {max-spaces-inside: 2, min-spaces-inside: 0}'
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: 2\n'
|
||||
' min-spaces-inside: 0\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: [a, b, c ]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ a, b, c ]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ a, b, c ]\n', conf, problem=(2, 11))
|
||||
|
||||
def test_min_spaces_empty(self):
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 0\n'
|
||||
' min-spaces-inside-empty: 0\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf)
|
||||
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: 1\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf, problem=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf)
|
||||
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: -1\n'
|
||||
' min-spaces-inside-empty: 3\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf, problem=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf)
|
||||
|
||||
def test_max_spaces_empty(self):
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 0\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf, problem=(2, 9))
|
||||
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 1\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf, problem=(2, 10))
|
||||
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 3\n'
|
||||
' min-spaces-inside-empty: -1\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf, problem=(2, 12))
|
||||
|
||||
def test_min_and_max_spaces_empty(self):
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 2\n'
|
||||
' min-spaces-inside-empty: 1\n')
|
||||
self.check('---\n'
|
||||
'array: []\n', conf, problem=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf, problem=(2, 11))
|
||||
|
||||
def test_mixed_empty_nonempty(self):
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: -1\n'
|
||||
' min-spaces-inside: 1\n'
|
||||
' max-spaces-inside-empty: 0\n'
|
||||
' min-spaces-inside-empty: 0\n')
|
||||
self.check('---\n'
|
||||
'array: [ a, b ]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [a, b]\n', conf,
|
||||
problem1=(2, 9), problem2=(2, 13))
|
||||
self.check('---\n'
|
||||
'array: []\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf,
|
||||
problem1=(2, 9))
|
||||
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: 0\n'
|
||||
' min-spaces-inside: -1\n'
|
||||
' max-spaces-inside-empty: 1\n'
|
||||
' min-spaces-inside-empty: 1\n')
|
||||
self.check('---\n'
|
||||
'array: [ a, b ]\n', conf,
|
||||
problem1=(2, 9), problem2=(2, 14))
|
||||
self.check('---\n'
|
||||
'array: [a, b]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: []\n', conf,
|
||||
problem1=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf)
|
||||
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: 2\n'
|
||||
' min-spaces-inside: 1\n'
|
||||
' max-spaces-inside-empty: 1\n'
|
||||
' min-spaces-inside-empty: 1\n')
|
||||
self.check('---\n'
|
||||
'array: [ a, b ]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [a, b ]\n', conf,
|
||||
problem1=(2, 9), problem2=(2, 15))
|
||||
self.check('---\n'
|
||||
'array: []\n', conf,
|
||||
problem1=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf,
|
||||
problem1=(2, 11))
|
||||
|
||||
conf = ('brackets:\n'
|
||||
' max-spaces-inside: 1\n'
|
||||
' min-spaces-inside: 1\n'
|
||||
' max-spaces-inside-empty: 1\n'
|
||||
' min-spaces-inside-empty: 1\n')
|
||||
self.check('---\n'
|
||||
'array: [ a, b ]\n', conf)
|
||||
self.check('---\n'
|
||||
'array: [a, b]\n', conf,
|
||||
problem1=(2, 9), problem2=(2, 13))
|
||||
self.check('---\n'
|
||||
'array: []\n', conf,
|
||||
problem1=(2, 9))
|
||||
self.check('---\n'
|
||||
'array: [ ]\n', conf)
|
||||
|
||||
@@ -43,7 +43,7 @@ class CommentsTestCase(RuleTestCase):
|
||||
|
||||
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'
|
||||
@@ -82,7 +82,7 @@ class CommentsTestCase(RuleTestCase):
|
||||
|
||||
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'
|
||||
@@ -104,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'
|
||||
@@ -134,7 +134,7 @@ class CommentsTestCase(RuleTestCase):
|
||||
|
||||
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'
|
||||
@@ -146,13 +146,13 @@ 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: yes\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'
|
||||
@@ -160,7 +160,7 @@ class CommentsTestCase(RuleTestCase):
|
||||
|
||||
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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
261
tests/rules/test_empty_values.py
Normal file
261
tests/rules/test_empty_values.py
Normal file
@@ -0,0 +1,261 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2017 Greg Dubicki
|
||||
#
|
||||
# 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 EmptyValuesTestCase(RuleTestCase):
|
||||
rule_id = 'empty-values'
|
||||
|
||||
def test_disabled(self):
|
||||
conf = ('empty-values: disable\n'
|
||||
'braces: disable\n'
|
||||
'commas: disable\n')
|
||||
self.check('---\n'
|
||||
'foo:\n', conf)
|
||||
self.check('---\n'
|
||||
'foo:\n'
|
||||
' bar:\n', conf)
|
||||
self.check('---\n'
|
||||
'{a:}\n', conf)
|
||||
self.check('---\n'
|
||||
'foo: {a:}\n', conf)
|
||||
self.check('---\n'
|
||||
'- {a:}\n'
|
||||
'- {a:, b: 2}\n'
|
||||
'- {a: 1, b:}\n'
|
||||
'- {a: 1, b: , }\n', conf)
|
||||
self.check('---\n'
|
||||
'{a: {b: , c: {d: 4, e:}}, f:}\n', conf)
|
||||
|
||||
def test_in_block_mappings_disabled(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: false,\n'
|
||||
' forbid-in-flow-mappings: false}\n')
|
||||
self.check('---\n'
|
||||
'foo:\n', conf)
|
||||
self.check('---\n'
|
||||
'foo:\n'
|
||||
'bar: aaa\n', conf)
|
||||
|
||||
def test_in_block_mappings_single_line(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: true,\n'
|
||||
' forbid-in-flow-mappings: false}\n')
|
||||
self.check('---\n'
|
||||
'implicitly-null:\n', conf, problem1=(2, 17))
|
||||
self.check('---\n'
|
||||
'implicitly-null:with-colons:in-key:\n', conf,
|
||||
problem1=(2, 36))
|
||||
self.check('---\n'
|
||||
'implicitly-null:with-colons:in-key2:\n', conf,
|
||||
problem1=(2, 37))
|
||||
|
||||
def test_in_block_mappings_all_lines(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: true,\n'
|
||||
' forbid-in-flow-mappings: false}\n')
|
||||
self.check('---\n'
|
||||
'foo:\n'
|
||||
'bar:\n'
|
||||
'foobar:\n', conf, problem1=(2, 5),
|
||||
problem2=(3, 5), problem3=(4, 8))
|
||||
|
||||
def test_in_block_mappings_explicit_end_of_document(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: true,\n'
|
||||
' forbid-in-flow-mappings: false}\n')
|
||||
self.check('---\n'
|
||||
'foo:\n'
|
||||
'...\n', conf, problem1=(2, 5))
|
||||
|
||||
def test_in_block_mappings_not_end_of_document(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: true,\n'
|
||||
' forbid-in-flow-mappings: false}\n')
|
||||
self.check('---\n'
|
||||
'foo:\n'
|
||||
'bar:\n'
|
||||
' aaa\n', conf, problem1=(2, 5))
|
||||
|
||||
def test_in_block_mappings_different_level(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: true,\n'
|
||||
' forbid-in-flow-mappings: false}\n')
|
||||
self.check('---\n'
|
||||
'foo:\n'
|
||||
' bar:\n'
|
||||
'aaa: bbb\n', conf, problem1=(3, 6))
|
||||
|
||||
def test_in_block_mappings_empty_flow_mapping(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: true,\n'
|
||||
' forbid-in-flow-mappings: false}\n'
|
||||
'braces: disable\n'
|
||||
'commas: disable\n')
|
||||
self.check('---\n'
|
||||
'foo: {a:}\n', conf)
|
||||
self.check('---\n'
|
||||
'- {a:, b: 2}\n'
|
||||
'- {a: 1, b:}\n'
|
||||
'- {a: 1, b: , }\n', conf)
|
||||
|
||||
def test_in_block_mappings_empty_block_sequence(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: true,\n'
|
||||
' forbid-in-flow-mappings: false}\n')
|
||||
self.check('---\n'
|
||||
'foo:\n'
|
||||
' -\n', conf)
|
||||
|
||||
def test_in_block_mappings_not_empty_or_explicit_null(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: true,\n'
|
||||
' forbid-in-flow-mappings: false}\n')
|
||||
self.check('---\n'
|
||||
'foo:\n'
|
||||
' bar:\n'
|
||||
' aaa\n', conf)
|
||||
self.check('---\n'
|
||||
'explicitly-null: null\n', conf)
|
||||
self.check('---\n'
|
||||
'explicitly-null:with-colons:in-key: null\n', conf)
|
||||
self.check('---\n'
|
||||
'false-null: nulL\n', conf)
|
||||
self.check('---\n'
|
||||
'empty-string: \'\'\n', conf)
|
||||
self.check('---\n'
|
||||
'nullable-boolean: false\n', conf)
|
||||
self.check('---\n'
|
||||
'nullable-int: 0\n', conf)
|
||||
self.check('---\n'
|
||||
'First occurrence: &anchor Foo\n'
|
||||
'Second occurrence: *anchor\n', conf)
|
||||
|
||||
def test_in_block_mappings_various_explicit_null(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: true,\n'
|
||||
' forbid-in-flow-mappings: false}\n')
|
||||
self.check('---\n'
|
||||
'null-alias: ~\n', conf)
|
||||
self.check('---\n'
|
||||
'null-key1: {?: val}\n', conf)
|
||||
self.check('---\n'
|
||||
'null-key2: {? !!null "": val}\n', conf)
|
||||
|
||||
def test_in_block_mappings_comments(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: true,\n'
|
||||
' forbid-in-flow-mappings: false}\n'
|
||||
'comments: disable\n')
|
||||
self.check('---\n'
|
||||
'empty: # comment\n'
|
||||
'foo:\n'
|
||||
' bar: # comment\n', conf,
|
||||
problem1=(2, 7),
|
||||
problem2=(4, 7))
|
||||
|
||||
def test_in_flow_mappings_disabled(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: false,\n'
|
||||
' forbid-in-flow-mappings: false}\n'
|
||||
'braces: disable\n'
|
||||
'commas: disable\n')
|
||||
self.check('---\n'
|
||||
'{a:}\n', conf)
|
||||
self.check('---\n'
|
||||
'foo: {a:}\n', conf)
|
||||
self.check('---\n'
|
||||
'- {a:}\n'
|
||||
'- {a:, b: 2}\n'
|
||||
'- {a: 1, b:}\n'
|
||||
'- {a: 1, b: , }\n', conf)
|
||||
self.check('---\n'
|
||||
'{a: {b: , c: {d: 4, e:}}, f:}\n', conf)
|
||||
|
||||
def test_in_flow_mappings_single_line(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: false,\n'
|
||||
' forbid-in-flow-mappings: true}\n'
|
||||
'braces: disable\n'
|
||||
'commas: disable\n')
|
||||
self.check('---\n'
|
||||
'{a:}\n', conf,
|
||||
problem=(2, 4))
|
||||
self.check('---\n'
|
||||
'foo: {a:}\n', conf,
|
||||
problem=(2, 9))
|
||||
self.check('---\n'
|
||||
'- {a:}\n'
|
||||
'- {a:, b: 2}\n'
|
||||
'- {a: 1, b:}\n'
|
||||
'- {a: 1, b: , }\n', conf,
|
||||
problem1=(2, 6),
|
||||
problem2=(3, 6),
|
||||
problem3=(4, 12),
|
||||
problem4=(5, 12))
|
||||
self.check('---\n'
|
||||
'{a: {b: , c: {d: 4, e:}}, f:}\n', conf,
|
||||
problem1=(2, 8),
|
||||
problem2=(2, 23),
|
||||
problem3=(2, 29))
|
||||
|
||||
def test_in_flow_mappings_multi_line(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: false,\n'
|
||||
' forbid-in-flow-mappings: true}\n'
|
||||
'braces: disable\n'
|
||||
'commas: disable\n')
|
||||
self.check('---\n'
|
||||
'foo: {\n'
|
||||
' a:\n'
|
||||
'}\n', conf,
|
||||
problem=(3, 5))
|
||||
self.check('---\n'
|
||||
'{\n'
|
||||
' a: {\n'
|
||||
' b: ,\n'
|
||||
' c: {\n'
|
||||
' d: 4,\n'
|
||||
' e:\n'
|
||||
' }\n'
|
||||
' },\n'
|
||||
' f:\n'
|
||||
'}\n', conf,
|
||||
problem1=(4, 7),
|
||||
problem2=(7, 9),
|
||||
problem3=(10, 5))
|
||||
|
||||
def test_in_flow_mappings_various_explicit_null(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: false,\n'
|
||||
' forbid-in-flow-mappings: true}\n'
|
||||
'braces: disable\n'
|
||||
'commas: disable\n')
|
||||
self.check('---\n'
|
||||
'{explicit-null: null}\n', conf)
|
||||
self.check('---\n'
|
||||
'{null-alias: ~}\n', conf)
|
||||
self.check('---\n'
|
||||
'null-key1: {?: val}\n', conf)
|
||||
self.check('---\n'
|
||||
'null-key2: {? !!null "": val}\n', conf)
|
||||
|
||||
def test_in_flow_mappings_comments(self):
|
||||
conf = ('empty-values: {forbid-in-block-mappings: false,\n'
|
||||
' forbid-in-flow-mappings: true}\n'
|
||||
'braces: disable\n'
|
||||
'commas: disable\n'
|
||||
'comments: disable\n')
|
||||
self.check('---\n'
|
||||
'{\n'
|
||||
' a: {\n'
|
||||
' b: , # comment\n'
|
||||
' c: {\n'
|
||||
' d: 4, # comment\n'
|
||||
' e: # comment\n'
|
||||
' }\n'
|
||||
' },\n'
|
||||
' f: # comment\n'
|
||||
'}\n', conf,
|
||||
problem1=(4, 7),
|
||||
problem2=(7, 9),
|
||||
problem3=(10, 5))
|
||||
@@ -15,6 +15,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from tests.common import RuleTestCase
|
||||
|
||||
from yamllint.parser import token_or_comment_generator, Comment
|
||||
from yamllint.rules.indentation import check
|
||||
|
||||
@@ -549,7 +550,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'
|
||||
@@ -562,7 +563,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'
|
||||
@@ -577,7 +578,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,8 +590,11 @@ class IndentationTestCase(RuleTestCase):
|
||||
' date: 1969\n'
|
||||
' - name: Linux\n'
|
||||
' date: 1991\n'
|
||||
' k4:\n'
|
||||
' -\n'
|
||||
' k5: v3\n'
|
||||
'...\n', conf)
|
||||
conf = 'indentation: {spaces: 2, indent-sequences: yes}'
|
||||
conf = 'indentation: {spaces: 2, indent-sequences: true}'
|
||||
self.check('---\n'
|
||||
'object:\n'
|
||||
' k1:\n'
|
||||
@@ -605,7 +609,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'
|
||||
@@ -618,7 +622,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'
|
||||
@@ -632,7 +636,7 @@ class IndentationTestCase(RuleTestCase):
|
||||
' date: 1991\n'
|
||||
'...\n', conf)
|
||||
|
||||
def test_consistent(self):
|
||||
def test_consistent_spaces(self):
|
||||
conf = ('indentation: {spaces: consistent,\n'
|
||||
' indent-sequences: whatever}\n'
|
||||
'document-start: disable\n')
|
||||
@@ -713,6 +717,142 @@ class IndentationTestCase(RuleTestCase):
|
||||
'- b\n'
|
||||
'- c\n', conf)
|
||||
|
||||
def test_consistent_spaces_and_indent_sequences(self):
|
||||
conf = 'indentation: {spaces: consistent, indent-sequences: true}'
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
'- 1\n'
|
||||
'- 2\n'
|
||||
'- 3\n'
|
||||
'list two:\n'
|
||||
' - a\n'
|
||||
' - b\n'
|
||||
' - c\n', conf, problem1=(3, 1))
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
' - 1\n'
|
||||
' - 2\n'
|
||||
' - 3\n'
|
||||
'list two:\n'
|
||||
' - a\n'
|
||||
' - b\n'
|
||||
' - c\n', conf, problem1=(7, 5))
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
' - 1\n'
|
||||
' - 2\n'
|
||||
' - 3\n'
|
||||
'list two:\n'
|
||||
'- a\n'
|
||||
'- b\n'
|
||||
'- c\n', conf, problem1=(7, 1))
|
||||
|
||||
conf = 'indentation: {spaces: consistent, indent-sequences: false}'
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
'- 1\n'
|
||||
'- 2\n'
|
||||
'- 3\n'
|
||||
'list two:\n'
|
||||
' - a\n'
|
||||
' - b\n'
|
||||
' - c\n', conf, problem1=(7, 5))
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
'- 1\n'
|
||||
'- 2\n'
|
||||
'- 3\n'
|
||||
'list two:\n'
|
||||
' - a\n'
|
||||
' - b\n'
|
||||
' - c\n', conf, problem1=(7, 3))
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
' - 1\n'
|
||||
' - 2\n'
|
||||
' - 3\n'
|
||||
'list two:\n'
|
||||
'- a\n'
|
||||
'- b\n'
|
||||
'- c\n', conf, problem1=(3, 3))
|
||||
|
||||
conf = ('indentation: {spaces: consistent,\n'
|
||||
' indent-sequences: consistent}')
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
'- 1\n'
|
||||
'- 2\n'
|
||||
'- 3\n'
|
||||
'list two:\n'
|
||||
' - a\n'
|
||||
' - b\n'
|
||||
' - c\n', conf, problem1=(7, 5))
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
' - 1\n'
|
||||
' - 2\n'
|
||||
' - 3\n'
|
||||
'list two:\n'
|
||||
'- a\n'
|
||||
'- b\n'
|
||||
'- c\n', conf, problem1=(7, 1))
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
'- 1\n'
|
||||
'- 2\n'
|
||||
'- 3\n'
|
||||
'list two:\n'
|
||||
'- a\n'
|
||||
'- b\n'
|
||||
'- c\n', conf)
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
' - 1\n'
|
||||
' - 2\n'
|
||||
' - 3\n'
|
||||
'list two:\n'
|
||||
' - a\n'
|
||||
' - b\n'
|
||||
' - c\n', conf, problem1=(7, 5))
|
||||
|
||||
conf = 'indentation: {spaces: consistent, indent-sequences: whatever}'
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
'- 1\n'
|
||||
'- 2\n'
|
||||
'- 3\n'
|
||||
'list two:\n'
|
||||
' - a\n'
|
||||
' - b\n'
|
||||
' - c\n', conf)
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
' - 1\n'
|
||||
' - 2\n'
|
||||
' - 3\n'
|
||||
'list two:\n'
|
||||
'- a\n'
|
||||
'- b\n'
|
||||
'- c\n', conf)
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
'- 1\n'
|
||||
'- 2\n'
|
||||
'- 3\n'
|
||||
'list two:\n'
|
||||
'- a\n'
|
||||
'- b\n'
|
||||
'- c\n', conf)
|
||||
self.check('---\n'
|
||||
'list one:\n'
|
||||
' - 1\n'
|
||||
' - 2\n'
|
||||
' - 3\n'
|
||||
'list two:\n'
|
||||
' - a\n'
|
||||
' - b\n'
|
||||
' - c\n', conf, problem1=(7, 5))
|
||||
|
||||
def test_indent_sequences_whatever(self):
|
||||
conf = 'indentation: {spaces: 4, indent-sequences: whatever}'
|
||||
self.check('---\n'
|
||||
@@ -1072,6 +1212,20 @@ class IndentationTestCase(RuleTestCase):
|
||||
' - name: Linux\n'
|
||||
' date: 1991\n'
|
||||
'...\n', conf, problem=(5, 10, 'syntax'))
|
||||
conf = 'indentation: {spaces: 2, indent-sequences: true}'
|
||||
self.check('---\n'
|
||||
'a:\n'
|
||||
'-\n' # empty list
|
||||
'b: c\n'
|
||||
'...\n', conf, problem=(3, 1))
|
||||
conf = 'indentation: {spaces: 2, indent-sequences: consistent}'
|
||||
self.check('---\n'
|
||||
'a:\n'
|
||||
' -\n' # empty list
|
||||
'b:\n'
|
||||
'-\n'
|
||||
'c: d\n'
|
||||
'...\n', conf, problem=(5, 1))
|
||||
|
||||
def test_over_indented(self):
|
||||
conf = 'indentation: {spaces: 2, indent-sequences: consistent}'
|
||||
@@ -1128,9 +1282,23 @@ class IndentationTestCase(RuleTestCase):
|
||||
' - subel\n'
|
||||
'...\n', conf,
|
||||
problem=(2, 3))
|
||||
conf = 'indentation: {spaces: 2, indent-sequences: false}'
|
||||
self.check('---\n'
|
||||
'a:\n'
|
||||
' -\n' # empty list
|
||||
'b: c\n'
|
||||
'...\n', conf, problem=(3, 3))
|
||||
conf = 'indentation: {spaces: 2, indent-sequences: consistent}'
|
||||
self.check('---\n'
|
||||
'a:\n'
|
||||
'-\n' # empty list
|
||||
'b:\n'
|
||||
' -\n'
|
||||
'c: d\n'
|
||||
'...\n', conf, problem=(5, 3))
|
||||
|
||||
def test_multi_lines(self):
|
||||
conf = 'indentation: {spaces: consistent, indent-sequences: yes}'
|
||||
conf = 'indentation: {spaces: consistent, indent-sequences: true}'
|
||||
self.check('---\n'
|
||||
'long_string: >\n'
|
||||
' bla bla blah\n'
|
||||
@@ -1438,7 +1606,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'
|
||||
@@ -1505,7 +1673,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)
|
||||
@@ -1534,7 +1702,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))
|
||||
@@ -1557,7 +1725,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)
|
||||
@@ -1588,7 +1756,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))
|
||||
@@ -1644,7 +1812,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'
|
||||
@@ -1682,7 +1850,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'
|
||||
@@ -1723,7 +1891,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'
|
||||
@@ -1761,7 +1929,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'
|
||||
@@ -1805,7 +1973,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'
|
||||
@@ -1827,7 +1995,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'
|
||||
@@ -1855,7 +2023,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'
|
||||
@@ -1883,7 +2051,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'
|
||||
@@ -1901,7 +2069,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'
|
||||
@@ -1919,7 +2087,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)
|
||||
|
||||
@@ -78,6 +78,15 @@ class KeyDuplicatesTestCase(RuleTestCase):
|
||||
' duplicates with\n'
|
||||
' many styles\n'
|
||||
': 1\n', conf)
|
||||
self.check('---\n'
|
||||
'Merge Keys are OK:\n'
|
||||
'anchor_one: &anchor_one\n'
|
||||
' one: one\n'
|
||||
'anchor_two: &anchor_two\n'
|
||||
' two: two\n'
|
||||
'anchor_reference:\n'
|
||||
' <<: *anchor_one\n'
|
||||
' <<: *anchor_two\n', conf)
|
||||
|
||||
def test_enabled(self):
|
||||
conf = 'key-duplicates: enable'
|
||||
@@ -147,6 +156,15 @@ class KeyDuplicatesTestCase(RuleTestCase):
|
||||
': 1\n', conf,
|
||||
problem1=(3, 1), problem2=(4, 1), problem3=(5, 3),
|
||||
problem4=(7, 3))
|
||||
self.check('---\n'
|
||||
'Merge Keys are OK:\n'
|
||||
'anchor_one: &anchor_one\n'
|
||||
' one: one\n'
|
||||
'anchor_two: &anchor_two\n'
|
||||
' two: two\n'
|
||||
'anchor_reference:\n'
|
||||
' <<: *anchor_one\n'
|
||||
' <<: *anchor_two\n', conf)
|
||||
|
||||
def test_key_tokens_in_flow_sequences(self):
|
||||
conf = 'key-duplicates: enable'
|
||||
|
||||
116
tests/rules/test_key_ordering.py
Normal file
116
tests/rules/test_key_ordering.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2017 Johannes F. Knauf
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from tests.common import RuleTestCase
|
||||
|
||||
|
||||
class KeyOrderingTestCase(RuleTestCase):
|
||||
rule_id = 'key-ordering'
|
||||
|
||||
def test_disabled(self):
|
||||
conf = 'key-ordering: disable'
|
||||
self.check('---\n'
|
||||
'block mapping:\n'
|
||||
' secondkey: a\n'
|
||||
' firstkey: b\n', conf)
|
||||
self.check('---\n'
|
||||
'flow mapping:\n'
|
||||
' {secondkey: a, firstkey: b}\n', conf)
|
||||
self.check('---\n'
|
||||
'second: before_first\n'
|
||||
'at: root\n', conf)
|
||||
self.check('---\n'
|
||||
'nested but OK:\n'
|
||||
' second: {first: 1}\n'
|
||||
' third:\n'
|
||||
' second: 2\n', conf)
|
||||
|
||||
def test_enabled(self):
|
||||
conf = 'key-ordering: enable'
|
||||
self.check('---\n'
|
||||
'block mapping:\n'
|
||||
' secondkey: a\n'
|
||||
' firstkey: b\n', conf,
|
||||
problem=(4, 3))
|
||||
self.check('---\n'
|
||||
'flow mapping:\n'
|
||||
' {secondkey: a, firstkey: b}\n', conf,
|
||||
problem=(3, 18))
|
||||
self.check('---\n'
|
||||
'second: before_first\n'
|
||||
'at: root\n', conf,
|
||||
problem=(3, 1))
|
||||
self.check('---\n'
|
||||
'nested but OK:\n'
|
||||
' second: {first: 1}\n'
|
||||
' third:\n'
|
||||
' second: 2\n', conf)
|
||||
|
||||
def test_word_length(self):
|
||||
conf = 'key-ordering: enable'
|
||||
self.check('---\n'
|
||||
'a: 1\n'
|
||||
'ab: 1\n'
|
||||
'abc: 1\n', conf)
|
||||
self.check('---\n'
|
||||
'a: 1\n'
|
||||
'abc: 1\n'
|
||||
'ab: 1\n', conf,
|
||||
problem=(4, 1))
|
||||
|
||||
def test_key_duplicates(self):
|
||||
conf = ('key-duplicates: disable\n'
|
||||
'key-ordering: enable')
|
||||
self.check('---\n'
|
||||
'key: 1\n'
|
||||
'key: 2\n', conf)
|
||||
|
||||
def test_case(self):
|
||||
conf = 'key-ordering: enable'
|
||||
self.check('---\n'
|
||||
'T-shirt: 1\n'
|
||||
'T-shirts: 2\n'
|
||||
't-shirt: 3\n'
|
||||
't-shirts: 4\n', conf)
|
||||
self.check('---\n'
|
||||
'T-shirt: 1\n'
|
||||
't-shirt: 2\n'
|
||||
'T-shirts: 3\n'
|
||||
't-shirts: 4\n', conf,
|
||||
problem=(4, 1))
|
||||
|
||||
def test_accents(self):
|
||||
conf = 'key-ordering: enable'
|
||||
self.check('---\n'
|
||||
'hair: true\n'
|
||||
'hais: true\n'
|
||||
'haïr: true\n'
|
||||
'haïssable: true\n', conf)
|
||||
self.check('---\n'
|
||||
'haïr: true\n'
|
||||
'hais: true\n', conf,
|
||||
problem=(3, 1))
|
||||
self.check('---\n'
|
||||
'haïr: true\n'
|
||||
'hais: true\n', conf,
|
||||
problem=(3, 1))
|
||||
|
||||
def test_key_tokens_in_flow_sequences(self):
|
||||
conf = 'key-ordering: enable'
|
||||
self.check('---\n'
|
||||
'[\n'
|
||||
' key: value, mappings, in, flow: sequence\n'
|
||||
']\n', conf)
|
||||
@@ -66,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'
|
||||
@@ -91,7 +91,7 @@ class LineLengthTestCase(RuleTestCase):
|
||||
'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'
|
||||
@@ -116,7 +116,7 @@ class LineLengthTestCase(RuleTestCase):
|
||||
'long_line: http://localhost/very/very/long/url\n'
|
||||
'...\n', conf, problem=(2, 21))
|
||||
|
||||
conf = ('line-length: {max: 20, allow-non-breakable-words: yes}\n'
|
||||
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',
|
||||
@@ -124,7 +124,7 @@ class LineLengthTestCase(RuleTestCase):
|
||||
|
||||
def test_non_breakable_inline_mappings(self):
|
||||
conf = 'line-length: {max: 20, ' \
|
||||
'allow-non-breakable-inline-mappings: yes}'
|
||||
'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)
|
||||
@@ -137,7 +137,7 @@ class LineLengthTestCase(RuleTestCase):
|
||||
conf, problem1=(2, 21), problem2=(3, 21))
|
||||
|
||||
conf = ('line-length: {max: 20,'
|
||||
' allow-non-breakable-inline-mappings: yes}\n'
|
||||
' allow-non-breakable-inline-mappings: true}\n'
|
||||
'trailing-spaces: disable')
|
||||
self.check('---\n'
|
||||
'long_line: and+some+space+at+the+end \n',
|
||||
@@ -150,7 +150,7 @@ class LineLengthTestCase(RuleTestCase):
|
||||
conf, problem=(2, 21))
|
||||
|
||||
# See https://github.com/adrienverge/yamllint/issues/21
|
||||
conf = 'line-length: {allow-non-breakable-inline-mappings: yes}'
|
||||
conf = 'line-length: {allow-non-breakable-inline-mappings: true}'
|
||||
self.check('---\n'
|
||||
'content: |\n'
|
||||
' {% this line is' + 99 * ' really' + ' long %}\n',
|
||||
|
||||
72
tests/rules/test_octal_values.py
Normal file
72
tests/rules/test_octal_values.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# -*- 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 OctalValuesTestCase(RuleTestCase):
|
||||
rule_id = 'octal-values'
|
||||
|
||||
def test_disabled(self):
|
||||
conf = ('octal-values: disable\n'
|
||||
'new-line-at-end-of-file: disable\n'
|
||||
'document-start: disable\n')
|
||||
self.check('user-city: 010', conf)
|
||||
self.check('user-city: 0o10', conf)
|
||||
|
||||
def test_implicit_octal_values(self):
|
||||
conf = ('octal-values: {forbid-implicit-octal: true}\n'
|
||||
'new-line-at-end-of-file: disable\n'
|
||||
'document-start: disable\n')
|
||||
self.check('user-city: 010', conf, problem=(1, 15))
|
||||
self.check('user-city: abc', conf)
|
||||
self.check('user-city: 010,0571', conf)
|
||||
self.check("user-city: '010'", conf)
|
||||
self.check('user-city: "010"', conf)
|
||||
self.check('user-city:\n'
|
||||
' - 010', conf, problem=(2, 8))
|
||||
self.check('user-city: [010]', conf, problem=(1, 16))
|
||||
self.check('user-city: {beijing: 010}', conf, problem=(1, 25))
|
||||
self.check('explicit-octal: 0o10', conf)
|
||||
self.check('not-number: 0abc', conf)
|
||||
self.check('zero: 0', conf)
|
||||
self.check('hex-value: 0x10', conf)
|
||||
self.check('number-values:\n'
|
||||
' - 0.10\n'
|
||||
' - .01\n'
|
||||
' - 0e3\n', conf)
|
||||
|
||||
def test_explicit_octal_values(self):
|
||||
conf = ('octal-values: {forbid-explicit-octal: true}\n'
|
||||
'new-line-at-end-of-file: disable\n'
|
||||
'document-start: disable\n')
|
||||
self.check('user-city: 0o10', conf, problem=(1, 16))
|
||||
self.check('user-city: abc', conf)
|
||||
self.check('user-city: 0o10,0571', conf)
|
||||
self.check("user-city: '0o10'", conf)
|
||||
self.check('user-city:\n'
|
||||
' - 0o10', conf, problem=(2, 9))
|
||||
self.check('user-city: [0o10]', conf, problem=(1, 17))
|
||||
self.check('user-city: {beijing: 0o10}', conf, problem=(1, 26))
|
||||
self.check('implicit-octal: 010', conf)
|
||||
self.check('not-number: 0oabc', conf)
|
||||
self.check('zero: 0', conf)
|
||||
self.check('hex-value: 0x10', conf)
|
||||
self.check('number-values:\n'
|
||||
' - 0.10\n'
|
||||
' - .01\n'
|
||||
' - 0e3\n', conf)
|
||||
self.check('user-city: "010"', conf)
|
||||
@@ -23,61 +23,59 @@ import locale
|
||||
import os
|
||||
import pty
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
import sys
|
||||
try:
|
||||
assert sys.version_info >= (2, 7)
|
||||
import unittest
|
||||
except AssertionError:
|
||||
import unittest2 as unittest
|
||||
|
||||
from tests.common import build_temp_workspace
|
||||
|
||||
from yamllint import cli
|
||||
|
||||
|
||||
@unittest.skipIf(sys.version_info < (2, 7), 'Python 2.6 not supported')
|
||||
class CommandLineTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.wd = tempfile.mkdtemp(prefix='yamllint-tests-')
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(CommandLineTestCase, cls).setUpClass()
|
||||
|
||||
# .yaml file at root
|
||||
with open(os.path.join(self.wd, 'a.yaml'), 'w') as f:
|
||||
f.write('---\n'
|
||||
'- 1 \n'
|
||||
'- 2')
|
||||
cls.wd = build_temp_workspace({
|
||||
# .yaml file at root
|
||||
'a.yaml': '---\n'
|
||||
'- 1 \n'
|
||||
'- 2',
|
||||
# file with only one warning
|
||||
'warn.yaml': 'key: value\n',
|
||||
# .yml file at root
|
||||
'empty.yml': '',
|
||||
# file in dir
|
||||
'sub/ok.yaml': '---\n'
|
||||
'key: value\n',
|
||||
# file in very nested dir
|
||||
's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml': '---\n'
|
||||
'key: value\n'
|
||||
'key: other value\n',
|
||||
# empty dir
|
||||
'empty-dir': [],
|
||||
# non-YAML file
|
||||
'no-yaml.json': '---\n'
|
||||
'key: value\n',
|
||||
# non-ASCII chars
|
||||
'non-ascii/utf-8': (
|
||||
u'---\n'
|
||||
u'- hétérogénéité\n'
|
||||
u'# 19.99 €\n'
|
||||
u'- お早う御座います。\n'
|
||||
u'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'),
|
||||
})
|
||||
|
||||
# .yml file at root
|
||||
open(os.path.join(self.wd, 'empty.yml'), 'w').close()
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(CommandLineTestCase, cls).tearDownClass()
|
||||
|
||||
# file in dir
|
||||
os.mkdir(os.path.join(self.wd, 'sub'))
|
||||
with open(os.path.join(self.wd, 'sub', 'ok.yaml'), 'w') as f:
|
||||
f.write('---\n'
|
||||
'key: value\n')
|
||||
|
||||
# file in very nested dir
|
||||
dir = self.wd
|
||||
for i in range(15):
|
||||
dir = os.path.join(dir, 's')
|
||||
os.mkdir(dir)
|
||||
with open(os.path.join(dir, 'file.yaml'), 'w') as f:
|
||||
f.write('---\n'
|
||||
'key: value\n'
|
||||
'key: other value\n')
|
||||
|
||||
# empty dir
|
||||
os.mkdir(os.path.join(self.wd, 'empty-dir'))
|
||||
|
||||
# non-YAML file
|
||||
with open(os.path.join(self.wd, 'no-yaml.json'), 'w') as f:
|
||||
f.write('---\n'
|
||||
'key: value\n')
|
||||
|
||||
# non-ASCII chars
|
||||
os.mkdir(os.path.join(self.wd, 'non-ascii'))
|
||||
with open(os.path.join(self.wd, 'non-ascii', 'utf-8'), 'wb') as f:
|
||||
f.write((u'---\n'
|
||||
u'- hétérogénéité\n'
|
||||
u'# 19.99 €\n'
|
||||
u'- お早う御座います。\n'
|
||||
u'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'))
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.wd)
|
||||
shutil.rmtree(cls.wd)
|
||||
|
||||
def test_find_files_recursively(self):
|
||||
self.assertEqual(
|
||||
@@ -85,7 +83,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'),
|
||||
@@ -140,8 +139,11 @@ class CommandLineTestCase(unittest.TestCase):
|
||||
|
||||
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
|
||||
self.assertEqual(out, '')
|
||||
self.assertRegexpMatches(err, r'^Options --config-file and '
|
||||
r'--config-data cannot be used')
|
||||
self.assertRegexpMatches(
|
||||
err.splitlines()[-1],
|
||||
r'^yamllint: error: argument -d\/--config-data: '
|
||||
r'not allowed with argument -c\/--config-file$'
|
||||
)
|
||||
|
||||
def test_run_with_bad_config(self):
|
||||
sys.stdout, sys.stderr = StringIO(), StringIO()
|
||||
@@ -247,6 +249,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')
|
||||
|
||||
@@ -279,7 +299,10 @@ class CommandLineTestCase(unittest.TestCase):
|
||||
# Make sure the default localization conditions on this "system"
|
||||
# support UTF-8 encoding.
|
||||
loc = locale.getlocale()
|
||||
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
|
||||
except locale.Error:
|
||||
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
|
||||
|
||||
sys.stdout, sys.stderr = StringIO(), StringIO()
|
||||
with self.assertRaises(SystemExit) as ctx:
|
||||
|
||||
@@ -14,8 +14,22 @@
|
||||
# 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 unittest
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
try:
|
||||
assert sys.version_info >= (2, 7)
|
||||
import unittest
|
||||
except AssertionError:
|
||||
import unittest2 as unittest
|
||||
|
||||
from tests.common import build_temp_workspace
|
||||
|
||||
from yamllint import cli
|
||||
from yamllint import config
|
||||
|
||||
|
||||
@@ -30,7 +44,7 @@ class SimpleConfigTestCase(unittest.TestCase):
|
||||
self.assertEqual(new.rules['colons']['max-spaces-before'], 0)
|
||||
self.assertEqual(new.rules['colons']['max-spaces-after'], 1)
|
||||
|
||||
self.assertEqual(len(new.enabled_rules()), 1)
|
||||
self.assertEqual(len(new.enabled_rules(None)), 1)
|
||||
|
||||
def test_invalid_conf(self):
|
||||
with self.assertRaises(config.YamlLintConfigError):
|
||||
@@ -62,6 +76,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'
|
||||
@@ -131,7 +184,7 @@ class ExtendedConfigTestCase(unittest.TestCase):
|
||||
self.assertEqual(new.rules['colons']['max-spaces-after'], 1)
|
||||
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
|
||||
|
||||
self.assertEqual(len(new.enabled_rules()), 2)
|
||||
self.assertEqual(len(new.enabled_rules(None)), 2)
|
||||
|
||||
def test_extend_remove_rule(self):
|
||||
old = config.YamlLintConfig('rules:\n'
|
||||
@@ -148,7 +201,7 @@ class ExtendedConfigTestCase(unittest.TestCase):
|
||||
self.assertEqual(new.rules['colons'], False)
|
||||
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
|
||||
|
||||
self.assertEqual(len(new.enabled_rules()), 1)
|
||||
self.assertEqual(len(new.enabled_rules(None)), 1)
|
||||
|
||||
def test_extend_edit_rule(self):
|
||||
old = config.YamlLintConfig('rules:\n'
|
||||
@@ -168,7 +221,7 @@ class ExtendedConfigTestCase(unittest.TestCase):
|
||||
self.assertEqual(new.rules['colons']['max-spaces-after'], 4)
|
||||
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
|
||||
|
||||
self.assertEqual(len(new.enabled_rules()), 2)
|
||||
self.assertEqual(len(new.enabled_rules(None)), 2)
|
||||
|
||||
def test_extend_reenable_rule(self):
|
||||
old = config.YamlLintConfig('rules:\n'
|
||||
@@ -186,7 +239,7 @@ class ExtendedConfigTestCase(unittest.TestCase):
|
||||
self.assertEqual(new.rules['colons']['max-spaces-after'], 1)
|
||||
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
|
||||
|
||||
self.assertEqual(len(new.enabled_rules()), 2)
|
||||
self.assertEqual(len(new.enabled_rules(None)), 2)
|
||||
|
||||
|
||||
class ExtendedLibraryConfigTestCase(unittest.TestCase):
|
||||
@@ -231,3 +284,94 @@ class ExtendedLibraryConfigTestCase(unittest.TestCase):
|
||||
self.assertEqual(sorted(new.rules.keys()), sorted(old.rules.keys()))
|
||||
for rule in new.rules:
|
||||
self.assertEqual(new.rules[rule], old.rules[rule])
|
||||
|
||||
|
||||
class IgnorePathConfigTestCase(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(IgnorePathConfigTestCase, cls).setUpClass()
|
||||
|
||||
bad_yaml = ('---\n'
|
||||
'- key: val1\n'
|
||||
' key: val2\n'
|
||||
'- trailing space \n'
|
||||
'- lonely hyphen\n')
|
||||
|
||||
cls.wd = build_temp_workspace({
|
||||
'bin/file.lint-me-anyway.yaml': bad_yaml,
|
||||
'bin/file.yaml': bad_yaml,
|
||||
'file-at-root.yaml': bad_yaml,
|
||||
'file.dont-lint-me.yaml': bad_yaml,
|
||||
'ign-dup/file.yaml': bad_yaml,
|
||||
'ign-dup/sub/dir/file.yaml': bad_yaml,
|
||||
'ign-trail/file.yaml': bad_yaml,
|
||||
'include/ign-dup/sub/dir/file.yaml': bad_yaml,
|
||||
's/s/ign-trail/file.yaml': bad_yaml,
|
||||
's/s/ign-trail/s/s/file.yaml': bad_yaml,
|
||||
's/s/ign-trail/s/s/file2.lint-me-anyway.yaml': bad_yaml,
|
||||
|
||||
'.yamllint': 'ignore: |\n'
|
||||
' *.dont-lint-me.yaml\n'
|
||||
' /bin/\n'
|
||||
' !/bin/*.lint-me-anyway.yaml\n'
|
||||
'\n'
|
||||
'extends: default\n'
|
||||
'\n'
|
||||
'rules:\n'
|
||||
' key-duplicates:\n'
|
||||
' ignore: |\n'
|
||||
' /ign-dup\n'
|
||||
' trailing-spaces:\n'
|
||||
' ignore: |\n'
|
||||
' ign-trail\n'
|
||||
' !*.lint-me-anyway.yaml\n',
|
||||
})
|
||||
|
||||
cls.backup_wd = os.getcwd()
|
||||
os.chdir(cls.wd)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(IgnorePathConfigTestCase, cls).tearDownClass()
|
||||
|
||||
os.chdir(cls.backup_wd)
|
||||
|
||||
shutil.rmtree(cls.wd)
|
||||
|
||||
@unittest.skipIf(sys.version_info < (2, 7), 'Python 2.6 not supported')
|
||||
def test_run_with_ignored_path(self):
|
||||
sys.stdout = StringIO()
|
||||
with self.assertRaises(SystemExit):
|
||||
cli.run(('-f', 'parsable', '.'))
|
||||
|
||||
out = sys.stdout.getvalue()
|
||||
out = '\n'.join(sorted(out.splitlines()))
|
||||
|
||||
keydup = '[error] duplication of key "key" in mapping (key-duplicates)'
|
||||
trailing = '[error] trailing spaces (trailing-spaces)'
|
||||
hyphen = '[error] too many spaces after hyphen (hyphens)'
|
||||
|
||||
self.assertEqual(out, '\n'.join((
|
||||
'./bin/file.lint-me-anyway.yaml:3:3: ' + keydup,
|
||||
'./bin/file.lint-me-anyway.yaml:4:17: ' + trailing,
|
||||
'./bin/file.lint-me-anyway.yaml:5:5: ' + hyphen,
|
||||
'./file-at-root.yaml:3:3: ' + keydup,
|
||||
'./file-at-root.yaml:4:17: ' + trailing,
|
||||
'./file-at-root.yaml:5:5: ' + hyphen,
|
||||
'./ign-dup/file.yaml:4:17: ' + trailing,
|
||||
'./ign-dup/file.yaml:5:5: ' + hyphen,
|
||||
'./ign-dup/sub/dir/file.yaml:4:17: ' + trailing,
|
||||
'./ign-dup/sub/dir/file.yaml:5:5: ' + hyphen,
|
||||
'./ign-trail/file.yaml:3:3: ' + keydup,
|
||||
'./ign-trail/file.yaml:5:5: ' + hyphen,
|
||||
'./include/ign-dup/sub/dir/file.yaml:3:3: ' + keydup,
|
||||
'./include/ign-dup/sub/dir/file.yaml:4:17: ' + trailing,
|
||||
'./include/ign-dup/sub/dir/file.yaml:5:5: ' + hyphen,
|
||||
'./s/s/ign-trail/file.yaml:3:3: ' + keydup,
|
||||
'./s/s/ign-trail/file.yaml:5:5: ' + hyphen,
|
||||
'./s/s/ign-trail/s/s/file.yaml:3:3: ' + keydup,
|
||||
'./s/s/ign-trail/s/s/file.yaml:5:5: ' + hyphen,
|
||||
'./s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:3:3: ' + keydup,
|
||||
'./s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:4:17: ' + trailing,
|
||||
'./s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:5:5: ' + hyphen,
|
||||
)))
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import io
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
try:
|
||||
assert sys.version_info >= (2, 7)
|
||||
import unittest
|
||||
except AssertionError:
|
||||
import unittest2 as unittest
|
||||
|
||||
from yamllint.config import YamlLintConfig
|
||||
from yamllint import linter
|
||||
|
||||
91
tests/test_module.py
Normal file
91
tests/test_module.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2017 Adrien Vergé
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import sys
|
||||
try:
|
||||
assert sys.version_info >= (2, 7)
|
||||
import unittest
|
||||
except AssertionError:
|
||||
import unittest2 as unittest
|
||||
|
||||
|
||||
PYTHON = sys.executable or 'python'
|
||||
|
||||
|
||||
@unittest.skipIf(sys.version_info < (2, 7), 'Python 2.6 not supported')
|
||||
class ModuleTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.wd = tempfile.mkdtemp(prefix='yamllint-tests-')
|
||||
|
||||
# file with only one warning
|
||||
with open(os.path.join(self.wd, 'warn.yaml'), 'w') as f:
|
||||
f.write('key: value\n')
|
||||
|
||||
# file in dir
|
||||
os.mkdir(os.path.join(self.wd, 'sub'))
|
||||
with open(os.path.join(self.wd, 'sub', 'nok.yaml'), 'w') as f:
|
||||
f.write('---\n'
|
||||
'list: [ 1, 1, 2, 3, 5, 8] \n')
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.wd)
|
||||
|
||||
def test_run_module_no_args(self):
|
||||
with self.assertRaises(subprocess.CalledProcessError) as ctx:
|
||||
subprocess.check_output([PYTHON, '-m', 'yamllint'],
|
||||
stderr=subprocess.STDOUT)
|
||||
self.assertEqual(ctx.exception.returncode, 2)
|
||||
self.assertRegexpMatches(ctx.exception.output.decode(),
|
||||
r'^usage: yamllint')
|
||||
|
||||
def test_run_module_on_bad_dir(self):
|
||||
with self.assertRaises(subprocess.CalledProcessError) as ctx:
|
||||
subprocess.check_output([PYTHON, '-m', 'yamllint',
|
||||
'/does/not/exist'],
|
||||
stderr=subprocess.STDOUT)
|
||||
self.assertRegexpMatches(ctx.exception.output.decode(),
|
||||
r'No such file or directory')
|
||||
|
||||
def test_run_module_on_file(self):
|
||||
out = subprocess.check_output(
|
||||
[PYTHON, '-m', 'yamllint', os.path.join(self.wd, 'warn.yaml')])
|
||||
lines = out.decode().splitlines()
|
||||
self.assertIn('/warn.yaml', lines[0])
|
||||
self.assertEqual('\n'.join(lines[1:]),
|
||||
' 1:1 warning missing document start "---"'
|
||||
' (document-start)\n')
|
||||
|
||||
def test_run_module_on_dir(self):
|
||||
with self.assertRaises(subprocess.CalledProcessError) as ctx:
|
||||
subprocess.check_output([PYTHON, '-m', 'yamllint', self.wd])
|
||||
self.assertEqual(ctx.exception.returncode, 1)
|
||||
|
||||
files = ctx.exception.output.decode().split('\n\n')
|
||||
self.assertIn(
|
||||
'/warn.yaml\n'
|
||||
' 1:1 warning missing document start "---"'
|
||||
' (document-start)',
|
||||
files[0])
|
||||
self.assertIn(
|
||||
'/sub/nok.yaml\n'
|
||||
' 2:9 error too many spaces inside brackets'
|
||||
' (brackets)\n'
|
||||
' 2:27 error trailing spaces (trailing-spaces)',
|
||||
files[1])
|
||||
@@ -14,7 +14,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
try:
|
||||
assert sys.version_info >= (2, 7)
|
||||
import unittest
|
||||
except AssertionError:
|
||||
import unittest2 as unittest
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
@@ -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,11 @@ conf_overrides = {
|
||||
'example-8.14': ('colons: {max-spaces-before: 1}\n'),
|
||||
'example-8.16': ('indentation: {spaces: 1}\n'),
|
||||
'example-8.17': ('indentation: disable\n'),
|
||||
'example-8.20': ('indentation: {indent-sequences: 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'),
|
||||
}
|
||||
@@ -133,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()).
|
||||
|
||||
@@ -22,7 +22,7 @@ indentation, etc."""
|
||||
|
||||
|
||||
APP_NAME = 'yamllint'
|
||||
APP_VERSION = '1.5.0'
|
||||
APP_VERSION = '1.11.1'
|
||||
APP_DESCRIPTION = __doc__
|
||||
|
||||
__author__ = u'Adrien Vergé'
|
||||
|
||||
4
yamllint/__main__.py
Normal file
4
yamllint/__main__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from yamllint.cli import run
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -15,13 +15,15 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import print_function
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
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
|
||||
|
||||
|
||||
@@ -36,6 +38,15 @@ def find_files_recursively(items):
|
||||
yield item
|
||||
|
||||
|
||||
def supports_color():
|
||||
supported_platform = not (platform.system() == 'Windows' and not
|
||||
('ANSICON' in os.environ or
|
||||
('TERM' in os.environ and
|
||||
os.environ['TERM'] == 'ANSI')))
|
||||
return (supported_platform and
|
||||
hasattr(sys.stdout, 'isatty') and sys.stdout.isatty())
|
||||
|
||||
|
||||
class Format(object):
|
||||
@staticmethod
|
||||
def parsable(problem, filename):
|
||||
@@ -77,14 +88,20 @@ def run(argv=None):
|
||||
description=APP_DESCRIPTION)
|
||||
parser.add_argument('files', metavar='FILE_OR_DIR', nargs='+',
|
||||
help='files to check')
|
||||
parser.add_argument('-c', '--config-file', dest='config_file',
|
||||
action='store', help='path to a custom configuration')
|
||||
parser.add_argument('-d', '--config-data', dest='config_data',
|
||||
action='store',
|
||||
help='custom configuration (as YAML source)')
|
||||
config_group = parser.add_mutually_exclusive_group()
|
||||
config_group.add_argument('-c', '--config-file', dest='config_file',
|
||||
action='store',
|
||||
help='path to a custom configuration')
|
||||
config_group.add_argument('-d', '--config-data', dest='config_data',
|
||||
action='store',
|
||||
help='custom configuration (as YAML source)')
|
||||
parser.add_argument('-f', '--format',
|
||||
choices=('parsable', 'standard'), default='standard',
|
||||
help='format for parsing output')
|
||||
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))
|
||||
|
||||
@@ -92,11 +109,6 @@ def run(argv=None):
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.config_file is not None and args.config_data is not None:
|
||||
print('Options --config-file and --config-data cannot be used '
|
||||
'simultaneously.', file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
|
||||
# User-global config is supposed to be in ~/.config/yamllint/config
|
||||
if 'XDG_CONFIG_HOME' in os.environ:
|
||||
user_global_config = os.path.join(
|
||||
@@ -121,16 +133,17 @@ def run(argv=None):
|
||||
print(e, file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
|
||||
return_code = 0
|
||||
max_level = 0
|
||||
|
||||
for file in find_files_recursively(args.files):
|
||||
filepath = file[2:] if file.startswith('./') else file
|
||||
try:
|
||||
first = True
|
||||
with open(file) as f:
|
||||
for problem in linter.run(f, conf):
|
||||
for problem in linter.run(f, conf, filepath):
|
||||
if args.format == 'parsable':
|
||||
print(Format.parsable(problem, file))
|
||||
elif sys.stdout.isatty():
|
||||
elif supports_color():
|
||||
if first:
|
||||
print('\033[4m%s\033[0m' % file)
|
||||
first = False
|
||||
@@ -143,13 +156,19 @@ def run(argv=None):
|
||||
|
||||
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)
|
||||
|
||||
@@ -4,9 +4,13 @@ rules:
|
||||
braces:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 0
|
||||
min-spaces-inside-empty: -1
|
||||
max-spaces-inside-empty: -1
|
||||
brackets:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 0
|
||||
min-spaces-inside-empty: -1
|
||||
max-spaces-inside-empty: -1
|
||||
colons:
|
||||
max-spaces-before: 0
|
||||
max-spaces-after: 1
|
||||
@@ -16,32 +20,39 @@ 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
|
||||
max-end: 0
|
||||
empty-values:
|
||||
forbid-in-block-mappings: false
|
||||
forbid-in-flow-mappings: false
|
||||
hyphens:
|
||||
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
|
||||
key-ordering: disable
|
||||
line-length:
|
||||
max: 80
|
||||
allow-non-breakable-words: yes
|
||||
allow-non-breakable-inline-mappings: no
|
||||
allow-non-breakable-words: true
|
||||
allow-non-breakable-inline-mappings: false
|
||||
new-line-at-end-of-file: enable
|
||||
new-lines:
|
||||
type: unix
|
||||
octal-values:
|
||||
forbid-implicit-octal: false
|
||||
forbid-explicit-octal: false
|
||||
trailing-spaces: enable
|
||||
truthy:
|
||||
level: warning
|
||||
|
||||
@@ -25,5 +25,5 @@ rules:
|
||||
indent-sequences: consistent
|
||||
line-length:
|
||||
level: warning
|
||||
allow-non-breakable-inline-mappings: yes
|
||||
allow-non-breakable-inline-mappings: true
|
||||
truthy: disable
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import os.path
|
||||
|
||||
import pathspec
|
||||
import yaml
|
||||
|
||||
import yamllint.rules
|
||||
@@ -29,6 +30,8 @@ class YamlLintConfig(object):
|
||||
def __init__(self, content=None, file=None):
|
||||
assert (content is None) ^ (file is None)
|
||||
|
||||
self.ignore = None
|
||||
|
||||
if file is not None:
|
||||
with open(file) as f:
|
||||
content = f.read()
|
||||
@@ -36,9 +39,14 @@ class YamlLintConfig(object):
|
||||
self.parse(content)
|
||||
self.validate()
|
||||
|
||||
def enabled_rules(self):
|
||||
def is_file_ignored(self, filepath):
|
||||
return self.ignore and self.ignore.match_file(filepath)
|
||||
|
||||
def enabled_rules(self, filepath):
|
||||
return [yamllint.rules.get(id) for id, val in self.rules.items()
|
||||
if val is not False]
|
||||
if val is not False and (
|
||||
filepath is None or 'ignore' not in val or
|
||||
not val['ignore'].match_file(filepath))]
|
||||
|
||||
def extend(self, base_config):
|
||||
assert isinstance(base_config, YamlLintConfig)
|
||||
@@ -53,6 +61,9 @@ class YamlLintConfig(object):
|
||||
|
||||
self.rules = base_config.rules
|
||||
|
||||
if base_config.ignore is not None:
|
||||
self.ignore = base_config.ignore
|
||||
|
||||
def parse(self, raw_content):
|
||||
try:
|
||||
conf = yaml.safe_load(raw_content)
|
||||
@@ -73,6 +84,13 @@ class YamlLintConfig(object):
|
||||
except Exception as e:
|
||||
raise YamlLintConfigError('invalid config: %s' % e)
|
||||
|
||||
if 'ignore' in conf:
|
||||
if type(conf['ignore']) != str:
|
||||
raise YamlLintConfigError(
|
||||
'invalid config: ignore should contain file patterns')
|
||||
self.ignore = pathspec.PathSpec.from_lines(
|
||||
'gitwildmatch', conf['ignore'].splitlines())
|
||||
|
||||
def validate(self):
|
||||
for id in self.rules:
|
||||
try:
|
||||
@@ -90,6 +108,14 @@ def validate_rule_conf(rule, conf):
|
||||
conf = {}
|
||||
|
||||
if type(conf) == dict:
|
||||
if ('ignore' in conf and
|
||||
type(conf['ignore']) != pathspec.pathspec.PathSpec):
|
||||
if type(conf['ignore']) != str:
|
||||
raise YamlLintConfigError(
|
||||
'invalid config: ignore should contain file patterns')
|
||||
conf['ignore'] = pathspec.PathSpec.from_lines(
|
||||
'gitwildmatch', conf['ignore'].splitlines())
|
||||
|
||||
if 'level' not in conf:
|
||||
conf['level'] = 'error'
|
||||
elif conf['level'] not in ('error', 'warning'):
|
||||
@@ -98,7 +124,7 @@ def validate_rule_conf(rule, conf):
|
||||
|
||||
options = getattr(rule, 'CONF', {})
|
||||
for optkey in conf:
|
||||
if optkey == 'level':
|
||||
if optkey in ('ignore', 'level'):
|
||||
continue
|
||||
if optkey not in options:
|
||||
raise YamlLintConfigError(
|
||||
|
||||
@@ -21,6 +21,16 @@ 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):
|
||||
@@ -53,8 +63,8 @@ class LintProblem(object):
|
||||
return '%d:%d: %s' % (self.line, self.column, self.message)
|
||||
|
||||
|
||||
def get_costemic_problems(buffer, conf):
|
||||
rules = conf.enabled_rules()
|
||||
def get_cosmetic_problems(buffer, conf, filepath):
|
||||
rules = conf.enabled_rules(filepath)
|
||||
|
||||
# Split token rules from line rules
|
||||
token_rules = [r for r in rules if r.TYPE == 'token']
|
||||
@@ -175,7 +185,7 @@ def get_syntax_error(buffer):
|
||||
return problem
|
||||
|
||||
|
||||
def _run(buffer, conf):
|
||||
def _run(buffer, conf, filepath):
|
||||
assert hasattr(buffer, '__getitem__'), \
|
||||
'_run() argument must be a buffer, not a stream'
|
||||
|
||||
@@ -183,7 +193,7 @@ def _run(buffer, conf):
|
||||
# right line
|
||||
syntax_error = get_syntax_error(buffer)
|
||||
|
||||
for problem in get_costemic_problems(buffer, conf):
|
||||
for problem in get_cosmetic_problems(buffer, conf, filepath):
|
||||
# Insert the syntax error (if any) at the right place...
|
||||
if (syntax_error and syntax_error.line <= problem.line and
|
||||
syntax_error.column <= problem.column):
|
||||
@@ -205,7 +215,7 @@ def _run(buffer, conf):
|
||||
yield syntax_error
|
||||
|
||||
|
||||
def run(input, conf):
|
||||
def run(input, conf, filepath=None):
|
||||
"""Lints a YAML source.
|
||||
|
||||
Returns a generator of LintProblem objects.
|
||||
@@ -213,11 +223,14 @@ def run(input, conf):
|
||||
:param input: buffer, string or stream to read from
|
||||
:param conf: yamllint configuration object
|
||||
"""
|
||||
if conf.is_file_ignored(filepath):
|
||||
return ()
|
||||
|
||||
if type(input) in (type(b''), type(u'')): # compat with Python 2 & 3
|
||||
return _run(input, conf)
|
||||
return _run(input, conf, filepath)
|
||||
elif hasattr(input, 'read'): # Python 2's file or Python 3's io.IOBase
|
||||
# We need to have everything in memory to parse correctly
|
||||
content = input.read()
|
||||
return _run(content, conf)
|
||||
return _run(content, conf, filepath)
|
||||
else:
|
||||
raise TypeError('input should be a string or a stream')
|
||||
|
||||
@@ -125,7 +125,8 @@ def token_or_comment_generator(buffer):
|
||||
curr = yaml_loader.get_token()
|
||||
while curr is not None:
|
||||
next = yaml_loader.get_token()
|
||||
nextnext = yaml_loader.peek_token()
|
||||
nextnext = (yaml_loader.peek_token()
|
||||
if yaml_loader.check_token() else None)
|
||||
|
||||
yield Token(curr.start_mark.line + 1, curr, prev, next, nextnext)
|
||||
|
||||
|
||||
@@ -24,12 +24,15 @@ from yamllint.rules import (
|
||||
document_end,
|
||||
document_start,
|
||||
empty_lines,
|
||||
empty_values,
|
||||
hyphens,
|
||||
indentation,
|
||||
key_duplicates,
|
||||
key_ordering,
|
||||
line_length,
|
||||
new_line_at_end_of_file,
|
||||
new_lines,
|
||||
octal_values,
|
||||
trailing_spaces,
|
||||
truthy,
|
||||
)
|
||||
@@ -44,12 +47,15 @@ _RULES = {
|
||||
document_end.ID: document_end,
|
||||
document_start.ID: document_start,
|
||||
empty_lines.ID: empty_lines,
|
||||
empty_values.ID: empty_values,
|
||||
hyphens.ID: hyphens,
|
||||
indentation.ID: indentation,
|
||||
key_duplicates.ID: key_duplicates,
|
||||
key_ordering.ID: key_ordering,
|
||||
line_length.ID: line_length,
|
||||
new_line_at_end_of_file.ID: new_line_at_end_of_file,
|
||||
new_lines.ID: new_lines,
|
||||
octal_values.ID: octal_values,
|
||||
trailing_spaces.ID: trailing_spaces,
|
||||
truthy.ID: truthy,
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ Use this rule to control the number of spaces inside braces (``{`` and ``}``).
|
||||
braces.
|
||||
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside
|
||||
braces.
|
||||
* ``min-spaces-inside-empty`` defines the minimal number of spaces required
|
||||
inside empty braces.
|
||||
* ``max-spaces-inside-empty`` defines the maximal number of spaces allowed
|
||||
inside empty braces.
|
||||
|
||||
.. rubric:: Examples
|
||||
|
||||
@@ -59,6 +63,30 @@ Use this rule to control the number of spaces inside braces (``{`` and ``}``).
|
||||
::
|
||||
|
||||
object: {key1: 4, key2: 8 }
|
||||
|
||||
#. With ``braces: {min-spaces-inside-empty: 0, max-spaces-inside-empty: 0}``
|
||||
|
||||
the following code snippet would **PASS**:
|
||||
::
|
||||
|
||||
object: {}
|
||||
|
||||
the following code snippet would **FAIL**:
|
||||
::
|
||||
|
||||
object: { }
|
||||
|
||||
#. With ``braces: {min-spaces-inside-empty: 1, max-spaces-inside-empty: -1}``
|
||||
|
||||
the following code snippet would **PASS**:
|
||||
::
|
||||
|
||||
object: { }
|
||||
|
||||
the following code snippet would **FAIL**:
|
||||
::
|
||||
|
||||
object: {}
|
||||
"""
|
||||
|
||||
|
||||
@@ -70,11 +98,27 @@ from yamllint.rules.common import spaces_after, spaces_before
|
||||
ID = 'braces'
|
||||
TYPE = 'token'
|
||||
CONF = {'min-spaces-inside': int,
|
||||
'max-spaces-inside': int}
|
||||
'max-spaces-inside': int,
|
||||
'min-spaces-inside-empty': int,
|
||||
'max-spaces-inside-empty': int}
|
||||
|
||||
|
||||
def check(conf, token, prev, next, nextnext, context):
|
||||
if isinstance(token, yaml.FlowMappingStartToken):
|
||||
if (isinstance(token, yaml.FlowMappingStartToken) and
|
||||
isinstance(next, yaml.FlowMappingEndToken)):
|
||||
problem = spaces_after(token, prev, next,
|
||||
min=(conf['min-spaces-inside-empty']
|
||||
if conf['min-spaces-inside-empty'] != -1
|
||||
else conf['min-spaces-inside']),
|
||||
max=(conf['max-spaces-inside-empty']
|
||||
if conf['max-spaces-inside-empty'] != -1
|
||||
else conf['max-spaces-inside']),
|
||||
min_desc='too few spaces inside empty braces',
|
||||
max_desc='too many spaces inside empty braces')
|
||||
if problem is not None:
|
||||
yield problem
|
||||
|
||||
elif isinstance(token, yaml.FlowMappingStartToken):
|
||||
problem = spaces_after(token, prev, next,
|
||||
min=conf['min-spaces-inside'],
|
||||
max=conf['max-spaces-inside'],
|
||||
|
||||
@@ -24,6 +24,10 @@ Use this rule to control the number of spaces inside brackets (``[`` and
|
||||
brackets.
|
||||
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside
|
||||
brackets.
|
||||
* ``min-spaces-inside-empty`` defines the minimal number of spaces required
|
||||
inside empty brackets.
|
||||
* ``max-spaces-inside-empty`` defines the maximal number of spaces allowed
|
||||
inside empty brackets.
|
||||
|
||||
.. rubric:: Examples
|
||||
|
||||
@@ -60,6 +64,30 @@ Use this rule to control the number of spaces inside brackets (``[`` and
|
||||
::
|
||||
|
||||
object: [1, 2, abc ]
|
||||
|
||||
#. With ``brackets: {min-spaces-inside-empty: 0, max-spaces-inside-empty: 0}``
|
||||
|
||||
the following code snippet would **PASS**:
|
||||
::
|
||||
|
||||
object: []
|
||||
|
||||
the following code snippet would **FAIL**:
|
||||
::
|
||||
|
||||
object: [ ]
|
||||
|
||||
#. With ``brackets: {min-spaces-inside-empty: 1, max-spaces-inside-empty: -1}``
|
||||
|
||||
the following code snippet would **PASS**:
|
||||
::
|
||||
|
||||
object: [ ]
|
||||
|
||||
the following code snippet would **FAIL**:
|
||||
::
|
||||
|
||||
object: []
|
||||
"""
|
||||
|
||||
|
||||
@@ -71,11 +99,28 @@ from yamllint.rules.common import spaces_after, spaces_before
|
||||
ID = 'brackets'
|
||||
TYPE = 'token'
|
||||
CONF = {'min-spaces-inside': int,
|
||||
'max-spaces-inside': int}
|
||||
'max-spaces-inside': int,
|
||||
'min-spaces-inside-empty': int,
|
||||
'max-spaces-inside-empty': int}
|
||||
|
||||
|
||||
def check(conf, token, prev, next, nextnext, context):
|
||||
if isinstance(token, yaml.FlowSequenceStartToken):
|
||||
if (isinstance(token, yaml.FlowSequenceStartToken) and
|
||||
isinstance(next, yaml.FlowSequenceEndToken)):
|
||||
problem = spaces_after(token, prev, next,
|
||||
min=(conf['min-spaces-inside-empty']
|
||||
if conf['min-spaces-inside-empty'] != -1
|
||||
else conf['min-spaces-inside']),
|
||||
max=(conf['max-spaces-inside-empty']
|
||||
if conf['max-spaces-inside-empty'] != -1
|
||||
else conf['max-spaces-inside']),
|
||||
min_desc='too few spaces inside empty brackets',
|
||||
max_desc=('too many spaces inside empty '
|
||||
'brackets'))
|
||||
if problem is not None:
|
||||
yield problem
|
||||
|
||||
elif isinstance(token, yaml.FlowSequenceStartToken):
|
||||
problem = spaces_after(token, prev, next,
|
||||
min=conf['min-spaces-inside'],
|
||||
max=conf['max-spaces-inside'],
|
||||
|
||||
@@ -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**:
|
||||
::
|
||||
|
||||
@@ -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**:
|
||||
::
|
||||
|
||||
@@ -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**:
|
||||
::
|
||||
|
||||
94
yamllint/rules/empty_values.py
Normal file
94
yamllint/rules/empty_values.py
Normal file
@@ -0,0 +1,94 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2017 Greg Dubicki
|
||||
#
|
||||
# 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 prevent nodes with empty content, that implicitly result in
|
||||
``null`` values.
|
||||
|
||||
.. rubric:: Options
|
||||
|
||||
* Use ``forbid-in-block-mappings`` to prevent empty values in block mappings.
|
||||
* Use ``forbid-in-flow-mappings`` to prevent empty values in flow mappings.
|
||||
|
||||
.. rubric:: Examples
|
||||
|
||||
#. With ``empty-values: {forbid-in-block-mappings: true}``
|
||||
|
||||
the following code snippets would **PASS**:
|
||||
::
|
||||
|
||||
some-mapping:
|
||||
sub-element: correctly indented
|
||||
|
||||
::
|
||||
|
||||
explicitly-null: null
|
||||
|
||||
the following code snippets would **FAIL**:
|
||||
::
|
||||
|
||||
some-mapping:
|
||||
sub-element: incorrectly indented
|
||||
|
||||
::
|
||||
|
||||
implicitly-null:
|
||||
|
||||
#. With ``empty-values: {forbid-in-flow-mappings: true}``
|
||||
|
||||
the following code snippet would **PASS**:
|
||||
::
|
||||
|
||||
{prop: null}
|
||||
{a: 1, b: 2, c: 3}
|
||||
|
||||
the following code snippets would **FAIL**:
|
||||
::
|
||||
|
||||
{prop: }
|
||||
|
||||
::
|
||||
|
||||
{a: 1, b:, c: 3}
|
||||
|
||||
"""
|
||||
|
||||
import yaml
|
||||
|
||||
from yamllint.linter import LintProblem
|
||||
|
||||
|
||||
ID = 'empty-values'
|
||||
TYPE = 'token'
|
||||
CONF = {'forbid-in-block-mappings': bool,
|
||||
'forbid-in-flow-mappings': bool}
|
||||
|
||||
|
||||
def check(conf, token, prev, next, nextnext, context):
|
||||
|
||||
if conf['forbid-in-block-mappings']:
|
||||
if isinstance(token, yaml.ValueToken) and isinstance(next, (
|
||||
yaml.KeyToken, yaml.BlockEndToken)):
|
||||
yield LintProblem(token.start_mark.line + 1,
|
||||
token.end_mark.column + 1,
|
||||
'empty value in block mapping')
|
||||
|
||||
if conf['forbid-in-flow-mappings']:
|
||||
if isinstance(token, yaml.ValueToken) and isinstance(next, (
|
||||
yaml.FlowEntryToken, yaml.FlowMappingEndToken)):
|
||||
yield LintProblem(token.start_mark.line + 1,
|
||||
token.end_mark.column + 1,
|
||||
'empty value in flow mapping')
|
||||
@@ -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**:
|
||||
::
|
||||
@@ -399,6 +399,10 @@ def _check(conf, token, prev, next, nextnext, context):
|
||||
# - item 1
|
||||
# - item 2
|
||||
indent = next.start_mark.column
|
||||
elif next.start_mark.column == token.start_mark.column:
|
||||
# -
|
||||
# key: value
|
||||
indent = next.start_mark.column
|
||||
else:
|
||||
# -
|
||||
# item 1
|
||||
@@ -469,7 +473,19 @@ def _check(conf, token, prev, next, nextnext, context):
|
||||
if context['indent-sequences'] is False:
|
||||
indent = context['stack'][-1].indent
|
||||
elif context['indent-sequences'] is True:
|
||||
indent = detect_indent(context['stack'][-1].indent, next)
|
||||
if (context['spaces'] == 'consistent' and
|
||||
next.start_mark.column -
|
||||
context['stack'][-1].indent == 0):
|
||||
# In this case, the block sequence item is not indented
|
||||
# (while it should be), but we don't know yet the
|
||||
# indentation it should have (because `spaces` is
|
||||
# `consistent` and its value has not been computed yet
|
||||
# -- this is probably the beginning of the document).
|
||||
# So we choose an arbitrary value (2).
|
||||
indent = 2
|
||||
else:
|
||||
indent = detect_indent(context['stack'][-1].indent,
|
||||
next)
|
||||
else: # 'whatever' or 'consistent'
|
||||
if next.start_mark.column == context['stack'][-1].indent:
|
||||
# key:
|
||||
|
||||
@@ -91,7 +91,9 @@ def check(conf, token, prev, next, nextnext, context):
|
||||
# This check is done because KeyTokens can be found inside flow
|
||||
# sequences... strange, but allowed.
|
||||
if len(context['stack']) > 0 and context['stack'][-1].type == MAP:
|
||||
if next.value in context['stack'][-1].keys:
|
||||
if (next.value in context['stack'][-1].keys and
|
||||
# `<<` is "merge key", see http://yaml.org/type/merge.html
|
||||
next.value != '<<'):
|
||||
yield LintProblem(
|
||||
next.start_mark.line + 1, next.start_mark.column + 1,
|
||||
'duplication of key "%s" in mapping' % next.value)
|
||||
|
||||
110
yamllint/rules/key_ordering.py
Normal file
110
yamllint/rules/key_ordering.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2017 Johannes F. Knauf
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Use this rule to enforce alphabetical ordering of keys in mappings. The sorting
|
||||
order uses the Unicode code point number. As a result, the ordering is
|
||||
case-sensitive and not accent-friendly (see examples below).
|
||||
|
||||
.. rubric:: Examples
|
||||
|
||||
#. With ``key-ordering: {}``
|
||||
|
||||
the following code snippet would **PASS**:
|
||||
::
|
||||
|
||||
- key 1: v
|
||||
key 2: val
|
||||
key 3: value
|
||||
- {a: 1, b: 2, c: 3}
|
||||
- T-shirt: 1
|
||||
T-shirts: 2
|
||||
t-shirt: 3
|
||||
t-shirts: 4
|
||||
- hair: true
|
||||
hais: true
|
||||
haïr: true
|
||||
haïssable: true
|
||||
|
||||
the following code snippet would **FAIL**:
|
||||
::
|
||||
|
||||
- key 2: v
|
||||
key 1: val
|
||||
|
||||
the following code snippet would **FAIL**:
|
||||
::
|
||||
|
||||
- {b: 1, a: 2}
|
||||
|
||||
the following code snippet would **FAIL**:
|
||||
::
|
||||
|
||||
- T-shirt: 1
|
||||
t-shirt: 2
|
||||
T-shirts: 3
|
||||
t-shirts: 4
|
||||
|
||||
the following code snippet would **FAIL**:
|
||||
::
|
||||
|
||||
- haïr: true
|
||||
hais: true
|
||||
"""
|
||||
|
||||
import yaml
|
||||
|
||||
from yamllint.linter import LintProblem
|
||||
|
||||
|
||||
ID = 'key-ordering'
|
||||
TYPE = 'token'
|
||||
CONF = {}
|
||||
|
||||
MAP, SEQ = range(2)
|
||||
|
||||
|
||||
class Parent(object):
|
||||
def __init__(self, type):
|
||||
self.type = type
|
||||
self.keys = []
|
||||
|
||||
|
||||
def check(conf, token, prev, next, nextnext, context):
|
||||
if 'stack' not in context:
|
||||
context['stack'] = []
|
||||
|
||||
if isinstance(token, (yaml.BlockMappingStartToken,
|
||||
yaml.FlowMappingStartToken)):
|
||||
context['stack'].append(Parent(MAP))
|
||||
elif isinstance(token, (yaml.BlockSequenceStartToken,
|
||||
yaml.FlowSequenceStartToken)):
|
||||
context['stack'].append(Parent(SEQ))
|
||||
elif isinstance(token, (yaml.BlockEndToken,
|
||||
yaml.FlowMappingEndToken,
|
||||
yaml.FlowSequenceEndToken)):
|
||||
context['stack'].pop()
|
||||
elif (isinstance(token, yaml.KeyToken) and
|
||||
isinstance(next, yaml.ScalarToken)):
|
||||
# This check is done because KeyTokens can be found inside flow
|
||||
# sequences... strange, but allowed.
|
||||
if len(context['stack']) > 0 and context['stack'][-1].type == MAP:
|
||||
if any(next.value < key for key in context['stack'][-1].keys):
|
||||
yield LintProblem(
|
||||
next.start_mark.line + 1, next.start_mark.column + 1,
|
||||
'wrong ordering of key "%s" in mapping' % next.value)
|
||||
else:
|
||||
context['stack'][-1].keys.append(next.value)
|
||||
@@ -22,7 +22,7 @@ Use this rule to set a limit to lines length.
|
||||
* ``max`` defines the maximal (inclusive) length of lines.
|
||||
* ``allow-non-breakable-words`` is used to allow non breakable words (without
|
||||
spaces inside) to overflow the limit. This is useful for long URLs, for
|
||||
instance. Use ``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.
|
||||
|
||||
@@ -44,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**:
|
||||
::
|
||||
@@ -68,15 +68,15 @@ Use this rule to set a limit to lines length.
|
||||
|
||||
- foobar: http://localhost/very/very/very/very/very/very/very/very/long/url
|
||||
|
||||
#. With ``line-length: {max: 60, allow-non-breakable-words: yes,
|
||||
allow-non-breakable-inline-mappings: yes}``
|
||||
#. 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: no}``
|
||||
#. With ``line-length: {max: 60, allow-non-breakable-words: false}``
|
||||
|
||||
the following code snippet would **FAIL**:
|
||||
::
|
||||
|
||||
93
yamllint/rules/octal_values.py
Normal file
93
yamllint/rules/octal_values.py
Normal file
@@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2017 ScienJus
|
||||
#
|
||||
# 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 prevent values with octal numbers. In YAML, numbers that
|
||||
start with ``0`` are interpreted as octal, but this is not always wanted.
|
||||
For instance ``010`` is the city code of Beijing, and should not be
|
||||
converted to ``8``.
|
||||
|
||||
.. rubric:: Examples
|
||||
|
||||
#. With ``octal-values: {forbid-implicit-octal: true}``
|
||||
|
||||
the following code snippets would **PASS**:
|
||||
::
|
||||
|
||||
user:
|
||||
city-code: '010'
|
||||
|
||||
the following code snippets would **PASS**:
|
||||
::
|
||||
|
||||
user:
|
||||
city-code: 010,021
|
||||
|
||||
the following code snippets would **FAIL**:
|
||||
::
|
||||
|
||||
user:
|
||||
city-code: 010
|
||||
|
||||
#. With ``octal-values: {forbid-explicit-octal: true}``
|
||||
|
||||
the following code snippets would **PASS**:
|
||||
::
|
||||
|
||||
user:
|
||||
city-code: '0o10'
|
||||
|
||||
the following code snippets would **FAIL**:
|
||||
::
|
||||
|
||||
user:
|
||||
city-code: 0o10
|
||||
"""
|
||||
|
||||
import yaml
|
||||
|
||||
from yamllint.linter import LintProblem
|
||||
|
||||
|
||||
ID = 'octal-values'
|
||||
TYPE = 'token'
|
||||
CONF = {'forbid-implicit-octal': bool,
|
||||
'forbid-explicit-octal': bool}
|
||||
|
||||
|
||||
def check(conf, token, prev, next, nextnext, context):
|
||||
if prev and isinstance(prev, yaml.tokens.TagToken):
|
||||
return
|
||||
|
||||
if conf['forbid-implicit-octal']:
|
||||
if isinstance(token, yaml.tokens.ScalarToken):
|
||||
if not token.style:
|
||||
val = token.value
|
||||
if val.isdigit() and len(val) > 1 and val[0] == '0':
|
||||
yield LintProblem(
|
||||
token.start_mark.line + 1, token.end_mark.column + 1,
|
||||
'forbidden implicit octal value "%s"' %
|
||||
token.value)
|
||||
|
||||
if conf['forbid-explicit-octal']:
|
||||
if isinstance(token, yaml.tokens.ScalarToken):
|
||||
if not token.style:
|
||||
val = token.value
|
||||
if len(val) > 2 and val[:2] == '0o' and val[2:].isdigit():
|
||||
yield LintProblem(
|
||||
token.start_mark.line + 1, token.end_mark.column + 1,
|
||||
'forbidden explicit octal value "%s"' %
|
||||
token.value)
|
||||
@@ -15,11 +15,12 @@
|
||||
# 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.
|
||||
Use this rule to forbid non-explictly typed truthy values other than ``true``
|
||||
and ``false``, for example ``YES``, ``False`` and ``off``.
|
||||
|
||||
This would prevent YAML parsers to tranform ``[yes, FALSE, Off]`` into ``[true,
|
||||
false, false]`` or ``{y: 1, yes: 2, on: 3, true: 4, True: 5}`` into ``{y: 1,
|
||||
true: 5}``.
|
||||
This can be useful to prevent surprises from YAML parsers 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
|
||||
|
||||
@@ -34,8 +35,7 @@ true: 5}``.
|
||||
|
||||
"yes": 1
|
||||
"on": 2
|
||||
"true": 3
|
||||
"True": 4
|
||||
"True": 3
|
||||
|
||||
explicit:
|
||||
string1: !!str True
|
||||
@@ -62,8 +62,7 @@ true: 5}``.
|
||||
|
||||
yes: 1
|
||||
on: 2
|
||||
true: 3
|
||||
True: 4
|
||||
True: 3
|
||||
"""
|
||||
|
||||
import yaml
|
||||
@@ -90,4 +89,4 @@ def check(conf, token, prev, next, nextnext, context):
|
||||
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")
|
||||
"truthy value should be true or false")
|
||||
|
||||
Reference in New Issue
Block a user