Compare commits

..

No commits in common. 'master' and 'v1.30.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,19 +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) 1.30.0 (2023-03-22)
------------------- -------------------

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

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

@ -46,7 +46,7 @@ class AnchorsTestCase(RuleTestCase):
' <<: *b_m\n' ' <<: *b_m\n'
' foo: bar\n' ' foo: bar\n'
'---\n' '---\n'
'{a: 1, &x b: 2, c: &y 3, *x : 4, e: *y}\n' '{a: 1, &x b: 2, c: &y 3, *x: 4, e: *y}\n'
'...\n', conf) '...\n', conf)
self.check('---\n' self.check('---\n'
'- &i 42\n' '- &i 42\n'
@ -74,14 +74,13 @@ class AnchorsTestCase(RuleTestCase):
' <<: *b_m\n' ' <<: *b_m\n'
' foo: bar\n' ' foo: bar\n'
'---\n' '---\n'
'{a: 1, &x b: 2, c: &x 3, *x : 4, e: *y}\n' '{a: 1, &x b: 2, c: &x 3, *x: 4, e: *y}\n'
'...\n', conf) '...\n', conf)
def test_forbid_undeclared_aliases(self): def test_forbid_undeclared_aliases(self):
conf = ('anchors:\n' conf = ('anchors:\n'
' forbid-undeclared-aliases: true\n' ' forbid-undeclared-aliases: true\n'
' forbid-duplicated-anchors: false\n' ' forbid-duplicated-anchors: false\n')
' forbid-unused-anchors: false\n')
self.check('---\n' self.check('---\n'
'- &b true\n' '- &b true\n'
'- &i 42\n' '- &i 42\n'
@ -107,7 +106,7 @@ class AnchorsTestCase(RuleTestCase):
' <<: *b_m\n' ' <<: *b_m\n'
' foo: bar\n' ' foo: bar\n'
'---\n' '---\n'
'{a: 1, &x b: 2, c: &y 3, *x : 4, e: *y}\n' '{a: 1, &x b: 2, c: &y 3, *x: 4, e: *y}\n'
'...\n', conf) '...\n', conf)
self.check('---\n' self.check('---\n'
'- &i 42\n' '- &i 42\n'
@ -123,7 +122,6 @@ class AnchorsTestCase(RuleTestCase):
'- *f_m\n' '- *f_m\n'
'- *f_s\n' # declared after '- *f_s\n' # declared after
'- &f_s [1, 2]\n' '- &f_s [1, 2]\n'
'...\n'
'---\n' '---\n'
'block mapping: &b_m\n' 'block mapping: &b_m\n'
' key: value\n' ' key: value\n'
@ -136,21 +134,20 @@ class AnchorsTestCase(RuleTestCase):
' <<: *b_m\n' ' <<: *b_m\n'
' foo: bar\n' ' foo: bar\n'
'---\n' '---\n'
'{a: 1, &x b: 2, c: &x 3, *x : 4, e: *y}\n' '{a: 1, &x b: 2, c: &x 3, *x: 4, e: *y}\n'
'...\n', conf, '...\n', conf,
problem1=(9, 3), problem1=(9, 3),
problem2=(10, 3), problem2=(10, 3),
problem3=(11, 3), problem3=(11, 3),
problem4=(12, 3), problem4=(12, 3),
problem5=(13, 3), problem5=(13, 3),
problem6=(25, 7), problem6=(24, 7),
problem7=(28, 37)) problem7=(27, 36))
def test_forbid_duplicated_anchors(self): def test_forbid_duplicated_anchors(self):
conf = ('anchors:\n' conf = ('anchors:\n'
' forbid-undeclared-aliases: false\n' ' forbid-undeclared-aliases: false\n'
' forbid-duplicated-anchors: true\n' ' forbid-duplicated-anchors: true\n')
' forbid-unused-anchors: false\n')
self.check('---\n' self.check('---\n'
'- &b true\n' '- &b true\n'
'- &i 42\n' '- &i 42\n'
@ -176,7 +173,7 @@ class AnchorsTestCase(RuleTestCase):
' <<: *b_m\n' ' <<: *b_m\n'
' foo: bar\n' ' foo: bar\n'
'---\n' '---\n'
'{a: 1, &x b: 2, c: &y 3, *x : 4, e: *y}\n' '{a: 1, &x b: 2, c: &y 3, *x: 4, e: *y}\n'
'...\n', conf) '...\n', conf)
self.check('---\n' self.check('---\n'
'- &i 42\n' '- &i 42\n'
@ -192,7 +189,6 @@ class AnchorsTestCase(RuleTestCase):
'- *f_m\n' '- *f_m\n'
'- *f_s\n' # declared after '- *f_s\n' # declared after
'- &f_s [1, 2]\n' '- &f_s [1, 2]\n'
'...\n'
'---\n' '---\n'
'block mapping: &b_m\n' 'block mapping: &b_m\n'
' key: value\n' ' key: value\n'
@ -205,77 +201,9 @@ class AnchorsTestCase(RuleTestCase):
' <<: *b_m\n' ' <<: *b_m\n'
' foo: bar\n' ' foo: bar\n'
'---\n' '---\n'
'{a: 1, &x b: 2, c: &x 3, *x : 4, e: *y}\n' '{a: 1, &x b: 2, c: &x 3, *x: 4, e: *y}\n'
'...\n', conf, '...\n', conf,
problem1=(5, 3), problem1=(5, 3),
problem2=(6, 3), problem2=(6, 3),
problem3=(22, 18), problem3=(21, 18),
problem4=(28, 20)) problem4=(27, 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.30.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:

@ -24,8 +24,6 @@ anchors.
later in the document). later in the document).
* Set ``forbid-duplicated-anchors`` to ``true`` to avoid duplications of a same * Set ``forbid-duplicated-anchors`` to ``true`` to avoid duplications of a same
anchor. 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) .. rubric:: Default values (when enabled)
@ -35,7 +33,6 @@ anchors.
anchors: anchors:
forbid-undeclared-aliases: true forbid-undeclared-aliases: true
forbid-duplicated-anchors: false forbid-duplicated-anchors: false
forbid-unused-anchors: false
.. rubric:: Examples .. rubric:: Examples
@ -81,26 +78,6 @@ anchors.
--- ---
- &anchor Foo Bar - &anchor Foo Bar
- &anchor [item 1, item 2] - &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
""" """
@ -112,22 +89,15 @@ from yamllint.linter import LintProblem
ID = 'anchors' ID = 'anchors'
TYPE = 'token' TYPE = 'token'
CONF = {'forbid-undeclared-aliases': bool, CONF = {'forbid-undeclared-aliases': bool,
'forbid-duplicated-anchors': bool, 'forbid-duplicated-anchors': bool}
'forbid-unused-anchors': bool}
DEFAULT = {'forbid-undeclared-aliases': True, DEFAULT = {'forbid-undeclared-aliases': True,
'forbid-duplicated-anchors': False, 'forbid-duplicated-anchors': False}
'forbid-unused-anchors': False}
def check(conf, token, prev, next, nextnext, context): def check(conf, token, prev, next, nextnext, context):
if (conf['forbid-undeclared-aliases'] or if conf['forbid-undeclared-aliases'] or conf['forbid-duplicated-anchors']:
conf['forbid-duplicated-anchors'] or if isinstance(token, (yaml.StreamStartToken, yaml.DocumentStartToken)):
conf['forbid-unused-anchors']): context['anchors'] = set()
if isinstance(token, (
yaml.StreamStartToken,
yaml.DocumentStartToken,
yaml.DocumentEndToken)):
context['anchors'] = {}
if (conf['forbid-undeclared-aliases'] and if (conf['forbid-undeclared-aliases'] and
isinstance(token, yaml.AliasToken) and isinstance(token, yaml.AliasToken) and
@ -143,32 +113,6 @@ def check(conf, token, prev, next, nextnext, context):
token.start_mark.line + 1, token.start_mark.column + 1, token.start_mark.line + 1, token.start_mark.column + 1,
f'found duplicated anchor "{token.value}"') f'found duplicated anchor "{token.value}"')
if conf['forbid-unused-anchors']: if conf['forbid-undeclared-aliases'] or conf['forbid-duplicated-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): if isinstance(token, yaml.AnchorToken):
context['anchors'][token.value] = { context['anchors'].add(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