Compare commits

..

23 Commits

Author SHA1 Message Date
Adrien Vergé
b9e1fd18c1 yamllint version 1.29.0 2023-01-10 18:57:21 +01:00
Peter Leitzen
fa0bb03f9a cli: Add --list-files command line option
This option lists the files to lint by yamllint, taking into account `ignore`
and `yaml-files` configuration options.
2023-01-10 18:48:38 +01:00
Matthew Gamble
2a904f8fc1 configuration: Allow using a list of strings in ignore configuration
This may feel more natural for some users, rather than embedding
multiple entries in a multi-line string.
2023-01-10 18:45:09 +01:00
Ville Skyttä
6194a282fc docs: Spelling and grammar fixes 2022-12-12 19:08:31 +01:00
Dimitri Papadopoulos
5ac3ed4490 Apply some pyupgrade suggestions
io.open() is an alias for for the builtin open() function:
	https://docs.python.org/3/library/io.html#io.open

If open() cannot open a file, an OSError is raised:
	https://docs.python.org/3/library/functions.html#open

EnvironmentError is kept for compatibility with previous versions;
starting from Python 3.3, it is an alias of OSError:
	https://docs.python.org/3/library/exceptions.html#EnvironmentError

Directly yield from an iterable instead of iterating to yield items.
2022-10-28 07:48:54 +02:00
Dimitri Papadopoulos
5b21a3d9ea Remove Unicode marker before strings
All strings are Unicode in Python 3. No need for u'€', just use '€'.
2022-10-28 07:46:33 +02:00
Dimitri Papadopoulos
5fbf44c203 docs: Fix typos 2022-10-28 07:42:28 +02:00
Michael Käufl
c9c5e0b1c7 CI: Add support for Python 3.11 2022-10-26 19:27:06 +02:00
Adrien Vergé
a6e0e1213a CI: Fix pip install problem with Pygments version
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.
2022-10-26 16:37:45 +02:00
Adrien Vergé
eb7b7ca627 docs: Fix Sphinx error on non-YAML code snippet
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.
2022-10-26 16:37:45 +02:00
Andrew Imeson
cec4f3383a cocs: Clarify disable-line and parser errors, workaround
Lots of user confusion expecting `disable-line` to work around parser
errors caused by templating syntax.

Relates to #61, #65, #128, #311, #460, #462
2022-10-24 14:44:05 +02:00
Andrew Imeson
52234b7a46 docs: remove erroneous example text in disable-file 2022-10-24 14:44:05 +02:00
Adrien Vergé
151b1c4086 style: Fix indentation test file missing whitespace
Commit 764586d "indentation: Fix indent-sequences in nested collections"
introduced 2 style-related problems:

    tests/rules/test_indentation.py:1394:45: E231 missing whitespace after ','
    tests/rules/test_indentation.py:1402:45: E231 missing whitespace after ','

Let's fix them.
2022-10-24 14:37:48 +02:00
Brian Brookman
764586d836 indentation: Fix indent-sequences in nested collections
When {spaces: consistent, indent-sequences: true} (the defaults),
and the expected indent for a block sequence that should be indented
is unknown, set the expected indent to an unknown value (-1) rather
than an arbitrary one (2). This ensures wrong-indentation is properly
caught when block sequences are nested.

Fixes issue #485
2022-10-18 18:04:49 +02:00
Dimitri Papadopoulos
47cd8f2e9e No need to inherit from object in Python 3 2022-10-18 17:52:06 +02:00
Dimitri Papadopoulos
4d271f3daf ci: Security hardening for GitHub Actions
https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs

