Compare commits

..

34 Commits

Author SHA1 Message Date
Philipp Huebner 2c6493c2e9 Debian: Package 1.23.0-1 5 years ago
Philipp Huebner f5d8c765c7 Merge tag 'v1.23.0' into packaging 5 years ago
Philipp Huebner 7cee10f77c Debian: Package 1.21.0-1 5 years ago
Philipp Huebner d457b37add Merge tag 'v1.21.0' into packaging 5 years ago
Philipp Huebner f90ef91ebc Debian: Package 1.20.0-2 5 years ago
Philipp Huebner 0ee814561d Merge tag 'v1.20.0' into packaging 5 years ago
Philipp Huebner 790662d369 Debian: Package 1.16.0-1 6 years ago
Philipp Huebner 92c5440d22 Merge tag 'v1.16.0' into packaging 6 years ago
Philipp Huebner cc8d56fb94 Debian: Package 1.15.0-1 6 years ago
Philipp Huebner d77cb4e2f6 Merge tag 'v1.15.0' into packaging 6 years ago
Philipp Huebner 1c31952230 Debian: Package 1.14.0-1 6 years ago
Philipp Huebner 8e97e6b151 Merge tag 'v1.14.0' into packaging 6 years ago
Philipp Huebner 5cd261d6cb Debian: Package 1.13.0-1 6 years ago
Philipp Huebner becf9fe98b Merge tag 'v1.13.0' into packaging 6 years ago
Philipp Huebner 251260c382 Debian: Package 1.11.1-1 7 years ago
Philipp Huebner c0f7ae1102 Merge tag 'v1.11.1' into packaging 7 years ago
Philipp Huebner b47f0f2451 Debian: Package 1.11.0-1 7 years ago
Philipp Huebner 5ca5add463 Merge tag 'v1.11.0' into packaging 7 years ago
Philipp Huebner 542b6738a6 Debian: Package 1.10.0-1 7 years ago
Philipp Huebner 2c3097ca78 Merge tag 'v1.10.0' into packaging 7 years ago
Philipp Huebner 5fbfcb13b1 Debian: Package 1.9.0-1 7 years ago
Philipp Huebner b8200259a2 Merge tag 'v1.9.0' into packaging 7 years ago
Philipp Huebner 0fd8b76eea Debian: Package 1.8.1-1 7 years ago
Philipp Huebner a4ab40c51a Merge tag 'v1.8.1' into packaging 7 years ago
Adrien Vergé aeb353159b Packaging: Debian: Package 1.5.0-1 8 years ago
Adrien Vergé c57d2784df Packaging: Debian: Package 1.3.2-1 8 years ago
Adrien Vergé c0da5eac54 to include in next Debian release
See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=819866#15
8 years ago
Adrien Vergé 69278a4553 Packaging: Debian: Package 1.2.1-1 8 years ago
Adrien Vergé e3baeefc81 Packaging: Debian: Package 1.2.0-1 8 years ago
Adrien Vergé d4d1d78ce6 Packaging: Add links to "new package" bugs for distros 8 years ago
Adrien Vergé c88cdf5316 Packaging: Update Ubuntu package 8 years ago
Adrien Vergé 1fb673be2e Packaging: Debian: Add man page 8 years ago
Adrien Vergé ea658ec3d6 Packaging: Add files for Debian 8 years ago
Adrien Vergé 014a6d2db4 Packaging: Add files for Ubuntu packaging (PPA) 8 years ago

@ -1,4 +0,0 @@
[flake8]
import-order-style = pep8
application-import-names = yamllint
ignore = W503,W504

@ -1,61 +0,0 @@
---
name: CI
on: # yamllint disable-line rule:truthy
push:
pull_request:
branches:
- master
permissions:
contents: read
jobs:
lint:
name: Linters
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
- run:
pip install flake8 flake8-import-order sphinx rstcheck[sphinx] doc8
- run: pip install .
- run: flake8 .
- run: doc8 $(git ls-files '*.rst')
- run: rstcheck --ignore-directives automodule $(git ls-files '*.rst')
- run: yamllint --strict $(git ls-files '*.yaml' '*.yml')
- run: make -C docs html
- name: Check for broken links in documentation
run: make -C docs linkcheck
test:
name: Tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version:
- '3.7'
- '3.8'
- '3.9'
- '3.10'
- '3.11'
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Append GitHub Actions system path
run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- run: pip install coverage
- run: pip install .
# https://github.com/AndreMiras/coveralls-python-action/issues/18
- run: echo -e "[run]\nrelative_files = True" > .coveragerc
- run: coverage run -m unittest discover
- name: Coveralls
uses: AndreMiras/coveralls-python-action@develop

1
.gitignore vendored

@ -4,4 +4,3 @@ __pycache__
/dist /dist
/yamllint.egg-info /yamllint.egg-info
/build /build
/.eggs

@ -0,0 +1,24 @@
---
dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069)
language: python
python:
- 2.7
- 3.5
- 3.6
- 3.7
- 3.8
- nightly
install:
- pip install pyyaml coveralls flake8 flake8-import-order doc8
- if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then pip install sphinx; fi
- pip install .
script:
- if [[ $TRAVIS_PYTHON_VERSION != nightly ]]; then flake8 .; fi
- if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then doc8 $(git ls-files '*.rst'); 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

@ -1,140 +1,6 @@
Changelog Changelog
========= =========
1.32.0 (2023-05-22)
-------------------
- Look for configuration file in parent directories
- Rule ``anchors``: add new option ``forbid-unused-anchors``
1.31.0 (2023-04-21)
-------------------
- Build: migrate from ``setup.py`` to ``pyproject.toml``
- Docs: update some outdated URLs
- Rule ``colons``: prevent error when space before is mandatory
1.30.0 (2023-03-22)
-------------------
- Rule ``anchors``: add new rule to detect undeclared or duplicated anchors
- Python API: prevent using ``is_file_ignored()`` with null ``filepath``
- Docs: fix misleading Python API example
- Docs: fix plain text code snippet example
- Docs: update pre-commit hook example
1.29.0 (2023-01-10)
-------------------
- Add support for Python 3.11, drop support for Python 3.6
- Rule ``float-values``: fix bug on strings containing fordidden values
- Stop releasing universal wheels
- Use proper Python 3 I/O type for file reading
- Rule ``indentation``: fix ``indent-sequences`` in nested collections
- Docs: clarify ``disable-line`` and parser errors, give a workaround
- Refactors to apply some pyupgrade suggestions
- Allow using a list of strings in ``ignore`` configuration
- Add ``--list-files`` command line option
1.28.0 (2022-09-12)
-------------------
- Better compress PNG image in documentation
- Remove ``__future__`` imports specific to Python 2
- Remove inheritance from ``object`` specific to Python 2
- Simplify GitHub Actions example in documentation
- Update ALE vim plugin link in documentation
- Update license to latest version of GPLv3
- Pre-compile disable/enable rules regexes
- Rule ``quoted-strings``: add ``allow-quoted-quotes`` option
- Add option ``ignore-from-file`` in config
1.27.1 (2022-07-08)
-------------------
- Fix failing test on ``key-duplicates`` for old PyYAML versions
1.27.0 (2022-07-08)
-------------------
- Add support for Python 3.10, drop Python 3.5
- Fix GitHub Actions workflow
- Refactor ``--format=auto`` logic
- Update GitHub format output to use groups
- Rule ``comments``: allow whitespace after the shebang marker
- Multiple minor fixes in documentation
- Configure Sphinx to make man page show up in apropos
- Attempt to clarify configuration file location in documentation
- Rule ``key-duplicates``: don't crash on redundant closing brackets or braces
- Use ``rstcheck`` to lint documentation on the CI
- Remove UTF-8 headers in Python files, since Python 2 isn't supported
- Add various tests to increase coverage
- Rule ``octal-values``: pre-compile regex for performance
- Add sections for Visual Studio Code and IntelliJ in documentation
- Rule ``new-lines``: add the ``type: platform`` config option
- Add the new rule ``float-values``
1.26.3 (2021-08-21)
-------------------
- Restore runtime dependency ``setuptools`` for Python < 3.8
1.26.2 (2021-08-03)
-------------------
- Fix ``python_requires`` to comply with PEP 345 and PEP 440
1.26.1 (2021-04-06)
-------------------
- Remove runtime dependency ``setuptools`` for Python < 3.8
- Fix ``line_length`` to skip all hash signs starting comment
1.26.0 (2021-01-29)
-------------------
- End support for Python 2 and Python 3.4, add support for Python 3.9
- Add ``forbid: non-empty`` option to ``braces`` and ``brackets`` rules
- Fix ``quoted-strings`` for explicit octal recognition
- Add documentation for integration with Arcanist
- Fix typos in changelog and README
- Stop using deprecated ``python setup.py test`` in tests
1.25.0 (2020-09-29)
-------------------
- Run tests on Travis both with and without UTF-8 locales
- Improve documentation with default values to rules with options
- Improve documentation with a Python API usage example
- Fix documentation on ``commas`` examples
- Packaging: move setuptools' configuration from ``setup.py`` to ``setup.cfg``
- Packaging: add extra info in PyPI metadata
- Improve documentation on ``yaml-files``
- Fix ``octal-values`` to prevent detection of ``8`` and ``9`` as octal values
- Fix ``quoted-strings`` Fix detecting strings with hashtag as requiring quotes
- Add ``forbid`` configuration to the ``braces`` and ``brackets`` rules
- Fix runtime dependencies missing ``setuptools``
- Add a new output format for GitHub Annotations (``--format github``)
- Fix DOS lines messing with rule IDs in directives
1.24.2 (2020-07-16)
-------------------
- Add ``locale`` config option and make ``key-ordering`` locale-aware
1.24.1 (2020-07-15)
-------------------
- Revert ``locale`` config option from version 1.24.0 because of a bug
1.24.0 (2020-07-15)
-------------------
- Specify config with environment variable ``YAMLLINT_CONFIG_FILE``
- Fix bug with CRLF in ``new-lines`` and ``require-starting-space``
- Do not run linter on directories whose names look like YAML files
- Add ``locale`` config option and make ``key-ordering`` locale-aware
1.23.0 (2020-04-17) 1.23.0 (2020-04-17)
------------------- -------------------

@ -1,48 +0,0 @@
Contributing
============
Pull requests are the best way to propose changes to the codebase.
Contributions are welcome, but they have to meet some criteria.
Pull Request Process
--------------------
1. Fork this Git repository and create your branch from ``master``.
2. Make sure the tests pass:
.. code:: bash
pip install --user .
python -m unittest discover # all tests...
python -m unittest tests/rules/test_commas.py # or just some tests (faster)
3. If you add code that should be tested, add tests.
4. Make sure the linters pass:
.. code:: bash
flake8 .
If you added/modified documentation:
.. code:: bash
doc8 $(git ls-files '*.rst')
If you touched YAML files:
.. code:: bash
yamllint --strict $(git ls-files '*.yaml' '*.yml')
5. If relevant, update documentation (either in ``docs`` directly or in rules
files themselves).
6. Write a `good commit message
<http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html>`_.
If the pull request has multiple commits, each must be atomic (single
irreducible change that makes sense on its own).
7. Then, open a pull request.

@ -1,7 +1,7 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007 Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school, You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary. if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>. <http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>. <http://www.gnu.org/philosophy/why-not-lgpl.html>.

@ -0,0 +1,4 @@
include LICENSE
include README.rst
include docs/*
include tests/*.py tests/rules/*.py tests/yaml-1.2-spec-examples/*

@ -8,8 +8,8 @@ repetition and cosmetic problems such as lines length, trailing spaces,
indentation, etc. indentation, etc.
.. image:: .. image::
https://github.com/adrienverge/yamllint/actions/workflows/ci.yaml/badge.svg?branch=master https://travis-ci.org/adrienverge/yamllint.svg?branch=master
:target: https://github.com/adrienverge/yamllint/actions/workflows/ci.yaml?query=branch%3Amaster :target: https://travis-ci.org/adrienverge/yamllint
:alt: CI tests status :alt: CI tests status
.. image:: .. image::
https://coveralls.io/repos/github/adrienverge/yamllint/badge.svg?branch=master https://coveralls.io/repos/github/adrienverge/yamllint/badge.svg?branch=master
@ -19,7 +19,11 @@ indentation, etc.
:target: https://yamllint.readthedocs.io/en/latest/?badge=latest :target: https://yamllint.readthedocs.io/en/latest/?badge=latest
:alt: Documentation status :alt: Documentation status
Written in Python (compatible with Python 3 only). Written in Python (compatible with Python 2 & 3).
⚠ Python 2 upstream support stopped on January 1, 2020. yamllint will keep
best-effort support for Python 2.7 until January 1, 2021. Passed that date,
yamllint will drop all Python 2-related code.
Documentation Documentation
------------- -------------
@ -74,7 +78,7 @@ Usage
# Output a parsable format (for syntax checking in editors like Vim, emacs...) # Output a parsable format (for syntax checking in editors like Vim, emacs...)
yamllint -f parsable file.yaml yamllint -f parsable file.yaml
`Read more in the complete documentation! <https://yamllint.readthedocs.io/>`__ `Read more in the complete documentation! <https://yamllint.readthedocs.io/>`_
Features Features
^^^^^^^^ ^^^^^^^^
@ -132,7 +136,7 @@ Specific files can be ignored (totally or for some rules only) using a
*.ignore-trailing-spaces.yaml *.ignore-trailing-spaces.yaml
/ascii-art/* /ascii-art/*
`Read more in the complete documentation! <https://yamllint.readthedocs.io/>`__ `Read more in the complete documentation! <https://yamllint.readthedocs.io/>`_
License License
------- -------

@ -2,7 +2,7 @@
# #
# You can set these variables from the command line. # You can set these variables from the command line.
SPHINXOPTS = -W SPHINXOPTS =
SPHINXBUILD = sphinx-build SPHINXBUILD = sphinx-build
PAPER = PAPER =
BUILDDIR = _build BUILDDIR = _build

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# yamllint documentation build configuration file, created by # yamllint documentation build configuration file, created by
# sphinx-quickstart on Thu Jan 21 21:18:52 2016. # sphinx-quickstart on Thu Jan 21 21:18:52 2016.
@ -5,9 +6,9 @@ import sys
import os import os
from unittest.mock import MagicMock from unittest.mock import MagicMock
sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('..')) # noqa
from yamllint import __copyright__, APP_NAME, APP_VERSION # noqa from yamllint import __copyright__, APP_NAME, APP_VERSION
# -- General configuration ------------------------------------------------ # -- General configuration ------------------------------------------------
@ -20,7 +21,7 @@ source_suffix = '.rst'
master_doc = 'index' master_doc = 'index'
project = APP_NAME project = APP_NAME
copyright = __copyright__.lstrip('Copyright ') copyright = __copyright__
version = APP_VERSION version = APP_VERSION
release = APP_VERSION release = APP_VERSION
@ -38,7 +39,7 @@ htmlhelp_basename = 'yamllintdoc'
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [
('index', 'yamllint', 'Linter for YAML files', ['Adrien Vergé'], 1) ('index', 'yamllint', '', [u'Adrien Vergé'], 1)
] ]
# -- Build with sphinx automodule without needing to install third-party libs # -- Build with sphinx automodule without needing to install third-party libs

@ -14,12 +14,10 @@ To use a custom configuration file, use the ``-c`` option:
If ``-c`` is not provided, yamllint will look for a configuration file in the If ``-c`` is not provided, yamllint will look for a configuration file in the
following locations (by order of preference): following locations (by order of preference):
- a file named ``.yamllint``, ``.yamllint.yaml``, or ``.yamllint.yml`` in the - ``.yamllint``, ``.yamllint.yaml`` or ``.yamllint.yml`` in the current working
current working directory, or a parent directory (the search for this file is directory
terminated at the user's home or filesystem root) - ``$XDG_CONFIG_HOME/yamllint/config``
- a filename referenced by ``$YAMLLINT_CONFIG_FILE``, if set - ``~/.config/yamllint/config``
- a file named ``$XDG_CONFIG_HOME/yamllint/config`` or
``~/.config/yamllint/config``, if present
Finally if no config file is found, the default configuration is applied. Finally if no config file is found, the default configuration is applied.
@ -124,8 +122,8 @@ warning level problems, only error level ones.
YAML files extensions YAML files extensions
--------------------- ---------------------
To configure what yamllint should consider as YAML files when listing To configure what yamllint should consider as YAML files, set ``yaml-files``
directories, set ``yaml-files`` configuration option. The default is: configuration option. The default is:
.. code-block:: yaml .. code-block:: yaml
@ -137,19 +135,11 @@ directories, set ``yaml-files`` configuration option. The default is:
The same rules as for ignoring paths apply (``.gitignore``-style path pattern, The same rules as for ignoring paths apply (``.gitignore``-style path pattern,
see below). see below).
If you need to know the exact list of files that yamllint would process,
without really linting them, you can use ``--list-files``:
.. code:: bash
yamllint --list-files .
Ignoring paths Ignoring paths
-------------- --------------
It is possible to exclude specific files or directories, so that the linter It is possible to exclude specific files or directories, so that the linter
doesn't process them. They can be provided either as a list of paths, or as a doesn't process them.
bulk string.
You can either totally ignore files (they won't be looked at): You can either totally ignore files (they won't be looked at):
@ -162,13 +152,6 @@ You can either totally ignore files (they won't be looked at):
all/this/directory/ all/this/directory/
*.template.yaml *.template.yaml
# or:
ignore:
- /this/specific/file.yaml
- all/this/directory/
- '*.template.yaml'
or ignore paths only for specific rules: or ignore paths only for specific rules:
.. code-block:: yaml .. code-block:: yaml
@ -181,18 +164,10 @@ or ignore paths only for specific rules:
/this-file-has-trailing-spaces-but-it-is-OK.yaml /this-file-has-trailing-spaces-but-it-is-OK.yaml
/generated/*.yaml /generated/*.yaml
# or:
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 Note that this ``.gitignore``-style path pattern allows complex path
exclusion/inclusion, see the `pathspec README file exclusion/inclusion, see the `pathspec README file
<https://pypi.org/project/pathspec/>`_ for more details. Here is a more complex <https://pypi.python.org/pypi/pathspec>`_ for more details.
example: Here is a more complex example:
.. code-block:: yaml .. code-block:: yaml
@ -213,43 +188,3 @@ example:
ignore: | ignore: |
*.ignore-trailing-spaces.yaml *.ignore-trailing-spaces.yaml
ascii-art/* ascii-art/*
You can also use the ``.gitignore`` file (or any list of files) through:
.. code-block:: yaml
ignore-from-file: .gitignore
or:
.. code-block:: yaml
ignore-from-file: [.gitignore, .yamlignore]
.. note:: However, this is mutually exclusive with the ``ignore`` key.
If you need to know the exact list of files that yamllint would process,
without really linting them, you can use ``--list-files``:
.. code:: bash
yamllint --list-files .
Setting the locale
------------------
It is possible to set the ``locale`` option globally. This is passed to Python's
`locale.setlocale
<https://docs.python.org/3/library/locale.html#locale.setlocale>`_,
so an empty string ``""`` will use the system default locale, while e.g.
``"en_US.UTF-8"`` will use that.
Currently this only affects the ``key-ordering`` rule. The default will order
by Unicode code point number, while locales will sort case and accents
properly as well.
.. code-block:: yaml
extends: default
locale: en_US.UTF-8

@ -2,17 +2,7 @@ Development
=========== ===========
yamllint provides both a script and a Python module. The latter can be used to yamllint provides both a script and a Python module. The latter can be used to
write your own linting tools. write your own linting tools:
Basic example of running the linter from Python:
.. code-block:: python
import yamllint
yaml_config = yamllint.config.YamlLintConfig("extends: default")
for p in yamllint.linter.run(open("example.yaml", "r"), yaml_config):
print(p.desc, p.line, p.rule)
.. automodule:: yamllint.linter .. automodule:: yamllint.linter
:members: :members:

@ -40,11 +40,6 @@ specific line:
# yamllint disable-line # yamllint disable-line
- { all : rules ,are disabled for this line} - { all : rules ,are disabled for this line}
You can't make yamllint ignore invalid YAML syntax on a line (which generates a
`syntax error`), such as when templating a YAML file with Jinja. In some cases,
you can workaround this by putting the templating syntax in a YAML comment. See
`Putting template flow control in comments`_.
If you need to disable multiple rules, it is allowed to chain rules like this: If you need to disable multiple rules, it is allowed to chain rules like this:
``# yamllint disable-line rule:hyphens rule:commas rule:indentation``. ``# yamllint disable-line rule:hyphens rule:commas rule:indentation``.
@ -94,6 +89,7 @@ For instance:
key: value 2 key: value 2
- This line is waaaaaaaaaay too long but yamllint will not report anything about it. - This line is waaaaaaaaaay too long but yamllint will not report anything about it.
This line will be checked by yamllint.
or: or:
@ -105,32 +101,3 @@ or:
key1: value1 key1: value1
{% endif %} {% endif %}
key2: value2 key2: value2
Putting template flow control in comments
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Alternatively for templating you can wrap the template statements in comments
to make it a valid YAML file. As long as the templating language doesn't use
the same comment symbol, it should be a valid template and valid YAML (pre and
post-template processing).
Example of a Jinja2 code that cannot be parsed as YAML because it contains
invalid tokens ``{%`` and ``%}``:
.. code-block:: text
# This file IS NOT valid YAML and will produce syntax errors
{% if extra_info %}
key1: value1
{% endif %}
key2: value2
But it can be fixed using YAML comments:
.. code-block:: yaml
# This file IS valid YAML because the Jinja is in a YAML comment
# {% if extra_info %}
key1: value1
# {% endif %}
key2: value2

@ -11,7 +11,7 @@ Screenshot
.. note:: .. note::
The default output format is inspired by `eslint <https://eslint.org/>`_, a The default output format is inspired by `eslint <http://eslint.org/>`_, a
great linting tool for Javascript. great linting tool for Javascript.
Table of contents Table of contents

@ -4,7 +4,7 @@ Integration with other software
Integration with pre-commit Integration with pre-commit
--------------------------- ---------------------------
You can integrate yamllint in the `pre-commit <https://pre-commit.com/>`_ tool. You can integrate yamllint in `pre-commit <http://pre-commit.com/>`_ tool.
Here is an example, to add in your .pre-commit-config.yaml Here is an example, to add in your .pre-commit-config.yaml
.. code:: yaml .. code:: yaml
@ -12,56 +12,8 @@ Here is an example, to add in your .pre-commit-config.yaml
--- ---
# Update the rev variable with the release version that you want, from the yamllint repo # Update the rev variable with the release version that you want, from the yamllint repo
# You can pass your custom .yamllint with args attribute. # You can pass your custom .yamllint with args attribute.
repos: - repo: https://github.com/adrienverge/yamllint.git
- repo: https://github.com/adrienverge/yamllint.git rev: v1.17.0
rev: v1.29.0 hooks:
hooks: - id: yamllint
- id: yamllint args: [-c=/path/to/.yamllint]
args: [--strict, -c=/path/to/.yamllint]
Integration with GitHub Actions
-------------------------------
yamllint auto-detects when it's running inside of `GitHub
Actions <https://github.com/features/actions>`_ and automatically uses the
suited output format to decorate code with linting errors. You can also force
the GitHub Actions output with ``yamllint --format github``.
A minimal example workflow using GitHub Actions:
.. code:: yaml
---
on: push # yamllint disable-line rule:truthy
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install yamllint
run: pip install yamllint
- name: Lint YAML files
run: yamllint .
Integration with Arcanist
-------------------------
You can configure yamllint to run on ``arc lint``. Here is an example
``.arclint`` file that makes use of this configuration.
.. code:: json
{
"linters": {
"yamllint": {
"type": "script-and-regex",
"script-and-regex.script": "yamllint",
"script-and-regex.regex": "/^(?P<line>\\d+):(?P<offset>\\d+) +(?P<severity>warning|error) +(?P<message>.*) +\\((?P<name>.*)\\)$/m",
"include": "(\\.(yml|yaml)$)"
}
}
}

@ -4,7 +4,7 @@ Quickstart
Installing yamllint Installing yamllint
------------------- -------------------
On Fedora / CentOS (note: `EPEL <https://docs.fedoraproject.org/en-US/epel/>`_ is On Fedora / CentOS (note: `EPEL <https://fedoraproject.org/wiki/EPEL>`_ is
required on CentOS): required on CentOS):
.. code:: bash .. code:: bash
@ -45,7 +45,7 @@ If you prefer installing from source, you can run, from the source directory:
.. code:: bash .. code:: bash
python -m build python setup.py sdist
pip install --user dist/yamllint-*.tar.gz pip install --user dist/yamllint-*.tar.gz
Running yamllint Running yamllint

@ -14,11 +14,6 @@ This page describes the rules and their options.
:local: :local:
:depth: 1 :depth: 1
anchors
-------
.. automodule:: yamllint.rules.anchors
braces braces
------ ------
@ -69,12 +64,6 @@ empty-values
.. automodule:: yamllint.rules.empty_values .. automodule:: yamllint.rules.empty_values
float-values
------------
.. automodule:: yamllint.rules.float_values
hyphens hyphens
------- -------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 41 KiB

@ -9,11 +9,11 @@ text editor.
Vim Vim
--- ---
Assuming that the `ALE <https://github.com/dense-analysis/ale>`_ plugin is Assuming that the `ALE <https://github.com/w0rp/ale>`_ plugin is
installed, yamllint is supported by default. It is automatically enabled when installed, yamllint is supported by default. It is automatically enabled when
editing YAML files. editing YAML files.
If you instead use the `syntastic <https://github.com/vim-syntastic/syntastic>`_ If you instead use the `syntastic <https://github.com/scrooloose/syntastic>`_
plugin, add this to your ``.vimrc``: plugin, add this to your ``.vimrc``:
:: ::
@ -23,7 +23,7 @@ plugin, add this to your ``.vimrc``:
Neovim Neovim
------ ------
Assuming that the `neomake <https://github.com/neomake/neomake>`_ plugin is Assuming that the `neomake <https://github.com/benekastah/neomake>`_ plugin is
installed, yamllint is supported by default. It is automatically enabled when installed, yamllint is supported by default. It is automatically enabled when
editing YAML files. editing YAML files.
@ -33,16 +33,6 @@ Emacs
If you are `flycheck <https://github.com/flycheck/flycheck>`_ user, you can use If you are `flycheck <https://github.com/flycheck/flycheck>`_ user, you can use
`flycheck-yamllint <https://github.com/krzysztof-magosa/flycheck-yamllint>`_ integration. `flycheck-yamllint <https://github.com/krzysztof-magosa/flycheck-yamllint>`_ integration.
Visual Studio Code
------------------
https://marketplace.visualstudio.com/items?itemName=fnando.linter
IntelliJ
--------
https://plugins.jetbrains.com/plugin/15349-yamllint
Other text editors Other text editors
------------------ ------------------

@ -0,0 +1,59 @@
Release new version for Debian
==============================
VERSION=1.5.0
WD=/tmp/pkg
TARGET=vm-debian8
# Build source archive
python3 setup.py sdist
# Copy files on a Debian system
ssh $TARGET mkdir $WD
rsync -av dist/yamllint-$VERSION.tar.gz $TARGET:$WD/yamllint_$VERSION.orig.tar.gz
ssh $TARGET tar -C $WD -xf $WD/yamllint_$VERSION.orig.tar.gz
if false; then
rsync -av packaging/debian/debian $TARGET:$WD/yamllint-$VERSION
else
ssh $TARGET "cd $WD && sudo apt-get update"
ssh $TARGET "cd $WD && apt-get source yamllint"
ssh $TARGET mv $WD'/yamllint-*/debian' $WD/yamllint-$VERSION # old version
fi
ssh $TARGET
cd $WD/yamllint-$VERSION/debian
# Add a new changelog entry:
dch -v $VERSION-1
# Build packages and sign dsc
debuild -S -sa
# Test if it builds
sudo pbuilder --create
sudo pbuilder --build ../../yamllint_$VERSION-1.dsc
# Run lintian to detect problems
lintian -i -I --show-overrides ../../yamllint_$VERSION-1_source.changes
# Retrieve modified files
rsync -av $TARGET:$WD/yamllint-$VERSION/debian packaging/debian
# Upload to mentors FTP (needed even for updates)
cat >>~/.dput.cf << EOF
[mentors]
fqdn = mentors.debian.net
incoming = /upload
method = http
allow_unsigned_uploads = 0
progress_indicator = 2
# Allow uploads for UNRELEASED packages
allowed_distributions = .*
EOF
dput mentors ../../yamllint_$VERSION-1_source.changes
# Do no upload to Debian FTP: the sponsor will do it
# File a new RFS bug by sending an email
See https://mentors.debian.net/sponsors/rfs-howto/yamllint

