Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d0f59876d | ||
|
|
ebd6b90d3e | ||
|
|
8aaa226830 | ||
|
|
15f8204427 | ||
|
|
404656394c | ||
|
|
06db2af9b0 |
@@ -1,6 +1,15 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
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)
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Basic example of running the linter from Python:
|
||||
import yamllint
|
||||
|
||||
yaml_config = yamllint.config.YamlLintConfig("extends: default")
|
||||
for p in yamllint.linter.run("example.yaml", yaml_config):
|
||||
for p in yamllint.linter.run(open("example.yaml", "r"), yaml_config):
|
||||
print(p.desc, p.line, p.rule)
|
||||
|
||||
.. automodule:: yamllint.linter
|
||||
|
||||
@@ -117,7 +117,7 @@ post-template processing).
|
||||
Example of a Jinja2 code that cannot be parsed as YAML because it contains
|
||||
invalid tokens ``{%`` and ``%}``:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: text
|
||||
|
||||
# This file IS NOT valid YAML and will produce syntax errors
|
||||
{% if extra_info %}
|
||||
|
||||
@@ -4,7 +4,7 @@ Integration with other software
|
||||
Integration with pre-commit
|
||||
---------------------------
|
||||
|
||||
You can integrate yamllint in `pre-commit <http://pre-commit.com/>`_ tool.
|
||||
You can integrate yamllint in the `pre-commit <https://pre-commit.com/>`_ tool.
|
||||
Here is an example, to add in your .pre-commit-config.yaml
|
||||
|
||||
.. code:: yaml
|
||||
@@ -12,11 +12,13 @@ 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
|
||||
# You can pass your custom .yamllint with args attribute.
|
||||
- repo: https://github.com/adrienverge/yamllint.git
|
||||
rev: v1.17.0
|
||||
hooks:
|
||||
- id: yamllint
|
||||
args: [-c=/path/to/.yamllint]
|
||||
repos:
|
||||
- repo: https://github.com/adrienverge/yamllint.git
|
||||
rev: v1.29.0
|
||||
hooks:
|
||||
- id: yamllint
|
||||
args: [--strict, -c=/path/to/.yamllint]
|
||||
|
||||
|
||||
Integration with GitHub Actions
|
||||
-------------------------------
|
||||
|
||||
@@ -14,6 +14,11 @@ This page describes the rules and their options.
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
anchors
|
||||
-------
|
||||
|
||||
.. automodule:: yamllint.rules.anchors
|
||||
|
||||
braces
|
||||
------
|
||||
|
||||
|
||||
209
tests/rules/test_anchors.py
Normal file
209
tests/rules/test_anchors.py
Normal file
@@ -0,0 +1,209 @@
|
||||
# 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')
|
||||
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,
|
||||
problem1=(9, 3),
|
||||
problem2=(10, 3),
|
||||
problem3=(11, 3),
|
||||
problem4=(12, 3),
|
||||
problem5=(13, 3),
|
||||
problem6=(24, 7),
|
||||
problem7=(27, 36))
|
||||
|
||||
def test_forbid_duplicated_anchors(self):
|
||||
conf = ('anchors:\n'
|
||||
' forbid-undeclared-aliases: false\n'
|
||||
' forbid-duplicated-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'
|
||||
'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=(21, 18),
|
||||
problem4=(27, 20))
|
||||
@@ -21,7 +21,7 @@ indentation, etc."""
|
||||
|
||||
|
||||
APP_NAME = 'yamllint'
|
||||
APP_VERSION = '1.29.0'
|
||||
APP_VERSION = '1.30.0'
|
||||
APP_DESCRIPTION = __doc__
|
||||
|
||||
__author__ = 'Adrien Vergé'
|
||||
|
||||
@@ -6,6 +6,7 @@ yaml-files:
|
||||
- '.yamllint'
|
||||
|
||||
rules:
|
||||
anchors: enable
|
||||
braces: enable
|
||||
brackets: enable
|
||||
colons: enable
|
||||
|
||||
@@ -223,7 +223,7 @@ def run(input, conf, filepath=None):
|
||||
:param input: buffer, string or stream to read from
|
||||
:param conf: yamllint configuration object
|
||||
"""
|
||||
if conf.is_file_ignored(filepath):
|
||||
if filepath is not None and conf.is_file_ignored(filepath):
|
||||
return ()
|
||||
|
||||
if isinstance(input, (bytes, str)):
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from yamllint.rules import (
|
||||
anchors,
|
||||
braces,
|
||||
brackets,
|
||||
colons,
|
||||
@@ -39,6 +40,7 @@ from yamllint.rules import (
|
||||
)
|
||||
|
||||
_RULES = {
|
||||
anchors.ID: anchors,
|
||||
braces.ID: braces,
|
||||
brackets.ID: brackets,
|
||||
colons.ID: colons,
|
||||
|
||||
118
yamllint/rules/anchors.py
Normal file
118
yamllint/rules/anchors.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# 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.
|
||||
|
||||
.. rubric:: Default values (when enabled)
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
rules:
|
||||
anchors:
|
||||
forbid-undeclared-aliases: true
|
||||
forbid-duplicated-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]
|
||||
"""
|
||||
|
||||
|
||||
import yaml
|
||||
|
||||
from yamllint.linter import LintProblem
|
||||
|
||||
|
||||
ID = 'anchors'
|
||||
TYPE = 'token'
|
||||
CONF = {'forbid-undeclared-aliases': bool,
|
||||
'forbid-duplicated-anchors': bool}
|
||||
DEFAULT = {'forbid-undeclared-aliases': True,
|
||||
'forbid-duplicated-anchors': False}
|
||||
|
||||
|
||||
def check(conf, token, prev, next, nextnext, context):
|
||||
if conf['forbid-undeclared-aliases'] or conf['forbid-duplicated-anchors']:
|
||||
if isinstance(token, (yaml.StreamStartToken, yaml.DocumentStartToken)):
|
||||
context['anchors'] = set()
|
||||
|
||||
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-undeclared-aliases'] or conf['forbid-duplicated-anchors']:
|
||||
if isinstance(token, yaml.AnchorToken):
|
||||
context['anchors'].add(token.value)
|
||||
Reference in New Issue
Block a user