Compare commits

..

48 Commits

Author SHA1 Message Date
Adrien Vergé
85ccd625a3 yamllint version 1.26.1 2021-04-06 16:02:55 +02:00
Patryk Małek
e53ea093e2 line_length: skip all hash signs starting comment 2021-03-24 18:04:48 +01:00
Adrien Vergé
5d8ef2ea23 CI: Simplify 'pip' commands 2021-03-24 17:16:57 +01:00
Adrien Vergé
4515269233 CI: Fix failing 'coverage' command because of $PATH
Very probably due to:
https://github.com/actions/virtual-environments/issues/2455#issuecomment-787511010
2021-03-24 17:16:57 +01:00
Adrien Vergé
66bf76a362 CI: Switch to GitHub Actions
Because Travis CI is dead.
2021-03-16 16:13:01 +01:00
Daniel M. Capella
8f682481c7 Remove runtime dep 'setuptools' for Python < 3.8
> In recent versions of setuptools and Python, console-script entry
points are using stdlib importlib by default, thus setuptools is no
longer needed as a runtime dependency.

https://github.com/pypa/setuptools/pull/2197
https://github.com/pypa/setuptools/blob/main/CHANGES.rst#v4730
https://docs.python.org/3/library/importlib.metadata.html
2021-02-11 08:55:08 +01:00
Adrien Vergé
0fff4e29e4 yamllint version 1.26.0 2021-01-29 18:10:35 +01:00
Adrien Vergé
1b378ed5b9 quoted-strings: Fix explicit octal recognition
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.
2021-01-11 16:38:29 +01:00
Adrien Vergé
a3fc64d134 End support for Python 2
As planned and advertized, yamllint drops support for Python 2 on 2021.
2021-01-06 07:55:10 +01:00
Rusty Geldmacher
ee4d163ff8 Allow only non-empty brackets/braces
We'd like to disallow brackets and braces in our YAML, but there's a
catch: the only way to describe an empty array or hash in YAML is to
supply an empty one (`[]` or `{}`). Otherwise, the value will be null.

This commit adds a `non-empty` option to `forbid` for brackets and
braces. When it is set, all flow and sequence mappings will cause errors
_except_ for empty ones.
2020-12-15 11:52:21 +01:00
Jason Mobarak
22335b294d Add support for Python 3.9, drop Python 3.4
Add support for Python 3.9 since it was officially released in October
and drop support for Python 3.4 since it is end-of-life (EOL).
2020-12-04 10:10:10 +01:00
Rex Ledesma
0f9dffde23 docs: Add configuration for integration with Arcanist 2020-10-24 13:24:55 +02:00
Mathieu Couette
cef0b48993 tests: Add unittest aliases to Python 2.7 2020-10-12 11:24:01 +02:00
Mathieu Couette
11b1f1c14e tests: Fix indentation issues 2020-10-12 11:24:01 +02:00
Mathieu Couette
9ee8c27ac9 tests: Replace deprecated aliases
https://docs.python.org/3/library/unittest.html#deprecated-aliases
2020-10-12 11:24:01 +02:00
Florian Bruhin
8eebab68ab Fix typo in changelog 2020-10-05 09:42:42 +02:00
Per Lundberg
2103bd73de README.rst: fix typo 2020-10-02 12:51:01 +02:00
Adrien Vergé
85c8631183 tests: Stop using deprecated 'python setup.py test'
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
2020-09-30 09:43:31 +02:00
Adrien Vergé
16e0f9d7b2 yamllint version 1.25.0 2020-09-29 08:52:34 +02:00
Mathieu Couette
1a4f9fe00f gitignore: Add /.eggs
Quick PR to ignore the `/.eggs` folder, which appears to be generated every
time the `python setup.py test` command is run.

The content of the `./.eggs/README.txt` file:

> This directory contains eggs that were downloaded by setuptools to build,
> test, and run plug-ins.
> 
> This directory caches those eggs to prevent repeated downloads.
> 
> However, it is safe to delete this directory.
2020-09-27 10:06:33 +02:00
Mathieu Couette
027d1b0a9a directives: Fix DOS lines messing with rule IDs
Fixes #325

The linter allows a directive to contain trailing whitespace characters like
\r, but does not trim them before iterating on the rules. As a result, the last
rule in the list contains the trailing whitespace characters and never matches
any existing rule.

I added the necessary trimming, as well as a test with 2 checks to go along
with it.
2020-09-26 11:12:26 +02:00
Andrew Imeson
67cb4eb24d Auto-change output format if GitHub Actions detected 2020-09-23 15:41:25 +02:00
Andrew Imeson
50c7453824 Add support for GitHub Annotations output format
Support the format used by GitHub Actions to annotate pull
requests with linter failures
2020-09-23 15:41:25 +02:00
Satoru SATOH
549b136a04 fix: add runtime dependency to setuptools
yamllint depends on pkg_resources.load_entry_point from setuptools to
make its command working, so this runtime dependency to setuptools is
necessary to be listed.
2020-09-14 10:14:10 +02:00
Satoru SATOH
333ae52c78 Add 'forbid' configurations to the braces and brackets rules
Add 'forbid' configuration parameters to the braces and brackets rules
to allow users to forbid the use of flow style collections, flow
mappings and flow sequences.
2020-09-09 20:01:25 +02:00
Julien Falque
0a88c55194 quoted-strings: Fix detecting strings with hashtag as requiring quotes 2020-09-08 11:53:06 +02:00
Julien Falque
ac19d1e427 octal-values: Prevent detection of 8 and 9 as octal values 2020-09-08 09:53:54 +02:00
Adrien Vergé
597e88bb7b docs: Make 'yaml-file' config documentation clearer
Related to https://github.com/adrienverge/yamllint/issues/311.
2020-09-02 18:23:51 +02:00
Satoru SATOH
29d2b50d50 enhancement: add some metadata to provide extra info in its PyPI page
Add some metadata (project_urls) to provide extra info in its PyPI page.

Signed-off-by: Satoru SATOH <satoru.satoh@gmail.com>
2020-09-02 18:02:27 +02:00
Satoru SATOH
4171cdafc9 Move setuptools' packaging configuration from setup.py to setup.cfg
Move setuptools' packaging configuration from setup.py to setup.cfg to
simplify setup.py and make its packaging more dedeclarative.

Signed-off-by: Satoru SATOH <satoru.satoh@gmail.com>
2020-09-02 18:02:27 +02:00
Sorin Sbarnea
d274543b72 docs: Add Python API usage example
Fixes: #297
2020-08-25 20:10:32 +02:00
Kirill Deyko
8da98f2122 commas: Fix example in documentation
Error in the example snippet, it would NOT pass otherwise actually:
```
$ cat test.yml
strange var:
  [10, 20,30, {x: 1, y: 2}]

$ yamllint -d "{extends: default, rules: {commas: {min-spaces-after: 1, max-spaces-after: 1}}}" test.yml
test.yml
  1:1       warning  missing document start "---"  (document-start)
  2:11      error    too few spaces after comma  (commas)
```
2020-08-18 20:07:54 +02:00
Benjamin Wuethrich
b65769c9d2 docs: Add default values to rules with options 2020-07-22 11:54:05 +02:00
Wolfgang Walther
b80997eba6 CI: Add build environment without UTF-8 locales to travis-ci
Preventing regressions like #285
2020-07-20 13:57:06 +02:00
Adrien Vergé
8b758d4e7e yamllint version 1.24.2 2020-07-16 09:35:08 +02:00
Wolfgang Walther
b5b436a3a4 Add global "locale" config option and make key-ordering rule locale-aware
Support sorting by locale with strcoll(). Properly handle case and accents.