@ -0,0 +1,163 @@
yamllint (1.23.0-1) unstable; urgency=medium
* New upstream version 1.23.0
-- Philipp Huebner <debalance@debian.org> Sun, 19 Apr 2020 10:19:31 +0200
yamllint (1.21.0-1) unstable; urgency=medium
* New upstream version 1.21.0
-- Philipp Huebner <debalance@debian.org> Sat, 11 Apr 2020 17:15:13 +0200
yamllint (1.20.0-2) unstable; urgency=medium
* Removed 'Installing yamllint' section in the manpage (Closes: #947440)
* Moved maintainership into the Python Applications Packaging Team
(Closes: 947416)
-- Philipp Huebner <debalance@debian.org> Fri, 28 Feb 2020 14:02:37 +0100
yamllint (1.20.0-1) unstable; urgency=medium
* New upstream version 1.20.0
* Updated Standards-Version: 4.5.0 (no changes needed)
* Rules-Requires-Root: no
-- Philipp Huebner <debalance@debian.org> Sun, 16 Feb 2020 00:39:28 +0100
yamllint (1.18.0-1) unstable; urgency=medium
* New upstream version 1.18.0
* Added dh-python to build-depends
* Updated Standards-Version: 4.4.1 (no changes needed)
-- Philipp Huebner <debalance@debian.org> Sun, 20 Oct 2019 19:40:24 +0200
yamllint (1.16.0-1) unstable; urgency=medium
* New upstream version 1.16.0
* Updated debhelper compat version: 12
* Updated Standards-Version: 4.4.0 (no changes needed)
-- Philipp Huebner <debalance@debian.org> Wed, 24 Jul 2019 17:35:30 +0200
yamllint (1.15.0-1) unstable; urgency=medium
* New upstream version 1.15.0
-- Philipp Huebner <debalance@debian.org> Sat, 16 Feb 2019 10:59:13 +0100
yamllint (1.14.0-1) unstable; urgency=medium
* New upstream version 1.14.0
* Updated Standards-Version: 4.3.0 (no changes needed)
* Updated debian/copyright
-- Philipp Huebner <debalance@debian.org> Sat, 19 Jan 2019 16:07:26 +0100
yamllint (1.13.0-1) unstable; urgency=medium
* New upstream version 1.13.0
* Updated Standards-Version: 4.2.1 (no changes needed)
-- Philipp Huebner <debalance@debian.org> Sun, 18 Nov 2018 19:16:27 +0100
yamllint (1.11.1-1) unstable; urgency=medium
* New upstream version 1.11.1
* Updated Standards-Version: 4.1.4 (no changes needed)
-- Philipp Huebner <debalance@debian.org> Wed, 09 May 2018 14:24:10 +0200
yamllint (1.11.0-1) unstable; urgency=medium
* New upstream version 1.11.0
* Update Standards-Version: 4.1.3 (no changes needed)
* Switch to debhelper compat level 11
-- Philipp Huebner <debalance@debian.org> Tue, 06 Mar 2018 17:35:44 +0100
yamllint (1.10.0-1) unstable; urgency=medium
* New upstream version 1.10.0
* Drop patches (fixed upstream)
-- Philipp Huebner <debalance@debian.org> Thu, 16 Nov 2017 19:18:18 +0100
yamllint (1.9.0-1) unstable; urgency=medium
* New upstream version 1.9.0
-- Philipp Huebner <debalance@debian.org> Sun, 22 Oct 2017 09:35:39 +0200
yamllint (1.8.1-1) unstable; urgency=medium
* New upstream version 1.8.1
* Add myself as Uploader
* Depend on python3-pkg-resources (Closes: #860075)
* Build-Depend on python3-pathspec
* Add patch to use python3 in tests
* debian/control: set testsuite to autopkgtest-pkg-python
* Update Standards-Version: 4.1.1 (no changes needed)
-- Philipp Huebner <debalance@debian.org> Wed, 04 Oct 2017 18:50:29 +0200
yamllint (1.5.0-1) unstable; urgency=medium
* Output color only on TTY
* Generalize line-length for mappings
* Fix line-length bug by scanning tokens securely
* New rule: truthy
* Fix distribution (don't distribute tests in site-packages)
* Exclude PTY-related test (doesn't work with pbuilder)
-- Adrien Vergé <adrienverge@gmail.com> Mon, 07 Nov 2016 18:51:37 +0100
yamllint (1.3.2-1) unstable; urgency=medium
* Allow disabling yamllint checks using comments
* Detect user config using `os.path.expanduser()`
* Fix non-ASCII comments bug and add tests
* Update standards version to 3.9.8
-- Adrien Vergé <adrienverge@gmail.com> Mon, 27 Jun 2016 22:17:05 +0200
yamllint (1.2.2-1) unstable; urgency=medium
* Update to new upstream version
-- Adrien Vergé <adrienverge@gmail.com> Fri, 24 Jun 2016 08:54:59 +0200
yamllint (1.2.1-1) unstable; urgency=medium
* Update to new upstream version
-- Adrien Vergé <adrienverge@gmail.com> Sun, 03 Apr 2016 11:30:02 +0200
yamllint (1.2.0-1) unstable; urgency=medium
* Update to new upstream version
* Build and include man page in package
* Fix Vcs-* fields in debian/control
* Fix description-synopsis-starts-with-article lintian warning
-- Adrien Vergé <adrienverge@gmail.com> Tue, 08 Mar 2016 08:20:23 +0100
yamllint (1.1.0-1) unstable; urgency=medium
* Update to new upstream version
-- Adrien Vergé <adrienverge@gmail.com> Fri, 04 Mar 2016 18:54:19 +0100
yamllint (1.0.4-1) unstable; urgency=medium
* Update to new upstream version
-- Adrien Vergé <adrienverge@gmail.com> Fri, 04 Mar 2016 12:54:19 +0100
yamllint (1.0.3-1) unstable; urgency=medium
* Initial release (Closes: #816609)
-- Adrien Vergé <adrienverge@gmail.com> Thu, 03 Mar 2016 09:10:38 +0100

@ -0,0 +1,30 @@
Source: yamllint
Maintainer: Python Applications Packaging Team <python-apps-team@lists.alioth.debian.org>
Uploaders: Adrien Vergé <adrienverge@gmail.com>, Philipp Huebner <debalance@debian.org>
Section: devel
Priority: optional
Build-Depends: debhelper,
debhelper-compat (= 12),
dh-python,
python3-all,
python3-nose,
python3-pathspec,
python3-setuptools,
python3-sphinx,
python3-yaml
Standards-Version: 4.5.0
Rules-Requires-Root: no
Vcs-Browser: https://salsa.debian.org/python-team/applications/yamllint
Vcs-Git: https://salsa.debian.org/python-team/applications/yamllint.git
Homepage: https://github.com/adrienverge/yamllint
Package: yamllint
Architecture: all
Depends: ${misc:Depends},
${python3:Depends},
${shlibs:Depends},
python3-pkg-resources
Description: Linter for YAML files
yamllint does not only check for syntax validity, but for weirdnesses like key
repetition and cosmetic problems such as lines length, trailing spaces,
indentation, etc.

@ -0,0 +1,29 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: yamllint
Source: https://github.com/adrienverge/yamllint
Files: *
Copyright: 2016-2019 Adrien Vergé <adrienverge@gmail.com>
License: GPL-3+
Files: debian/*
Copyright: 2016-2019 Adrien Vergé <adrienverge@gmail.com>
2017-2019 Philipp Huebner <debalance@debian.org>
License: GPL-3+
License: GPL-3+
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/>.
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

@ -0,0 +1,62 @@
Description: clean docs
Remove content unsuitable for Debian
Author: Philipp Huebner <debalance@debian.org>
Index: yamllint/docs/quickstart.rst
===================================================================
--- yamllint.orig/docs/quickstart.rst
+++ yamllint/docs/quickstart.rst
@@ -1,53 +1,6 @@
Quickstart
==========
-Installing yamllint
--------------------
-
-On Fedora / CentOS (note: `EPEL <https://fedoraproject.org/wiki/EPEL>`_ is
-required on CentOS):
-
-.. code:: bash
-
- sudo dnf install yamllint
-
-On Debian 8+ / Ubuntu 16.04+:
-
-.. code:: bash
-
- sudo apt-get install yamllint
-
-On Mac OS 10.11+:
-
-.. code:: bash
-
- brew install yamllint
-
-On FreeBSD:
-
-.. code:: sh
-
- pkg install py36-yamllint
-
-On OpenBSD:
-
-.. code:: sh
-
- doas pkg_add py3-yamllint
-
-Alternatively using pip, the Python package manager:
-
-.. code:: bash
-
- pip install --user yamllint
-
-If you prefer installing from source, you can run, from the source directory:
-
-.. code:: bash
-
- python setup.py sdist
- pip install --user dist/yamllint-*.tar.gz
-
Running yamllint
----------------

@ -0,0 +1,13 @@
#!/usr/bin/make -f
export DH_VERBOSE=1
%:
NOSE_EXCLUDE=test_run_colored_output \
dh $@ --with python3 --buildsystem=pybuild
override_dh_auto_build:
dh_auto_build
PYTHONPATH=. http_proxy='127.0.0.1:9' sphinx-build -N -bman docs build/man
override_dh_installman:
dh_installman build/man/yamllint.1

@ -0,0 +1,2 @@
yamllint source: debian-watch-does-not-check-gpg-signature
yamllint source: no-dh-sequencer

@ -0,0 +1,4 @@
Bug-Database: https://github.com/adrienverge/yamllint/issues
FAQ: https://yamllint.readthedocs.org/
Name: yamllint
Repository: https://github.com/adrienverge/yamllint

@ -0,0 +1,3 @@
# watch control file for uscan
version=3
https://github.com/adrienverge/yamllint/tags .*/archive/v(\d\S*)\.tar\.gz

@ -0,0 +1,17 @@
Packaging in Fedora
-------------------
https://bugzilla.redhat.com/show_bug.cgi?id=1309907
Packaging in Debian
-------------------
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816609
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816611
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=817245
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=819866
Packaging in Ubuntu
-------------------
https://bugs.launchpad.net/ubuntu/+bug/1554406

@ -0,0 +1,30 @@
Upload new version to PPA
=========================
WORKDIR=/tmp/yamllint-ubuntu
VERSION=1.2.0
git clone -b packaging https://github.com/adrienverge/yamllint.git $WORKDIR
cd $WORKDIR
# Build source archive
python3 setup.py sdist
cd dist
mv yamllint-$VERSION.tar.gz yamllint_$VERSION.orig.tar.gz
tar -xvf yamllint_$VERSION.orig.tar.gz
cp -r ../packaging/ubuntu/debian yamllint-$VERSION/
cd yamllint-$VERSION/debian
# Add a new changelog entry:
dch -v $VERSION-0ubuntu1
# Build packages and sign dsc
debuild -S -sa
# Test if it builds
sudo pbuilder --create
sudo pbuilder --build ../yamllint_$VERSION-0ubuntu1.dsc
# Upload to PPA
dput ppa:adrienverge/ppa ../yamllint_$VERSION-0ubuntu1_source.changes

@ -0,0 +1,17 @@
yamllint (1.2.0-0ubuntu1) trusty; urgency=medium
* Update to new upstream version
-- Adrien Vergé <adrienverge@gmail.com> Mon, 07 Mar 2016 07:54:19 +0100
yamllint (1.0.3-0ubuntu2) trusty; urgency=medium
* Add python3-nose to build dependencies
-- Adrien Vergé <adrienverge@gmail.com> Wed, 02 Mar 2016 14:12:07 +0100
yamllint (1.0.3-0ubuntu1) trusty; urgency=low
* Initial release
-- Adrien Vergé <adrienverge@gmail.com> Wed, 02 Mar 2016 13:26:29 +0100

@ -0,0 +1,27 @@
Source: yamllint
Section: devel
Priority: optional
Maintainer: Adrien Vergé <adrienverge@gmail.com>
Build-Depends:
debhelper (>=9),
python3-all,
python3-nose,
python3-setuptools,
python3-sphinx,
python3-yaml,
Standards-Version: 3.9.7
Homepage: https://github.com/adrienverge/yamllint
Vcs-Git: git://anonscm.debian.org/collab-maint/yamllint.git
Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/yamllint.git
Package: yamllint
Architecture: any
Depends:
python3-yaml,
${misc:Depends},
${python3:Depends},
${shlibs:Depends},
Description: A linter for YAML files
yamllint does not only check for syntax validity, but for weirdnesses like key
repetition and cosmetic problems such as lines length, trailing spaces,
indentation, etc.

@ -0,0 +1,19 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: yamllint
Source: https://github.com/adrienverge/yamllint
Files: *
Copyright: 2016 Adrien Vergé <adrienverge@gmail.com>
License: GPL-3+
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/>.

@ -0,0 +1,11 @@
#!/usr/bin/make -f
%:
dh $@ --with python3 --buildsystem=pybuild
override_dh_auto_build:
dh_auto_build
PYTHONPATH=. http_proxy='127.0.0.1:9' sphinx-build -N -bman docs build/man
override_dh_installman:
dh_installman build/man/yamllint.1

@ -0,0 +1,4 @@
Bug-Database: https://github.com/adrienverge/yamllint/issues
FAQ: https://yamllint.readthedocs.org/
Name: yamllint
Repository: https://github.com/adrienverge/yamllint

@ -0,0 +1,3 @@
# watch control file for uscan
version=3
https://github.com/adrienverge/yamllint/tags .*/archive/v(\d\S*)\.tar\.gz

@ -1,54 +0,0 @@
[project]
name = "yamllint"
description = "A linter for YAML files."
readme = {file = "README.rst", content-type = "text/x-rst"}
requires-python = ">=3.7"
license = {text = "GPL-3.0-or-later"}
authors = [{name = "Adrien Vergé"}]
keywords = ["yaml", "lint", "linter", "syntax", "checker"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Programming Language :: Python",
"Topic :: Software Development",
"Topic :: Software Development :: Debuggers",
"Topic :: Software Development :: Quality Assurance",
"Topic :: Software Development :: Testing",
]
dependencies = [
"pathspec >= 0.5.3",
"pyyaml",
]
dynamic = ["version"]
[project.optional-dependencies]
dev = [
"doc8",
"flake8",
"flake8-import-order",
"rstcheck[sphinx]",
"sphinx",
]
[project.scripts]
yamllint = "yamllint.cli:run"
[project.urls]
homepage = "https://github.com/adrienverge/yamllint"
repository = "https://github.com/adrienverge/yamllint"
documentation = "https://yamllint.readthedocs.io"
[build-system]
build-backend = "setuptools.build_meta"
requires = ["setuptools >= 61"]
[tool.setuptools]
packages = ["yamllint", "yamllint.conf", "yamllint.rules"]
[tool.setuptools.package-data]
yamllint = ["conf/*.yaml"]
[tool.setuptools.dynamic]
version = {attr = "yamllint.__version__"}

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

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -13,8 +14,43 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from setuptools import setup from setuptools import find_packages, setup
# This is only kept for backward-compatibility with older versions that don't from yamllint import (__author__, __license__,
# support new packaging standards (e.g. PEP 517 or PEP 660): APP_NAME, APP_VERSION, APP_DESCRIPTION)
setup()
setup(
name=APP_NAME,
version=APP_VERSION,
author=__author__,
description=APP_DESCRIPTION.split('\n')[0],
long_description=APP_DESCRIPTION,
license=__license__,
keywords=['yaml', 'lint', 'linter', 'syntax', 'checker'],
url='https://github.com/adrienverge/yamllint',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Topic :: Software Development',
'Topic :: Software Development :: Debuggers',
'Topic :: Software Development :: Quality Assurance',
'Topic :: Software Development :: Testing',
],
packages=find_packages(exclude=['tests', 'tests.*']),
entry_points={'console_scripts': ['yamllint=yamllint.cli:run']},
package_data={'yamllint': ['conf/*.yaml']},
install_requires=['pathspec >=0.5.3', 'pyyaml'],
test_suite='tests',
)

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -13,9 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import contextlib
import os import os
import shutil
import tempfile import tempfile
import unittest import unittest
@ -58,7 +57,7 @@ def build_temp_workspace(files):
tempdir = tempfile.mkdtemp(prefix='yamllint-tests-') tempdir = tempfile.mkdtemp(prefix='yamllint-tests-')
for path, content in files.items(): for path, content in files.items():
path = os.path.join(tempdir, path).encode('utf-8') path = os.path.join(tempdir, path)
if not os.path.exists(os.path.dirname(path)): if not os.path.exists(os.path.dirname(path)):
os.makedirs(os.path.dirname(path)) os.makedirs(os.path.dirname(path))
@ -70,17 +69,3 @@ def build_temp_workspace(files):
f.write(content) f.write(content)
return tempdir return tempdir
@contextlib.contextmanager
def temp_workspace(files):
"""Provide a temporary workspace that is automatically cleaned up."""
backup_wd = os.getcwd()
wd = build_temp_workspace(files)
try:
os.chdir(wd)
yield
finally:
os.chdir(backup_wd)
shutil.rmtree(wd)

@ -1,281 +0,0 @@
# Copyright (C) 2023 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 AnchorsTestCase(RuleTestCase):
rule_id = 'anchors'
def test_disabled(self):
conf = 'anchors: disable'
self.check('---\n'
'- &b true\n'
'- &i 42\n'
'- &s hello\n'
'- &f_m {k: v}\n'
'- &f_s [1, 2]\n'
'- *b\n'
'- *i\n'
'- *s\n'
'- *f_m\n'
'- *f_s\n'
'---\n' # redeclare anchors in a new document
'- &b true\n'
'- &i 42\n'
'- &s hello\n'
'- *b\n'
'- *i\n'
'- *s\n'
'---\n'
'block mapping: &b_m\n'
' key: value\n'
'extended:\n'
' <<: *b_m\n'
' foo: bar\n'
'---\n'
'{a: 1, &x b: 2, c: &y 3, *x : 4, e: *y}\n'
'...\n', conf)
self.check('---\n'
'- &i 42\n'
'---\n'
'- &b true\n'
'- &b true\n'
'- &b true\n'
'- &s hello\n'
'- *b\n'
'- *i\n' # declared in a previous document
'- *f_m\n' # never declared
'- *f_m\n'
'- *f_m\n'
'- *f_s\n' # declared after
'- &f_s [1, 2]\n'
'---\n'
'block mapping: &b_m\n'
' key: value\n'
'---\n'
'block mapping 1: &b_m_bis\n'
' key: value\n'
'block mapping 2: &b_m_bis\n'
' key: value\n'
'extended:\n'
' <<: *b_m\n'
' foo: bar\n'
'---\n'
'{a: 1, &x b: 2, c: &x 3, *x : 4, e: *y}\n'
'...\n', conf)
def test_forbid_undeclared_aliases(self):
conf = ('anchors:\n'
' forbid-undeclared-aliases: true\n'
' forbid-duplicated-anchors: false\n'
' forbid-unused-anchors: false\n')
self.check('---\n'
'- &b true\n'
'- &i 42\n'
'- &s hello\n'
'- &f_m {k: v}\n'
'- &f_s [1, 2]\n'
'- *b\n'
'- *i\n'
'- *s\n'
'- *f_m\n'
'- *f_s\n'
'---\n' # redeclare anchors in a new document
'- &b true\n'
'- &i 42\n'
'- &s hello\n'
'- *b\n'
'- *i\n'
'- *s\n'
'---\n'
'block mapping: &b_m\n'
' key: value\n'
'extended:\n'
' <<: *b_m\n'
' foo: bar\n'
'---\n'
'{a: 1, &x b: 2, c: &y 3, *x : 4, e: *y}\n'
'...\n', conf)
self.check('---\n'
'- &i 42\n'
'---\n'
'- &b true\n'
'- &b true\n'
'- &b true\n'
'- &s hello\n'
'- *b\n'
'- *i\n' # declared in a previous document
'- *f_m\n' # never declared
'- *f_m\n'
'- *f_m\n'
'- *f_s\n' # declared after
'- &f_s [1, 2]\n'
'...\n'
'---\n'
'block mapping: &b_m\n'
' key: value\n'
'---\n'
'block mapping 1: &b_m_bis\n'
' key: value\n'
'block mapping 2: &b_m_bis\n'
' key: value\n'
'extended:\n'
' <<: *b_m\n'
' foo: bar\n'
'---\n'
'{a: 1, &x b: 2, c: &x 3, *x : 4, e: *y}\n'
'...\n', conf,
problem1=(9, 3),
problem2=(10, 3),
problem3=(11, 3),
problem4=(12, 3),
problem5=(13, 3),
problem6=(25, 7),
problem7=(28, 37))
def test_forbid_duplicated_anchors(self):
conf = ('anchors:\n'
' forbid-undeclared-aliases: false\n'
' forbid-duplicated-anchors: true\n'
' forbid-unused-anchors: false\n')
self.check('---\n'
'- &b true\n'
'- &i 42\n'
'- &s hello\n'
'- &f_m {k: v}\n'
'- &f_s [1, 2]\n'
'- *b\n'
'- *i\n'
'- *s\n'
'- *f_m\n'
'- *f_s\n'
'---\n' # redeclare anchors in a new document
'- &b true\n'
'- &i 42\n'
'- &s hello\n'
'- *b\n'
'- *i\n'
'- *s\n'
'---\n'
'block mapping: &b_m\n'
' key: value\n'
'extended:\n'
' <<: *b_m\n'
' foo: bar\n'
'---\n'
'{a: 1, &x b: 2, c: &y 3, *x : 4, e: *y}\n'
'...\n', conf)
self.check('---\n'
'- &i 42\n'
'---\n'
'- &b true\n'
'- &b true\n'
'- &b true\n'
'- &s hello\n'
'- *b\n'
'- *i\n' # declared in a previous document
'- *f_m\n' # never declared
'- *f_m\n'
'- *f_m\n'
'- *f_s\n' # declared after
'- &f_s [1, 2]\n'
'...\n'
'---\n'
'block mapping: &b_m\n'
' key: value\n'
'---\n'
'block mapping 1: &b_m_bis\n'
' key: value\n'
'block mapping 2: &b_m_bis\n'
' key: value\n'
'extended:\n'
' <<: *b_m\n'
' foo: bar\n'
'---\n'
'{a: 1, &x b: 2, c: &x 3, *x : 4, e: *y}\n'
'...\n', conf,
problem1=(5, 3),
problem2=(6, 3),
problem3=(22, 18),
problem4=(28, 20))
def test_forbid_unused_anchors(self):
conf = ('anchors:\n'
' forbid-undeclared-aliases: false\n'
' forbid-duplicated-anchors: false\n'
' forbid-unused-anchors: true\n')
self.check('---\n'
'- &b true\n'
'- &i 42\n'
'- &s hello\n'
'- &f_m {k: v}\n'
'- &f_s [1, 2]\n'
'- *b\n'
'- *i\n'
'- *s\n'
'- *f_m\n'
'- *f_s\n'
'---\n' # redeclare anchors in a new document
'- &b true\n'
'- &i 42\n'
'- &s hello\n'
'- *b\n'
'- *i\n'
'- *s\n'
'---\n'
'block mapping: &b_m\n'
' key: value\n'
'extended:\n'
' <<: *b_m\n'
' foo: bar\n'
'---\n'
'{a: 1, &x b: 2, c: &y 3, *x : 4, e: *y}\n'
'...\n', conf)
self.check('---\n'
'- &i 42\n'
'---\n'
'- &b true\n'
'- &b true\n'
'- &b true\n'
'- &s hello\n'
'- *b\n'
'- *i\n' # declared in a previous document
'- *f_m\n' # never declared
'- *f_m\n'
'- *f_m\n'
'- *f_s\n' # declared after
'- &f_s [1, 2]\n'
'...\n'
'---\n'
'block mapping: &b_m\n'
' key: value\n'
'---\n'
'block mapping 1: &b_m_bis\n'
' key: value\n'
'block mapping 2: &b_m_bis\n'
' key: value\n'
'extended:\n'
' <<: *b_m\n'
' foo: bar\n'
'---\n'
'{a: 1, &x b: 2, c: &x 3, *x : 4, e: *y}\n'
'...\n', conf,
problem1=(2, 3),
problem2=(7, 3),
problem3=(14, 3),
problem4=(17, 16),
problem5=(22, 18))

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -30,60 +31,6 @@ class ColonTestCase(RuleTestCase):
'dict6: { a: 1, b, c: 3 }\n' 'dict6: { a: 1, b, c: 3 }\n'
'dict7: { a: 1, b, c: 3 }\n', conf) 'dict7: { a: 1, b, c: 3 }\n', conf)
def test_forbid(self):
conf = ('braces:\n'
' forbid: false\n')
self.check('---\n'
'dict: {}\n', conf)
self.check('---\n'
'dict: {a}\n', conf)
self.check('---\n'
'dict: {a: 1}\n', conf)
self.check('---\n'
'dict: {\n'
' a: 1\n'
'}\n', conf)
conf = ('braces:\n'
' forbid: true\n')
self.check('---\n'
'dict:\n'
' a: 1\n', conf)
self.check('---\n'
'dict: {}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {a}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {a: 1}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {\n'
' a: 1\n'
'}\n', conf, problem=(2, 8))
conf = ('braces:\n'
' forbid: non-empty\n')
self.check('---\n'
'dict:\n'
' a: 1\n', conf)
self.check('---\n'
'dict: {}\n', conf)
self.check('---\n'
'dict: {\n'
'}\n', conf)
self.check('---\n'
'dict: {\n'
'# commented: value\n'
'# another: value2\n'
'}\n', conf)
self.check('---\n'
'dict: {a}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {a: 1}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {\n'
' a: 1\n'
'}\n', conf, problem=(2, 8))
def test_min_spaces(self): def test_min_spaces(self):
conf = ('braces:\n' conf = ('braces:\n'
' max-spaces-inside: -1\n' ' max-spaces-inside: -1\n'

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -30,58 +31,6 @@ class ColonTestCase(RuleTestCase):
'array6: [ a, b, c ]\n' 'array6: [ a, b, c ]\n'
'array7: [ a, b, c ]\n', conf) 'array7: [ a, b, c ]\n', conf)
def test_forbid(self):
conf = ('brackets:\n'
' forbid: false\n')
self.check('---\n'
'array: []\n', conf)
self.check('---\n'
'array: [a, b]\n', conf)
self.check('---\n'
'array: [\n'
' a,\n'
' b\n'
']\n', conf)
conf = ('brackets:\n'
' forbid: true\n')
self.check('---\n'
'array:\n'
' - a\n'
' - b\n', conf)
self.check('---\n'
'array: []\n', conf, problem=(2, 9))
self.check('---\n'
'array: [a, b]\n', conf, problem=(2, 9))
self.check('---\n'
'array: [\n'
' a,\n'
' b\n'
']\n', conf, problem=(2, 9))
conf = ('brackets:\n'
' forbid: non-empty\n')
self.check('---\n'
'array:\n'
' - a\n'
' - b\n', conf)
self.check('---\n'
'array: []\n', conf)
self.check('---\n'
'array: [\n\n'
']\n', conf)
self.check('---\n'
'array: [\n'
'# a comment\n'
']\n', conf)
self.check('---\n'
'array: [a, b]\n', conf, problem=(2, 9))
self.check('---\n'
'array: [\n'
' a,\n'
' b\n'
']\n', conf, problem=(2, 9))
def test_min_spaces(self): def test_min_spaces(self):
conf = ('brackets:\n' conf = ('brackets:\n'
' max-spaces-inside: -1\n' ' max-spaces-inside: -1\n'

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -256,19 +257,3 @@ class ColonTestCase(RuleTestCase):
' property: {a: 1, b: 2, c : 3}\n', conf, ' property: {a: 1, b: 2, c : 3}\n', conf,
problem1=(3, 11), problem2=(4, 4), problem1=(3, 11), problem2=(4, 4),
problem3=(8, 23), problem4=(8, 28)) problem3=(8, 23), problem4=(8, 28))
# Although accepted by PyYAML, `{*x: 4}` is not valid YAML: it should be
# noted `{*x : 4}`. The reason is that a colon can be part of an anchor
# name. See commit message for more details.
def test_with_alias_as_key(self):
conf = 'colons: {max-spaces-before: 0, max-spaces-after: 1}'
self.check('---\n'
'- anchor: &a key\n'
'- *a: 42\n'
'- {*a: 42}\n'
'- *a : 42\n'
'- {*a : 42}\n'
'- *a : 42\n'
'- {*a : 42}\n',
conf,
problem1=(7, 6), problem2=(8, 7))

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -96,7 +97,7 @@ class CommentsTestCase(RuleTestCase):
'#!/bin/env my-interpreter\n' '#!/bin/env my-interpreter\n'
'', conf, '', conf,
problem1=(1, 2), problem2=(3, 2), problem3=(4, 2)) problem1=(1, 2), problem2=(3, 2), problem3=(4, 2))
self.check('#! is a valid shebang too\n', self.check('#! not a shebang\n',
conf, problem1=(1, 2)) conf, problem1=(1, 2))
self.check('key: #!/not/a/shebang\n', self.check('key: #!/not/a/shebang\n',
conf, problem1=(1, 8)) conf, problem1=(1, 8))
@ -116,7 +117,8 @@ class CommentsTestCase(RuleTestCase):
'#comment\n' '#comment\n'
'#!/bin/env my-interpreter\n', conf, '#!/bin/env my-interpreter\n', conf,
problem2=(3, 2), problem3=(4, 2)) problem2=(3, 2), problem3=(4, 2))
self.check('#! is a valid shebang too\n', conf) self.check('#! not a shebang\n',
conf, problem1=(1, 2))
self.check('key: #!/not/a/shebang\n', self.check('key: #!/not/a/shebang\n',
conf, problem1=(1, 8)) conf, problem1=(1, 8))
@ -184,27 +186,6 @@ class CommentsTestCase(RuleTestCase):
'inline: comment #\n' 'inline: comment #\n'
'foo: bar\n', conf) 'foo: bar\n', conf)
def test_empty_comment_crlf_dos_newlines(self):
conf = ('comments:\n'
' require-starting-space: true\n'
' min-spaces-from-content: 2\n'
'new-lines:\n'
' type: dos\n')
self.check('---\r\n'
'# This is paragraph 1.\r\n'
'#\r\n'
'# This is paragraph 2.\r\n', conf)
def test_empty_comment_crlf_disabled_newlines(self):
conf = ('comments:\n'
' require-starting-space: true\n'
' min-spaces-from-content: 2\n'
'new-lines: disable\n')
self.check('---\r\n'
'# This is paragraph 1.\r\n'
'#\r\n'
'# This is paragraph 2.\r\n', conf)
def test_first_line(self): def test_first_line(self):
conf = ('comments:\n' conf = ('comments:\n'
' require-starting-space: true\n' ' require-starting-space: true\n'

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -71,22 +72,3 @@ class DocumentEndTestCase(RuleTestCase):
'---\n' '---\n'
'third: document\n' 'third: document\n'
'...\n', conf, problem=(6, 1)) '...\n', conf, problem=(6, 1))
def test_directives(self):
conf = 'document-end: {present: true}'
self.check('%YAML 1.2\n'
'---\n'
'document: end\n'
'...\n', conf)
self.check('%YAML 1.2\n'
'%TAG ! tag:clarkevans.com,2002:\n'
'---\n'
'document: end\n'
'...\n', conf)
self.check('---\n'
'first: document\n'
'...\n'
'%YAML 1.2\n'
'---\n'
'second: document\n'
'...\n', conf)

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 Greg Dubicki # Copyright (C) 2017 Greg Dubicki
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,128 +0,0 @@
# Copyright (C) 2022 the yamllint contributors
#
# 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 FloatValuesTestCase(RuleTestCase):
rule_id = 'float-values'
def test_disabled(self):
conf = 'float-values: disable\n'
self.check('---\n'
'- 0.0\n'
'- .NaN\n'
'- .INF\n'
'- .1\n'
'- 10e-6\n',
conf)
def test_numeral_before_decimal(self):
conf = (
'float-values:\n'
' require-numeral-before-decimal: true\n'
' forbid-scientific-notation: false\n'
' forbid-nan: false\n'
' forbid-inf: false\n')
self.check('---\n'
'- 0.0\n'
'- .1\n'
'- \'.1\'\n'
'- string.1\n'
'- .1string\n'
'- !custom_tag .2\n'
'- &angle1 0.0\n'
'- *angle1\n'
'- &angle2 .3\n'
'- *angle2\n',
conf,
problem1=(3, 3),
problem2=(10, 11))
def test_scientific_notation(self):
conf = (
'float-values:\n'
' require-numeral-before-decimal: false\n'
' forbid-scientific-notation: true\n'
' forbid-nan: false\n'
' forbid-inf: false\n')
self.check('---\n'
'- 10e6\n'
'- 10e-6\n'
'- 0.00001\n'
'- \'10e-6\'\n'
'- string10e-6\n'
'- 10e-6string\n'
'- !custom_tag 10e-6\n'
'- &angle1 0.000001\n'
'- *angle1\n'
'- &angle2 10e-6\n'
'- *angle2\n'
'- &angle3 10e6\n'
'- *angle3\n',
conf,
problem1=(2, 3),
problem2=(3, 3),
problem3=(11, 11),
problem4=(13, 11))
def test_nan(self):
conf = (
'float-values:\n'
' require-numeral-before-decimal: false\n'
' forbid-scientific-notation: false\n'
' forbid-nan: true\n'
' forbid-inf: false\n')
self.check('---\n'
'- .NaN\n'
'- .NAN\n'
'- \'.NaN\'\n'
'- a.NaN\n'
'- .NaNa\n'
'- !custom_tag .NaN\n'
'- &angle .nan\n'
'- *angle\n',
conf,
problem1=(2, 3),
problem2=(3, 3),
problem3=(8, 10))
def test_inf(self):
conf = (
'float-values:\n'
' require-numeral-before-decimal: false\n'
' forbid-scientific-notation: false\n'
' forbid-nan: false\n'
' forbid-inf: true\n')
self.check('---\n'
'- .inf\n'
'- .INF\n'
'- -.inf\n'
'- -.INF\n'
'- \'.inf\'\n'
'- ∞.infinity\n'
'- .infinity∞\n'
'- !custom_tag .inf\n'
'- &angle .inf\n'
'- *angle\n'
'- &angle -.inf\n'
'- *angle\n',
conf,
problem1=(2, 3),
problem2=(3, 3),
problem3=(4, 3),
problem4=(5, 3),
problem5=(10, 10),
problem6=(12, 10))

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -1370,45 +1371,6 @@ class IndentationTestCase(RuleTestCase):
' key: value\n' ' key: value\n'
'...\n', conf, problem=(2, 2)) '...\n', conf, problem=(2, 2))
def test_nested_collections_with_spaces_consistent(self):
"""Tests behavior of {spaces: consistent} in nested collections to
ensure wrong-indentation is properly caught--especially when the
expected indent value is initially unknown. For details, see
https://github.com/adrienverge/yamllint/issues/485.
"""
conf = ('indentation: {spaces: consistent,\n'
' indent-sequences: true}')
self.check('---\n'
'- item:\n'
' - elem\n'
'- item:\n'
' - elem\n'
'...\n', conf, problem=(3, 3))
conf = ('indentation: {spaces: consistent,\n'
' indent-sequences: false}')
self.check('---\n'
'- item:\n'
' - elem\n'
'- item:\n'
' - elem\n'
'...\n', conf, problem=(5, 5))
conf = ('indentation: {spaces: consistent,\n'
' indent-sequences: consistent}')
self.check('---\n'
'- item:\n'
' - elem\n'
'- item:\n'
' - elem\n'
'...\n', conf, problem=(5, 5))
conf = ('indentation: {spaces: consistent,\n'
' indent-sequences: whatever}')
self.check('---\n'
'- item:\n'
' - elem\n'
'- item:\n'
' - elem\n'
'...\n', conf)
def test_return(self): def test_return(self):
conf = 'indentation: {spaces: consistent}' conf = 'indentation: {spaces: consistent}'
self.check('---\n' self.check('---\n'

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -86,10 +87,6 @@ class KeyDuplicatesTestCase(RuleTestCase):
'anchor_reference:\n' 'anchor_reference:\n'
' <<: *anchor_one\n' ' <<: *anchor_one\n'
' <<: *anchor_two\n', conf) ' <<: *anchor_two\n', conf)
self.check('---\n'
'{a: 1, b: 2}}\n', conf, problem=(2, 13, 'syntax'))
self.check('---\n'
'[a, b, c]]\n', conf, problem=(2, 10, 'syntax'))
def test_enabled(self): def test_enabled(self):
conf = 'key-duplicates: enable' conf = 'key-duplicates: enable'
@ -168,10 +165,6 @@ class KeyDuplicatesTestCase(RuleTestCase):
'anchor_reference:\n' 'anchor_reference:\n'
' <<: *anchor_one\n' ' <<: *anchor_one\n'
' <<: *anchor_two\n', conf) ' <<: *anchor_two\n', conf)
self.check('---\n'
'{a: 1, b: 2}}\n', conf, problem=(2, 13, 'syntax'))
self.check('---\n'
'[a, b, c]]\n', conf, problem=(2, 10, 'syntax'))
def test_key_tokens_in_flow_sequences(self): def test_key_tokens_in_flow_sequences(self):
conf = 'key-duplicates: enable' conf = 'key-duplicates: enable'

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 Johannes F. Knauf # Copyright (C) 2017 Johannes F. Knauf
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -13,8 +14,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import locale
from tests.common import RuleTestCase from tests.common import RuleTestCase
@ -104,6 +103,10 @@ class KeyOrderingTestCase(RuleTestCase):
'haïr: true\n' 'haïr: true\n'
'hais: true\n', conf, 'hais: true\n', conf,
problem=(3, 1)) 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): def test_key_tokens_in_flow_sequences(self):
conf = 'key-ordering: enable' conf = 'key-ordering: enable'
@ -111,39 +114,3 @@ class KeyOrderingTestCase(RuleTestCase):
'[\n' '[\n'
' key: value, mappings, in, flow: sequence\n' ' key: value, mappings, in, flow: sequence\n'
']\n', conf) ']\n', conf)
def test_locale_case(self):
self.addCleanup(locale.setlocale, locale.LC_ALL, (None, None))
try:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
except locale.Error: # pragma: no cover
self.skipTest('locale en_US.UTF-8 not available')
conf = ('key-ordering: enable')
self.check('---\n'
't-shirt: 1\n'
'T-shirt: 2\n'
't-shirts: 3\n'
'T-shirts: 4\n', conf)
self.check('---\n'
't-shirt: 1\n'
't-shirts: 2\n'
'T-shirt: 3\n'
'T-shirts: 4\n', conf,
problem=(4, 1))
def test_locale_accents(self):
self.addCleanup(locale.setlocale, locale.LC_ALL, (None, None))
try:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
except locale.Error: # pragma: no cover
self.skipTest('locale en_US.UTF-8 not available')
conf = ('key-ordering: enable')
self.check('---\n'
'hair: true\n'
'haïr: true\n'
'hais: true\n'
'haïssable: true\n', conf)
self.check('---\n'
'hais: true\n'
'haïr: true\n', conf,
problem=(3, 1))

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -13,6 +14,9 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
import unittest
from tests.common import RuleTestCase from tests.common import RuleTestCase
@ -115,27 +119,6 @@ class LineLengthTestCase(RuleTestCase):
'long_line: http://localhost/very/very/long/url\n' 'long_line: http://localhost/very/very/long/url\n'
'...\n', conf, problem=(2, 21)) '...\n', conf, problem=(2, 21))
conf = 'line-length: {max: 20, allow-non-breakable-words: true}'
self.check('---\n'
'# http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf)
self.check('---\n'
'## http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf)
self.check('---\n'
'# # http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf,
problem=(2, 21))
self.check('---\n'
'#A http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf,
problem1=(2, 2, 'comments'),
problem2=(2, 21, 'line-length'))
conf = ('line-length: {max: 20, allow-non-breakable-words: true}\n' conf = ('line-length: {max: 20, allow-non-breakable-words: true}\n'
'trailing-spaces: disable') 'trailing-spaces: disable')
self.check('---\n' self.check('---\n'
@ -176,17 +159,18 @@ class LineLengthTestCase(RuleTestCase):
' {% this line is' + 99 * ' really' + ' long %}\n', ' {% this line is' + 99 * ' really' + ' long %}\n',
conf, problem=(3, 81)) conf, problem=(3, 81))
@unittest.skipIf(sys.version_info < (3, 0), 'Python 2 not supported')
def test_unicode(self): def test_unicode(self):
conf = 'line-length: {max: 53}' conf = 'line-length: {max: 53}'
self.check('---\n' self.check('---\n'
'# This is a test to check if “line-length” works nice\n' '# This is a test to check if “line-length” works nice\n'
'with: “unicode characters” that span across bytes! ↺\n', 'with: “unicode characters” that span accross bytes! ↺\n',
conf) conf)
conf = 'line-length: {max: 51}' conf = 'line-length: {max: 52}'
self.check('---\n' self.check('---\n'
'# This is a test to check if “line-length” works nice\n' '# This is a test to check if “line-length” works nice\n'
'with: “unicode characters” that span across bytes! ↺\n', 'with: “unicode characters” that span accross bytes! ↺\n',
conf, problem1=(2, 52), problem2=(3, 52)) conf, problem1=(2, 53), problem2=(3, 53))
def test_with_dos_newlines(self): def test_with_dos_newlines(self):
conf = ('line-length: {max: 10}\n' conf = ('line-length: {max: 10}\n'

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -13,8 +14,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from unittest import mock
from tests.common import RuleTestCase from tests.common import RuleTestCase
@ -41,16 +40,6 @@ class NewLinesTestCase(RuleTestCase):
self.check('---\ntext\n', conf) self.check('---\ntext\n', conf)
self.check('---\r\ntext\r\n', conf, problem=(1, 4)) self.check('---\r\ntext\r\n', conf, problem=(1, 4))
def test_unix_type_required_st_sp(self):
# If we find a CRLF when looking for Unix newlines, yamllint
# should always raise, regardless of logic with
# require-starting-space.
conf = ('new-line-at-end-of-file: disable\n'
'new-lines: {type: unix}\n'
'comments:\n'
' require-starting-space: true\n')
self.check('---\r\n#\r\n', conf, problem=(1, 4))
def test_dos_type(self): def test_dos_type(self):
conf = ('new-line-at-end-of-file: disable\n' conf = ('new-line-at-end-of-file: disable\n'
'new-lines: {type: dos}\n') 'new-lines: {type: dos}\n')
@ -60,37 +49,3 @@ class NewLinesTestCase(RuleTestCase):
self.check('\r\n', conf) self.check('\r\n', conf)
self.check('---\ntext\n', conf, problem=(1, 4)) self.check('---\ntext\n', conf, problem=(1, 4))
self.check('---\r\ntext\r\n', conf) self.check('---\r\ntext\r\n', conf)
def test_platform_type(self):
conf = ('new-line-at-end-of-file: disable\n'
'new-lines: {type: platform}\n')
self.check('', conf)
# mock the Linux new-line-character
with mock.patch('yamllint.rules.new_lines.linesep', '\n'):
self.check('\n', conf)
self.check('\r\n', conf, problem=(1, 1))
self.check('---\ntext\n', conf)
self.check('---\r\ntext\r\n', conf, problem=(1, 4))
self.check('---\r\ntext\n', conf, problem=(1, 4))
# FIXME: the following tests currently don't work
# because only the first line is checked for line-endings
# see: issue #475
# ---
# self.check('---\ntext\r\nfoo\n', conf, problem=(2, 4))
# self.check('---\ntext\r\n', conf, problem=(2, 4))
# mock the Windows new-line-character
with mock.patch('yamllint.rules.new_lines.linesep', '\r\n'):
self.check('\r\n', conf)
self.check('\n', conf, problem=(1, 1))
self.check('---\r\ntext\r\n', conf)
self.check('---\ntext\n', conf, problem=(1, 4))
self.check('---\ntext\r\n', conf, problem=(1, 4))
# FIXME: the following tests currently don't work
# because only the first line is checked for line-endings
# see: issue #475
# ---
# self.check('---\r\ntext\nfoo\r\n', conf, problem=(2, 4))
# self.check('---\r\ntext\n', conf, problem=(2, 4))

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -32,7 +33,6 @@ class OctalValuesTestCase(RuleTestCase):
' forbid-explicit-octal: false\n' ' forbid-explicit-octal: false\n'
'new-line-at-end-of-file: disable\n' 'new-line-at-end-of-file: disable\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('after-tag: !custom_tag 010', conf)
self.check('user-city: 010', conf, problem=(1, 15)) self.check('user-city: 010', conf, problem=(1, 15))
self.check('user-city: abc', conf) self.check('user-city: abc', conf)
self.check('user-city: 010,0571', conf) self.check('user-city: 010,0571', conf)
@ -50,8 +50,6 @@ class OctalValuesTestCase(RuleTestCase):
' - 0.10\n' ' - 0.10\n'
' - .01\n' ' - .01\n'
' - 0e3\n', conf) ' - 0e3\n', conf)
self.check('with-decimal-digits: 012345678', conf)
self.check('with-decimal-digits: 012345679', conf)
def test_explicit_octal_values(self): def test_explicit_octal_values(self):
conf = ('octal-values:\n' conf = ('octal-values:\n'
@ -76,5 +74,3 @@ class OctalValuesTestCase(RuleTestCase):
' - .01\n' ' - .01\n'
' - 0e3\n', conf) ' - 0e3\n', conf)
self.check('user-city: "010"', conf) self.check('user-city: "010"', conf)
self.check('with-decimal-digits: 0o012345678', conf)
self.check('with-decimal-digits: 0o012345679', conf)

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2018 ClearScore # Copyright (C) 2018 ClearScore
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -329,8 +330,7 @@ class QuotedTestCase(RuleTestCase):
'- "%wheel ALL=(ALL) NOPASSWD: ALL"\n' '- "%wheel ALL=(ALL) NOPASSWD: ALL"\n'
'- \'"quoted"\'\n' '- \'"quoted"\'\n'
'- "\'foo\' == \'bar\'"\n' '- "\'foo\' == \'bar\'"\n'
'- "\'Mac\' in ansible_facts.product_name"\n' '- "\'Mac\' in ansible_facts.product_name"\n',
'- \'foo # bar\'\n',
conf) conf)
self.check('---\n' self.check('---\n'
'k1: ""\n' 'k1: ""\n'
@ -435,124 +435,3 @@ class QuotedTestCase(RuleTestCase):
'- foo bar\n' '- foo bar\n'
'- "foo bar"\n', '- "foo bar"\n',
conf, problem1=(3, 3), problem2=(7, 3), problem3=(11, 3)) conf, problem1=(3, 3), problem2=(7, 3), problem3=(11, 3))
def test_octal_values(self):
conf = 'quoted-strings: {required: true}\n'
self.check('---\n'
'- 100\n'
'- 0100\n'
'- 0o100\n'
'- 777\n'
'- 0777\n'
'- 0o777\n'
'- 800\n'
'- 0800\n'
'- 0o800\n'
'- "0800"\n'
'- "0o800"\n',
conf,
problem1=(9, 3), problem2=(10, 3))
def test_allow_quoted_quotes(self):
conf = ('quoted-strings: {quote-type: single,\n'
' required: false,\n'
' allow-quoted-quotes: false}\n')
self.check('---\n'
'foo1: "[barbaz]"\n' # fails
'foo2: "[bar\'baz]"\n', # fails
conf, problem1=(2, 7), problem2=(3, 7))
conf = ('quoted-strings: {quote-type: single,\n'
' required: false,\n'
' allow-quoted-quotes: true}\n')
self.check('---\n'
'foo1: "[barbaz]"\n' # fails
'foo2: "[bar\'baz]"\n',
conf, problem1=(2, 7))
conf = ('quoted-strings: {quote-type: single,\n'
' required: true,\n'
' allow-quoted-quotes: false}\n')
self.check('---\n'
'foo1: "[barbaz]"\n' # fails
'foo2: "[bar\'baz]"\n', # fails
conf, problem1=(2, 7), problem2=(3, 7))
conf = ('quoted-strings: {quote-type: single,\n'
' required: true,\n'
' allow-quoted-quotes: true}\n')
self.check('---\n'
'foo1: "[barbaz]"\n' # fails
'foo2: "[bar\'baz]"\n',
conf, problem1=(2, 7))
conf = ('quoted-strings: {quote-type: single,\n'
' required: only-when-needed,\n'
' allow-quoted-quotes: false}\n')
self.check('---\n'
'foo1: "[barbaz]"\n' # fails
'foo2: "[bar\'baz]"\n', # fails
conf, problem1=(2, 7), problem2=(3, 7))
conf = ('quoted-strings: {quote-type: single,\n'
' required: only-when-needed,\n'
' allow-quoted-quotes: true}\n')
self.check('---\n'
'foo1: "[barbaz]"\n' # fails
'foo2: "[bar\'baz]"\n',
conf, problem1=(2, 7))
conf = ('quoted-strings: {quote-type: double,\n'
' required: false,\n'
' allow-quoted-quotes: false}\n')
self.check("---\n"
"foo1: '[barbaz]'\n" # fails
"foo2: '[bar\"baz]'\n", # fails
conf, problem1=(2, 7), problem2=(3, 7))
conf = ('quoted-strings: {quote-type: double,\n'
' required: false,\n'
' allow-quoted-quotes: true}\n')
self.check("---\n"
"foo1: '[barbaz]'\n" # fails
"foo2: '[bar\"baz]'\n",
conf, problem1=(2, 7))
conf = ('quoted-strings: {quote-type: double,\n'
' required: true,\n'
' allow-quoted-quotes: false}\n')
self.check("---\n"
"foo1: '[barbaz]'\n" # fails
"foo2: '[bar\"baz]'\n", # fails
conf, problem1=(2, 7), problem2=(3, 7))
conf = ('quoted-strings: {quote-type: double,\n'
' required: true,\n'
' allow-quoted-quotes: true}\n')
self.check("---\n"
"foo1: '[barbaz]'\n" # fails
"foo2: '[bar\"baz]'\n",
conf, problem1=(2, 7))
conf = ('quoted-strings: {quote-type: double,\n'
' required: only-when-needed,\n'
' allow-quoted-quotes: false}\n')
self.check("---\n"
"foo1: '[barbaz]'\n" # fails
"foo2: '[bar\"baz]'\n", # fails
conf, problem1=(2, 7), problem2=(3, 7))
conf = ('quoted-strings: {quote-type: double,\n'
' required: only-when-needed,\n'
' allow-quoted-quotes: true}\n')
self.check("---\n"
"foo1: '[barbaz]'\n" # fails
"foo2: '[bar\"baz]'\n",
conf, problem1=(2, 7))
conf = ('quoted-strings: {quote-type: any}\n')
self.check("---\n"
"foo1: '[barbaz]'\n"
"foo2: '[bar\"baz]'\n",
conf)

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Peter Ericson # Copyright (C) 2016 Peter Ericson
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -13,23 +14,25 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from io import StringIO try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import fcntl import fcntl
import locale import locale
import os import os
import pty import pty
import shutil import shutil
import sys import sys
import tempfile
import unittest import unittest
from tests.common import build_temp_workspace, temp_workspace from tests.common import build_temp_workspace
from yamllint import cli from yamllint import cli
from yamllint import config from yamllint import config
class RunContext: class RunContext(object):
"""Context manager for ``cli.run()`` to capture exit code and streams.""" """Context manager for ``cli.run()`` to capture exit code and streams."""
def __init__(self, case): def __init__(self, case):
@ -52,16 +55,6 @@ class RunContext:
return self._raises_ctx.exception.code return self._raises_ctx.exception.code
# Check system's UTF-8 availability
def utf8_available():
try:
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
locale.setlocale(locale.LC_ALL, (None, None))
return True
except locale.Error: # pragma: no cover
return False
class CommandLineTestCase(unittest.TestCase): class CommandLineTestCase(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
@ -79,9 +72,6 @@ class CommandLineTestCase(unittest.TestCase):
# file in dir # file in dir
'sub/ok.yaml': '---\n' 'sub/ok.yaml': '---\n'
'key: value\n', 'key: value\n',
# directory that looks like a yaml file
'sub/directory.yaml/not-yaml.txt': '',
'sub/directory.yaml/empty.yml': '',
# file in very nested dir # file in very nested dir
's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml': '---\n' 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml': '---\n'
'key: value\n' 'key: value\n'
@ -93,21 +83,14 @@ class CommandLineTestCase(unittest.TestCase):
'key: value\n', 'key: value\n',
# non-ASCII chars # non-ASCII chars
'non-ascii/éçäγλνπ¥/utf-8': ( 'non-ascii/éçäγλνπ¥/utf-8': (
'---\n' u'---\n'
'- hétérogénéité\n' u'- hétérogénéité\n'
'# 19.99 €\n' u'# 19.99 €\n'
'- お早う御座います。\n' u'- お早う御座います。\n'
'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'), u'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'),
# dos line endings yaml # dos line endings yaml
'dos.yml': '---\r\n' 'dos.yml': '---\r\n'
'dos: true', 'dos: true',
# different key-ordering by locale
'c.yaml': '---\n'
'A: true\n'
'a: true',
'en.yaml': '---\n'
'a: true\n'
'A: true'
}) })
@classmethod @classmethod
@ -116,19 +99,14 @@ class CommandLineTestCase(unittest.TestCase):
shutil.rmtree(cls.wd) shutil.rmtree(cls.wd)
@unittest.skipIf(not utf8_available() and sys.version_info < (3, 7),
'UTF-8 paths not supported')
def test_find_files_recursively(self): def test_find_files_recursively(self):
conf = config.YamlLintConfig('extends: default') conf = config.YamlLintConfig('extends: default')
self.assertEqual( self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)), sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'a.yaml'), [os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'c.yaml'),
os.path.join(self.wd, 'dos.yml'), os.path.join(self.wd, 'dos.yml'),
os.path.join(self.wd, 'empty.yml'), os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 'en.yaml'),
os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'), os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'),
os.path.join(self.wd, 'sub/directory.yaml/empty.yml'),
os.path.join(self.wd, 'sub/ok.yaml'), os.path.join(self.wd, 'sub/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')], os.path.join(self.wd, 'warn.yaml')],
) )
@ -153,7 +131,6 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual( self.assertEqual(
sorted(cli.find_files_recursively(items, conf)), sorted(cli.find_files_recursively(items, conf)),
[os.path.join(self.wd, '/etc/another/file'), [os.path.join(self.wd, '/etc/another/file'),
os.path.join(self.wd, 'sub/directory.yaml/empty.yml'),
os.path.join(self.wd, 'sub/ok.yaml')], os.path.join(self.wd, 'sub/ok.yaml')],
) )
@ -163,8 +140,6 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual( self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)), sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'a.yaml'), [os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'c.yaml'),
os.path.join(self.wd, 'en.yaml'),
os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'), os.path.join(self.wd, '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')] os.path.join(self.wd, 'warn.yaml')]
@ -176,8 +151,7 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual( self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)), sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'dos.yml'), [os.path.join(self.wd, 'dos.yml'),
os.path.join(self.wd, 'empty.yml'), os.path.join(self.wd, 'empty.yml')]
os.path.join(self.wd, 'sub/directory.yaml/empty.yml')]
) )
conf = config.YamlLintConfig('extends: default\n' conf = config.YamlLintConfig('extends: default\n'
@ -194,15 +168,11 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual( self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)), sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'a.yaml'), [os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'c.yaml'),
os.path.join(self.wd, 'dos.yml'), os.path.join(self.wd, 'dos.yml'),
os.path.join(self.wd, 'empty.yml'), os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 'en.yaml'),
os.path.join(self.wd, 'no-yaml.json'), os.path.join(self.wd, 'no-yaml.json'),
os.path.join(self.wd, 'non-ascii/éçäγλνπ¥/utf-8'), os.path.join(self.wd, 'non-ascii/éçäγλνπ¥/utf-8'),
os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'), os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'),
os.path.join(self.wd, 'sub/directory.yaml/empty.yml'),
os.path.join(self.wd, 'sub/directory.yaml/not-yaml.txt'),
os.path.join(self.wd, 'sub/ok.yaml'), os.path.join(self.wd, 'sub/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')] os.path.join(self.wd, 'warn.yaml')]
) )
@ -215,15 +185,11 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual( self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)), sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'a.yaml'), [os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'c.yaml'),
os.path.join(self.wd, 'dos.yml'), os.path.join(self.wd, 'dos.yml'),
os.path.join(self.wd, 'empty.yml'), os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 'en.yaml'),
os.path.join(self.wd, 'no-yaml.json'), os.path.join(self.wd, 'no-yaml.json'),
os.path.join(self.wd, 'non-ascii/éçäγλνπ¥/utf-8'), os.path.join(self.wd, 'non-ascii/éçäγλνπ¥/utf-8'),
os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'), os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'),
os.path.join(self.wd, 'sub/directory.yaml/empty.yml'),
os.path.join(self.wd, 'sub/directory.yaml/not-yaml.txt'),
os.path.join(self.wd, 'sub/ok.yaml'), os.path.join(self.wd, 'sub/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')] os.path.join(self.wd, 'warn.yaml')]
) )
@ -242,19 +208,19 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(()) cli.run(())
self.assertNotEqual(ctx.returncode, 0) self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'^usage') self.assertRegexpMatches(ctx.stderr, r'^usage')
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('--unknown-arg', )) cli.run(('--unknown-arg', ))
self.assertNotEqual(ctx.returncode, 0) self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'^usage') self.assertRegexpMatches(ctx.stderr, r'^usage')
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('-c', './conf.yaml', '-d', 'relaxed', 'file')) cli.run(('-c', './conf.yaml', '-d', 'relaxed', 'file'))
self.assertNotEqual(ctx.returncode, 0) self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegex( self.assertRegexpMatches(
ctx.stderr.splitlines()[-1], ctx.stderr.splitlines()[-1],
r'^yamllint: error: argument -d\/--config-data: ' r'^yamllint: error: argument -d\/--config-data: '
r'not allowed with argument -c\/--config-file$' r'not allowed with argument -c\/--config-file$'
@ -265,31 +231,21 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-', 'file')) cli.run(('-', 'file'))
self.assertNotEqual(ctx.returncode, 0) self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'^usage') self.assertRegexpMatches(ctx.stderr, r'^usage')
def test_run_with_bad_config(self): def test_run_with_bad_config(self):
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('-d', 'rules: {a: b}', 'file')) cli.run(('-d', 'rules: {a: b}', 'file'))
self.assertEqual(ctx.returncode, -1) self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'^invalid config: no such rule') self.assertRegexpMatches(ctx.stderr, r'^invalid config: no such rule')
def test_run_with_empty_config(self): def test_run_with_empty_config(self):
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('-d', '', 'file')) cli.run(('-d', '', 'file'))
self.assertEqual(ctx.returncode, -1) self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'^invalid config: not a dict') self.assertRegexpMatches(ctx.stderr, r'^invalid config: not a dict')
def test_run_with_implicit_extends_config(self):
path = os.path.join(self.wd, 'warn.yaml')
with RunContext(self) as ctx:
cli.run(('-d', 'default', '-f', 'parsable', path))
expected_out = ('%s:1:1: [warning] missing document start "---" '
'(document-start)\n' % path)
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (0, expected_out, ''))
def test_run_with_config_file(self): def test_run_with_config_file(self):
with open(os.path.join(self.wd, 'config'), 'w') as f: with open(os.path.join(self.wd, 'config'), 'w') as f:
@ -306,7 +262,6 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml'))) cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.returncode, 1) self.assertEqual(ctx.returncode, 1)
@unittest.skipIf(os.environ.get('GITHUB_RUN_ID'), '$HOME not overridable')
def test_run_with_user_global_config_file(self): def test_run_with_user_global_config_file(self):
home = os.path.join(self.wd, 'fake-home') home = os.path.join(self.wd, 'fake-home')
dir = os.path.join(home, '.config', 'yamllint') dir = os.path.join(home, '.config', 'yamllint')
@ -330,83 +285,11 @@ class CommandLineTestCase(unittest.TestCase):
cli.run((os.path.join(self.wd, 'a.yaml'), )) cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.returncode, 1) self.assertEqual(ctx.returncode, 1)
def test_run_with_user_xdg_config_home_in_env(self):
self.addCleanup(os.environ.__delitem__, 'XDG_CONFIG_HOME')
with tempfile.TemporaryDirectory('w') as d:
os.environ['XDG_CONFIG_HOME'] = d
os.makedirs(os.path.join(d, 'yamllint'))
with open(os.path.join(d, 'yamllint', 'config'), 'w') as f:
f.write('extends: relaxed')
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', os.path.join(self.wd, 'warn.yaml')))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', ''))
def test_run_with_user_yamllint_config_file_in_env(self):
self.addCleanup(os.environ.__delitem__, 'YAMLLINT_CONFIG_FILE')
with tempfile.NamedTemporaryFile('w') as f:
os.environ['YAMLLINT_CONFIG_FILE'] = f.name
f.write('rules: {trailing-spaces: disable}')
f.flush()
with RunContext(self) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.returncode, 0)
with tempfile.NamedTemporaryFile('w') as f:
os.environ['YAMLLINT_CONFIG_FILE'] = f.name
f.write('rules: {trailing-spaces: enable}')
f.flush()
with RunContext(self) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.returncode, 1)
def test_run_with_locale(self):
# check for availability of locale, otherwise skip the test
# reset to default before running the test,
# as the first two runs don't use setlocale()
try:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
except locale.Error: # pragma: no cover
self.skipTest('locale en_US.UTF-8 not available')
locale.setlocale(locale.LC_ALL, (None, None))
# C + en.yaml should fail
with RunContext(self) as ctx:
cli.run(('-d', 'rules: { key-ordering: enable }',
os.path.join(self.wd, 'en.yaml')))
self.assertEqual(ctx.returncode, 1)
# C + c.yaml should pass
with RunContext(self) as ctx:
cli.run(('-d', 'rules: { key-ordering: enable }',
os.path.join(self.wd, 'c.yaml')))
self.assertEqual(ctx.returncode, 0)
# the next two runs use setlocale() inside,
# so we need to clean up afterwards
self.addCleanup(locale.setlocale, locale.LC_ALL, (None, None))
# en_US + en.yaml should pass
with RunContext(self) as ctx:
cli.run(('-d', 'locale: en_US.UTF-8\n'
'rules: { key-ordering: enable }',
os.path.join(self.wd, 'en.yaml')))
self.assertEqual(ctx.returncode, 0)
# en_US + c.yaml should fail
with RunContext(self) as ctx:
cli.run(('-d', 'locale: en_US.UTF-8\n'
'rules: { key-ordering: enable }',
os.path.join(self.wd, 'c.yaml')))
self.assertEqual(ctx.returncode, 1)
def test_run_version(self): def test_run_version(self):
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('--version', )) cli.run(('--version', ))
self.assertEqual(ctx.returncode, 0) self.assertEqual(ctx.returncode, 0)
self.assertRegex(ctx.stdout + ctx.stderr, r'yamllint \d+\.\d+') self.assertRegexpMatches(ctx.stdout + ctx.stderr, r'yamllint \d+\.\d+')
def test_run_non_existing_file(self): def test_run_non_existing_file(self):
path = os.path.join(self.wd, 'i-do-not-exist.yaml') path = os.path.join(self.wd, 'i-do-not-exist.yaml')
@ -415,7 +298,7 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-f', 'parsable', path)) cli.run(('-f', 'parsable', path))
self.assertEqual(ctx.returncode, -1) self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'No such file or directory') self.assertRegexpMatches(ctx.stderr, r'No such file or directory')
def test_run_one_problem_file(self): def test_run_one_problem_file(self):
path = os.path.join(self.wd, 'a.yaml') path = os.path.join(self.wd, 'a.yaml')
@ -457,12 +340,18 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-f', 'parsable', path)) cli.run(('-f', 'parsable', path))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', '')) self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', ''))
@unittest.skipIf(not utf8_available(), 'C.UTF-8 not available')
def test_run_non_ascii_file(self): def test_run_non_ascii_file(self):
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
self.addCleanup(locale.setlocale, locale.LC_ALL, (None, None))
path = os.path.join(self.wd, 'non-ascii', 'éçäγλνπ¥', 'utf-8') path = os.path.join(self.wd, 'non-ascii', 'éçäγλνπ¥', 'utf-8')
# Make sure the default localization conditions on this "system"
# support UTF-8 encoding.
loc = locale.getlocale()
try:
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
except locale.Error:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
self.addCleanup(locale.setlocale, locale.LC_ALL, loc)
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('-f', 'parsable', path)) cli.run(('-f', 'parsable', path))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', '')) self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', ''))
@ -569,55 +458,6 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual( self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, '')) (ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
def test_run_format_colored_warning(self):
path = os.path.join(self.wd, 'warn.yaml')
with RunContext(self) as ctx:
cli.run((path, '--format', 'colored'))
expected_out = (
'\033[4m%s\033[0m\n'
' \033[2m1:1\033[0m \033[33mwarning\033[0m '
'missing document start "---" \033[2m(document-start)\033[0m\n'
'\n' % path)
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (0, expected_out, ''))
def test_run_format_github(self):
path = os.path.join(self.wd, 'a.yaml')
with RunContext(self) as ctx:
cli.run((path, '--format', 'github'))
expected_out = (
'::group::%s\n'
'::error file=%s,line=2,col=4::2:4 [trailing-spaces] trailing'
' spaces\n'
'::error file=%s,line=3,col=4::3:4 [new-line-at-end-of-file] no'
' new line character at the end of file\n'
'::endgroup::\n\n'
% (path, path, path))
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
def test_github_actions_detection(self):
path = os.path.join(self.wd, 'a.yaml')
self.addCleanup(os.environ.__delitem__, 'GITHUB_ACTIONS')
self.addCleanup(os.environ.__delitem__, 'GITHUB_WORKFLOW')
with RunContext(self) as ctx:
os.environ['GITHUB_ACTIONS'] = 'something'
os.environ['GITHUB_WORKFLOW'] = 'something'
cli.run((path, ))
expected_out = (
'::group::%s\n'
'::error file=%s,line=2,col=4::2:4 [trailing-spaces] trailing'
' spaces\n'
'::error file=%s,line=3,col=4::3:4 [new-line-at-end-of-file] no'
' new line character at the end of file\n'
'::endgroup::\n\n'
% (path, path, path))
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
def test_run_read_from_stdin(self): def test_run_read_from_stdin(self):
# prepares stdin with an invalid yaml string so that we can check # prepares stdin with an invalid yaml string so that we can check
# for its specific error, and be assured that stdin was read # for its specific error, and be assured that stdin was read
@ -677,121 +517,3 @@ class CommandLineTestCase(unittest.TestCase):
'\n' % path) '\n' % path)
self.assertEqual( self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, '')) (ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
def test_run_list_files(self):
with RunContext(self) as ctx:
cli.run(('--list-files', self.wd))
self.assertEqual(ctx.returncode, 0)
self.assertEqual(
sorted(ctx.stdout.splitlines()),
[os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'c.yaml'),
os.path.join(self.wd, 'dos.yml'),
os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 'en.yaml'),
os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'),
os.path.join(self.wd, 'sub/directory.yaml/empty.yml'),
os.path.join(self.wd, 'sub/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')]
)
config = '{ignore: "*.yml", yaml-files: ["*.*"]}'
with RunContext(self) as ctx:
cli.run(('--list-files', '-d', config, self.wd))
self.assertEqual(ctx.returncode, 0)
self.assertEqual(
sorted(ctx.stdout.splitlines()),
[os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'c.yaml'),
os.path.join(self.wd, 'en.yaml'),
os.path.join(self.wd, 'no-yaml.json'),
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/directory.yaml/not-yaml.txt'),
os.path.join(self.wd, 'sub/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')]
)
class CommandLineConfigTestCase(unittest.TestCase):
def test_config_file(self):
workspace = {'a.yml': 'hello: world\n'}
conf = ('---\n'
'extends: relaxed\n')
for conf_file in ('.yamllint', '.yamllint.yml', '.yamllint.yaml'):
with self.subTest(conf_file):
with temp_workspace(workspace):
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', '.'))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr),
(0, './a.yml:1:1: [warning] missing document '
'start "---" (document-start)\n', ''))
with temp_workspace({**workspace, **{conf_file: conf}}):
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', '.'))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr),
(0, '', ''))
def test_parent_config_file(self):
workspace = {'a/b/c/d/e/f/g/a.yml': 'hello: world\n'}
conf = ('---\n'
'extends: relaxed\n')
for conf_file in ('.yamllint', '.yamllint.yml', '.yamllint.yaml'):
with self.subTest(conf_file):
with temp_workspace(workspace):
with RunContext(self) as ctx:
os.chdir('a/b/c/d/e/f')
cli.run(('-f', 'parsable', '.'))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr),
(0, './g/a.yml:1:1: [warning] missing '
'document start "---" (document-start)\n',
''))
with temp_workspace({**workspace, **{conf_file: conf}}):
with RunContext(self) as ctx:
os.chdir('a/b/c/d/e/f')
cli.run(('-f', 'parsable', '.'))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr),
(0, '', ''))
def test_multiple_parent_config_file(self):
workspace = {'a/b/c/3spaces.yml': 'array:\n'
' - item\n',
'a/b/c/4spaces.yml': 'array:\n'
' - item\n',
'a/.yamllint': '---\n'
'extends: relaxed\n'
'rules:\n'
' indentation:\n'
' spaces: 4\n',
}
conf3 = ('---\n'
'extends: relaxed\n'
'rules:\n'
' indentation:\n'
' spaces: 3\n')
with temp_workspace(workspace):
with RunContext(self) as ctx:
os.chdir('a/b/c')
cli.run(('-f', 'parsable', '.'))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr),
(0, './3spaces.yml:2:4: [warning] wrong indentation: '
'expected 4 but found 3 (indentation)\n', ''))
with temp_workspace({**workspace, **{'a/b/.yamllint.yml': conf3}}):
with RunContext(self) as ctx:
os.chdir('a/b/c')
cli.run(('-f', 'parsable', '.'))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr),
(0, './4spaces.yml:2:5: [warning] wrong indentation: '
'expected 3 but found 4 (indentation)\n', ''))

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -13,7 +14,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from io import StringIO try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import os import os
import shutil import shutil
import sys import sys
@ -22,7 +26,6 @@ import unittest
from tests.common import build_temp_workspace from tests.common import build_temp_workspace
from yamllint.config import YamlLintConfigError
from yamllint import cli from yamllint import cli
from yamllint import config from yamllint import config
@ -45,7 +48,7 @@ class SimpleConfigTestCase(unittest.TestCase):
config.YamlLintConfig('not: valid: yaml') config.YamlLintConfig('not: valid: yaml')
def test_unknown_rule(self): def test_unknown_rule(self):
with self.assertRaisesRegex( with self.assertRaisesRegexp(
config.YamlLintConfigError, config.YamlLintConfigError,
'invalid config: no such rule: "this-one-does-not-exist"'): 'invalid config: no such rule: "this-one-does-not-exist"'):
config.YamlLintConfig('rules:\n' config.YamlLintConfig('rules:\n'
@ -64,7 +67,7 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertEqual(c.rules['colons']['max-spaces-after'], 1) self.assertEqual(c.rules['colons']['max-spaces-after'], 1)
def test_unknown_option(self): def test_unknown_option(self):
with self.assertRaisesRegex( with self.assertRaisesRegexp(
config.YamlLintConfigError, config.YamlLintConfigError,
'invalid config: unknown option "abcdef" for rule "colons"'): 'invalid config: unknown option "abcdef" for rule "colons"'):
config.YamlLintConfig('rules:\n' config.YamlLintConfig('rules:\n'
@ -102,7 +105,7 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertEqual(c.rules['indentation']['check-multi-line-strings'], self.assertEqual(c.rules['indentation']['check-multi-line-strings'],
False) False)
with self.assertRaisesRegex( with self.assertRaisesRegexp(
config.YamlLintConfigError, config.YamlLintConfigError,
'invalid config: option "indent-sequences" of "indentation" ' 'invalid config: option "indent-sequences" of "indentation" '
'should be in '): 'should be in '):
@ -122,7 +125,7 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertEqual(c.rules['hyphens'], False) self.assertEqual(c.rules['hyphens'], False)
def test_validate_rule_conf(self): def test_validate_rule_conf(self):
class Rule: class Rule(object):
ID = 'fake' ID = 'fake'
self.assertFalse(config.validate_rule_conf(Rule, False)) self.assertFalse(config.validate_rule_conf(Rule, False))
@ -190,41 +193,6 @@ class SimpleConfigTestCase(unittest.TestCase):
config.validate_rule_conf, Rule, config.validate_rule_conf, Rule,
{'multiple': ['item4']}) {'multiple': ['item4']})
def test_invalid_rule(self):
with self.assertRaisesRegex(
config.YamlLintConfigError,
'invalid config: rule "colons": should be either '
'"enable", "disable" or a dict'):
config.YamlLintConfig('rules:\n'
' colons: invalid\n')
def test_invalid_ignore(self):
with self.assertRaisesRegex(
config.YamlLintConfigError,
'invalid config: ignore should contain file patterns'):
config.YamlLintConfig('ignore: yes\n')
def test_invalid_rule_ignore(self):
with self.assertRaisesRegex(
config.YamlLintConfigError,
'invalid config: ignore should contain file patterns'):
config.YamlLintConfig('rules:\n'
' colons:\n'
' ignore: yes\n')
def test_invalid_locale(self):
with self.assertRaisesRegex(
config.YamlLintConfigError,
'invalid config: locale should be a string'):
config.YamlLintConfig('locale: yes\n')
def test_invalid_yaml_files(self):
with self.assertRaisesRegex(
config.YamlLintConfigError,
'invalid config: yaml-files should be a list of file '
'patterns'):
config.YamlLintConfig('yaml-files: yes\n')
class ExtendedConfigTestCase(unittest.TestCase): class ExtendedConfigTestCase(unittest.TestCase):
def test_extend_on_object(self): def test_extend_on_object(self):
@ -369,26 +337,6 @@ class ExtendedConfigTestCase(unittest.TestCase):
self.assertEqual(c.rules['colons']['max-spaces-before'], 0) self.assertEqual(c.rules['colons']['max-spaces-before'], 0)
self.assertEqual(c.rules['colons']['max-spaces-after'], 1) self.assertEqual(c.rules['colons']['max-spaces-after'], 1)
def test_extended_ignore_str(self):
with tempfile.NamedTemporaryFile('w') as f:
f.write('ignore: |\n'
' *.template.yaml\n')
f.flush()
c = config.YamlLintConfig('extends: ' + f.name + '\n')
self.assertEqual(c.ignore.match_file('test.template.yaml'), True)
self.assertEqual(c.ignore.match_file('test.yaml'), False)
def test_extended_ignore_list(self):
with tempfile.NamedTemporaryFile('w') as f:
f.write('ignore:\n'
' - "*.template.yaml"\n')
f.flush()
c = config.YamlLintConfig('extends: ' + f.name + '\n')
self.assertEqual(c.ignore.match_file('test.template.yaml'), True)
self.assertEqual(c.ignore.match_file('test.yaml'), False)
class ExtendedLibraryConfigTestCase(unittest.TestCase): class ExtendedLibraryConfigTestCase(unittest.TestCase):
def test_extend_config_disable_rule(self): def test_extend_config_disable_rule(self):
@ -440,10 +388,10 @@ class ExtendedLibraryConfigTestCase(unittest.TestCase):
self.assertEqual(new.rules['empty-lines']['max-end'], 0) self.assertEqual(new.rules['empty-lines']['max-end'], 0)
class IgnoreConfigTestCase(unittest.TestCase): class IgnorePathConfigTestCase(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super(IgnorePathConfigTestCase, cls).setUpClass()
bad_yaml = ('---\n' bad_yaml = ('---\n'
'- key: val1\n' '- key: val1\n'
@ -463,6 +411,22 @@ class IgnoreConfigTestCase(unittest.TestCase):
's/s/ign-trail/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/file.yaml': bad_yaml,
's/s/ign-trail/s/s/file2.lint-me-anyway.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() cls.backup_wd = os.getcwd()
@ -470,255 +434,13 @@ class IgnoreConfigTestCase(unittest.TestCase):
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
super().tearDownClass() super(IgnorePathConfigTestCase, cls).tearDownClass()
os.chdir(cls.backup_wd) os.chdir(cls.backup_wd)
shutil.rmtree(cls.wd) shutil.rmtree(cls.wd)
def test_mutually_exclusive_ignore_keys(self): def test_run_with_ignored_path(self):
self.assertRaises(
YamlLintConfigError,
config.YamlLintConfig, 'extends: default\n'
'ignore-from-file: .gitignore\n'
'ignore: |\n'
' *.dont-lint-me.yaml\n'
' /bin/\n')
def test_ignore_from_file_not_exist(self):
self.assertRaises(
FileNotFoundError,
config.YamlLintConfig, 'extends: default\n'
'ignore-from-file: not_found_file\n')
def test_ignore_from_file_incorrect_type(self):
self.assertRaises(
YamlLintConfigError,
config.YamlLintConfig, 'extends: default\n'
'ignore-from-file: 0\n')
self.assertRaises(
YamlLintConfigError,
config.YamlLintConfig, 'extends: default\n'
'ignore-from-file: [0]\n')
def test_no_ignore(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,
'./bin/file.yaml:3:3: ' + keydup,
'./bin/file.yaml:4:17: ' + trailing,
'./bin/file.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,
'./file.dont-lint-me.yaml:3:3: ' + keydup,
'./file.dont-lint-me.yaml:4:17: ' + trailing,
'./file.dont-lint-me.yaml:5:5: ' + hyphen,
'./ign-dup/file.yaml:3:3: ' + keydup,
'./ign-dup/file.yaml:4:17: ' + trailing,
'./ign-dup/file.yaml:5:5: ' + hyphen,
'./ign-dup/sub/dir/file.yaml:3:3: ' + keydup,
'./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:4:17: ' + trailing,
'./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:4:17: ' + trailing,
'./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:4:17: ' + trailing,
'./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,
)))
def test_run_with_ignore_str(self):
with open(os.path.join(self.wd, '.yamllint'), 'w') as f:
f.write('extends: default\n'
'ignore: |\n'
' *.dont-lint-me.yaml\n'
' /bin/\n'
' !/bin/*.lint-me-anyway.yaml\n'
'rules:\n'
' key-duplicates:\n'
' ignore: |\n'
' /ign-dup\n'
' trailing-spaces:\n'
' ignore: |\n'
' ign-trail\n'
' !*.lint-me-anyway.yaml\n')
sys.stdout = StringIO()
with self.assertRaises(SystemExit):
cli.run(('-f', 'parsable', '.'))
out = sys.stdout.getvalue()
out = '\n'.join(sorted(out.splitlines()))
docstart = '[warning] missing document start "---" (document-start)'
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((
'./.yamllint:1:1: ' + docstart,
'./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,
)))
def test_run_with_ignore_list(self):
with open(os.path.join(self.wd, '.yamllint'), 'w') as f:
f.write('extends: default\n'
'ignore:\n'
' - "*.dont-lint-me.yaml"\n'
' - "/bin/"\n'
' - "!/bin/*.lint-me-anyway.yaml"\n'
'rules:\n'
' key-duplicates:\n'
' ignore:\n'
' - "/ign-dup"\n'
' trailing-spaces:\n'
' ignore:\n'
' - "ign-trail"\n'
' - "!*.lint-me-anyway.yaml"\n')
sys.stdout = StringIO()
with self.assertRaises(SystemExit):
cli.run(('-f', 'parsable', '.'))
out = sys.stdout.getvalue()
out = '\n'.join(sorted(out.splitlines()))
docstart = '[warning] missing document start "---" (document-start)'
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((
'./.yamllint:1:1: ' + docstart,
'./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,
)))
def test_run_with_ignore_from_file(self):
with open(os.path.join(self.wd, '.yamllint'), 'w') as f:
f.write('extends: default\n'
'ignore-from-file: .gitignore\n')
with open(os.path.join(self.wd, '.gitignore'), 'w') as f:
f.write('*.dont-lint-me.yaml\n'
'/bin/\n'
'!/bin/*.lint-me-anyway.yaml\n')
sys.stdout = StringIO()
with self.assertRaises(SystemExit):
cli.run(('-f', 'parsable', '.'))
out = sys.stdout.getvalue()
out = '\n'.join(sorted(out.splitlines()))
docstart = '[warning] missing document start "---" (document-start)'
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((
'./.yamllint:1:1: ' + docstart,
'./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:3:3: ' + keydup,
'./ign-dup/file.yaml:4:17: ' + trailing,
'./ign-dup/file.yaml:5:5: ' + hyphen,
'./ign-dup/sub/dir/file.yaml:3:3: ' + keydup,
'./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:4:17: ' + trailing,
'./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:4:17: ' + trailing,
'./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:4:17: ' + trailing,
'./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,
)))
def test_run_with_ignored_from_file(self):
with open(os.path.join(self.wd, '.yamllint'), 'w') as f:
f.write('ignore-from-file: [.gitignore, .yamlignore]\n'
'extends: default\n')
with open(os.path.join(self.wd, '.gitignore'), 'w') as f:
f.write('*.dont-lint-me.yaml\n'
'/bin/\n')
with open(os.path.join(self.wd, '.yamlignore'), 'w') as f:
f.write('!/bin/*.lint-me-anyway.yaml\n')
sys.stdout = StringIO() sys.stdout = StringIO()
with self.assertRaises(SystemExit): with self.assertRaises(SystemExit):
cli.run(('-f', 'parsable', '.')) cli.run(('-f', 'parsable', '.'))
@ -739,23 +461,18 @@ class IgnoreConfigTestCase(unittest.TestCase):
'./file-at-root.yaml:3:3: ' + keydup, './file-at-root.yaml:3:3: ' + keydup,
'./file-at-root.yaml:4:17: ' + trailing, './file-at-root.yaml:4:17: ' + trailing,
'./file-at-root.yaml:5:5: ' + hyphen, './file-at-root.yaml:5:5: ' + hyphen,
'./ign-dup/file.yaml:3:3: ' + keydup,
'./ign-dup/file.yaml:4:17: ' + trailing, './ign-dup/file.yaml:4:17: ' + trailing,
'./ign-dup/file.yaml:5:5: ' + hyphen, './ign-dup/file.yaml:5:5: ' + hyphen,
'./ign-dup/sub/dir/file.yaml:3:3: ' + keydup,
'./ign-dup/sub/dir/file.yaml:4:17: ' + trailing, './ign-dup/sub/dir/file.yaml:4:17: ' + trailing,
'./ign-dup/sub/dir/file.yaml:5:5: ' + hyphen, './ign-dup/sub/dir/file.yaml:5:5: ' + hyphen,
'./ign-trail/file.yaml:3:3: ' + keydup, './ign-trail/file.yaml:3:3: ' + keydup,
'./ign-trail/file.yaml:4:17: ' + trailing,
'./ign-trail/file.yaml:5:5: ' + hyphen, './ign-trail/file.yaml:5:5: ' + hyphen,
'./include/ign-dup/sub/dir/file.yaml:3:3: ' + keydup, './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:4:17: ' + trailing,
'./include/ign-dup/sub/dir/file.yaml:5:5: ' + hyphen, './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:3:3: ' + keydup,
'./s/s/ign-trail/file.yaml:4:17: ' + trailing,
'./s/s/ign-trail/file.yaml:5:5: ' + hyphen, './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:3:3: ' + keydup,
'./s/s/ign-trail/s/s/file.yaml:4:17: ' + trailing,
'./s/s/ign-trail/s/s/file.yaml:5:5: ' + hyphen, './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: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:4:17: ' + trailing,

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -31,10 +32,10 @@ class LinterTestCase(unittest.TestCase):
linter.run(b'test: document', self.fake_config()) linter.run(b'test: document', self.fake_config())
def test_run_on_unicode(self): def test_run_on_unicode(self):
linter.run('test: document', self.fake_config()) linter.run(u'test: document', self.fake_config())
def test_run_on_stream(self): def test_run_on_stream(self):
linter.run(io.StringIO('hello'), self.fake_config()) linter.run(io.StringIO(u'hello'), self.fake_config())
def test_run_on_int(self): def test_run_on_int(self):
self.assertRaises(TypeError, linter.run, 42, self.fake_config()) self.assertRaises(TypeError, linter.run, 42, self.fake_config())
@ -44,23 +45,13 @@ class LinterTestCase(unittest.TestCase):
['h', 'e', 'l', 'l', 'o'], self.fake_config()) ['h', 'e', 'l', 'l', 'o'], self.fake_config())
def test_run_on_non_ascii_chars(self): def test_run_on_non_ascii_chars(self):
s = ('- hétérogénéité\n' s = (u'- hétérogénéité\n'
'# 19.99 €\n') u'# 19.99 €\n')
linter.run(s, self.fake_config()) linter.run(s, self.fake_config())
linter.run(s.encode('utf-8'), self.fake_config()) linter.run(s.encode('utf-8'), self.fake_config())
linter.run(s.encode('iso-8859-15'), self.fake_config()) linter.run(s.encode('iso-8859-15'), self.fake_config())
s = ('- お早う御座います。\n' s = (u'- お早う御座います。\n'
'# الأَبْجَدِيَّة العَرَبِيَّة\n') u'# الأَبْجَدِيَّة العَرَبِيَّة\n')
linter.run(s, self.fake_config()) linter.run(s, self.fake_config())
linter.run(s.encode('utf-8'), self.fake_config()) linter.run(s.encode('utf-8'), self.fake_config())
def test_linter_problem_repr_without_rule(self):
problem = linter.LintProblem(1, 2, 'problem')
self.assertEqual(str(problem), '1:2: problem')
def test_linter_problem_repr_with_rule(self):
problem = linter.LintProblem(1, 2, 'problem', 'rule-id')
self.assertEqual(str(problem), '1:2: problem (rule-id)')

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 Adrien Vergé # Copyright (C) 2017 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -46,15 +47,16 @@ class ModuleTestCase(unittest.TestCase):
subprocess.check_output([PYTHON, '-m', 'yamllint'], subprocess.check_output([PYTHON, '-m', 'yamllint'],
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
self.assertEqual(ctx.exception.returncode, 2) self.assertEqual(ctx.exception.returncode, 2)
self.assertRegex(ctx.exception.output.decode(), r'^usage: yamllint') self.assertRegexpMatches(ctx.exception.output.decode(),
r'^usage: yamllint')
def test_run_module_on_bad_dir(self): def test_run_module_on_bad_dir(self):
with self.assertRaises(subprocess.CalledProcessError) as ctx: with self.assertRaises(subprocess.CalledProcessError) as ctx:
subprocess.check_output([PYTHON, '-m', 'yamllint', subprocess.check_output([PYTHON, '-m', 'yamllint',
'/does/not/exist'], '/does/not/exist'],
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
self.assertRegex(ctx.exception.output.decode(), self.assertRegexpMatches(ctx.exception.output.decode(),
r'No such file or directory') r'No such file or directory')
def test_run_module_on_file(self): def test_run_module_on_file(self):
out = subprocess.check_output( out = subprocess.check_output(

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -13,6 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from io import open
import os import os
from tests.common import RuleTestCase from tests.common import RuleTestCase

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -231,34 +232,6 @@ class YamllintDirectivesTestCase(RuleTestCase):
problem1=(3, 18, 'trailing-spaces'), problem1=(3, 18, 'trailing-spaces'),
problem2=(4, 8, 'colons')) problem2=(4, 8, 'colons'))
def test_disable_directive_with_rules_and_dos_lines(self):
conf = self.conf + 'new-lines: {type: dos}\n'
self.check('---\r\n'
'- [valid , YAML]\r\n'
'# yamllint disable rule:trailing-spaces\r\n'
'- trailing spaces \r\n'
'- bad : colon\r\n'
'- [valid , YAML]\r\n'
'# yamllint enable rule:trailing-spaces\r\n'
'- bad : colon and spaces \r\n'
'- [valid , YAML]\r\n',
conf,
problem1=(5, 8, 'colons'),
problem2=(8, 7, 'colons'),
problem3=(8, 26, 'trailing-spaces'))
self.check('---\r\n'
'- [valid , YAML]\r\n'
'- trailing spaces \r\n'
'- bad : colon\r\n'
'- [valid , YAML]\r\n'
'# yamllint disable-line rule:colons\r\n'
'- bad : colon and spaces \r\n'
'- [valid , YAML]\r\n',
conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(4, 8, 'colons'),
problem3=(7, 26, 'trailing-spaces'))
def test_directive_on_last_line(self): def test_directive_on_last_line(self):
conf = 'new-line-at-end-of-file: {}' conf = 'new-line-at-end-of-file: {}'
self.check('---\n' self.check('---\n'

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -21,10 +22,10 @@ indentation, etc."""
APP_NAME = 'yamllint' APP_NAME = 'yamllint'
APP_VERSION = '1.32.0' APP_VERSION = '1.23.0'
APP_DESCRIPTION = __doc__ APP_DESCRIPTION = __doc__
__author__ = 'Adrien Vergé' __author__ = u'Adrien Vergé'
__copyright__ = 'Copyright 2022, Adrien Vergé' __copyright__ = u'Copyright 2016, Adrien Vergé'
__license__ = 'GPLv3' __license__ = 'GPLv3'
__version__ = APP_VERSION __version__ = APP_VERSION

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -13,8 +14,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
import argparse import argparse
import locale import io
import os import os
import platform import platform
import sys import sys
@ -46,7 +49,7 @@ def supports_color():
hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()) hasattr(sys.stdout, 'isatty') and sys.stdout.isatty())
class Format: class Format(object):
@staticmethod @staticmethod
def parsable(problem, filename): def parsable(problem, filename):
return ('%(file)s:%(line)s:%(column)s: [%(level)s] %(message)s' % return ('%(file)s:%(line)s:%(column)s: [%(level)s] %(message)s' %
@ -81,47 +84,19 @@ class Format:
line += ' \033[2m(%s)\033[0m' % problem.rule line += ' \033[2m(%s)\033[0m' % problem.rule
return line return line
@staticmethod
def github(problem, filename):
line = '::'
line += problem.level
line += ' file=' + filename + ','
line += 'line=' + format(problem.line) + ','
line += 'col=' + format(problem.column)
line += '::'
line += format(problem.line)
line += ':'
line += format(problem.column)
line += ' '
if problem.rule:
line += '[' + problem.rule + '] '
line += problem.desc
return line
def show_problems(problems, file, args_format, no_warn): def show_problems(problems, file, args_format, no_warn):
max_level = 0 max_level = 0
first = True first = True
if args_format == 'auto':
if ('GITHUB_ACTIONS' in os.environ and
'GITHUB_WORKFLOW' in os.environ):
args_format = 'github'
elif supports_color():
args_format = 'colored'
for problem in problems: for problem in problems:
max_level = max(max_level, PROBLEM_LEVELS[problem.level]) max_level = max(max_level, PROBLEM_LEVELS[problem.level])
if no_warn and (problem.level != 'error'): if no_warn and (problem.level != 'error'):
continue continue
if args_format == 'parsable': if args_format == 'parsable':
print(Format.parsable(problem, file)) print(Format.parsable(problem, file))
elif args_format == 'github': elif args_format == 'colored' or \
if first: (args_format == 'auto' and supports_color()):
print('::group::%s' % file)
first = False
print(Format.github(problem, file))
elif args_format == 'colored':
if first: if first:
print('\033[4m%s\033[0m' % file) print('\033[4m%s\033[0m' % file)
first = False first = False
@ -132,28 +107,12 @@ def show_problems(problems, file, args_format, no_warn):
first = False first = False
print(Format.standard(problem, file)) print(Format.standard(problem, file))
if not first and args_format == 'github':
print('::endgroup::')
if not first and args_format != 'parsable': if not first and args_format != 'parsable':
print('') print('')
return max_level return max_level
def find_project_config_filepath(path='.'):
for filename in ('.yamllint', '.yamllint.yaml', '.yamllint.yml'):
filepath = os.path.join(path, filename)
if os.path.isfile(filepath):
return filepath
if os.path.abspath(path) == os.path.abspath(os.path.expanduser('~')):
return None
if os.path.abspath(path) == os.path.abspath(os.path.join(path, '..')):
return None
return find_project_config_filepath(path=os.path.join(path, '..'))
def run(argv=None): def run(argv=None):
parser = argparse.ArgumentParser(prog=APP_NAME, parser = argparse.ArgumentParser(prog=APP_NAME,
description=APP_DESCRIPTION) description=APP_DESCRIPTION)
@ -170,11 +129,8 @@ def run(argv=None):
config_group.add_argument('-d', '--config-data', dest='config_data', config_group.add_argument('-d', '--config-data', dest='config_data',
action='store', action='store',
help='custom configuration (as YAML source)') help='custom configuration (as YAML source)')
parser.add_argument('--list-files', action='store_true', dest='list_files',
help='list files to lint and exit')
parser.add_argument('-f', '--format', parser.add_argument('-f', '--format',
choices=('parsable', 'standard', 'colored', 'github', choices=('parsable', 'standard', 'colored', 'auto'),
'auto'),
default='auto', help='format for parsing output') default='auto', help='format for parsing output')
parser.add_argument('-s', '--strict', parser.add_argument('-s', '--strict',
action='store_true', action='store_true',
@ -188,17 +144,13 @@ def run(argv=None):
args = parser.parse_args(argv) args = parser.parse_args(argv)
if 'YAMLLINT_CONFIG_FILE' in os.environ:
user_global_config = os.path.expanduser(
os.environ['YAMLLINT_CONFIG_FILE'])
# User-global config is supposed to be in ~/.config/yamllint/config # User-global config is supposed to be in ~/.config/yamllint/config
elif 'XDG_CONFIG_HOME' in os.environ: if 'XDG_CONFIG_HOME' in os.environ:
user_global_config = os.path.join( user_global_config = os.path.join(
os.environ['XDG_CONFIG_HOME'], 'yamllint', 'config') os.environ['XDG_CONFIG_HOME'], 'yamllint', 'config')
else: else:
user_global_config = os.path.expanduser('~/.config/yamllint/config') user_global_config = os.path.expanduser('~/.config/yamllint/config')
project_config_filepath = find_project_config_filepath()
try: try:
if args.config_data is not None: if args.config_data is not None:
if args.config_data != '' and ':' not in args.config_data: if args.config_data != '' and ':' not in args.config_data:
@ -206,8 +158,12 @@ def run(argv=None):
conf = YamlLintConfig(content=args.config_data) conf = YamlLintConfig(content=args.config_data)
elif args.config_file is not None: elif args.config_file is not None:
conf = YamlLintConfig(file=args.config_file) conf = YamlLintConfig(file=args.config_file)
elif project_config_filepath: elif os.path.isfile('.yamllint'):
conf = YamlLintConfig(file=project_config_filepath) conf = YamlLintConfig(file='.yamllint')
elif os.path.isfile('.yamllint.yaml'):
conf = YamlLintConfig(file='.yamllint.yaml')
elif os.path.isfile('.yamllint.yml'):
conf = YamlLintConfig(file='.yamllint.yml')
elif os.path.isfile(user_global_config): elif os.path.isfile(user_global_config):
conf = YamlLintConfig(file=user_global_config) conf = YamlLintConfig(file=user_global_config)
else: else:
@ -216,23 +172,14 @@ def run(argv=None):
print(e, file=sys.stderr) print(e, file=sys.stderr)
sys.exit(-1) sys.exit(-1)
if conf.locale is not None:
locale.setlocale(locale.LC_ALL, conf.locale)
if args.list_files:
for file in find_files_recursively(args.files, conf):
if not conf.is_file_ignored(file):
print(file)
sys.exit(0)
max_level = 0 max_level = 0
for file in find_files_recursively(args.files, conf): for file in find_files_recursively(args.files, conf):
filepath = file[2:] if file.startswith('./') else file filepath = file[2:] if file.startswith('./') else file
try: try:
with open(file, newline='') as f: with io.open(file, newline='') as f:
problems = linter.run(f, conf, filepath) problems = linter.run(f, conf, filepath)
except OSError as e: except EnvironmentError as e:
print(e, file=sys.stderr) print(e, file=sys.stderr)
sys.exit(-1) sys.exit(-1)
prob_level = show_problems(problems, file, args_format=args.format, prob_level = show_problems(problems, file, args_format=args.format,
@ -243,7 +190,7 @@ def run(argv=None):
if args.stdin: if args.stdin:
try: try:
problems = linter.run(sys.stdin, conf, '') problems = linter.run(sys.stdin, conf, '')
except OSError as e: except EnvironmentError as e:
print(e, file=sys.stderr) print(e, file=sys.stderr)
sys.exit(-1) sys.exit(-1)
prob_level = show_problems(problems, 'stdin', args_format=args.format, prob_level = show_problems(problems, 'stdin', args_format=args.format,

@ -6,7 +6,6 @@ yaml-files:
- '.yamllint' - '.yamllint'
rules: rules:
anchors: enable
braces: enable braces: enable
brackets: enable brackets: enable
colons: enable colons: enable
@ -20,7 +19,6 @@ rules:
level: warning level: warning
empty-lines: enable empty-lines: enable
empty-values: disable empty-values: disable
float-values: disable
hyphens: enable hyphens: enable
indentation: enable indentation: enable
key-duplicates: enable key-duplicates: enable

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -13,7 +14,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import fileinput
import os.path import os.path
import pathspec import pathspec
@ -26,7 +26,7 @@ class YamlLintConfigError(Exception):
pass pass
class YamlLintConfig: class YamlLintConfig(object):
def __init__(self, content=None, file=None): def __init__(self, content=None, file=None):
assert (content is None) ^ (file is None) assert (content is None) ^ (file is None)
@ -35,8 +35,6 @@ class YamlLintConfig:
self.yaml_files = pathspec.PathSpec.from_lines( self.yaml_files = pathspec.PathSpec.from_lines(
'gitwildmatch', ['*.yaml', '*.yml', '.yamllint']) 'gitwildmatch', ['*.yaml', '*.yml', '.yamllint'])
self.locale = None
if file is not None: if file is not None:
with open(file) as f: with open(file) as f:
content = f.read() content = f.read()
@ -48,7 +46,7 @@ class YamlLintConfig:
return self.ignore and self.ignore.match_file(filepath) return self.ignore and self.ignore.match_file(filepath)
def is_yaml_file(self, filepath): def is_yaml_file(self, filepath):
return self.yaml_files.match_file(os.path.basename(filepath)) return self.yaml_files.match_file(filepath)
def enabled_rules(self, filepath): def enabled_rules(self, filepath):
return [yamllint.rules.get(id) for id, val in self.rules.items() return [yamllint.rules.get(id) for id, val in self.rules.items()
@ -97,31 +95,12 @@ class YamlLintConfig:
except Exception as e: except Exception as e:
raise YamlLintConfigError('invalid config: %s' % e) raise YamlLintConfigError('invalid config: %s' % e)
if 'ignore' in conf and 'ignore-from-file' in conf: if 'ignore' in conf:
raise YamlLintConfigError( if not isinstance(conf['ignore'], str):
'invalid config: ignore and ignore-from-file keys cannot be '
'used together')
elif 'ignore-from-file' in conf:
if isinstance(conf['ignore-from-file'], str):
conf['ignore-from-file'] = [conf['ignore-from-file']]
if not (isinstance(conf['ignore-from-file'], list) and all(
isinstance(ln, str) for ln in conf['ignore-from-file'])):
raise YamlLintConfigError(
'invalid config: ignore-from-file should contain '
'filename(s), either as a list or string')
with fileinput.input(conf['ignore-from-file']) as f:
self.ignore = pathspec.PathSpec.from_lines('gitwildmatch', f)
elif 'ignore' in conf:
if isinstance(conf['ignore'], str):
self.ignore = pathspec.PathSpec.from_lines(
'gitwildmatch', conf['ignore'].splitlines())
elif (isinstance(conf['ignore'], list) and
all(isinstance(line, str) for line in conf['ignore'])):
self.ignore = pathspec.PathSpec.from_lines(
'gitwildmatch', conf['ignore'])
else:
raise YamlLintConfigError( raise YamlLintConfigError(
'invalid config: ignore should contain file patterns') 'invalid config: ignore should contain file patterns')
self.ignore = pathspec.PathSpec.from_lines(
'gitwildmatch', conf['ignore'].splitlines())
if 'yaml-files' in conf: if 'yaml-files' in conf:
if not (isinstance(conf['yaml-files'], list) if not (isinstance(conf['yaml-files'], list)
@ -132,12 +111,6 @@ class YamlLintConfig:
self.yaml_files = pathspec.PathSpec.from_lines('gitwildmatch', self.yaml_files = pathspec.PathSpec.from_lines('gitwildmatch',
conf['yaml-files']) conf['yaml-files'])
if 'locale' in conf:
if not isinstance(conf['locale'], str):
raise YamlLintConfigError(
'invalid config: locale should be a string')
self.locale = conf['locale']
def validate(self): def validate(self):
for id in self.rules: for id in self.rules:
try: try:
@ -155,16 +128,11 @@ def validate_rule_conf(rule, conf):
if isinstance(conf, dict): if isinstance(conf, dict):
if ('ignore' in conf and if ('ignore' in conf and
not isinstance(conf['ignore'], pathspec.pathspec.PathSpec)): not isinstance(conf['ignore'], pathspec.pathspec.PathSpec)):
if isinstance(conf['ignore'], str): if not isinstance(conf['ignore'], str):
conf['ignore'] = pathspec.PathSpec.from_lines(
'gitwildmatch', conf['ignore'].splitlines())
elif (isinstance(conf['ignore'], list) and
all(isinstance(line, str) for line in conf['ignore'])):
conf['ignore'] = pathspec.PathSpec.from_lines(
'gitwildmatch', conf['ignore'])
else:
raise YamlLintConfigError( raise YamlLintConfigError(
'invalid config: ignore should contain file patterns') 'invalid config: ignore should contain file patterns')
conf['ignore'] = pathspec.PathSpec.from_lines(
'gitwildmatch', conf['ignore'].splitlines())
if 'level' not in conf: if 'level' not in conf:
conf['level'] = 'error' conf['level'] = 'error'
@ -175,7 +143,7 @@ def validate_rule_conf(rule, conf):
options = getattr(rule, 'CONF', {}) options = getattr(rule, 'CONF', {})
options_default = getattr(rule, 'DEFAULT', {}) options_default = getattr(rule, 'DEFAULT', {})
for optkey in conf: for optkey in conf:
if optkey in ('ignore', 'ignore-from-file', 'level'): if optkey in ('ignore', 'level'):
continue continue
if optkey not in options: if optkey not in options:
raise YamlLintConfigError( raise YamlLintConfigError(

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -14,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import re import re
import io
import yaml import yaml
@ -30,11 +30,8 @@ PROBLEM_LEVELS = {
'error': 2, 'error': 2,
} }
DISABLE_RULE_PATTERN = re.compile(r'^# yamllint disable( rule:\S+)*\s*$')
ENABLE_RULE_PATTERN = re.compile(r'^# yamllint enable( rule:\S+)*\s*$')
class LintProblem(object):
class LintProblem:
"""Represents a linting problem found by yamllint.""" """Represents a linting problem found by yamllint."""
def __init__(self, line, column, desc='<no description>', rule=None): def __init__(self, line, column, desc='<no description>', rule=None):
#: Line on which the problem was found (starting at 1) #: Line on which the problem was found (starting at 1)
@ -84,11 +81,13 @@ def get_cosmetic_problems(buffer, conf, filepath):
self.all_rules = {r.ID for r in rules} self.all_rules = {r.ID for r in rules}
def process_comment(self, comment): def process_comment(self, comment):
comment = str(comment) try:
comment = str(comment)
except UnicodeError:
return # this certainly wasn't a yamllint directive comment
if DISABLE_RULE_PATTERN.match(comment): if re.match(r'^# yamllint disable( rule:\S+)*\s*$', comment):
items = comment[18:].rstrip().split(' ') rules = [item[5:] for item in comment[18:].split(' ')][1:]
rules = [item[5:] for item in items][1:]
if len(rules) == 0: if len(rules) == 0:
self.rules = self.all_rules.copy() self.rules = self.all_rules.copy()
else: else:
@ -96,9 +95,8 @@ def get_cosmetic_problems(buffer, conf, filepath):
if id in self.all_rules: if id in self.all_rules:
self.rules.add(id) self.rules.add(id)
elif ENABLE_RULE_PATTERN.match(comment): elif re.match(r'^# yamllint enable( rule:\S+)*\s*$', comment):
items = comment[17:].rstrip().split(' ') rules = [item[5:] for item in comment[17:].split(' ')][1:]
rules = [item[5:] for item in items][1:]
if len(rules) == 0: if len(rules) == 0:
self.rules.clear() self.rules.clear()
else: else:
@ -110,11 +108,13 @@ def get_cosmetic_problems(buffer, conf, filepath):
class DisableLineDirective(DisableDirective): class DisableLineDirective(DisableDirective):
def process_comment(self, comment): def process_comment(self, comment):
comment = str(comment) try:
comment = str(comment)
except UnicodeError:
return # this certainly wasn't a yamllint directive comment
if re.match(r'^# yamllint disable-line( rule:\S+)*\s*$', comment): if re.match(r'^# yamllint disable-line( rule:\S+)*\s*$', comment):
items = comment[23:].rstrip().split(' ') rules = [item[5:] for item in comment[23:].split(' ')][1:]
rules = [item[5:] for item in items][1:]
if len(rules) == 0: if len(rules) == 0:
self.rules = self.all_rules.copy() self.rules = self.all_rules.copy()
else: else:
@ -122,7 +122,7 @@ def get_cosmetic_problems(buffer, conf, filepath):
if id in self.all_rules: if id in self.all_rules:
self.rules.add(id) self.rules.add(id)
# Use a cache to store problems and flush it only when an end of line is # Use a cache to store problems and flush it only when a end of line is
# found. This allows the use of yamllint directive to disable some rules on # found. This allows the use of yamllint directive to disable some rules on
# some lines. # some lines.
cache = [] cache = []
@ -203,11 +203,15 @@ def _run(buffer, conf, filepath):
syntax_error.column <= problem.column): syntax_error.column <= problem.column):
yield syntax_error yield syntax_error
# Discard the problem since it is at the same place as the syntax # If there is already a yamllint error at the same place, discard
# error and is probably redundant (and maybe it's just a 'warning', # it as it is probably redundant (and maybe it's just a 'warning',
# in which case the script won't even exit with a failure status). # in which case the script won't even exit with a failure status).
if (syntax_error.line == problem.line and
syntax_error.column == problem.column):
syntax_error = None
continue
syntax_error = None syntax_error = None
continue
yield problem yield problem
@ -223,12 +227,12 @@ def run(input, conf, filepath=None):
:param input: buffer, string or stream to read from :param input: buffer, string or stream to read from
:param conf: yamllint configuration object :param conf: yamllint configuration object
""" """
if filepath is not None and conf.is_file_ignored(filepath): if conf.is_file_ignored(filepath):
return () return ()
if isinstance(input, (bytes, str)): if isinstance(input, (type(b''), type(u''))): # compat with Python 2 & 3
return _run(input, conf, filepath) return _run(input, conf, filepath)
elif isinstance(input, io.IOBase): elif hasattr(input, 'read'): # Python 2's file or Python 3's io.IOBase
# We need to have everything in memory to parse correctly # We need to have everything in memory to parse correctly
content = input.read() content = input.read()
return _run(content, conf, filepath) return _run(content, conf, filepath)

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -16,7 +17,7 @@
import yaml import yaml
class Line: class Line(object):
def __init__(self, line_no, buffer, start, end): def __init__(self, line_no, buffer, start, end):
self.line_no = line_no self.line_no = line_no
self.start = start self.start = start
@ -28,7 +29,7 @@ class Line:
return self.buffer[self.start:self.end] return self.buffer[self.start:self.end]
class Token: class Token(object):
def __init__(self, line_no, curr, prev, next, nextnext): def __init__(self, line_no, curr, prev, next, nextnext):
self.line_no = line_no self.line_no = line_no
self.curr = curr self.curr = curr
@ -37,7 +38,7 @@ class Token:
self.nextnext = nextnext self.nextnext = nextnext
class Comment: class Comment(object):
def __init__(self, line_no, column_no, buffer, pointer, def __init__(self, line_no, column_no, buffer, pointer,
token_before=None, token_after=None, comment_before=None): token_before=None, token_after=None, comment_before=None):
self.line_no = line_no self.line_no = line_no
@ -132,7 +133,8 @@ def token_or_comment_generator(buffer):
yield Token(curr.start_mark.line + 1, curr, prev, next, nextnext) yield Token(curr.start_mark.line + 1, curr, prev, next, nextnext)
yield from comments_between_tokens(curr, next) for comment in comments_between_tokens(curr, next):
yield comment
prev = curr prev = curr
curr = next curr = next

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -14,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from yamllint.rules import ( from yamllint.rules import (
anchors,
braces, braces,
brackets, brackets,
colons, colons,
@ -33,14 +33,12 @@ from yamllint.rules import (
new_line_at_end_of_file, new_line_at_end_of_file,
new_lines, new_lines,
octal_values, octal_values,
float_values,
quoted_strings, quoted_strings,
trailing_spaces, trailing_spaces,
truthy, truthy,
) )
_RULES = { _RULES = {
anchors.ID: anchors,
braces.ID: braces, braces.ID: braces,
brackets.ID: brackets, brackets.ID: brackets,
colons.ID: colons, colons.ID: colons,
@ -51,7 +49,6 @@ _RULES = {
document_start.ID: document_start, document_start.ID: document_start,
empty_lines.ID: empty_lines, empty_lines.ID: empty_lines,
empty_values.ID: empty_values, empty_values.ID: empty_values,
float_values.ID: float_values,
hyphens.ID: hyphens, hyphens.ID: hyphens,
indentation.ID: indentation, indentation.ID: indentation,
key_duplicates.ID: key_duplicates, key_duplicates.ID: key_duplicates,

@ -1,174 +0,0 @@
# Copyright (C) 2023 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/>.
"""
Use this rule to report duplicated anchors and aliases referencing undeclared
anchors.
.. rubric:: Options
* Set ``forbid-undeclared-aliases`` to ``true`` to avoid aliases that reference
an anchor that hasn't been declared (either not declared at all, or declared
later in the document).
* Set ``forbid-duplicated-anchors`` to ``true`` to avoid duplications of a same
anchor.
* Set ``forbid-unused-anchors`` to ``true`` to avoid anchors being declared but
not used anywhere in the YAML document via alias.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
anchors:
forbid-undeclared-aliases: true
forbid-duplicated-anchors: false
forbid-unused-anchors: false
.. rubric:: Examples
#. With ``anchors: {forbid-undeclared-aliases: true}``
the following code snippet would **PASS**:
::
---
- &anchor
foo: bar
- *anchor
the following code snippet would **FAIL**:
::
---
- &anchor
foo: bar
- *unknown
the following code snippet would **FAIL**:
::
---
- &anchor
foo: bar
- <<: *unknown
extra: value
#. With ``anchors: {forbid-duplicated-anchors: true}``
the following code snippet would **PASS**:
::
---
- &anchor1 Foo Bar
- &anchor2 [item 1, item 2]
the following code snippet would **FAIL**:
::
---
- &anchor Foo Bar
- &anchor [item 1, item 2]
#. With ``anchors: {forbid-unused-anchors: true}``
the following code snippet would **PASS**:
::
---
- &anchor
foo: bar
- *anchor
the following code snippet would **FAIL**:
::
---
- &anchor
foo: bar
- items:
- item1
- item2
"""
import yaml
from yamllint.linter import LintProblem
ID = 'anchors'
TYPE = 'token'
CONF = {'forbid-undeclared-aliases': bool,
'forbid-duplicated-anchors': bool,
'forbid-unused-anchors': bool}
DEFAULT = {'forbid-undeclared-aliases': True,
'forbid-duplicated-anchors': False,
'forbid-unused-anchors': False}
def check(conf, token, prev, next, nextnext, context):
if (conf['forbid-undeclared-aliases'] or
conf['forbid-duplicated-anchors'] or
conf['forbid-unused-anchors']):
if isinstance(token, (
yaml.StreamStartToken,
yaml.DocumentStartToken,
yaml.DocumentEndToken)):
context['anchors'] = {}
if (conf['forbid-undeclared-aliases'] and
isinstance(token, yaml.AliasToken) and
token.value not in context['anchors']):
yield LintProblem(
token.start_mark.line + 1, token.start_mark.column + 1,
f'found undeclared alias "{token.value}"')
if (conf['forbid-duplicated-anchors'] and
isinstance(token, yaml.AnchorToken) and
token.value in context['anchors']):
yield LintProblem(
token.start_mark.line + 1, token.start_mark.column + 1,
f'found duplicated anchor "{token.value}"')
if conf['forbid-unused-anchors']:
# Unused anchors can only be detected at the end of Document.
# End of document can be either
# - end of stream
# - end of document sign '...'
# - start of a new document sign '---'
# If next token indicates end of document,
# check if the anchors have been used or not.
# If they haven't been used, report problem on those anchors.
if isinstance(next, (yaml.StreamEndToken,
yaml.DocumentStartToken,
yaml.DocumentEndToken)):
for anchor, info in context['anchors'].items():
if not info['used']:
yield LintProblem(info['line'] + 1,
info['column'] + 1,
f'found unused anchor "{anchor}"')
elif isinstance(token, yaml.AliasToken):
context['anchors'].get(token.value, {})['used'] = True
if (conf['forbid-undeclared-aliases'] or
conf['forbid-duplicated-anchors'] or
conf['forbid-unused-anchors']):
if isinstance(token, yaml.AnchorToken):
context['anchors'][token.value] = {
'line': token.start_mark.line,
'column': token.start_mark.column,
'used': False
}

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -14,15 +15,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
Use this rule to control the use of flow mappings or number of spaces inside Use this rule to control the number of spaces inside braces (``{`` and ``}``).
braces (``{`` and ``}``).
.. rubric:: Options .. rubric:: Options
* ``forbid`` is used to forbid the use of flow mappings which are denoted by
surrounding braces (``{`` and ``}``). Use ``true`` to forbid the use of flow
mappings completely. Use ``non-empty`` to forbid the use of all flow
mappings except for empty ones.
* ``min-spaces-inside`` defines the minimal number of spaces required inside * ``min-spaces-inside`` defines the minimal number of spaces required inside
braces. braces.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside * ``max-spaces-inside`` defines the maximal number of spaces allowed inside
@ -32,46 +28,8 @@ braces (``{`` and ``}``).
* ``max-spaces-inside-empty`` defines the maximal number of spaces allowed * ``max-spaces-inside-empty`` defines the maximal number of spaces allowed
inside empty braces. inside empty braces.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
braces:
forbid: false
min-spaces-inside: 0
max-spaces-inside: 0
min-spaces-inside-empty: -1
max-spaces-inside-empty: -1
.. rubric:: Examples .. rubric:: Examples
#. With ``braces: {forbid: true}``
the following code snippet would **PASS**:
::
object:
key1: 4
key2: 8
the following code snippet would **FAIL**:
::
object: { key1: 4, key2: 8 }
#. With ``braces: {forbid: non-empty}``
the following code snippet would **PASS**:
::
object: {}
the following code snippet would **FAIL**:
::
object: { key1: 4, key2: 8 }
#. With ``braces: {min-spaces-inside: 0, max-spaces-inside: 0}`` #. With ``braces: {min-spaces-inside: 0, max-spaces-inside: 0}``
the following code snippet would **PASS**: the following code snippet would **PASS**:
@ -134,39 +92,23 @@ braces (``{`` and ``}``).
import yaml import yaml
from yamllint.linter import LintProblem
from yamllint.rules.common import spaces_after, spaces_before from yamllint.rules.common import spaces_after, spaces_before
ID = 'braces' ID = 'braces'
TYPE = 'token' TYPE = 'token'
CONF = {'forbid': (bool, 'non-empty'), CONF = {'min-spaces-inside': int,
'min-spaces-inside': int,
'max-spaces-inside': int, 'max-spaces-inside': int,
'min-spaces-inside-empty': int, 'min-spaces-inside-empty': int,
'max-spaces-inside-empty': int} 'max-spaces-inside-empty': int}
DEFAULT = {'forbid': False, DEFAULT = {'min-spaces-inside': 0,
'min-spaces-inside': 0,
'max-spaces-inside': 0, 'max-spaces-inside': 0,
'min-spaces-inside-empty': -1, 'min-spaces-inside-empty': -1,
'max-spaces-inside-empty': -1} 'max-spaces-inside-empty': -1}
def check(conf, token, prev, next, nextnext, context): def check(conf, token, prev, next, nextnext, context):
if (conf['forbid'] is True and if (isinstance(token, yaml.FlowMappingStartToken) and
isinstance(token, yaml.FlowMappingStartToken)):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow mapping')
elif (conf['forbid'] == 'non-empty' and
isinstance(token, yaml.FlowMappingStartToken) and
not isinstance(next, yaml.FlowMappingEndToken)):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow mapping')
elif (isinstance(token, yaml.FlowMappingStartToken) and
isinstance(next, yaml.FlowMappingEndToken)): isinstance(next, yaml.FlowMappingEndToken)):
problem = spaces_after(token, prev, next, problem = spaces_after(token, prev, next,
min=(conf['min-spaces-inside-empty'] min=(conf['min-spaces-inside-empty']

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -14,15 +15,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
Use this rule to control the use of flow sequences or the number of spaces Use this rule to control the number of spaces inside brackets (``[`` and
inside brackets (``[`` and ``]``). ``]``).
.. rubric:: Options .. rubric:: Options
* ``forbid`` is used to forbid the use of flow sequences which are denoted by
surrounding brackets (``[`` and ``]``). Use ``true`` to forbid the use of
flow sequences completely. Use ``non-empty`` to forbid the use of all flow
sequences except for empty ones.
* ``min-spaces-inside`` defines the minimal number of spaces required inside * ``min-spaces-inside`` defines the minimal number of spaces required inside
brackets. brackets.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside * ``max-spaces-inside`` defines the maximal number of spaces allowed inside
@ -32,47 +29,8 @@ inside brackets (``[`` and ``]``).
* ``max-spaces-inside-empty`` defines the maximal number of spaces allowed * ``max-spaces-inside-empty`` defines the maximal number of spaces allowed
inside empty brackets. inside empty brackets.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
brackets:
forbid: false
min-spaces-inside: 0
max-spaces-inside: 0
min-spaces-inside-empty: -1
max-spaces-inside-empty: -1
.. rubric:: Examples .. rubric:: Examples
#. With ``brackets: {forbid: true}``
the following code snippet would **PASS**:
::
object:
- 1
- 2
- abc
the following code snippet would **FAIL**:
::
object: [ 1, 2, abc ]
#. With ``brackets: {forbid: non-empty}``
the following code snippet would **PASS**:
::
object: []
the following code snippet would **FAIL**:
::
object: [ 1, 2, abc ]
#. With ``brackets: {min-spaces-inside: 0, max-spaces-inside: 0}`` #. With ``brackets: {min-spaces-inside: 0, max-spaces-inside: 0}``
the following code snippet would **PASS**: the following code snippet would **PASS**:
@ -135,39 +93,23 @@ inside brackets (``[`` and ``]``).
import yaml import yaml
from yamllint.linter import LintProblem
from yamllint.rules.common import spaces_after, spaces_before from yamllint.rules.common import spaces_after, spaces_before
ID = 'brackets' ID = 'brackets'
TYPE = 'token' TYPE = 'token'
CONF = {'forbid': (bool, 'non-empty'), CONF = {'min-spaces-inside': int,
'min-spaces-inside': int,
'max-spaces-inside': int, 'max-spaces-inside': int,
'min-spaces-inside-empty': int, 'min-spaces-inside-empty': int,
'max-spaces-inside-empty': int} 'max-spaces-inside-empty': int}
DEFAULT = {'forbid': False, DEFAULT = {'min-spaces-inside': 0,
'min-spaces-inside': 0,
'max-spaces-inside': 0, 'max-spaces-inside': 0,
'min-spaces-inside-empty': -1, 'min-spaces-inside-empty': -1,
'max-spaces-inside-empty': -1} 'max-spaces-inside-empty': -1}
def check(conf, token, prev, next, nextnext, context): def check(conf, token, prev, next, nextnext, context):
if (conf['forbid'] is True and if (isinstance(token, yaml.FlowSequenceStartToken) and
isinstance(token, yaml.FlowSequenceStartToken)):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow sequence')
elif (conf['forbid'] == 'non-empty' and
isinstance(token, yaml.FlowSequenceStartToken) and
not isinstance(next, yaml.FlowSequenceEndToken)):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow sequence')
elif (isinstance(token, yaml.FlowSequenceStartToken) and
isinstance(next, yaml.FlowSequenceEndToken)): isinstance(next, yaml.FlowSequenceEndToken)):
problem = spaces_after(token, prev, next, problem = spaces_after(token, prev, next,
min=(conf['min-spaces-inside-empty'] min=(conf['min-spaces-inside-empty']

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -23,15 +24,6 @@ Use this rule to control the number of spaces before and after colons (``:``).
* ``max-spaces-after`` defines the maximal number of spaces allowed after * ``max-spaces-after`` defines the maximal number of spaces allowed after
colons (use ``-1`` to disable). colons (use ``-1`` to disable).
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
colons:
max-spaces-before: 0
max-spaces-after: 1
.. rubric:: Examples .. rubric:: Examples
#. With ``colons: {max-spaces-before: 0, max-spaces-after: 1}`` #. With ``colons: {max-spaces-before: 0, max-spaces-after: 1}``
@ -92,9 +84,7 @@ DEFAULT = {'max-spaces-before': 0,
def check(conf, token, prev, next, nextnext, context): def check(conf, token, prev, next, nextnext, context):
if isinstance(token, yaml.ValueToken) and not ( if isinstance(token, yaml.ValueToken):
isinstance(prev, yaml.AliasToken) and
token.start_mark.pointer - prev.end_mark.pointer == 1):
problem = spaces_before(token, prev, next, problem = spaces_before(token, prev, next,
max=conf['max-spaces-before'], max=conf['max-spaces-before'],
max_desc='too many spaces before colon') max_desc='too many spaces before colon')

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -25,16 +26,6 @@ Use this rule to control the number of spaces before and after commas (``,``).
* ``max-spaces-after`` defines the maximal number of spaces allowed after * ``max-spaces-after`` defines the maximal number of spaces allowed after
commas (use ``-1`` to disable). commas (use ``-1`` to disable).
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
commas:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
.. rubric:: Examples .. rubric:: Examples
#. With ``commas: {max-spaces-before: 0}`` #. With ``commas: {max-spaces-before: 0}``
@ -75,7 +66,7 @@ Use this rule to control the number of spaces before and after commas (``,``).
:: ::
strange var: strange var:
[10, 20, 30, {x: 1, y: 2}] [10, 20,30, {x: 1, y: 2}]
the following code snippet would **FAIL**: the following code snippet would **FAIL**:
:: ::

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -27,16 +28,6 @@ Use this rule to control the position and formatting of comments.
content. It defines the minimal required number of spaces between a comment content. It defines the minimal required number of spaces between a comment
and its preceding content. and its preceding content.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
comments:
require-starting-space: true
ignore-shebangs: true
min-spaces-from-content: 2
.. rubric:: Examples .. rubric:: Examples
#. With ``comments: {require-starting-space: true}`` #. With ``comments: {require-starting-space: true}``
@ -73,6 +64,8 @@ Use this rule to control the position and formatting of comments.
""" """
import re
from yamllint.linter import LintProblem from yamllint.linter import LintProblem
@ -102,11 +95,9 @@ def check(conf, comment):
if (conf['ignore-shebangs'] and if (conf['ignore-shebangs'] and
comment.line_no == 1 and comment.line_no == 1 and
comment.column_no == 1 and comment.column_no == 1 and
comment.buffer[text_start] == '!'): re.match(r'^!\S', comment.buffer[text_start:])):
return return
# We can test for both \r and \r\n just by checking first char elif comment.buffer[text_start] not in (' ', '\n', '\0'):
# \r itself is a valid newline on some older OS.
elif comment.buffer[text_start] not in {' ', '\n', '\r', '\x00'}:
column = comment.column_no + text_start - comment.pointer column = comment.column_no + text_start - comment.pointer
yield LintProblem(comment.line_no, yield LintProblem(comment.line_no,
column, column,

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -117,7 +118,8 @@ def check(conf, comment):
# # comment # # comment
# - 1 # - 1
# - 2 # - 2
prev_line_indent = max(prev_line_indent, next_line_indent) if prev_line_indent <= next_line_indent:
prev_line_indent = next_line_indent
# If two indents are valid but a previous comment went back to normal # If two indents are valid but a previous comment went back to normal
# indent, for the next ones to do the same. In other words, avoid this: # indent, for the next ones to do the same. In other words, avoid this:

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -21,14 +22,6 @@ Use this rule to require or forbid the use of document end marker (``...``).
* Set ``present`` to ``true`` when the document end marker is required, or to * Set ``present`` to ``true`` when the document end marker is required, or to
``false`` when it is forbidden. ``false`` when it is forbidden.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
document-end:
present: true
.. rubric:: Examples .. rubric:: Examples
#. With ``document-end: {present: true}`` #. With ``document-end: {present: true}``
@ -99,13 +92,11 @@ def check(conf, token, prev, next, nextnext, context):
prev_is_end_or_stream_start = isinstance( prev_is_end_or_stream_start = isinstance(
prev, (yaml.DocumentEndToken, yaml.StreamStartToken) prev, (yaml.DocumentEndToken, yaml.StreamStartToken)
) )
prev_is_directive = isinstance(prev, yaml.DirectiveToken)
if is_stream_end and not prev_is_end_or_stream_start: if is_stream_end and not prev_is_end_or_stream_start:
yield LintProblem(token.start_mark.line, 1, yield LintProblem(token.start_mark.line, 1,
'missing document end "..."') 'missing document end "..."')
elif is_start and not (prev_is_end_or_stream_start elif is_start and not prev_is_end_or_stream_start:
or prev_is_directive):
yield LintProblem(token.start_mark.line + 1, 1, yield LintProblem(token.start_mark.line + 1, 1,
'missing document end "..."') 'missing document end "..."')

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -21,14 +22,6 @@ Use this rule to require or forbid the use of document start marker (``---``).
* Set ``present`` to ``true`` when the document start marker is required, or to * Set ``present`` to ``true`` when the document start marker is required, or to
``false`` when it is forbidden. ``false`` when it is forbidden.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
document-start:
present: true
.. rubric:: Examples .. rubric:: Examples
#. With ``document-start: {present: true}`` #. With ``document-start: {present: true}``

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -24,16 +25,6 @@ Use this rule to set a maximal number of allowed consecutive blank lines.
* ``max-end`` defines the maximal number of empty lines allowed at the end of * ``max-end`` defines the maximal number of empty lines allowed at the end of
the file. This option takes precedence over ``max``. the file. This option takes precedence over ``max``.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
empty-lines:
max: 2
max-start: 0
max-end: 0
.. rubric:: Examples .. rubric:: Examples
#. With ``empty-lines: {max: 1}`` #. With ``empty-lines: {max: 1}``

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 Greg Dubicki # Copyright (C) 2017 Greg Dubicki
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -22,15 +23,6 @@ Use this rule to prevent nodes with empty content, that implicitly result in
* Use ``forbid-in-block-mappings`` to prevent empty values in block mappings. * Use ``forbid-in-block-mappings`` to prevent empty values in block mappings.
* Use ``forbid-in-flow-mappings`` to prevent empty values in flow mappings. * Use ``forbid-in-flow-mappings`` to prevent empty values in flow mappings.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
empty-values:
forbid-in-block-mappings: true
forbid-in-flow-mappings: true
.. rubric:: Examples .. rubric:: Examples
#. With ``empty-values: {forbid-in-block-mappings: true}`` #. With ``empty-values: {forbid-in-block-mappings: true}``

@ -1,158 +0,0 @@
# Copyright (C) 2022 the yamllint contributors
# 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 limit the permitted values for floating-point numbers.
YAML permits three classes of float expressions: approximation to real numbers,
positive and negative infinity and "not a number".
.. rubric:: Options
* Use ``require-numeral-before-decimal`` to require floats to start
with a numeral (ex ``0.0`` instead of ``.0``).
* Use ``forbid-scientific-notation`` to forbid scientific notation.
* Use ``forbid-nan`` to forbid NaN (not a number) values.
* Use ``forbid-inf`` to forbid infinite values.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
float-values:
forbid-inf: false
forbid-nan: false
forbid-scientific-notation: false
require-numeral-before-decimal: false
.. rubric:: Examples
#. With ``float-values: {require-numeral-before-decimal: true}``
the following code snippets would **PASS**:
::
anemometer:
angle: 0.0
the following code snippets would **FAIL**:
::
anemometer:
angle: .0
#. With ``float-values: {forbid-scientific-notation: true}``
the following code snippets would **PASS**:
::
anemometer:
angle: 0.00001
the following code snippets would **FAIL**:
::
anemometer:
angle: 10e-6
#. With ``float-values: {forbid-nan: true}``
the following code snippets would **FAIL**:
::
anemometer:
angle: .NaN
#. With ``float-values: {forbid-inf: true}``
the following code snippets would **FAIL**:
::
anemometer:
angle: .inf
"""
import re
import yaml
from yamllint.linter import LintProblem
ID = 'float-values'
TYPE = 'token'
CONF = {
'require-numeral-before-decimal': bool,
'forbid-scientific-notation': bool,
'forbid-nan': bool,
'forbid-inf': bool,
}
DEFAULT = {
'require-numeral-before-decimal': False,
'forbid-scientific-notation': False,
'forbid-nan': False,
'forbid-inf': False,
}
IS_NUMERAL_BEFORE_DECIMAL_PATTERN = (
re.compile('[-+]?(\\.[0-9]+)([eE][-+]?[0-9]+)?$')
)
IS_SCIENTIFIC_NOTATION_PATTERN = re.compile(
'[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)$'
)
IS_INF_PATTERN = re.compile('[-+]?(\\.inf|\\.Inf|\\.INF)$')
IS_NAN_PATTERN = re.compile('(\\.nan|\\.NaN|\\.NAN)$')
def check(conf, token, prev, next, nextnext, context):
if prev and isinstance(prev, yaml.tokens.TagToken):
return
if not isinstance(token, yaml.tokens.ScalarToken):
return
if token.style:
return
val = token.value
if conf['forbid-nan'] and IS_NAN_PATTERN.match(val):
yield LintProblem(
token.start_mark.line + 1,
token.start_mark.column + 1,
f'forbidden not a number value "{token.value}"',
)
if conf['forbid-inf'] and IS_INF_PATTERN.match(val):
yield LintProblem(
token.start_mark.line + 1,
token.start_mark.column + 1,
f'forbidden infinite value "{token.value}"',
)
if conf[
'forbid-scientific-notation'
] and IS_SCIENTIFIC_NOTATION_PATTERN.match(val):
yield LintProblem(
token.start_mark.line + 1,
token.start_mark.column + 1,
f'forbidden scientific notation "{token.value}"',
)
if conf[
'require-numeral-before-decimal'
] and IS_NUMERAL_BEFORE_DECIMAL_PATTERN.match(val):
yield LintProblem(
token.start_mark.line + 1,
token.start_mark.column + 1,
f'forbidden decimal missing 0 prefix "{token.value}"',
)

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé # Copyright (C) 2016 Adrien Vergé
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -21,14 +22,6 @@ Use this rule to control the number of spaces after hyphens (``-``).
* ``max-spaces-after`` defines the maximal number of spaces allowed after * ``max-spaces-after`` defines the maximal number of spaces allowed after
hyphens. hyphens.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
hyphens:
max-spaces-after: 1
.. rubric:: Examples .. rubric:: Examples
#. With ``hyphens: {max-spaces-after: 1}`` #. With ``hyphens: {max-spaces-after: 1}``

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save