The idea is that the software supply chain relies on 3rd party actions
that could be compromised. Mitigate this risk by giving these actions
minimal rights to the repository. Here read-only access is good enough.
2022-10-14 17:30:28 +02:00
Dimitri Papadopoulos
22ddf4c8e5 linter: Use proper Python 3 I/O type for reading
Co-authored-by: Adrien Vergé <adrienverge@gmail.com>
2022-10-14 17:29:48 +02:00
Adrien Vergé
b8c85f0dfd build: Stop releasing universal wheels
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
2022-10-11 11:38:01 +02:00
Dimitri Papadopoulos
e0f749bf5d comments-indentation: Refactor to use 'max' builtin
It is unnecessary to use an `if` statement to check the maximum of two
values and then assign the value to a name. You can use the max
built-in do do this. It is straightforward and more readable.
2022-10-07 17:01:51 +02:00
Dimitri Papadopoulos
19d00809d1 quoted-strings: Merge comparisons with in
To check if a variable is equal to one of many values, combine the
values into a tuple and check if the variable is contained in it
instead of checking for equality against each of the values. This
is faster, less verbose, and more readable.
2022-10-07 17:01:51 +02:00
Adrien Vergé
008db4aa09 float-values: Fix bug on strings containing fordidden values
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
2022-10-04 08:59:51 +02:00
Dimitri Papadopoulos Orfanos
e8391de711 Drop support for Python 3.6
Python 3.6 reached end-of-life on 2021-12-23.
https://peps.python.org/pep-0494/
2022-09-13 07:59:17 +02:00
Dimitri Papadopoulos
a5adec1570 ci: Update GitHub Actions
https://github.com/actions/checkout
https://github.com/actions/setup-python
2022-09-13 07:58:03 +02:00
24 changed files with 313 additions and 75 deletions

View File

@@ -8,18 +8,21 @@ on: # yamllint disable-line rule:truthy
branches: branches:
- master - master
permissions:
contents: read
jobs: jobs:
lint: lint:
name: Linters name: Linters
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v4
- run: - run:
python -m pip install flake8 flake8-import-order doc8 sphinx python -m pip install flake8 flake8-import-order sphinx
rstcheck[sphinx] rstcheck[sphinx] doc8
- run: python -m pip install . - run: python -m pip install .
- run: flake8 . - run: flake8 .
- run: doc8 $(git ls-files '*.rst') - run: doc8 $(git ls-files '*.rst')
@@ -34,16 +37,16 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: python-version:
- '3.6'
- '3.7' - '3.7'
- '3.8' - '3.8'
- '3.9' - '3.9'
- '3.10' - '3.10'
- '3.11'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2 uses: actions/setup-python@v4
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Append GitHub Actions system path - name: Append GitHub Actions system path

View File

@@ -1,6 +1,19 @@
Changelog Changelog
========= =========
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) 1.28.0 (2022-09-12)
------------------- -------------------

View File

@@ -38,7 +38,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', [u'Adrien Vergé'], 1) ('index', 'yamllint', 'Linter for YAML files', ['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

View File

@@ -136,11 +136,19 @@ 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. doesn't process them. They can be provided either as a list of paths, or as a
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):
@@ -153,6 +161,13 @@ 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
@@ -165,6 +180,14 @@ 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.python.org/pypi/pathspec>`_ for more details. <https://pypi.python.org/pypi/pathspec>`_ for more details.
@@ -204,6 +227,13 @@ or:
.. note:: However, this is mutually exclusive with the ``ignore`` key. .. 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 Setting the locale
------------------ ------------------

View File

@@ -40,6 +40,11 @@ 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``.
@@ -89,7 +94,6 @@ 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:
@@ -101,3 +105,32 @@ 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::
# 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

View File

@@ -26,7 +26,7 @@ Actions <https://github.com/features/actions>`_ and automatically uses the
suited output format to decorate code with linting errors. You can also force suited output format to decorate code with linting errors. You can also force
the GitHub Actions output with ``yamllint --format github``. the GitHub Actions output with ``yamllint --format github``.
An minimal example workflow using GitHub Actions: A minimal example workflow using GitHub Actions:
.. code:: yaml .. code:: yaml

View File