Note: this is a second implementation, for context see:
https://github.com/adrienverge/yamllint/pull/280
https://github.com/adrienverge/yamllint/issues/285
https://github.com/adrienverge/yamllint/pull/288
2020-07-16 09:34:13 +02:00
Adrien Vergé
0fceca2354 yamllint version 1.24.1 2020-07-15 14:49:51 +02:00
Adrien Vergé
9403f1f3ec Revert "Add global "locale" config option"
This reverts commit 9e90c77, because it caused a bug that affected
different people just after being released:
https://github.com/adrienverge/yamllint/issues/285
https://github.com/adrienverge/yamllint/issues/286
2020-07-15 14:48:48 +02:00
Adrien Vergé
0016390e78 yamllint version 1.24.0 2020-07-15 11:50:36 +02:00
Wolfgang Walther
9e90c777cb Add global "locale" config option and make key-ordering rule locale-aware
Support sorting by locale with strcoll(). Properly handle case and accents.
2020-07-15 11:46:05 +02:00
Jonathan Sokolowski
a2218988ee config: Do no match directories that look like YAML files
Fixes #279
2020-07-10 09:27:34 +02:00
Adrien Vergé
954fdd5e8f style: Fix 'noqa' for flake8 3.8.0
There was a change in behavior of E402, see:
https://gitlab.com/pycqa/flake8/-/issues/638#note_345108633
2020-07-08 16:27:08 +02:00
Sorin Sbarnea
bbcad943b6 style: Ignore flake8 warnings W503 and W504
Avoid W503/W504 with current code as the current code not compliant
and they are contradictory.
2020-05-03 16:55:57 +02:00
Adrien Vergé
30c90dbf70 Add contribution instructions in CONTRIBUTING.rst
Closes https://github.com/adrienverge/yamllint/issues/263.
2020-05-03 16:51:22 +02:00
Brad Solomon
512fe17047 Fix bug with CRLF in new-lines and require-starting-space
Pound-signs followed by a lone CRLF should not
raise if require-starting-space is specified.

If require-starting-space is true, *and* either:
- new-lines: disbale, or
- newlines: type: dos
is specified, a line with `#\r` or `#\r\n` should
not raise a false positive.

This commit also uses a Set for O(1) membership testing
and uses the correct escape sequence for the nul byte.

If we find a CRLF when looking for Unix newlines, yamllint
should always raise, regardless of logic with
require-starting-space.

Closes: Issue #171.
2020-04-30 16:38:19 +02:00
Will Badart
278a79f093 Mention YAMLLINT_CONFIG_FILE in the documentation 2020-04-29 09:43:16 +02:00
Brad Solomon
e98aacf62c Add Python 3.8 to PyPI/trove classifier data
3.8 is now formally supported in .travis.yml
as of this commit.
2020-04-29 09:39:50 +02:00
Will Badart
94c0416f6b Specify config with environment variable YAMLLINT_CONFIG_FILE
Add option to specify config file with environment variable.
Add test case.
2020-04-28 11:13:32 +02:00
46 changed files with 1061 additions and 136 deletions

54
.github/workflows/ci.yaml vendored Normal file
View File

@@ -0,0 +1,54 @@
---
name: CI
on: # yamllint disable-line rule:truthy
push:
pull_request:
branches:
- master
jobs:
lint:
name: Linters
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.9
- run: python -m pip install flake8 flake8-import-order doc8 sphinx
- run: python -m pip install .
- run: flake8 .
- run: doc8 $(git ls-files '*.rst')
- run: yamllint --strict $(git ls-files '*.yaml' '*.yml')
- run: python setup.py build_sphinx
test:
name: Tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- 3.5
- 3.6
- 3.7
- 3.8
- 3.9
- nightly
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.pyver }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.pyver }}
- name: Fix GitHub Actions path
run: echo /home/runner/.local/bin >>$GITHUB_PATH
- run: pip install coveralls
- run: pip install .
- run: coverage run --source=yamllint -m unittest discover
- name: Coveralls
uses: AndreMiras/coveralls-python-action@develop

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ __pycache__
/dist
/yamllint.egg-info
/build
/.eggs

View File

@@ -1,24 +0,0 @@
---
dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069)
language: python
python:
- 2.7
- 3.5
- 3.6
- 3.7
- 3.8
- nightly
install:
- pip install pyyaml coveralls flake8 flake8-import-order doc8
- if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then pip install sphinx; fi
- pip install .
script:
- if [[ $TRAVIS_PYTHON_VERSION != nightly ]]; then flake8 .; fi
- if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then doc8 $(git ls-files '*.rst'); fi
- yamllint --strict $(git ls-files '*.yaml' '*.yml')
- coverage run --source=yamllint setup.py test
- if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then
python setup.py build_sphinx;
fi
after_success:
coveralls

View File

@@ -1,6 +1,57 @@
Changelog
=========
1.26.1 (2021-04-06)
-------------------
- Remove runtime dependency ``setuptools`` for Python < 3.8
- Fix ``line_length`` to skip all hash signs starting comment
1.26.0 (2021-01-29)
-------------------
- End support for Python 2 and Python 3.4, add support for Python 3.9
- Add ``forbid: non-empty`` option to ``braces`` and ``brackets`` rules
- Fix ``quoted-strings`` for explicit octal recognition
- Add documentation for integration with Arcanist
- Fix typos in changelog and README
- Stop using deprecated ``python setup.py test`` in tests
1.25.0 (2020-09-29)
-------------------
- Run tests on Travis both with and without UTF-8 locales
- Improve documentation with default values to rules with options
- Improve documentation with a Python API usage example
- Fix documentation on ``commas`` examples
- Packaging: move setuptools' configuration from ``setup.py`` to ``setup.cfg``
- Packaging: add extra info in PyPI metadata
- Improve documentation on ``yaml-files``
- Fix ``octal-values`` to prevent detection of ``8`` and ``9`` as octal values
- Fix ``quoted-strings`` Fix detecting strings with hashtag as requiring quotes
- Add ``forbid`` configuration to the ``braces`` and ``brackets`` rules
- Fix runtime dependencies missing ``setuptools``
- Add a new output format for GitHub Annotations (``--format github``)
- Fix DOS lines messing with rule IDs in directives
1.24.2 (2020-07-16)
-------------------
- Add ``locale`` config option and make ``key-ordering`` locale-aware
1.24.1 (2020-07-15)
-------------------
- Revert ``locale`` config option from version 1.24.0 because of a bug
1.24.0 (2020-07-15)
-------------------
- Specify config with environment variable ``YAMLLINT_CONFIG_FILE``
- Fix bug with CRLF in ``new-lines`` and ``require-starting-space``
- Do not run linter on directories whose names look like YAML files
- Add ``locale`` config option and make ``key-ordering`` locale-aware
1.23.0 (2020-04-17)
-------------------

46
CONTRIBUTING.rst Normal file
View File

@@ -0,0 +1,46 @@
Contributing
============
Pull requests are the best way to propose changes to the codebase.
Contributions are welcome, but they have to meet some criteria.
Pull Request Process
--------------------
1. Fork this Git repository and create your branch from ``master``.
2. Make sure the tests pass:
.. code:: bash
pip install --user .
python -m unittest discover # all tests...
python -m unittest tests/rules/test_commas.py # or just some tests (faster)
3. If you add code that should be tested, add tests.
4. Make sure the linters pass:
.. code:: bash
flake8 .
If you added/modified documentation:
.. code:: bash
doc8 $(git ls-files '*.rst')
If you touched YAML files:
.. code:: bash
yamllint --strict $(git ls-files '*.yaml' '*.yml')
5. If relevant, update documentation (either in ``docs`` directly or in rules
files themselves).
6. Write a `good commit message
<http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html>`_.
7. Then, open a pull request.

View File

@@ -19,11 +19,7 @@ indentation, etc.
:target: https://yamllint.readthedocs.io/en/latest/?badge=latest
:alt: Documentation status
Written in Python (compatible with Python 2 & 3).
⚠ Python 2 upstream support stopped on January 1, 2020. yamllint will keep
best-effort support for Python 2.7 until January 1, 2021. Passed that date,
yamllint will drop all Python 2-related code.
Written in Python (compatible with Python 3 only).
Documentation
-------------

View File

@@ -6,9 +6,9 @@ import sys
import os
from unittest.mock import MagicMock
sys.path.insert(0, os.path.abspath('..')) # noqa
sys.path.insert(0, os.path.abspath('..'))
from yamllint import __copyright__, APP_NAME, APP_VERSION
from yamllint import __copyright__, APP_NAME, APP_VERSION # noqa
# -- General configuration ------------------------------------------------

