Compare commits

..

No commits in common. 'master' and 'v1.29.0' have entirely different histories.

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

@ -21,15 +21,14 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
- run: - run:
pip install flake8 flake8-import-order sphinx rstcheck[sphinx] doc8 python -m pip install flake8 flake8-import-order sphinx
- run: pip install . rstcheck[sphinx] doc8
- run: python -m pip install .
- run: flake8 . - run: flake8 .
- run: doc8 $(git ls-files '*.rst') - run: doc8 $(git ls-files '*.rst')
- run: rstcheck --ignore-directives automodule $(git ls-files '*.rst') - run: rstcheck --ignore-directives automodule $(git ls-files '*.rst')
- run: yamllint --strict $(git ls-files '*.yaml' '*.yml') - run: yamllint --strict $(git ls-files '*.yaml' '*.yml')
- run: make -C docs html - run: python setup.py build_sphinx
- name: Check for broken links in documentation
run: make -C docs linkcheck
test: test:
name: Tests name: Tests
@ -52,10 +51,8 @@ jobs:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Append GitHub Actions system path - name: Append GitHub Actions system path
run: echo "$HOME/.local/bin" >> $GITHUB_PATH run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- run: pip install coverage - run: pip install coveralls
- run: pip install . - run: pip install .
# https://github.com/AndreMiras/coveralls-python-action/issues/18 - run: coverage run --source=yamllint -m unittest discover
- run: echo -e "[run]\nrelative_files = True" > .coveragerc
- run: coverage run -m unittest discover
- name: Coveralls - name: Coveralls
uses: AndreMiras/coveralls-python-action@develop uses: AndreMiras/coveralls-python-action@develop

@ -1,28 +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) 1.29.0 (2023-01-10)
------------------- -------------------

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

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

@ -15,8 +15,7 @@ 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 - a file named ``.yamllint``, ``.yamllint.yaml``, or ``.yamllint.yml`` in the
current working directory, or a parent directory (the search for this file is current working directory
terminated at the user's home or filesystem root)
- a filename referenced by ``$YAMLLINT_CONFIG_FILE``, if set - a filename referenced by ``$YAMLLINT_CONFIG_FILE``, if set
- a file named ``$XDG_CONFIG_HOME/yamllint/config`` or - a file named ``$XDG_CONFIG_HOME/yamllint/config`` or
``~/.config/yamllint/config``, if present ``~/.config/yamllint/config``, if present
@ -191,8 +190,8 @@ or ignore paths only for specific rules:
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

@ -11,7 +11,7 @@ Basic example of running the linter from Python:
import yamllint import yamllint
yaml_config = yamllint.config.YamlLintConfig("extends: default") yaml_config = yamllint.config.YamlLintConfig("extends: default")
for p in yamllint.linter.run(open("example.yaml", "r"), yaml_config): for p in yamllint.linter.run("example.yaml", yaml_config):
print(p.desc, p.line, p.rule) print(p.desc, p.line, p.rule)
.. automodule:: yamllint.linter .. automodule:: yamllint.linter

@ -117,7 +117,7 @@ post-template processing).
Example of a Jinja2 code that cannot be parsed as YAML because it contains Example of a Jinja2 code that cannot be parsed as YAML because it contains
invalid tokens ``{%`` and ``%}``: invalid tokens ``{%`` and ``%}``:
.. code-block:: text .. code-block::
# This file IS NOT valid YAML and will produce syntax errors # This file IS NOT valid YAML and will produce syntax errors
{% if extra_info %} {% if extra_info %}

@ -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,13 +12,11 @@ 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 Integration with GitHub Actions
------------------------------- -------------------------------

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

@ -13,7 +13,7 @@ Assuming that the `ALE <https://github.com/dense-analysis/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.

@ -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,69 @@
[flake8]
import-order-style = pep8
application-import-names = yamllint
ignore = W503,W504
[build_sphinx]
all-files = 1
source-dir = docs
build-dir = docs/_build
warning-is-error = 1
[metadata]
keywords =
yaml
lint
linter
syntax
checker
url = https://github.com/adrienverge/yamllint
classifiers =
Development Status :: 5 - Production/Stable
Environment :: Console
Intended Audience :: Developers
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Programming Language :: Python :: 3
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Topic :: Software Development
Topic :: Software Development :: Debuggers
Topic :: Software Development :: Quality Assurance
Topic :: Software Development :: Testing
project_urls =
Documentation = https://yamllint.readthedocs.io
Download = https://pypi.org/project/yamllint/#files
Bug Tracker = https://github.com/adrienverge/yamllint/issues
Source Code = https://github.com/adrienverge/yamllint
[options]
packages = find:
python_requires = >=3.7
include_package_data = True
install_requires =
pathspec >= 0.5.3
pyyaml
setuptools
test_suite = tests
[options.packages.find]
exclude =
tests
tests.*
[options.package_data]
yamllint = conf/*.yaml
[options.entry_points]
console_scripts =
yamllint = yamllint.cli:run
[coverage:run]
relative_files = True

@ -15,6 +15,15 @@
from setuptools import setup from setuptools import 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__,
)

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

@ -256,19 +256,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))

@ -71,22 +71,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)

@ -734,64 +734,3 @@ class CommandLineConfigTestCase(unittest.TestCase):
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr),
(0, '', '')) (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', ''))

@ -21,7 +21,7 @@ indentation, etc."""
APP_NAME = 'yamllint' APP_NAME = 'yamllint'
APP_VERSION = '1.32.0' APP_VERSION = '1.29.0'
APP_DESCRIPTION = __doc__ APP_DESCRIPTION = __doc__
__author__ = 'Adrien Vergé' __author__ = 'Adrien Vergé'

@ -141,19 +141,6 @@ def show_problems(problems, file, args_format, no_warn):
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)
@ -198,7 +185,6 @@ def run(argv=None):
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 +192,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:

@ -6,7 +6,6 @@ yaml-files:
- '.yamllint' - '.yamllint'
rules: rules:
anchors: enable
braces: enable braces: enable
brackets: enable brackets: enable
colons: enable colons: enable

@ -223,7 +223,7 @@ 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, (bytes, str)):

@ -14,7 +14,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,
@ -40,7 +39,6 @@ from yamllint.rules import (
) )
_RULES = { _RULES = {
anchors.ID: anchors,
braces.ID: braces, braces.ID: braces,
brackets.ID: brackets, brackets.ID: brackets,
colons.ID: colons, colons.ID: colons,

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

@ -92,9 +92,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')

@ -99,13 +99,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 "..."')

@ -17,7 +17,7 @@
Use this rule to require a new line character (``\\n``) at the end of files. Use this rule to require a new line character (``\\n``) at the end of files.
The POSIX standard `requires the last line to end with a new line character The POSIX standard `requires the last line to end with a new line character
<https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206>`_. <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206>`_.
All UNIX tools expect a new line at the end of files. Most text editors use All UNIX tools expect a new line at the end of files. Most text editors use
this convention too. this convention too.
""" """

Loading…
Cancel
Save