@@ -1,6 +1,3 @@
[bdist_wheel]
universal = 1
[flake8] [flake8]
import-order-style = pep8 import-order-style = pep8
application-import-names = yamllint application-import-names = yamllint
@@ -27,11 +24,11 @@ classifiers =
Intended Audience :: Developers Intended Audience :: Developers
License :: OSI Approved :: GNU General Public License v3 (GPLv3) License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Topic :: Software Development Topic :: Software Development
Topic :: Software Development :: Debuggers Topic :: Software Development :: Debuggers
Topic :: Software Development :: Quality Assurance Topic :: Software Development :: Quality Assurance
@@ -46,7 +43,7 @@ project_urls =
[options] [options]
packages = find: packages = find:
python_requires = >=3.6 python_requires = >=3.7
include_package_data = True include_package_data = True
install_requires = install_requires =

View File

@@ -40,6 +40,8 @@ class FloatValuesTestCase(RuleTestCase):
'- 0.0\n' '- 0.0\n'
'- .1\n' '- .1\n'
'- \'.1\'\n' '- \'.1\'\n'
'- string.1\n'
'- .1string\n'
'- !custom_tag .2\n' '- !custom_tag .2\n'
'- &angle1 0.0\n' '- &angle1 0.0\n'
'- *angle1\n' '- *angle1\n'
@@ -47,7 +49,7 @@ class FloatValuesTestCase(RuleTestCase):
'- *angle2\n', '- *angle2\n',
conf, conf,
problem1=(3, 3), problem1=(3, 3),
problem2=(8, 11)) problem2=(10, 11))
def test_scientific_notation(self): def test_scientific_notation(self):
conf = ( conf = (
@@ -61,6 +63,8 @@ class FloatValuesTestCase(RuleTestCase):
'- 10e-6\n' '- 10e-6\n'
'- 0.00001\n' '- 0.00001\n'
'- \'10e-6\'\n' '- \'10e-6\'\n'
'- string10e-6\n'
'- 10e-6string\n'
'- !custom_tag 10e-6\n' '- !custom_tag 10e-6\n'
'- &angle1 0.000001\n' '- &angle1 0.000001\n'
'- *angle1\n' '- *angle1\n'
@@ -71,8 +75,8 @@ class FloatValuesTestCase(RuleTestCase):
conf, conf,
problem1=(2, 3), problem1=(2, 3),
problem2=(3, 3), problem2=(3, 3),
problem3=(9, 11), problem3=(11, 11),
problem4=(11, 11)) problem4=(13, 11))
def test_nan(self): def test_nan(self):
conf = ( conf = (
@@ -85,13 +89,15 @@ class FloatValuesTestCase(RuleTestCase):
'- .NaN\n' '- .NaN\n'
'- .NAN\n' '- .NAN\n'
'- \'.NaN\'\n' '- \'.NaN\'\n'
'- a.NaN\n'
'- .NaNa\n'
'- !custom_tag .NaN\n' '- !custom_tag .NaN\n'
'- &angle .nan\n' '- &angle .nan\n'
'- *angle\n', '- *angle\n',
conf, conf,
problem1=(2, 3), problem1=(2, 3),
problem2=(3, 3), problem2=(3, 3),
problem3=(6, 10)) problem3=(8, 10))
def test_inf(self): def test_inf(self):
conf = ( conf = (
@@ -106,6 +112,8 @@ class FloatValuesTestCase(RuleTestCase):
'- -.inf\n' '- -.inf\n'
'- -.INF\n' '- -.INF\n'
'- \'.inf\'\n' '- \'.inf\'\n'
'- ∞.infinity\n'
'- .infinity∞\n'
'- !custom_tag .inf\n' '- !custom_tag .inf\n'
'- &angle .inf\n' '- &angle .inf\n'
'- *angle\n' '- *angle\n'
@@ -116,5 +124,5 @@ class FloatValuesTestCase(RuleTestCase):
problem2=(3, 3), problem2=(3, 3),
problem3=(4, 3), problem3=(4, 3),
problem4=(5, 3), problem4=(5, 3),
problem5=(8, 10), problem5=(10, 10),
problem6=(10, 10)) problem6=(12, 10))

View File

@@ -1370,6 +1370,45 @@ 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'

View File