View File

@@ -16,6 +16,7 @@ following locations (by order of preference):
- ``.yamllint``, ``.yamllint.yaml`` or ``.yamllint.yml`` in the current working
directory
- the file referenced by ``$YAMLLINT_CONFIG_FILE``, if set
- ``$XDG_CONFIG_HOME/yamllint/config``
- ``~/.config/yamllint/config``
@@ -122,8 +123,8 @@ warning level problems, only error level ones.
YAML files extensions
---------------------
To configure what yamllint should consider as YAML files, set ``yaml-files``
configuration option. The default is:
To configure what yamllint should consider as YAML files when listing
directories, set ``yaml-files`` configuration option. The default is:
.. code-block:: yaml
@@ -188,3 +189,22 @@ Here is a more complex example:
ignore: |
*.ignore-trailing-spaces.yaml
ascii-art/*
Setting the locale
------------------
It is possible to set the ``locale`` option globally. This is passed to Python's
`locale.setlocale
<https://docs.python.org/3/library/locale.html#locale.setlocale>`_,
so an empty string ``""`` will use the system default locale, while e.g.
``"en_US.UTF-8"`` will use that.
Currently this only affects the ``key-ordering`` rule. The default will order
by Unicode code point number, while locales will sort case and accents
properly as well.
.. code-block:: yaml
extends: default
locale: en_US.UTF-8

View File

@@ -2,7 +2,17 @@ Development
===========
yamllint provides both a script and a Python module. The latter can be used to
write your own linting tools:
write your own linting tools.
Basic example of running the linter from Python:
.. code-block:: python
import yamllint
yaml_config = yamllint.config.YamlLintConfig("extends: default")
for p in yamllint.linter.run("example.yaml", yaml_config):
print(p.desc, p.line, p.rule)
.. automodule:: yamllint.linter
:members:

View File

@@ -17,3 +17,56 @@ Here is an example, to add in your .pre-commit-config.yaml
hooks:
- id: yamllint
args: [-c=/path/to/.yamllint]
Integration with GitHub Actions
-------------------------------
yamllint auto-detects when it's running inside of `GitHub
Actions<https://github.com/features/actions>` and automatically uses the suited
output format to decorate code with linting errors automatically. You can also
force the GitHub Actions output with ``yamllint --format github``.
An example workflow using GitHub Actions:
.. code:: yaml
---
name: yamllint test
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install yamllint
run: pip install yamllint
- name: Lint YAML files
run: yamllint .
Integration with Arcanist
-------------------------
You can configure yamllint to run on ``arc lint``. Here is an example
``.arclint`` file that makes use of this configuration.
.. code:: json
{
"linters": {
"yamllint": {
"type": "script-and-regex",
"script-and-regex.script": "yamllint",
"script-and-regex.regex": "/^(?P<line>\\d+):(?P<offset>\\d+) +(?P<severity>warning|error) +(?P<message>.*) +\\((?P<name>.*)\\)$/m",
"include": "(\\.(yml|yaml)$)"
}
}
}

View File

@@ -4,9 +4,69 @@ universal = 1
[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.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
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.5.*
include_package_data = True
install_requires =
pathspec >= 0.5.3
pyyaml
setuptools; python_version < "3.8"
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

View File

@@ -14,7 +14,7 @@
# 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 setuptools import find_packages, setup
from setuptools import setup
from yamllint import (__author__, __license__,
APP_NAME, APP_VERSION, APP_DESCRIPTION)
@@ -27,30 +27,4 @@ setup(
description=APP_DESCRIPTION.split('\n')[0],
long_description=APP_DESCRIPTION,
license=__license__,
keywords=['yaml', 'lint', 'linter', 'syntax', 'checker'],
url='https://github.com/adrienverge/yamllint',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Topic :: Software Development',
'Topic :: Software Development :: Debuggers',
'Topic :: Software Development :: Quality Assurance',
'Topic :: Software Development :: Testing',
],
packages=find_packages(exclude=['tests', 'tests.*']),
entry_points={'console_scripts': ['yamllint=yamllint.cli:run']},
package_data={'yamllint': ['conf/*.yaml']},
install_requires=['pathspec >=0.5.3', 'pyyaml'],
test_suite='tests',
)

View File

@@ -57,7 +57,7 @@ def build_temp_workspace(files):
tempdir = tempfile.mkdtemp(prefix='yamllint-tests-')
for path, content in files.items():
path = os.path.join(tempdir, path)
path = os.path.join(tempdir, path).encode('utf-8')
if not os.path.exists(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))

View File

@@ -31,6 +31,60 @@ class ColonTestCase(RuleTestCase):
'dict6: { a: 1, b, c: 3 }\n'
'dict7: { a: 1, b, c: 3 }\n', conf)
def test_forbid(self):
conf = ('braces:\n'
' forbid: false\n')
self.check('---\n'
'dict: {}\n', conf)
self.check('---\n'
'dict: {a}\n', conf)
self.check('---\n'
'dict: {a: 1}\n', conf)
self.check('---\n'
'dict: {\n'
' a: 1\n'
'}\n', conf)
conf = ('braces:\n'
' forbid: true\n')
self.check('---\n'
'dict:\n'
' a: 1\n', conf)
self.check('---\n'
'dict: {}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {a}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {a: 1}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {\n'
' a: 1\n'
'}\n', conf, problem=(2, 8))
conf = ('braces:\n'
' forbid: non-empty\n')
self.check('---\n'
'dict:\n'
' a: 1\n', conf)
self.check('---\n'
'dict: {}\n', conf)
self.check('---\n'
'dict: {\n'
'}\n', conf)
self.check('---\n'
'dict: {\n'
'# commented: value\n'
'# another: value2\n'
'}\n', conf)
self.check('---\n'
'dict: {a}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {a: 1}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {\n'
' a: 1\n'
'}\n', conf, problem=(2, 8))
def test_min_spaces(self):
conf = ('braces:\n'
' max-spaces-inside: -1\n'

View File

@@ -31,6 +31,58 @@ class ColonTestCase(RuleTestCase):
'array6: [ a, b, c ]\n'
'array7: [ a, b, c ]\n', conf)
def test_forbid(self):
conf = ('brackets:\n'
' forbid: false\n')
self.check('---\n'
'array: []\n', conf)
self.check('---\n'
'array: [a, b]\n', conf)
self.check('---\n'
'array: [\n'
' a,\n'
' b\n'
']\n', conf)
conf = ('brackets:\n'
' forbid: true\n')
self.check('---\n'
'array:\n'
' - a\n'
' - b\n', conf)
self.check('---\n'
'array: []\n', conf, problem=(2, 9))
self.check('---\n'
'array: [a, b]\n', conf, problem=(2, 9))
self.check('---\n'
'array: [\n'
' a,\n'
' b\n'
']\n', conf, problem=(2, 9))
conf = ('brackets:\n'
' forbid: non-empty\n')
self.check('---\n'
'array:\n'
' - a\n'
' - b\n', conf)
self.check('---\n'
'array: []\n', conf)
self.check('---\n'
'array: [\n\n'
']\n', conf)
self.check('---\n'
'array: [\n'
'# a comment\n'
']\n', conf)
self.check('---\n'
'array: [a, b]\n', conf, problem=(2, 9))
self.check('---\n'
'array: [\n'
' a,\n'
' b\n'
']\n', conf, problem=(2, 9))
def test_min_spaces(self):
conf = ('brackets:\n'
' max-spaces-inside: -1\n'

View File

@@ -186,6 +186,27 @@ class CommentsTestCase(RuleTestCase):
'inline: comment #\n'
'foo: bar\n', conf)
def test_empty_comment_crlf_dos_newlines(self):
conf = ('comments:\n'
' require-starting-space: true\n'
' min-spaces-from-content: 2\n'
'new-lines:\n'
' type: dos\n')
self.check('---\r\n'
'# This is paragraph 1.\r\n'
'#\r\n'
'# This is paragraph 2.\r\n', conf)
def test_empty_comment_crlf_disabled_newlines(self):
conf = ('comments:\n'
' require-starting-space: true\n'
' min-spaces-from-content: 2\n'
'new-lines: disable\n')
self.check('---\r\n'
'# This is paragraph 1.\r\n'
'#\r\n'
'# This is paragraph 2.\r\n', conf)
def test_first_line(self):
conf = ('comments:\n'
' require-starting-space: true\n'

View File

@@ -14,6 +14,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import locale
from tests.common import RuleTestCase
@@ -103,10 +105,6 @@ class KeyOrderingTestCase(RuleTestCase):
'haïr: true\n'
'hais: true\n', conf,
problem=(3, 1))
self.check('---\n'
'haïr: true\n'
'hais: true\n', conf,
problem=(3, 1))
def test_key_tokens_in_flow_sequences(self):
conf = 'key-ordering: enable'
@@ -114,3 +112,39 @@ class KeyOrderingTestCase(RuleTestCase):
'[\n'
' key: value, mappings, in, flow: sequence\n'
']\n', conf)
def test_locale_case(self):
self.addCleanup(locale.setlocale, locale.LC_ALL, (None, None))
try:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
except locale.Error:
self.skipTest('locale en_US.UTF-8 not available')
conf = ('key-ordering: enable')
self.check('---\n'
't-shirt: 1\n'
'T-shirt: 2\n'
't-shirts: 3\n'
'T-shirts: 4\n', conf)
self.check('---\n'
't-shirt: 1\n'
't-shirts: 2\n'
'T-shirt: 3\n'
'T-shirts: 4\n', conf,
problem=(4, 1))
def test_locale_accents(self):
self.addCleanup(locale.setlocale, locale.LC_ALL, (None, None))
try:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
except locale.Error:
self.skipTest('locale en_US.UTF-8 not available')
conf = ('key-ordering: enable')
self.check('---\n'
'hair: true\n'
'haïr: true\n'
'hais: true\n'
'haïssable: true\n', conf)
self.check('---\n'
'hais: true\n'
'haïr: true\n', conf,
problem=(3, 1))

View File

@@ -14,9 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
import unittest
from tests.common import RuleTestCase
@@ -119,6 +116,27 @@ class LineLengthTestCase(RuleTestCase):
'long_line: http://localhost/very/very/long/url\n'
'...\n', conf, problem=(2, 21))
conf = 'line-length: {max: 20, allow-non-breakable-words: true}'
self.check('---\n'
'# http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf)
self.check('---\n'
'## http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf)
self.check('---\n'
'# # http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf,
problem=(2, 21))
self.check('---\n'
'#A http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf,
problem1=(2, 2, 'comments'),
problem2=(2, 21, 'line-length'))
conf = ('line-length: {max: 20, allow-non-breakable-words: true}\n'
'trailing-spaces: disable')
self.check('---\n'
@@ -159,7 +177,6 @@ class LineLengthTestCase(RuleTestCase):
' {% this line is' + 99 * ' really' + ' long %}\n',
conf, problem=(3, 81))
@unittest.skipIf(sys.version_info < (3, 0), 'Python 2 not supported')
def test_unicode(self):
conf = 'line-length: {max: 53}'
self.check('---\n'

View File

@@ -40,6 +40,16 @@ class NewLinesTestCase(RuleTestCase):
self.check('---\ntext\n', conf)
self.check('---\r\ntext\r\n', conf, problem=(1, 4))
def test_unix_type_required_st_sp(self):
# If we find a CRLF when looking for Unix newlines, yamllint
# should always raise, regardless of logic with
# require-starting-space.
conf = ('new-line-at-end-of-file: disable\n'
'new-lines: {type: unix}\n'
'comments:\n'
' require-starting-space: true\n')
self.check('---\r\n#\r\n', conf, problem=(1, 4))
def test_dos_type(self):
conf = ('new-line-at-end-of-file: disable\n'
'new-lines: {type: dos}\n')

View File

@@ -50,6 +50,8 @@ class OctalValuesTestCase(RuleTestCase):
' - 0.10\n'
' - .01\n'
' - 0e3\n', conf)
self.check('with-decimal-digits: 012345678', conf)
self.check('with-decimal-digits: 012345679', conf)
def test_explicit_octal_values(self):
conf = ('octal-values:\n'
@@ -74,3 +76,5 @@ class OctalValuesTestCase(RuleTestCase):
' - .01\n'
' - 0e3\n', conf)
self.check('user-city: "010"', conf)
self.check('with-decimal-digits: 0o012345678', conf)
self.check('with-decimal-digits: 0o012345679', conf)

View File

@@ -330,7 +330,8 @@ class QuotedTestCase(RuleTestCase):
'- "%wheel ALL=(ALL) NOPASSWD: ALL"\n'
'- \'"quoted"\'\n'
'- "\'foo\' == \'bar\'"\n'
'- "\'Mac\' in ansible_facts.product_name"\n',
'- "\'Mac\' in ansible_facts.product_name"\n'
'- \'foo # bar\'\n',
conf)
self.check('---\n'
'k1: ""\n'
@@ -435,3 +436,21 @@ class QuotedTestCase(RuleTestCase):
'- foo bar\n'
'- "foo bar"\n',
conf, problem1=(3, 3), problem2=(7, 3), problem3=(11, 3))
def test_octal_values(self):
conf = 'quoted-strings: {required: true}\n'
self.check('---\n'
'- 100\n'
'- 0100\n'
'- 0o100\n'
'- 777\n'
'- 0777\n'
'- 0o777\n'
'- 800\n'
'- 0800\n'
'- 0o800\n'
'- "0800"\n'
'- "0o800"\n',
conf,
problem1=(9, 3), problem2=(10, 3))

View File

@@ -14,16 +14,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
from io import StringIO
import fcntl
import locale
import os
import pty
import shutil
import sys
import tempfile
import unittest
from tests.common import build_temp_workspace
@@ -55,6 +53,16 @@ class RunContext(object):
return self._raises_ctx.exception.code
# Check system's UTF-8 availability
def utf8_available():
try:
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
locale.setlocale(locale.LC_ALL, (None, None))
return True
except locale.Error:
return False
class CommandLineTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
@@ -72,6 +80,9 @@ class CommandLineTestCase(unittest.TestCase):
# file in dir
'sub/ok.yaml': '---\n'
'key: value\n',
# directory that looks like a yaml file
'sub/directory.yaml/not-yaml.txt': '',
'sub/directory.yaml/empty.yml': '',
# file in very nested dir
's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml': '---\n'
'key: value\n'
@@ -82,7 +93,7 @@ class CommandLineTestCase(unittest.TestCase):
'no-yaml.json': '---\n'
'key: value\n',
# non-ASCII chars
'non-ascii/éçäγλνπ¥/utf-8': (
u'non-ascii/éçäγλνπ¥/utf-8': (
u'---\n'
u'- hétérogénéité\n'
u'# 19.99 €\n'
@@ -91,6 +102,13 @@ class CommandLineTestCase(unittest.TestCase):
# dos line endings yaml
'dos.yml': '---\r\n'
'dos: true',
# different key-ordering by locale
'c.yaml': '---\n'
'A: true\n'
'a: true',
'en.yaml': '---\n'
'a: true\n'
'A: true'
})
@classmethod
@@ -99,14 +117,19 @@ class CommandLineTestCase(unittest.TestCase):
shutil.rmtree(cls.wd)
@unittest.skipIf(not utf8_available() and sys.version_info < (3, 7),
'UTF-8 paths not supported')
def test_find_files_recursively(self):
conf = config.YamlLintConfig('extends: default')
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[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')],
)
@@ -131,6 +154,7 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(
sorted(cli.find_files_recursively(items, conf)),
[os.path.join(self.wd, '/etc/another/file'),
os.path.join(self.wd, 'sub/directory.yaml/empty.yml'),
os.path.join(self.wd, 'sub/ok.yaml')],
)
@@ -140,6 +164,8 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[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, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'),
os.path.join(self.wd, 'sub/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')]
@@ -151,7 +177,8 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'dos.yml'),
os.path.join(self.wd, 'empty.yml')]
os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 'sub/directory.yaml/empty.yml')]
)
conf = config.YamlLintConfig('extends: default\n'
@@ -168,11 +195,15 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[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, 'no-yaml.json'),
os.path.join(self.wd, 'non-ascii/éçäγλνπ¥/utf-8'),
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/directory.yaml/not-yaml.txt'),
os.path.join(self.wd, 'sub/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')]
)
@@ -185,11 +216,15 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[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, 'no-yaml.json'),
os.path.join(self.wd, 'non-ascii/éçäγλνπ¥/utf-8'),
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/directory.yaml/not-yaml.txt'),
os.path.join(self.wd, 'sub/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')]
)
@@ -208,19 +243,19 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(())
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^usage')
self.assertRegex(ctx.stderr, r'^usage')
with RunContext(self) as ctx:
cli.run(('--unknown-arg', ))
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^usage')
self.assertRegex(ctx.stderr, r'^usage')
with RunContext(self) as ctx:
cli.run(('-c', './conf.yaml', '-d', 'relaxed', 'file'))
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(
self.assertRegex(
ctx.stderr.splitlines()[-1],
r'^yamllint: error: argument -d\/--config-data: '
r'not allowed with argument -c\/--config-file$'
@@ -231,21 +266,21 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-', 'file'))
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^usage')
self.assertRegex(ctx.stderr, r'^usage')
def test_run_with_bad_config(self):
with RunContext(self) as ctx:
cli.run(('-d', 'rules: {a: b}', 'file'))
self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^invalid config: no such rule')
self.assertRegex(ctx.stderr, r'^invalid config: no such rule')
def test_run_with_empty_config(self):
with RunContext(self) as ctx:
cli.run(('-d', '', 'file'))
self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^invalid config: not a dict')
self.assertRegex(ctx.stderr, r'^invalid config: not a dict')
def test_run_with_config_file(self):
with open(os.path.join(self.wd, 'config'), 'w') as f:
@@ -262,6 +297,7 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.returncode, 1)
@unittest.skipIf(os.environ.get('GITHUB_RUN_ID'), '$HOME not overridable')
def test_run_with_user_global_config_file(self):
home = os.path.join(self.wd, 'fake-home')
dir = os.path.join(home, '.config', 'yamllint')
@@ -285,11 +321,70 @@ class CommandLineTestCase(unittest.TestCase):
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.returncode, 1)
def test_run_with_user_yamllint_config_file_in_env(self):
self.addCleanup(os.environ.__delitem__, 'YAMLLINT_CONFIG_FILE')
with tempfile.NamedTemporaryFile('w') as f:
os.environ['YAMLLINT_CONFIG_FILE'] = f.name
f.write('rules: {trailing-spaces: disable}')
f.flush()
with RunContext(self) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.returncode, 0)
with tempfile.NamedTemporaryFile('w') as f:
os.environ['YAMLLINT_CONFIG_FILE'] = f.name
f.write('rules: {trailing-spaces: enable}')
f.flush()
with RunContext(self) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.returncode, 1)
def test_run_with_locale(self):
# check for availability of locale, otherwise skip the test
# reset to default before running the test,
# as the first two runs don't use setlocale()
try:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
except locale.Error:
self.skipTest('locale en_US.UTF-8 not available')
locale.setlocale(locale.LC_ALL, (None, None))
# C + en.yaml should fail
with RunContext(self) as ctx:
cli.run(('-d', 'rules: { key-ordering: enable }',
os.path.join(self.wd, 'en.yaml')))
self.assertEqual(ctx.returncode, 1)
# C + c.yaml should pass
with RunContext(self) as ctx:
cli.run(('-d', 'rules: { key-ordering: enable }',
os.path.join(self.wd, 'c.yaml')))
self.assertEqual(ctx.returncode, 0)
# the next two runs use setlocale() inside,
# so we need to clean up afterwards
self.addCleanup(locale.setlocale, locale.LC_ALL, (None, None))
# en_US + en.yaml should pass
with RunContext(self) as ctx:
cli.run(('-d', 'locale: en_US.UTF-8\n'
'rules: { key-ordering: enable }',
os.path.join(self.wd, 'en.yaml')))
self.assertEqual(ctx.returncode, 0)
# en_US + c.yaml should fail
with RunContext(self) as ctx:
cli.run(('-d', 'locale: en_US.UTF-8\n'
'rules: { key-ordering: enable }',
os.path.join(self.wd, 'c.yaml')))
self.assertEqual(ctx.returncode, 1)
def test_run_version(self):
with RunContext(self) as ctx:
cli.run(('--version', ))
self.assertEqual(ctx.returncode, 0)
self.assertRegexpMatches(ctx.stdout + ctx.stderr, r'yamllint \d+\.\d+')
self.assertRegex(ctx.stdout + ctx.stderr, r'yamllint \d+\.\d+')
def test_run_non_existing_file(self):
path = os.path.join(self.wd, 'i-do-not-exist.yaml')
@@ -298,7 +393,7 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-f', 'parsable', path))
self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'No such file or directory')
self.assertRegex(ctx.stderr, r'No such file or directory')
def test_run_one_problem_file(self):
path = os.path.join(self.wd, 'a.yaml')
@@ -340,18 +435,12 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-f', 'parsable', path))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', ''))
@unittest.skipIf(not utf8_available(), 'C.UTF-8 not available')
def test_run_non_ascii_file(self):
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
self.addCleanup(locale.setlocale, locale.LC_ALL, (None, None))
path = os.path.join(self.wd, 'non-ascii', 'éçäγλνπ¥', 'utf-8')
# Make sure the default localization conditions on this "system"
# support UTF-8 encoding.
loc = locale.getlocale()
try:
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
except locale.Error:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
self.addCleanup(locale.setlocale, locale.LC_ALL, loc)
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', path))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', ''))
@@ -458,6 +547,38 @@ class CommandLineTestCase(unittest.TestCase):
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
def test_run_format_github(self):
path = os.path.join(self.wd, 'a.yaml')
with RunContext(self) as ctx:
cli.run((path, '--format', 'github'))
expected_out = (
'::error file=%s,line=2,col=4::[trailing-spaces] trailing'
' spaces\n'
'::error file=%s,line=3,col=4::[new-line-at-end-of-file] no'
' new line character at the end of file\n'
% (path, path))
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
def test_github_actions_detection(self):
path = os.path.join(self.wd, 'a.yaml')
self.addCleanup(os.environ.__delitem__, 'GITHUB_ACTIONS')
self.addCleanup(os.environ.__delitem__, 'GITHUB_WORKFLOW')
with RunContext(self) as ctx:
os.environ['GITHUB_ACTIONS'] = 'something'
os.environ['GITHUB_WORKFLOW'] = 'something'
cli.run((path, ))
expected_out = (
'::error file=%s,line=2,col=4::[trailing-spaces] trailing'
' spaces\n'
'::error file=%s,line=3,col=4::[new-line-at-end-of-file] no'
' new line character at the end of file\n'
% (path, path))
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
def test_run_read_from_stdin(self):
# prepares stdin with an invalid yaml string so that we can check
# for its specific error, and be assured that stdin was read

View File

@@ -14,10 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
from io import StringIO
import os
import shutil
import sys
@@ -48,7 +45,7 @@ class SimpleConfigTestCase(unittest.TestCase):
config.YamlLintConfig('not: valid: yaml')
def test_unknown_rule(self):
with self.assertRaisesRegexp(
with self.assertRaisesRegex(
config.YamlLintConfigError,
'invalid config: no such rule: "this-one-does-not-exist"'):
config.YamlLintConfig('rules:\n'
@@ -67,7 +64,7 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertEqual(c.rules['colons']['max-spaces-after'], 1)
def test_unknown_option(self):
with self.assertRaisesRegexp(
with self.assertRaisesRegex(
config.YamlLintConfigError,
'invalid config: unknown option "abcdef" for rule "colons"'):
config.YamlLintConfig('rules:\n'
@@ -105,7 +102,7 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertEqual(c.rules['indentation']['check-multi-line-strings'],
False)
with self.assertRaisesRegexp(
with self.assertRaisesRegex(
config.YamlLintConfigError,
'invalid config: option "indent-sequences" of "indentation" '
'should be in '):

View File

@@ -47,16 +47,15 @@ class ModuleTestCase(unittest.TestCase):
subprocess.check_output([PYTHON, '-m', 'yamllint'],
stderr=subprocess.STDOUT)
self.assertEqual(ctx.exception.returncode, 2)
self.assertRegexpMatches(ctx.exception.output.decode(),
r'^usage: yamllint')
self.assertRegex(ctx.exception.output.decode(), r'^usage: yamllint')
def test_run_module_on_bad_dir(self):
with self.assertRaises(subprocess.CalledProcessError) as ctx:
subprocess.check_output([PYTHON, '-m', 'yamllint',
'/does/not/exist'],
stderr=subprocess.STDOUT)
self.assertRegexpMatches(ctx.exception.output.decode(),
r'No such file or directory')
self.assertRegex(ctx.exception.output.decode(),
r'No such file or directory')
def test_run_module_on_file(self):
out = subprocess.check_output(

View File

@@ -232,6 +232,34 @@ class YamllintDirectivesTestCase(RuleTestCase):
problem1=(3, 18, 'trailing-spaces'),
problem2=(4, 8, 'colons'))
def test_disable_directive_with_rules_and_dos_lines(self):
conf = self.conf + 'new-lines: {type: dos}\n'
self.check('---\r\n'
'- [valid , YAML]\r\n'
'# yamllint disable rule:trailing-spaces\r\n'
'- trailing spaces \r\n'
'- bad : colon\r\n'
'- [valid , YAML]\r\n'
'# yamllint enable rule:trailing-spaces\r\n'
'- bad : colon and spaces \r\n'
'- [valid , YAML]\r\n',
conf,
problem1=(5, 8, 'colons'),
problem2=(8, 7, 'colons'),
problem3=(8, 26, 'trailing-spaces'))
self.check('---\r\n'
'- [valid , YAML]\r\n'
'- trailing spaces \r\n'
'- bad : colon\r\n'
'- [valid , YAML]\r\n'
'# yamllint disable-line rule:colons\r\n'
'- bad : colon and spaces \r\n'
'- [valid , YAML]\r\n',
conf,
problem1=(3, 18, 'trailing-spaces'),
problem2=(4, 8, 'colons'),
problem3=(7, 26, 'trailing-spaces'))
def test_directive_on_last_line(self):
conf = 'new-line-at-end-of-file: {}'
self.check('---\n'

View File

@@ -22,7 +22,7 @@ indentation, etc."""
APP_NAME = 'yamllint'
APP_VERSION = '1.23.0'
APP_VERSION = '1.26.1'
APP_DESCRIPTION = __doc__
__author__ = u'Adrien Vergé'

View File

@@ -18,6 +18,7 @@ from __future__ import print_function
import argparse
import io
import locale
import os
import platform
import sys
@@ -84,6 +85,19 @@ class Format(object):
line += ' \033[2m(%s)\033[0m' % problem.rule
return line
@staticmethod
def github(problem, filename):
line = '::'
line += problem.level
line += ' file=' + filename + ','
line += 'line=' + format(problem.line) + ','
line += 'col=' + format(problem.column)
line += '::'
if problem.rule:
line += '[' + problem.rule + '] '
line += problem.desc
return line
def show_problems(problems, file, args_format, no_warn):
max_level = 0
@@ -95,6 +109,10 @@ def show_problems(problems, file, args_format, no_warn):
continue
if args_format == 'parsable':
print(Format.parsable(problem, file))
elif args_format == 'github' or (args_format == 'auto' and
'GITHUB_ACTIONS' in os.environ and
'GITHUB_WORKFLOW' in os.environ):
print(Format.github(problem, file))
elif args_format == 'colored' or \
(args_format == 'auto' and supports_color()):
if first:
@@ -130,7 +148,8 @@ def run(argv=None):
action='store',
help='custom configuration (as YAML source)')
parser.add_argument('-f', '--format',
choices=('parsable', 'standard', 'colored', 'auto'),
choices=('parsable', 'standard', 'colored', 'github',
'auto'),
default='auto', help='format for parsing output')
parser.add_argument('-s', '--strict',
action='store_true',
@@ -144,8 +163,11 @@ def run(argv=None):
args = parser.parse_args(argv)
if 'YAMLLINT_CONFIG_FILE' in os.environ:
user_global_config = os.path.expanduser(
os.environ['YAMLLINT_CONFIG_FILE'])
# User-global config is supposed to be in ~/.config/yamllint/config
if 'XDG_CONFIG_HOME' in os.environ:
elif 'XDG_CONFIG_HOME' in os.environ:
user_global_config = os.path.join(
os.environ['XDG_CONFIG_HOME'], 'yamllint', 'config')
else:
@@ -172,6 +194,9 @@ def run(argv=None):
print(e, file=sys.stderr)
sys.exit(-1)
if conf.locale is not None:
locale.setlocale(locale.LC_ALL, conf.locale)
max_level = 0
for file in find_files_recursively(args.files, conf):

View File

@@ -35,6 +35,8 @@ class YamlLintConfig(object):
self.yaml_files = pathspec.PathSpec.from_lines(
'gitwildmatch', ['*.yaml', '*.yml', '.yamllint'])
self.locale = None
if file is not None:
with open(file) as f:
content = f.read()
@@ -46,7 +48,7 @@ class YamlLintConfig(object):
return self.ignore and self.ignore.match_file(filepath)
def is_yaml_file(self, filepath):
return self.yaml_files.match_file(filepath)
return self.yaml_files.match_file(os.path.basename(filepath))
def enabled_rules(self, filepath):
return [yamllint.rules.get(id) for id, val in self.rules.items()
@@ -111,6 +113,12 @@ class YamlLintConfig(object):
self.yaml_files = pathspec.PathSpec.from_lines('gitwildmatch',
conf['yaml-files'])
if 'locale' in conf:
if not isinstance(conf['locale'], str):
raise YamlLintConfigError(
'invalid config: locale should be a string')
self.locale = conf['locale']
def validate(self):
for id in self.rules:
try:

View File

@@ -87,7 +87,8 @@ def get_cosmetic_problems(buffer, conf, filepath):
return # this certainly wasn't a yamllint directive comment
if re.match(r'^# yamllint disable( rule:\S+)*\s*$', comment):
rules = [item[5:] for item in comment[18:].split(' ')][1:]
items = comment[18:].rstrip().split(' ')
rules = [item[5:] for item in items][1:]
if len(rules) == 0:
self.rules = self.all_rules.copy()
else:
@@ -96,7 +97,8 @@ def get_cosmetic_problems(buffer, conf, filepath):
self.rules.add(id)
elif re.match(r'^# yamllint enable( rule:\S+)*\s*$', comment):
rules = [item[5:] for item in comment[17:].split(' ')][1:]
items = comment[17:].rstrip().split(' ')
rules = [item[5:] for item in items][1:]
if len(rules) == 0:
self.rules.clear()
else:
@@ -114,7 +116,8 @@ def get_cosmetic_problems(buffer, conf, filepath):
return # this certainly wasn't a yamllint directive comment
if re.match(r'^# yamllint disable-line( rule:\S+)*\s*$', comment):
rules = [item[5:] for item in comment[23:].split(' ')][1:]
items = comment[23:].rstrip().split(' ')
rules = [item[5:] for item in items][1:]
if len(rules) == 0:
self.rules = self.all_rules.copy()
else:
@@ -230,7 +233,7 @@ def run(input, conf, filepath=None):
if conf.is_file_ignored(filepath):
return ()
if isinstance(input, (type(b''), type(u''))): # compat with Python 2 & 3
if isinstance(input, (bytes, str)):
return _run(input, conf, filepath)
elif hasattr(input, 'read'): # Python 2's file or Python 3's io.IOBase
# We need to have everything in memory to parse correctly

View File

@@ -15,10 +15,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Use this rule to control the number of spaces inside braces (``{`` and ``}``).
Use this rule to control the use of flow mappings or number of spaces inside
braces (``{`` and ``}``).
.. rubric:: Options
* ``forbid`` is used to forbid the use of flow mappings which are denoted by
surrounding braces (``{`` and ``}``). Use ``true`` to forbid the use of flow
mappings completely. Use ``non-empty`` to forbid the use of all flow
mappings except for empty ones.
* ``min-spaces-inside`` defines the minimal number of spaces required inside
braces.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside
@@ -28,8 +33,46 @@ Use this rule to control the number of spaces inside braces (``{`` and ``}``).
* ``max-spaces-inside-empty`` defines the maximal number of spaces allowed
inside empty braces.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
braces:
forbid: false
min-spaces-inside: 0
max-spaces-inside: 0
min-spaces-inside-empty: -1
max-spaces-inside-empty: -1
.. rubric:: Examples
#. With ``braces: {forbid: true}``
the following code snippet would **PASS**:
::
object:
key1: 4
key2: 8
the following code snippet would **FAIL**:
::
object: { key1: 4, key2: 8 }
#. With ``braces: {forbid: non-empty}``
the following code snippet would **PASS**:
::
object: {}
the following code snippet would **FAIL**:
::
object: { key1: 4, key2: 8 }
#. With ``braces: {min-spaces-inside: 0, max-spaces-inside: 0}``
the following code snippet would **PASS**:
@@ -92,23 +135,39 @@ Use this rule to control the number of spaces inside braces (``{`` and ``}``).
import yaml
from yamllint.linter import LintProblem
from yamllint.rules.common import spaces_after, spaces_before
ID = 'braces'
TYPE = 'token'
CONF = {'min-spaces-inside': int,
CONF = {'forbid': (bool, 'non-empty'),
'min-spaces-inside': int,
'max-spaces-inside': int,
'min-spaces-inside-empty': int,
'max-spaces-inside-empty': int}
DEFAULT = {'min-spaces-inside': 0,
DEFAULT = {'forbid': False,
'min-spaces-inside': 0,
'max-spaces-inside': 0,
'min-spaces-inside-empty': -1,
'max-spaces-inside-empty': -1}
def check(conf, token, prev, next, nextnext, context):
if (isinstance(token, yaml.FlowMappingStartToken) and
if (conf['forbid'] is True and
isinstance(token, yaml.FlowMappingStartToken)):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow mapping')
elif (conf['forbid'] == 'non-empty' and
isinstance(token, yaml.FlowMappingStartToken) and
not isinstance(next, yaml.FlowMappingEndToken)):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow mapping')
elif (isinstance(token, yaml.FlowMappingStartToken) and
isinstance(next, yaml.FlowMappingEndToken)):
problem = spaces_after(token, prev, next,
min=(conf['min-spaces-inside-empty']

View File

@@ -15,11 +15,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Use this rule to control the number of spaces inside brackets (``[`` and
``]``).
Use this rule to control the use of flow sequences or the number of spaces
inside brackets (``[`` and ``]``).
.. rubric:: Options
* ``forbid`` is used to forbid the use of flow sequences which are denoted by
surrounding brackets (``[`` and ``]``). Use ``true`` to forbid the use of
flow sequences completely. Use ``non-empty`` to forbid the use of all flow
sequences except for empty ones.
* ``min-spaces-inside`` defines the minimal number of spaces required inside
brackets.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside
@@ -29,8 +33,47 @@ Use this rule to control the number of spaces inside brackets (``[`` and
* ``max-spaces-inside-empty`` defines the maximal number of spaces allowed
inside empty brackets.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
brackets:
forbid: false
min-spaces-inside: 0
max-spaces-inside: 0
min-spaces-inside-empty: -1
max-spaces-inside-empty: -1
.. rubric:: Examples
#. With ``brackets: {forbid: true}``
the following code snippet would **PASS**:
::
object:
- 1
- 2
- abc
the following code snippet would **FAIL**:
::
object: [ 1, 2, abc ]
#. With ``brackets: {forbid: non-empty}``
the following code snippet would **PASS**:
::
object: []
the following code snippet would **FAIL**:
::
object: [ 1, 2, abc ]
#. With ``brackets: {min-spaces-inside: 0, max-spaces-inside: 0}``
the following code snippet would **PASS**:
@@ -93,23 +136,39 @@ Use this rule to control the number of spaces inside brackets (``[`` and
import yaml
from yamllint.linter import LintProblem
from yamllint.rules.common import spaces_after, spaces_before
ID = 'brackets'
TYPE = 'token'
CONF = {'min-spaces-inside': int,
CONF = {'forbid': (bool, 'non-empty'),
'min-spaces-inside': int,
'max-spaces-inside': int,
'min-spaces-inside-empty': int,
'max-spaces-inside-empty': int}
DEFAULT = {'min-spaces-inside': 0,
DEFAULT = {'forbid': False,
'min-spaces-inside': 0,
'max-spaces-inside': 0,
'min-spaces-inside-empty': -1,
'max-spaces-inside-empty': -1}
def check(conf, token, prev, next, nextnext, context):
if (isinstance(token, yaml.FlowSequenceStartToken) and
if (conf['forbid'] is True and
isinstance(token, yaml.FlowSequenceStartToken)):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow sequence')
elif (conf['forbid'] == 'non-empty' and
isinstance(token, yaml.FlowSequenceStartToken) and
not isinstance(next, yaml.FlowSequenceEndToken)):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow sequence')
elif (isinstance(token, yaml.FlowSequenceStartToken) and
isinstance(next, yaml.FlowSequenceEndToken)):
problem = spaces_after(token, prev, next,
min=(conf['min-spaces-inside-empty']

View File

@@ -24,6 +24,15 @@ Use this rule to control the number of spaces before and after colons (``:``).
* ``max-spaces-after`` defines the maximal number of spaces allowed after
colons (use ``-1`` to disable).
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
colons:
max-spaces-before: 0
max-spaces-after: 1
.. rubric:: Examples
#. With ``colons: {max-spaces-before: 0, max-spaces-after: 1}``

View File

@@ -26,6 +26,16 @@ Use this rule to control the number of spaces before and after commas (``,``).
* ``max-spaces-after`` defines the maximal number of spaces allowed after
commas (use ``-1`` to disable).
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
commas:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
.. rubric:: Examples
#. With ``commas: {max-spaces-before: 0}``
@@ -66,7 +76,7 @@ Use this rule to control the number of spaces before and after commas (``,``).
::
strange var:
[10, 20,30, {x: 1, y: 2}]
[10, 20, 30, {x: 1, y: 2}]
the following code snippet would **FAIL**:
::

View File

@@ -28,6 +28,16 @@ Use this rule to control the position and formatting of comments.
content. It defines the minimal required number of spaces between a comment
and its preceding content.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
comments:
require-starting-space: true
ignore-shebangs: true
min-spaces-from-content: 2
.. rubric:: Examples
#. With ``comments: {require-starting-space: true}``
@@ -97,7 +107,9 @@ def check(conf, comment):
comment.column_no == 1 and
re.match(r'^!\S', comment.buffer[text_start:])):
return
elif comment.buffer[text_start] not in (' ', '\n', '\0'):
# We can test for both \r and \r\n just by checking first char
# \r itself is a valid newline on some older OS.
elif comment.buffer[text_start] not in {' ', '\n', '\r', '\x00'}:
column = comment.column_no + text_start - comment.pointer
yield LintProblem(comment.line_no,
column,

View File

@@ -22,6 +22,14 @@ Use this rule to require or forbid the use of document end marker (``...``).
* Set ``present`` to ``true`` when the document end marker is required, or to
``false`` when it is forbidden.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
document-end:
present: true
.. rubric:: Examples
#. With ``document-end: {present: true}``

View File

@@ -22,6 +22,14 @@ Use this rule to require or forbid the use of document start marker (``---``).
* Set ``present`` to ``true`` when the document start marker is required, or to
``false`` when it is forbidden.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
document-start:
present: true
.. rubric:: Examples
#. With ``document-start: {present: true}``

View File

@@ -25,6 +25,16 @@ Use this rule to set a maximal number of allowed consecutive blank lines.
* ``max-end`` defines the maximal number of empty lines allowed at the end of
the file. This option takes precedence over ``max``.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
empty-lines:
max: 2
max-start: 0
max-end: 0
.. rubric:: Examples
#. With ``empty-lines: {max: 1}``

View File

@@ -23,6 +23,15 @@ Use this rule to prevent nodes with empty content, that implicitly result in
* Use ``forbid-in-block-mappings`` to prevent empty values in block mappings.
* Use ``forbid-in-flow-mappings`` to prevent empty values in flow mappings.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
empty-values:
forbid-in-block-mappings: true
forbid-in-flow-mappings: true
.. rubric:: Examples
#. With ``empty-values: {forbid-in-block-mappings: true}``

View File

@@ -22,6 +22,14 @@ Use this rule to control the number of spaces after hyphens (``-``).
* ``max-spaces-after`` defines the maximal number of spaces allowed after
hyphens.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
hyphens:
max-spaces-after: 1
.. rubric:: Examples
#. With ``hyphens: {max-spaces-after: 1}``

View File

@@ -32,6 +32,16 @@ Use this rule to control the indentation.
* ``check-multi-line-strings`` defines whether to lint indentation in
multi-line strings. Set to ``true`` to enable, ``false`` to disable.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
indentation:
spaces: consistent
indent-sequences: true
check-multi-line-strings: false
.. rubric:: Examples
#. With ``indentation: {spaces: 1}``

View File

@@ -16,8 +16,10 @@
"""
Use this rule to enforce alphabetical ordering of keys in mappings. The sorting
order uses the Unicode code point number. As a result, the ordering is
case-sensitive and not accent-friendly (see examples below).
order uses the Unicode code point number as a default. As a result, the
ordering is case-sensitive and not accent-friendly (see examples below).
This can be changed by setting the global ``locale`` option. This allows to
sort case and accents properly.
.. rubric:: Examples
@@ -63,8 +65,24 @@ case-sensitive and not accent-friendly (see examples below).
- haïr: true
hais: true
#. With global option ``locale: "en_US.UTF-8"`` and rule ``key-ordering: {}``
as opposed to before, the following code snippet would now **PASS**:
::
- t-shirt: 1
T-shirt: 2
t-shirts: 3
T-shirts: 4
- hair: true
haïr: true
hais: true
haïssable: true
"""
from locale import strcoll
import yaml
from yamllint.linter import LintProblem
@@ -101,7 +119,8 @@ def check(conf, token, prev, next, nextnext, context):
# This check is done because KeyTokens can be found inside flow
# sequences... strange, but allowed.
if len(context['stack']) > 0 and context['stack'][-1].type == MAP:
if any(next.value < key for key in context['stack'][-1].keys):
if any(strcoll(next.value, key) < 0
for key in context['stack'][-1].keys):
yield LintProblem(
next.start_mark.line + 1, next.start_mark.column + 1,
'wrong ordering of key "%s" in mapping' % next.value)

View File

@@ -17,10 +17,6 @@
"""
Use this rule to set a limit to lines length.
Note: with Python 2, the ``line-length`` rule may not work properly with
unicode characters because of the way strings are represented in bytes. We
recommend running yamllint with Python 3.
.. rubric:: Options
* ``max`` defines the maximal (inclusive) length of lines.
@@ -30,6 +26,16 @@ recommend running yamllint with Python 3.
* ``allow-non-breakable-inline-mappings`` implies ``allow-non-breakable-words``
and extends it to also allow non-breakable words in inline mappings.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
line-length:
max: 80
allow-non-breakable-words: true
allow-non-breakable-inline-mappings: false
.. rubric:: Examples
#. With ``line-length: {max: 70}``
@@ -134,7 +140,11 @@ def check(conf, line):
start += 1
if start != line.end:
if line.buffer[start] in ('#', '-'):
if line.buffer[start] == '#':
while line.buffer[start] == '#':
start += 1
start += 1
elif line.buffer[start] == '-':
start += 2
if line.buffer.find(' ', start, line.end) == -1:

View File

@@ -21,6 +21,14 @@ Use this rule to force the type of new line characters.
* Set ``type`` to ``unix`` to use UNIX-typed new line characters (``\\n``), or
``dos`` to use DOS-typed new line characters (``\\r\\n``).
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
new-lines:
type: unix
"""

View File

@@ -20,6 +20,20 @@ start with ``0`` are interpreted as octal, but this is not always wanted.
For instance ``010`` is the city code of Beijing, and should not be
converted to ``8``.
.. rubric:: Options
* Use ``forbid-implicit-octal`` to prevent numbers starting with ``0``.
* Use ``forbid-explicit-octal`` to prevent numbers starting with ``0o``.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
octal-values:
forbid-implicit-octal: true
forbid-explicit-octal: true
.. rubric:: Examples
#. With ``octal-values: {forbid-implicit-octal: true}``
@@ -57,6 +71,8 @@ converted to ``8``.
city-code: 0o10
"""
import re
import yaml
from yamllint.linter import LintProblem
@@ -70,6 +86,10 @@ DEFAULT = {'forbid-implicit-octal': True,
'forbid-explicit-octal': True}
def _is_octal_number(string):
return re.match(r'^[0-7]+$', string) is not None
def check(conf, token, prev, next, nextnext, context):
if prev and isinstance(prev, yaml.tokens.TagToken):
return
@@ -78,7 +98,8 @@ def check(conf, token, prev, next, nextnext, context):
if isinstance(token, yaml.tokens.ScalarToken):
if not token.style:
val = token.value
if val.isdigit() and len(val) > 1 and val[0] == '0':
if (val.isdigit() and len(val) > 1 and val[0] == '0' and
_is_octal_number(val[1:])):
yield LintProblem(
token.start_mark.line + 1, token.end_mark.column + 1,
'forbidden implicit octal value "%s"' %
@@ -88,7 +109,8 @@ def check(conf, token, prev, next, nextnext, context):
if isinstance(token, yaml.tokens.ScalarToken):
if not token.style:
val = token.value
if len(val) > 2 and val[:2] == '0o' and val[2:].isdigit():
if (len(val) > 2 and val[:2] == '0o' and
_is_octal_number(val[2:])):
yield LintProblem(
token.start_mark.line + 1, token.end_mark.column + 1,
'forbidden explicit octal value "%s"' %

View File

@@ -34,6 +34,17 @@ used.
**Note**: Multi-line strings (with ``|`` or ``>``) will not be checked.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
quoted-strings:
quote-type: any
required: true
extra-required: []
extra-allowed: []
.. rubric:: Examples
#. With ``quoted-strings: {quote-type: any, required: true}``
@@ -133,6 +144,17 @@ def VALIDATE(conf):
DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
# https://stackoverflow.com/a/36514274
yaml.resolver.Resolver.add_implicit_resolver(
'tag:yaml.org,2002:int',
re.compile(r'''^(?:[-+]?0b[0-1_]+
|[-+]?0o?[0-7_]+
|[-+]?0[0-7_]+
|[-+]?(?:0|[1-9][0-9_]*)
|[-+]?0x[0-9a-fA-F_]+
|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
list('-+0123456789'))
def _quote_match(quote_type, token_style):
return ((quote_type == 'any') or
@@ -149,7 +171,7 @@ def _quotes_are_needed(string):
try:
a, b = loader.get_token(), loader.get_token()
if (isinstance(a, yaml.ScalarToken) and a.style is None and
isinstance(b, yaml.BlockEndToken)):
isinstance(b, yaml.BlockEndToken) and a.value == string):
return False
return True
except yaml.scanner.ScannerError:

View File

@@ -34,6 +34,15 @@ This can be useful to prevent surprises from YAML parsers transforming
``truthy`` rule applies to both keys and values. Set this option to ``false``
to prevent this.
.. rubric:: Default values (when enabled)
.. code-block:: yaml
rules:
truthy:
allowed-values: ['true', 'false']
check-keys: true
.. rubric:: Examples
#. With ``truthy: {}``