To follow Git's implementation, use `GitIgnoreSpec.from_lines(…)`. The
change for using this is Git allows including files from excluded
directories which directly contradicts the gitignore docs.
Thanks to Wong Hoi Sing Edison for the initial proposal.
Fixes https://github.com/adrienverge/yamllint/issues/800.
Python 3.9 is officially "end-of-life" since 2025-10-31.
Note: Python 3.14 is officially supported since commit 823a96e "CI: Test
more versions of Python".
On double-quoted multiline strings, quotes aren't needed if lines are
broken on spaces, e.g.:
multiline:
"this is a sentence
cut into words"
But quotes are needed when at least one line ends with a backslash
character (`\`), meaning that the next spaces should be removed:
multiline:
"https://example.com/a/very/very\
/very/very/long/URL"
This commit fixes that.
Fixes https://github.com/adrienverge/yamllint/issues/275
This fixes a problem in the custom versions `v1.37.0.devN` built for
publishing on TestPyPI since commit 325fafa "Publish each master commit
with a unique version on TestPyPI".
Such versions are constructed by suffixing `.devN`, where `N` is the
number of commits since last tags. In the `N = 0` case, we need to add
`--long` to `git describe --tags`. Otherwise it yields `v1.37.0` instead
of `v1.37.0-0-gbe92e15`, and the CI fails, e.g. on
https://github.com/adrienverge/yamllint/actions/runs/14017589005/job/39245225221
packaging.version.InvalidVersion: Invalid version: '1.37.0.devv1.37.0'
This follows commit 7d52df7 "CI: Ignore version duplicates when
publishing to TestPyPI" with a better design:
- Only publish builds from `master` branch on TestPyPI.
- Version every non-tag commit with a `.devN` suffix, e.g. `1.36.2.dev1`.
This prevents duplicates on TestPyPI.
- `twine check` built packages.
See discussion at
https://github.com/adrienverge/yamllint/issues/721#issuecomment-2731027692
for more details.
This commit prevents the failure of the GitHub Actions job "Publish
distribution package to TestPyPI" on most pushes (see discussion at
https://github.com/adrienverge/yamllint/issues/721#issuecomment-2727167030),
since recent commit a3e1325 "CI: Publish PyPI releases using GitHub
Actions workflows".
Indeed, TestPyPI doesn't allow repushing the same version. Use
`skip-existing` to prevent to such failures on the CI.
An alternative would be to use the `setuptools-scm` plugin to define a
unique version for each commit (e.g. `1.36.2.dev2+gd4f1c14`), but it
requires more changes (define the version in `pyproject.toml`, fetch
tags from GitHub in `publish.yaml`, exclude `.*` + `.github/**` files in
`MANIFEST.in`). Moreover it doesn't work as is: to prevent errors like
`HTTP 400 The use of local versions in '1.36.2.dev2+gd4f1c14' is not
allowed` we would need `local_scheme = "no-local-version"`, which would
push duplicated versions on TestPyPI anyway.
This commit fixes a problem reported by Nicholas Bollweg at
https://github.com/adrienverge/yamllint/pull/725. Recent commit a3e1325
"CI: Publish PyPI releases using GitHub Actions workflows" and automatic
publication of version 1.36.1 revealed a problem that had probably been
there for some time: most documentation and test files weren't included
in the source distribution when running `python -m build` (unless an
`yamllint.egg-info` from a previous run listed them).
I see two solutions:
1. Add plugin `setuptools-scm` in `pyproject.toml`:
requires = ["setuptools >= 61", "setuptools-scm >= 8"]
This would add all files tracked by Git in the sdist, including a few
that aren't really needed: `.flake8`, `.github`, `.readthedocs.yaml`…
2. Declare extra files to embed in `MANIFEST.in`. This is what this
commit does.
I checked that:
- All files packages before 1.36.0 are correctly included now:
tar -tvf dist/yamllint-1.36.0.tar.gz | cut -b65- \
> /tmp/sdist-files-before
rm -rf yamllint.egg-info && python -m build && \
tar -tvf dist/yamllint-1.36.1.tar.gz | cut -b65- \
> /tmp/sdist-files-after
git diff --no-index /tmp/sdist-files-before /tmp/sdist-files-after
- These extra files are still not installed by `pip install` (they are
not needed):
pip install yamllint==1.36.0 && \
tree ~/.local/lib/python3.13/site-packages/yamllint \
> /tmp/pip-install-before
pip install dist/yamllint-1.36.1.tar.gz && \
tree ~/.local/lib/python3.13/site-packages/yamllint \
> /tmp/pip-install-after
git diff --no-index /tmp/pip-install-before /tmp/pip-install-after
Since I don't have Python 3.14 on my system (yet), here are the steps I
used to reproduce:
sudo dnf install python3.14
python3.14 -m venv /tmp/py3.14
source /tmp/py3.14/bin/activate
pip install yaml pathspec
python3.14 -m unittest discover
# or
python3.14 -m unittest tests.test_cli.CommandLineTestCase.test_run_default_format_output_in_tty
This commit fixes that by piping *only* `sys.stdout`, not `sys.stderr`
too. I'm not sure why I wrote this code in 2016 (see commit a2c68fdf),
but the new one makes more sense to me now.
Fixes https://bugzilla.redhat.com/show_bug.cgi?id=2336947
This is a simple preparation for the next commit, to get rid of
low-level `fcntl.fcntl()` calls, since `os.set_blocking()` now exists
(since Python 3.5).
Since yamllint version 0.5.2 (in 2016, just after yamllint was created)
this example in documentation doesn't work. Indeed, commit dbbecb5
(which aimed to solve another problem) removed the ability to import
yamllint submodules directly.
This commit makes it clearer inside documentation.
Fixes https://github.com/adrienverge/yamllint/issues/698
Commit 2344380 "Cleanly skip broken symlinks that are ignored" fixed a
problem on symbolic links but also introduced a change on how ignored
files should be treated, depending on whether they're explicitely passed
as command-line arguments or not [^1].
This change is annoying for users that dynamically build the list of
files to pass as arguments, e.g. [^2]:
find -name '*\.yaml' | xargs yamllint
The present commit adds unit tests for `yamllint [FILES]...` and
`yamllint --list-files [FILES]...`, that passed with previous version
1.34.0, and restore the behavior of this version.
As a result it also reverts the API change of commit 2344380 on
`yamllint.linter.run(stream, config)`.
[^1]: https://github.com/adrienverge/yamllint/issues/657#issuecomment-1948009315
[^2]: https://github.com/adrienverge/yamllint/issues/657#issuecomment-1948093680
Before this commit, yamllint would output "[Errno 2] No such file or
directory" when running on a directory which contained a broken symbolic
link, even if the file is set to be ignored in yamllint configuration.
This commit fixes that, and adds corresponding tests.
As a side effect this changes `yamllint.linter.run(stream, config)`, so
tools that would use this API need to filter ignored files beforehand.
Fixes https://github.com/adrienverge/yamllint/issues/399
https://yamllint.readthedocs.io/en/v1.34.0/ was rendered differently
than previous versions, very probably due to change of commit ca7f8b9
"Migrate to use .readthedocs.yaml for docs generation".
Let's restore the `sphinx_rtd_theme` theme.
Specification of YAML ≤ 1.1 has 22 boolean values:
y | Y | n | N
yes | Yes | YES | no | No | NO
true | True | TRUE | false | False | FALSE
on | On | ON | off | Off | OFF
Whereas YAML 1.2 spec recognizes only 6 [^1]:
true | True | TRUE | false | False | FALSE
For documents that explicit state their YAML spec version at the top of
the document, let's adapt the list of forbidden values.
In the future, we should:
- implement a configuration option to declare the default YAML spec
version, e.g. `default-yaml-spec-version: 1.2`,
- consider making 1.2 the default in a future release (this would be a
slight breaking change, but yamllint always tried to be
1.2-compatible).
- consider adapting yamllint to other 1.1 vs. 1.2 differences [^2].
Solves: https://github.com/adrienverge/yamllint/issues/587
Related to: #559#540#430#344#247#232#158
[^1]: https://yaml.org/spec/1.2.2/#1032-tag-resolution
[^2]: https://yaml.org/spec/1.2.2/ext/changes/#changes-in-version-12-revision-120-2009-07-21
Existing `anchors` options use quotes around the anchor name:
2:3 error found undeclared alias "unknown" (anchors)
4:3 error found duplicated anchor "dup" (anchors)
Let's do the same in the newly-added option `forbid-unused-anchors`:
5:3 error found unused anchor "not used" (anchors)
Because `setup.py` is deprecated, let's switch from:
python setup.py build_sphinx
to:
make -C docs html
to build Sphinx documentation.
The generated HTML files in `docs/_build/html` are exactly the same (I
compared with `diff -qr`).
Also add `-W` (turn warnings into errors) to the `sphinx-build` options
to keep the previous behavior.
In the rare case when the key before `:` is an alias (e.g. `{*x : 4}`),
the space before `:` is required (although this requirement is not
enforced by PyYAML), the reason being that a colon can be part of an
anchor name. Consequently, this commit adapts the `colons` rule to avoid
failures when this happens.
See this comment from Tina Müller for more details:
https://github.com/adrienverge/yamllint/pull/550#discussion_r1155297373
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 this comment from Tina Müller for more details:
https://github.com/adrienverge/yamllint/pull/550#discussion_r1155297373
Even if it's not a problem for yamllint, let's fix our tests to include
valid YAML snippets.
According to the YAML specification [^1]:
- > It is an error for an alias node to use an anchor that does not
> previously occur in the document.
The `forbid-undeclared-aliases` option checks that aliases do have a
matching anchor declared previously in the document. Since this is
required by the YAML spec, this option is enabled by default.
- > The alias refers to the most recent preceding node having the same
> anchor.
This means that having a same anchor repeated in a document is
allowed. However users could want to avoid this, so the new option
`forbid-duplicated-anchors` allows that. It's disabled by default.
- > It is not an error to specify an anchor that is not used by any
> alias node.
This means that it's OK to declare anchors but don't have any alias
referencing them. However users could want to avoid this, so a new
option (e.g. `forbid-unused-anchors`) could be implemented in the
future. See https://github.com/adrienverge/yamllint/pull/537.
Fixes#395Closes#420
[^1]: https://yaml.org/spec/1.2.2/#71-alias-nodes
As reported in https://github.com/adrienverge/yamllint/pull/548, there
might be a problem with pathspec 0.11.1 which does't allow calling
`match_file()` with argument `None` anymore.
The `linter.run()` function shouldn't call
`YamlLintConfig.is_file_ignored(None)` anyway.
Recently `python setup.py build_sphinx` started failing with:
pkg_resources.VersionConflict: (Pygments 2.3.1
(/usr/lib/python3/dist-packages), Requirement.parse('Pygments>=2.12'))
The reason is that `doc8` 1.0.0 installs `Pygments` 2.3.1, then `Sphinx`
5.3.0 needs `Pygments` ≥ 2.12.
The easiest fix is to change the install order.
This problem was just introduced by commit cec4f33 "Clarify disable-line
and parser errors, workaround" and produced this error when building
documentation:
docs/disable_with_comments.rst:120:Could not lex literal_block as
"yaml". Highlighting skipped.
This reverts commit 3c525ab "Release as a universal wheel".
Python 2 support was definitely dropped in early 2021 in commit a3fc64d,
since then it's no longer useful to build universal wheels.
According to the `wheel` documentation:
> If your project contains no C extensions and is expected to work on
> both Python 2 and 3, you will want to tell wheel to produce universal
> wheels
Partly fixes https://github.com/adrienverge/yamllint/issues/501
The rule correctly reports number values like `.1`, `1e2`, `.NaN` and
`.Inf`, but it also reported false positives on strings like `.1two3`,
`1e2a`, `.NaNa` and `.Infinit∞`.
The regexps need to end with an end delimiter (`$`) otherwise longer
strings can be matched too.
Fixes https://github.com/adrienverge/yamllint/issues/495
Commit c268a82 "key-duplicates: Don't crash on redundant closing
brackets or braces" fixed a problem but introduced another one: it
crashes on systems with (I guess) an old version of PyYAML. This is
probably linked to the "Allow colon in a plain scalar in a flow context"
issue on PyYAML [1].
For example, this problem happens on CentOS 8:
FAIL: test_disabled (tests.rules.test_key_duplicates.KeyDuplicatesTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "…/tests/rules/test_key_duplicates.py", line 90, in test_disabled
'{a:1, b:2}}\n', conf, problem=(2, 11, 'syntax'))
File "…/tests/common.py", line 54, in check
self.assertEqual(real_problems, expected_problems)
AssertionError: Lists differ: …
- [2:3: syntax error: found unexpected ':' (syntax)]
+ [2:11: <no description>]
I propose to simply fix the *space following a colon* problem, since
it's not related to what the original author @tamere-allo-peter tried to
fix.
[1]: https://github.com/yaml/pyyaml/pull/45
To be consistent with other existing messages, e.g.:
- forbidden not a number value ".NaN"
- found forbidden document start "---"
- missing document start "---"
- truthy value should be one of ["true"]
- forbidden implicit octal value "0777"
The `# -*- coding: utf-8 -*-` headers were useful for Python 2, and
aren't needed for Python 3 where UTF-8 is the default.
yamllint support of Python 2 was dropped in early 2021, see commit
a3fc64d "End support for Python 2".
Let's drop these headers.
This reverts commit 8f68248 "Remove runtime dep 'setuptools' for Python
< 3.8". It looks like removing setuptools induces problems on some
systems, see for example the linked discussion.
Fixes https://github.com/adrienverge/yamllint/issues/380.
PyYAML implements YAML spec version 1.1, not 1.2. Hence, values starting
with `0o` are not considered as numbers: they are just strings, so they
need quotes when `quoted-strings: {required: true}`.
>>> import yaml
>>> yaml.resolver.Resolver().resolve(yaml.nodes.ScalarNode, '100', (True, False))
'tag:yaml.org,2002:int'
>>> yaml.resolver.Resolver().resolve(yaml.nodes.ScalarNode, '0100', (True, False))
'tag:yaml.org,2002:int'
>>> yaml.resolver.Resolver().resolve(yaml.nodes.ScalarNode, '0o100', (True, False))
'tag:yaml.org,2002:str'
Let's try to prevent that.
Fixes https://github.com/adrienverge/yamllint/issues/351.
Using `python setup.py test` is now deprecated [1], users are encouraged
to be explicit about the test command.
Running yamllint tests using the Python standard library (`unittest`)
can be done using:
python -m unittest discover
Why not nose, tox or pytest? Because they would add a dependency, make
tests running more complicated and verbose for new users, and their
benefit is not worth for this simple project (only 2 runtime
dependencies: PyYAML and pathspec).
Resolves https://github.com/adrienverge/yamllint/issues/328.
[1]: https://github.com/pypa/setuptools/pull/1878
Add ability to:
- require strings to be quoted if they match a pattern (PCRE regex)
- allow quoted strings if they match a pattern, while `require:
only-when-needed` is enforced.
Co-Authored-By: Leo Feyer (https://github.com/adrienverge/yamllint/pull/246)
The rule worked for values like:
flow-map: {a: foo, b: "bar"}
block-map:
a: foo
b: "bar"
But not for:
flow-seq: [foo, "bar"]
block-seq:
- foo
- "bar"
Also add tests to make sure there will be no regression.
Fixes: #208.