@@ -92,12 +92,12 @@ class CommandLineTestCase(unittest.TestCase):
'no-yaml.json': '---\n' 'no-yaml.json': '---\n'
'key: value\n', 'key: value\n',
# non-ASCII chars # non-ASCII chars
u'non-ascii/éçäγλνπ¥/utf-8': ( 'non-ascii/éçäγλνπ¥/utf-8': (
u'---\n' '---\n'
u'- hétérogénéité\n' '- hétérogénéité\n'
u'# 19.99 €\n' '# 19.99 €\n'
u'- お早う御座います。\n' '- お早う御座います。\n'
u'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'), '# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'),
# dos line endings yaml # dos line endings yaml
'dos.yml': '---\r\n' 'dos.yml': '---\r\n'
'dos: true', 'dos: true',
@@ -678,6 +678,39 @@ 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_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): class CommandLineConfigTestCase(unittest.TestCase):
def test_config_file(self): def test_config_file(self):

View File

@@ -369,7 +369,7 @@ 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(self): def test_extended_ignore_str(self):
with tempfile.NamedTemporaryFile('w') as f: with tempfile.NamedTemporaryFile('w') as f:
f.write('ignore: |\n' f.write('ignore: |\n'
' *.template.yaml\n') ' *.template.yaml\n')
@@ -379,6 +379,16 @@ class ExtendedConfigTestCase(unittest.TestCase):
self.assertEqual(c.ignore.match_file('test.template.yaml'), True) self.assertEqual(c.ignore.match_file('test.template.yaml'), True)
self.assertEqual(c.ignore.match_file('test.yaml'), False) 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):
@@ -539,7 +549,7 @@ class IgnoreConfigTestCase(unittest.TestCase):
'./s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:5:5: ' + hyphen, './s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:5:5: ' + hyphen,
))) )))
def test_run_with_ignore(self): def test_run_with_ignore_str(self):
with open(os.path.join(self.wd, '.yamllint'), 'w') as f: with open(os.path.join(self.wd, '.yamllint'), 'w') as f:
f.write('extends: default\n' f.write('extends: default\n'
'ignore: |\n' 'ignore: |\n'
@@ -593,6 +603,60 @@ class IgnoreConfigTestCase(unittest.TestCase):
'./s/s/ign-trail/s/s/file2.lint-me-anyway.yaml:5:5: ' + hyphen, './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): def test_run_with_ignore_from_file(self):
with open(os.path.join(self.wd, '.yamllint'), 'w') as f: with open(os.path.join(self.wd, '.yamllint'), 'w') as f:
f.write('extends: default\n' f.write('extends: default\n'

View File

@@ -31,10 +31,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(u'test: document', self.fake_config()) linter.run('test: document', self.fake_config())
def test_run_on_stream(self): def test_run_on_stream(self):
linter.run(io.StringIO(u'hello'), self.fake_config()) linter.run(io.StringIO('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,14 +44,14 @@ 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 = (u'- hétérogénéité\n' s = ('- hétérogénéité\n'
u'# 19.99 €\n') '# 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 = (u'- お早う御座います。\n' s = ('- お早う御座います。\n'
u'# الأَبْجَدِيَّة العَرَبِيَّة\n') '# الأَبْجَدِيَّة العَرَبِيَّة\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())

View File

@@ -13,7 +13,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 io import open
import os import os
from tests.common import RuleTestCase from tests.common import RuleTestCase

View File

@@ -21,10 +21,10 @@ indentation, etc."""
APP_NAME = 'yamllint' APP_NAME = 'yamllint'
APP_VERSION = '1.28.0' APP_VERSION = '1.29.0'
APP_DESCRIPTION = __doc__ APP_DESCRIPTION = __doc__
__author__ = u'Adrien Vergé' __author__ = 'Adrien Vergé'
__copyright__ = u'Copyright 2022, Adrien Vergé' __copyright__ = 'Copyright 2022, Adrien Vergé'
__license__ = 'GPLv3' __license__ = 'GPLv3'
__version__ = APP_VERSION __version__ = APP_VERSION

View File

@@ -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/>.
import argparse import argparse
import io
import locale import locale
import os import os
import platform import platform
@@ -47,7 +46,7 @@ def supports_color():
hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()) hasattr(sys.stdout, 'isatty') and sys.stdout.isatty())
class Format(object): class Format:
@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' %
@@ -158,6 +157,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', 'github',
'auto'), 'auto'),
@@ -208,14 +209,20 @@ def run(argv=None):
if conf.locale is not None: if conf.locale is not None:
locale.setlocale(locale.LC_ALL, conf.locale) 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 io.open(file, newline='') as f: with open(file, newline='') as f:
problems = linter.run(f, conf, filepath) problems = linter.run(f, conf, filepath)
except EnvironmentError as e: except OSError 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,
@@ -226,7 +233,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 EnvironmentError as e: except OSError 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,

View File

@@ -112,11 +112,16 @@ class YamlLintConfig:
with fileinput.input(conf['ignore-from-file']) as f: with fileinput.input(conf['ignore-from-file']) as f:
self.ignore = pathspec.PathSpec.from_lines('gitwildmatch', f) self.ignore = pathspec.PathSpec.from_lines('gitwildmatch', f)
elif 'ignore' in conf: elif 'ignore' in conf:
if not isinstance(conf['ignore'], str): if isinstance(conf['ignore'], str):
raise YamlLintConfigError(
'invalid config: ignore should contain file patterns')
self.ignore = pathspec.PathSpec.from_lines( self.ignore = pathspec.PathSpec.from_lines(
'gitwildmatch', conf['ignore'].splitlines()) '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(
'invalid config: ignore should contain file patterns')
if 'yaml-files' in conf: if 'yaml-files' in conf:
if not (isinstance(conf['yaml-files'], list) if not (isinstance(conf['yaml-files'], list)
@@ -150,11 +155,16 @@ 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 not isinstance(conf['ignore'], str): if isinstance(conf['ignore'], str):
raise YamlLintConfigError(
'invalid config: ignore should contain file patterns')
conf['ignore'] = pathspec.PathSpec.from_lines( conf['ignore'] = pathspec.PathSpec.from_lines(
'gitwildmatch', conf['ignore'].splitlines()) '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(
'invalid config: ignore should contain file patterns')
if 'level' not in conf: if 'level' not in conf:
conf['level'] = 'error' conf['level'] = 'error'

View File

@@ -14,6 +14,7 @@
# 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
@@ -33,7 +34,7 @@ DISABLE_RULE_PATTERN = re.compile(r'^# yamllint disable( rule:\S+)*\s*$')
ENABLE_RULE_PATTERN = re.compile(r'^# yamllint enable( 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)
@@ -121,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 a end of line is # Use a cache to store problems and flush it only when an 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 = []
@@ -227,7 +228,7 @@ def run(input, conf, filepath=None):
if isinstance(input, (bytes, str)): if isinstance(input, (bytes, str)):
return _run(input, conf, filepath) return _run(input, conf, filepath)
elif hasattr(input, 'read'): # Python 2's file or Python 3's io.IOBase elif isinstance(input, 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)

View File

@@ -132,8 +132,7 @@ 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)
for comment in comments_between_tokens(curr, next): yield from comments_between_tokens(curr, next)
yield comment
prev = curr prev = curr
curr = next curr = next

View File

@@ -117,8 +117,7 @@ def check(conf, comment):
# # comment # # comment
# - 1 # - 1
# - 2 # - 2
if prev_line_indent <= next_line_indent: prev_line_indent = max(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:

View File

@@ -107,13 +107,13 @@ DEFAULT = {
} }
IS_NUMERAL_BEFORE_DECIMAL_PATTERN = ( IS_NUMERAL_BEFORE_DECIMAL_PATTERN = (
re.compile('[-+]?(\\.[0-9]+)([eE][-+]?[0-9]+)?') re.compile('[-+]?(\\.[0-9]+)([eE][-+]?[0-9]+)?$')
) )
IS_SCIENTIFIC_NOTATION_PATTERN = re.compile( IS_SCIENTIFIC_NOTATION_PATTERN = re.compile(
'[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)' '[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)$'
) )
IS_INF_PATTERN = re.compile('[-+]?(\\.inf|\\.Inf|\\.INF)') IS_INF_PATTERN = re.compile('[-+]?(\\.inf|\\.Inf|\\.INF)$')
IS_NAN_PATTERN = re.compile('\\.nan|\\.NaN|\\.NAN') IS_NAN_PATTERN = re.compile('(\\.nan|\\.NaN|\\.NAN)$')
def check(conf, token, prev, next, nextnext, context): def check(conf, token, prev, next, nextnext, context):

View File

@@ -341,14 +341,18 @@ def _check(conf, token, prev, next, nextnext, context):
expected = detect_indent(expected, token) expected = detect_indent(expected, token)
if found_indentation != expected: if found_indentation != expected:
yield LintProblem(token.start_mark.line + 1, found_indentation + 1, if expected < 0:
'wrong indentation: expected %d but found %d' % message = 'wrong indentation: expected at least %d' % \
(expected, found_indentation)) (found_indentation + 1)
else:
message = 'wrong indentation: expected %d but found %d' % \
(expected, found_indentation)
yield LintProblem(token.start_mark.line + 1,
found_indentation + 1, message)
if (isinstance(token, yaml.ScalarToken) and if (isinstance(token, yaml.ScalarToken) and
conf['check-multi-line-strings']): conf['check-multi-line-strings']):
for problem in check_scalar_indentation(conf, token, context): yield from check_scalar_indentation(conf, token, context)
yield problem
# Step 2.a: # Step 2.a:
@@ -493,8 +497,8 @@ def _check(conf, token, prev, next, nextnext, context):
# indentation it should have (because `spaces` is # indentation it should have (because `spaces` is
# `consistent` and its value has not been computed yet # `consistent` and its value has not been computed yet
# -- this is probably the beginning of the document). # -- this is probably the beginning of the document).
# So we choose an arbitrary value (2). # So we choose an unknown value (-1).
indent = 2 indent = -1
else: else:
indent = detect_indent(context['stack'][-1].indent, indent = detect_indent(context['stack'][-1].indent,
next) next)
@@ -576,8 +580,7 @@ def _check(conf, token, prev, next, nextnext, context):
def check(conf, token, prev, next, nextnext, context): def check(conf, token, prev, next, nextnext, context):
try: try:
for problem in _check(conf, token, prev, next, nextnext, context): yield from _check(conf, token, prev, next, nextnext, context)
yield problem
except AssertionError: except AssertionError:
yield LintProblem(token.start_mark.line + 1, yield LintProblem(token.start_mark.line + 1,
token.start_mark.column + 1, token.start_mark.column + 1,

View File

@@ -93,7 +93,7 @@ TYPE = 'token'
MAP, SEQ = range(2) MAP, SEQ = range(2)
class Parent(object): class Parent:
def __init__(self, type): def __init__(self, type):
self.type = type self.type = type
self.keys = [] self.keys = []

View File

@@ -166,7 +166,7 @@ def VALIDATE(conf):
return 'cannot use both "required: false" and "extra-allowed"' return 'cannot use both "required: false" and "extra-allowed"'
DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str' DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str'
# https://stackoverflow.com/a/36514274 # https://stackoverflow.com/a/36514274
yaml.resolver.Resolver.add_implicit_resolver( yaml.resolver.Resolver.add_implicit_resolver(
@@ -228,7 +228,7 @@ def check(conf, token, prev, next, nextnext, context):
return return
# Ignore multi-line strings # Ignore multi-line strings
if (not token.plain) and (token.style == "|" or token.style == ">"): if not token.plain and token.style in ("|", ">"):
return return
quote_type = conf['quote-type'] quote_type = conf['quote-type']

View File

@@ -14,7 +14,7 @@
# 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 forbid non-explictly typed truthy values other than allowed Use this rule to forbid non-explicitly typed truthy values other than allowed
ones (by default: ``true`` and ``false``), for example ``YES`` or ``off``. ones (by default: ``true`` and ``false``), for example ``YES`` or ``off``.
This can be useful to prevent surprises from YAML parsers transforming This can be useful to prevent surprises from YAML parsers transforming