Compare commits

...

84 Commits

Author SHA1 Message Date
Philipp Huebner
2c6493c2e9 Debian: Package 1.23.0-1 2020-04-19 10:23:36 +02:00
Philipp Huebner
f5d8c765c7 Merge tag 'v1.23.0' into packaging 2020-04-19 10:22:56 +02:00
Philipp Huebner
7cee10f77c Debian: Package 1.21.0-1 2020-04-19 10:11:25 +02:00
Philipp Huebner
d457b37add Merge tag 'v1.21.0' into packaging 2020-04-19 10:10:17 +02:00
Adrien Vergé
a54cbce1b6 yamllint version 1.23.0 2020-04-17 10:31:52 +02:00
Adrien Vergé
b711fd993e quoted-strings: Add options extra-required and extra-allowed
Add ability to:
- require strings to be quoted if they match a pattern (PCRE regex)
- allow quoted strings if they match a pattern, while `require:
  only-when-needed` is enforced.

Co-Authored-By: Leo Feyer (https://github.com/adrienverge/yamllint/pull/246)
2020-04-17 10:29:55 +02:00
Adrien Vergé
d68022b846 config: Allow generic types inside lists
For example it's possible to define a conf like:

    rule:
      foo: [str],
      bar: [int, bool, 'magic'],
2020-04-17 10:29:55 +02:00
Adrien Vergé
851d34b9fd config: Allow rules to validate their configuration 2020-04-17 10:29:55 +02:00
Adrien Vergé
483a8d89a5 yamllint version 1.22.1 2020-04-15 07:55:57 +02:00
Adrien Vergé
fa87913566 quoted-strings: Fix only-when-needed on corner cases
Change implementation of `required: only-when-needed`, because
maintaining a list of `START_TOKENS` and just looking at the first
character of string values has proven to be partially broken.

Cf. discussion at
https://github.com/adrienverge/yamllint/pull/246#issuecomment-612354097.

Fixes https://github.com/adrienverge/yamllint/issues/242 and
https://github.com/adrienverge/yamllint/pull/244.
2020-04-15 07:48:59 +02:00
Adrien Vergé
961c496b4f yamllint version 1.22.0 2020-04-13 14:32:08 +02:00
Adrien Vergé
ce7d3fcc7b quoted-strings: Remove test_quotes_required()
It is exactly the same tests as `test_quote_type_any()`.
2020-04-13 14:28:02 +02:00
Adrien Vergé
0bffba1e13 quoted-strings: Remove test_single_quotes_required()
It is exactly the same tests as `test_quote_type_single()`.
2020-04-13 14:28:02 +02:00
Adrien Vergé
2d8639c3a1 quoted-strings: Fix broken rule for list items
The rule worked for values like:

    flow-map: {a: foo, b: "bar"}
    block-map:
      a: foo
      b: "bar"

But not for:

    flow-seq: [foo, "bar"]
    block-seq:
      - foo
      - "bar"

Also add tests to make sure there will be no regression.

Fixes: #208.
2020-04-13 14:15:29 +02:00
Adrien Vergé
e284d74be1 quoted-strings: Rename tests names for clarity
And move only-when-needed tests at the end for readability.
2020-04-13 14:15:29 +02:00
Adrien Vergé
1a13837e84 docs: Sunset Python 2
Keep supporting Python 2.7 for one extra year after upstream dropped it:
https://www.python.org/doc/sunset-python-2/
2020-04-09 16:29:43 +02:00
Adrien Vergé
46ed0c02be truthy: Add missing test removed from PR
See https://github.com/adrienverge/yamllint/pull/247#discussion_r405421376.
2020-04-08 12:31:12 +02:00
ilyam8
6ce11dedb4 truthy: add check-keys option 2020-04-08 12:26:21 +02:00
Adrien Vergé
542ae758f5 yamllint version 1.21.0 2020-03-24 07:53:14 +01:00
Rui Pinge
3a6a09b7b6 Add support for redundant quotes in quoted-strings rule
Co-Authored-By: Adrien Vergé
2020-03-24 07:44:07 +01:00
Rui Pinge
15aea73fbe Fix quoted-strings rules not working for string values matching scalars 2020-03-14 14:22:29 +01:00
Philipp Huebner
f90ef91ebc Debian: Package 1.20.0-2 2020-02-28 14:19:10 +01:00
Philipp Huebner
0ee814561d Merge tag 'v1.20.0' into packaging 2020-02-28 14:18:08 +01:00
Martin Packman
91763f5476 Fix new-lines rule on Python 3
Use io.open() when reading files in cli which has the same behaviour
in Python 2 and Python 3, and supply the newline='' parameter which
handles but does not translate line endings.

Add dos.yml test file with windows newlines.

Also add to file finding test expected output.

Add test for new-lines rule through the cli.

Validates files are read with the correct universal newlines setting.

Fixes adrienverge/yamllint#228
2020-02-13 12:02:45 +01:00
Martin Packman
5b049e4229 Add RunContext helper for cli tests
Single context manager that includes exit code and output streams.

Use new RunContext throughout test_cli.

Largely non-functional change, saving some repetition of setup.

Also improve some failures by bundling multiple assertions into one.
2020-02-13 12:02:45 +01:00
Adrien Vergé
044c7f0248 cli: Test unicode chars in paths too 2020-01-17 16:01:05 +01:00
Adrien Vergé
734d5d5f73 CI: Run tests on Python 3.8
Python 3.8 was released in October 2019.
2020-01-03 09:33:35 +01:00
dhutty
fd86455076 CI: Disable building on Python 3.4
As can be seen in https://travis-ci.org/adrienverge/yamllint/builds/631325436?utm_source=github_status&utm_medium=notification
The dependency, pathspec, requires Python '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*' but the running Python is 3.4.8

This commit stops Travis building yamllint against 3.4 so that CI can pass again.
2020-01-03 09:29:33 +01:00
Adrien Vergé
13a0f11e7c yamllint version 1.20.0 2019-12-26 16:06:29 +01:00
Sylvestre Ledru
43b95e99d1 Use 'syntax' as rule name upon syntax errors 2019-12-17 19:29:49 +01:00
ffapitalle
8fa9eb3ced Add --no-warnings option to suppress warning messages
Use `--no-warnings` option to hide warning messages. It only shows
problems marked as errors.
2019-12-12 09:12:53 +01:00
Adrien Vergé
da3788e95a yamllint version 1.19.0 2019-11-19 11:28:21 +01:00
Joel Baranick
fb400dc64b Allow disabling all checks for a file
Allow disabling of a file, even if it is invalid YAML (syntax error) by
including `# yamllint disable-file` in the first line.
2019-11-19 11:26:31 +01:00
Adrien Vergé
92324ae730 yamllint version 1.18.0 2019-10-15 09:49:20 +02:00
Imran Iqbal
7359785ea0 fix(default.yaml): disable empty-values & octal-values by default
* Close #204
2019-10-15 09:41:32 +02:00
Hossein Zolfi
579a975b70 docs: Fix pre-commit config file
* pre-commit show warning for unsupported key (sha)
* Demonstrate how to use custom yamllint
2019-10-01 11:36:07 +02:00
Imran Iqbal
f3d9196aa0 docs(configuration): improve yaml-files code example
* A straight copy/paste of the existing example into the `.yamllint` file results in a `yamllint` error!
2019-09-10 19:47:25 +02:00
Ibrahim AshShohail
881d301883 feat: Support reading config from .yamllint.yml and .yamllint.yaml
Signed-off-by: Ibrahim AshShohail <me@ibrasho.com>
2019-08-27 09:49:09 +02:00
Adrien Vergé
b62b424dd4 feat: Lint .yamllint by default 2019-08-26 10:01:40 +02:00
Adrien Vergé
ce0336e430 yamllint version 1.17.0 2019-08-12 16:54:51 +02:00
grzesuav
063c854658 feat: Make YAML file extensions configurable 2019-08-12 16:53:30 +02:00
xatier
673bdbd324 fix(truthy): Fix extra whitespace 2019-08-11 14:50:11 +02:00
Philipp Huebner
790662d369 Debian: Package 1.16.0-1 2019-07-24 17:39:43 +02:00
Philipp Huebner
92c5440d22 Merge tag 'v1.16.0' into packaging 2019-07-24 17:38:51 +02:00
Remi Pointel
cb5fe2c050 add OpenBSD installation instructions. 2019-07-09 10:04:48 +02:00
Adrien Vergé
930c8eea94 docs: Simplify installation instruction in the README 2019-07-07 18:13:43 +02:00
Adrien Vergé
f6a24552d9 yamllint version 1.16.0 2019-06-07 10:04:55 +02:00
Adrien Vergé
0ba193331b truthy: Validate options passed to 'allowed-values'
Make sure values passed in allowed values are correct ones. This is
possible thanks to previous commit, and should prevent users from
writing incorrect configurations.
2019-06-07 09:59:26 +02:00
Adrien Vergé
f65553c4f7 config: Validate config options with list of enums
Allow rules to declare a list of valid values for an option.

For example, a rule like:

    CONF = {'allowed-values': list}

... allowed any value to be passed in the list (including bad ones).

It is now possible to declare:

    CONF = {'allowed-values': ['value1', 'value2', 'value3']}

... so that the list passed to the options must contain only values in
`['value1', 'value2', 'value3']`.
2019-06-07 09:59:26 +02:00
Adrien Vergé
0fef4c14e7 truthy: Try to make docs on allowed-values more explicit
Edit documentation for the `truthy` rule, in order to:
- add quotes to examples (`'yes'` instead of `yes`) to avoid
  misconfigurations,
- group truthy values in the `allowed-values` option paragraph, for
  easier reading.
2019-06-07 09:59:10 +02:00
Ondrej Vaško
4ef7e05f3a truthy: Add allowed-values configuration option
Allows using key `allowed-values` for `truthy` section in configuration file (#150).

This allows to use configuration `truthy: allowed-values: ["yes", "no",
"..."]`, to set custom allowed truthy values.

This is especially useful for people using ansible, where values like
`yes` or `no` are valid and officially supported, but yamllint reports
them as illegal.

Implemented by difference of set of TRUTHY constants and configured
allowed values.

Signed-off-by: Ondrej Vasko <ondrej.vaskoo@gmail.com>
2019-06-06 09:59:00 +02:00
xatier
43c50379e0 Sort import orders 2019-05-27 11:10:30 +02:00
Adrien Vergé
fec2c2fba7 fix(parser): Correctly handle DOS new lines in 'line' rules
Do not consider the trailing `\r` of a line a part of it.
2019-04-09 16:48:00 +02:00
Mateusz Piotrowski
2a66ec2e5e Add FreeBSD installation instructions 2019-03-21 18:40:28 +01:00
Philipp Huebner
cc8d56fb94 Debian: Package 1.15.0-1 2019-02-16 11:00:44 +01:00
Philipp Huebner
d77cb4e2f6 Merge tag 'v1.15.0' into packaging 2019-02-16 10:58:48 +01:00
Adrien Vergé
37700ab3e6 yamllint version 1.15.0 2019-02-11 14:18:11 +01:00
Adrien Vergé
f66661e36d docs(cli): Add a paragraph about standard input
See commit 05dfcbc "cli: Add command line option - to read from standard
input", cc @miguelbarao.
2019-02-11 14:16:33 +01:00
Adrien Vergé
d6b89e94e4 chore(docs): Fix conf.py styling 2019-02-11 14:09:20 +01:00
Miguel Barao
05dfcbc109 cli: Add command line option - to read from standard input
If YAML files are given as arguments, parses these files.
If yamllint is run with - option, stdin.
If no arguments are given, just fail.
2019-02-11 14:04:48 +01:00
Philipp Huebner
1c31952230 Debian: Package 1.14.0-1 2019-01-19 16:11:37 +01:00
Philipp Huebner
8e97e6b151 Merge tag 'v1.14.0' into packaging 2019-01-19 16:10:26 +01:00
Philipp Huebner
5cd261d6cb Debian: Package 1.13.0-1 2018-11-18 21:54:52 +01:00
Philipp Huebner
becf9fe98b Merge tag 'v1.13.0' into packaging 2018-11-18 21:53:45 +01:00
Philipp Huebner
251260c382 Debian: Package 1.11.1-1 2018-05-09 19:22:24 +02:00
Philipp Huebner
c0f7ae1102 Merge tag 'v1.11.1' into packaging 2018-05-09 19:21:30 +02:00
Philipp Huebner
b47f0f2451 Debian: Package 1.11.0-1 2018-03-06 17:59:45 +01:00
Philipp Huebner
5ca5add463 Merge tag 'v1.11.0' into packaging 2018-03-06 17:59:24 +01:00
Philipp Huebner
542b6738a6 Debian: Package 1.10.0-1 2018-03-06 17:59:14 +01:00
Philipp Huebner
2c3097ca78 Merge tag 'v1.10.0' into packaging 2018-03-06 17:58:19 +01:00
Philipp Huebner
5fbfcb13b1 Debian: Package 1.9.0-1 2017-10-22 09:39:21 +02:00
Philipp Huebner
b8200259a2 Merge tag 'v1.9.0' into packaging 2017-10-22 09:25:28 +02:00
Philipp Huebner
0fd8b76eea Debian: Package 1.8.1-1 2017-10-04 18:54:47 +02:00
Philipp Huebner
a4ab40c51a Merge tag 'v1.8.1' into packaging 2017-10-04 16:47:16 +02:00
Adrien Vergé
aeb353159b Packaging: Debian: Package 1.5.0-1 2016-11-09 16:19:15 +01:00
Adrien Vergé
c57d2784df Packaging: Debian: Package 1.3.2-1 2016-11-07 18:42:15 +01:00
Adrien Vergé
c0da5eac54 to include in next Debian release
See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=819866#15
2016-11-07 18:42:15 +01:00
Adrien Vergé
69278a4553 Packaging: Debian: Package 1.2.1-1 2016-11-07 18:42:15 +01:00
Adrien Vergé
e3baeefc81 Packaging: Debian: Package 1.2.0-1 2016-11-07 18:42:15 +01:00
Adrien Vergé
d4d1d78ce6 Packaging: Add links to "new package" bugs for distros 2016-11-07 18:42:15 +01:00
Adrien Vergé
c88cdf5316 Packaging: Update Ubuntu package 2016-11-07 18:42:15 +01:00
Adrien Vergé
1fb673be2e Packaging: Debian: Add man page 2016-11-07 18:42:15 +01:00
Adrien Vergé
ea658ec3d6 Packaging: Add files for Debian 2016-11-07 18:42:14 +01:00
Adrien Vergé
014a6d2db4 Packaging: Add files for Ubuntu packaging (PPA) 2016-11-07 18:42:14 +01:00
54 changed files with 1886 additions and 333 deletions

View File

@@ -3,10 +3,10 @@ dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069)
language: python
python:
- 2.7
- 3.4
- 3.5
- 3.6
- 3.7
- 3.8
- nightly
install:
- pip install pyyaml coveralls flake8 flake8-import-order doc8

View File

@@ -1,6 +1,71 @@
Changelog
=========
1.23.0 (2020-04-17)
-------------------
- Allow rules to validate their configuration
- Add options ``extra-required`` and ``extra-allowed`` to ``quoted-strings``
1.22.1 (2020-04-15)
-------------------
- Fix ``quoted-strings`` rule with ``only-when-needed`` on corner cases
1.22.0 (2020-04-13)
-------------------
- Add ``check-keys`` option to the ``truthy`` rule
- Fix ``quoted-strings`` rule not working on sequences items
- Sunset Python 2
1.21.0 (2020-03-24)
-------------------
- Fix ``new-lines`` rule on Python 3 with DOS line endings
- Fix ``quoted-strings`` rule not working for string values matching scalars
- Add ``required: only-when-needed`` option to the ``quoted-strings`` rule
1.20.0 (2019-12-26)
-------------------
- Add --no-warnings option to suppress warning messages
- Use 'syntax' as rule name upon syntax errors
1.19.0 (2019-11-19)
-------------------
- Allow disabling all checks for a file with ``# yamllint disable-file``
1.18.0 (2019-10-15)
-------------------
- Lint ``.yamllint`` config file by default
- Also read config from ``.yamllint.yml`` and ``.yamllint.yaml``
- Improve documentation for ``yaml-files``
- Update documentation for ``pre-commit``
- Explicitly disable ``empty-values`` and ``octal-values`` rules
1.17.0 (2019-08-12)
-------------------
- Simplify installation instructions in the README
- Add OpenBSD installation instructions
- Make YAML file extensions configurable
1.16.0 (2019-06-07)
-------------------
- Add FreeBSD installation instructions
- Fix the ``line`` rule to correctly handle DOS new lines
- Add the ``allowed-values`` option to the ``truthy`` rule
- Allow configuration options to be a list of enums
1.15.0 (2019-02-11)
-------------------
- Allow linting from standard input with ``yamllint -``
1.14.0 (2019-01-14)
-------------------

View File

@@ -21,6 +21,10 @@ indentation, etc.
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.
Documentation
-------------
@@ -38,31 +42,16 @@ Screenshot
Installation
^^^^^^^^^^^^
On Fedora / CentOS (note: `EPEL <https://fedoraproject.org/wiki/EPEL>`_ is
required on CentOS):
.. code:: bash
sudo dnf install yamllint
On Debian 8+ / Ubuntu 16.04+:
.. code:: bash
sudo apt-get install yamllint
On Mac OS 10.11+:
.. code:: bash
brew install yamllint
Alternatively using pip, the Python package manager:
Using pip, the Python package manager:
.. code:: bash
pip install --user yamllint
yamllint is also packaged for all major operating systems, see installation
examples (``dnf``, ``apt-get``...) `in the documentation
<https://yamllint.readthedocs.io/en/stable/quickstart.html>`_.
Usage
^^^^^

View File

@@ -48,7 +48,7 @@ man_pages = [
class Mock(MagicMock):
@classmethod
def __getattr__(cls, name):
return MagicMock()
return MagicMock()
MOCK_MODULES = ['pathspec', 'yaml']

View File

@@ -14,7 +14,8 @@ To use a custom configuration file, use the ``-c`` option:
If ``-c`` is not provided, yamllint will look for a configuration file in the
following locations (by order of preference):
- ``.yamllint`` in the current working directory
- ``.yamllint``, ``.yamllint.yaml`` or ``.yamllint.yml`` in the current working
directory
- ``$XDG_CONFIG_HOME/yamllint/config``
- ``~/.config/yamllint/config``
@@ -115,6 +116,25 @@ return code will be:
* ``1`` if one or more errors occur
* ``2`` if no errors occur, but one or more warnings occur
If the script is invoked with the ``--no-warnings`` option, it won't output
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:
.. code-block:: yaml
yaml-files:
- '*.yaml'
- '*.yml'
- '.yamllint'
The same rules as for ignoring paths apply (``.gitignore``-style path pattern,
see below).
Ignoring paths
--------------

View File

@@ -73,3 +73,31 @@ It is possible, although not recommend, to disabled **all** rules:
If you need to disable multiple rules, it is allowed to chain rules like this:
``# yamllint disable rule:hyphens rule:commas rule:indentation``.
Disabling all checks for a file
-------------------------------
To prevent yamllint from reporting problems for a specific file, add the
directive comment ``# yamllint disable-file`` as the first line of the file.
For instance:
.. code-block:: yaml
# yamllint disable-file
# The following mapping contains the same key twice, but I know what I'm doing:
key: value 1
key: value 2
- This line is waaaaaaaaaay too long but yamllint will not report anything about it.
This line will be checked by yamllint.
or:
.. code-block:: jinja
# yamllint disable-file
# This file is not valid YAML because it is a Jinja template
{% if extra_info %}
key1: value1
{% endif %}
key2: value2

View File

@@ -10,8 +10,10 @@ Here is an example, to add in your .pre-commit-config.yaml
.. code:: yaml
---
# Update the sha variable with the release version that you want, from the yamllint repo
# Update the rev variable with the release version that you want, from the yamllint repo
# You can pass your custom .yamllint with args attribute.
- repo: https://github.com/adrienverge/yamllint.git
sha: v1.8.1
rev: v1.17.0
hooks:
- id: yamllint
args: [-c=/path/to/.yamllint]

View File

@@ -4,7 +4,8 @@ Quickstart
Installing yamllint
-------------------
On Fedora / CentOS:
On Fedora / CentOS (note: `EPEL <https://fedoraproject.org/wiki/EPEL>`_ is
required on CentOS):
.. code:: bash
@@ -22,6 +23,18 @@ On Mac OS 10.11+:
brew install yamllint
On FreeBSD:
.. code:: sh
pkg install py36-yamllint
On OpenBSD:
.. code:: sh
doas pkg_add py3-yamllint
Alternatively using pip, the Python package manager:
.. code:: bash
@@ -50,6 +63,12 @@ You can also lint all YAML files in a whole directory:
yamllint .
Or lint a YAML stream from standard input:
.. code:: bash
echo -e 'this: is\nvalid: YAML' | yamllint -
The output will look like (colors are not displayed here):
::

59
packaging/debian/README Normal file
View File

@@ -0,0 +1,59 @@
Release new version for Debian
==============================
VERSION=1.5.0
WD=/tmp/pkg
TARGET=vm-debian8
# Build source archive
python3 setup.py sdist
# Copy files on a Debian system
ssh $TARGET mkdir $WD
rsync -av dist/yamllint-$VERSION.tar.gz $TARGET:$WD/yamllint_$VERSION.orig.tar.gz
ssh $TARGET tar -C $WD -xf $WD/yamllint_$VERSION.orig.tar.gz
if false; then
rsync -av packaging/debian/debian $TARGET:$WD/yamllint-$VERSION
else
ssh $TARGET "cd $WD && sudo apt-get update"
ssh $TARGET "cd $WD && apt-get source yamllint"
ssh $TARGET mv $WD'/yamllint-*/debian' $WD/yamllint-$VERSION # old version
fi
ssh $TARGET
cd $WD/yamllint-$VERSION/debian
# Add a new changelog entry:
dch -v $VERSION-1
# Build packages and sign dsc
debuild -S -sa
# Test if it builds
sudo pbuilder --create
sudo pbuilder --build ../../yamllint_$VERSION-1.dsc
# Run lintian to detect problems
lintian -i -I --show-overrides ../../yamllint_$VERSION-1_source.changes
# Retrieve modified files
rsync -av $TARGET:$WD/yamllint-$VERSION/debian packaging/debian
# Upload to mentors FTP (needed even for updates)
cat >>~/.dput.cf << EOF
[mentors]
fqdn = mentors.debian.net
incoming = /upload
method = http
allow_unsigned_uploads = 0
progress_indicator = 2
# Allow uploads for UNRELEASED packages
allowed_distributions = .*
EOF
dput mentors ../../yamllint_$VERSION-1_source.changes
# Do no upload to Debian FTP: the sponsor will do it
# File a new RFS bug by sending an email
See https://mentors.debian.net/sponsors/rfs-howto/yamllint

View File

@@ -0,0 +1,163 @@
yamllint (1.23.0-1) unstable; urgency=medium
* New upstream version 1.23.0
-- Philipp Huebner <debalance@debian.org> Sun, 19 Apr 2020 10:19:31 +0200
yamllint (1.21.0-1) unstable; urgency=medium
* New upstream version 1.21.0
-- Philipp Huebner <debalance@debian.org> Sat, 11 Apr 2020 17:15:13 +0200
yamllint (1.20.0-2) unstable; urgency=medium
* Removed 'Installing yamllint' section in the manpage (Closes: #947440)
* Moved maintainership into the Python Applications Packaging Team
(Closes: 947416)
-- Philipp Huebner <debalance@debian.org> Fri, 28 Feb 2020 14:02:37 +0100
yamllint (1.20.0-1) unstable; urgency=medium
* New upstream version 1.20.0
* Updated Standards-Version: 4.5.0 (no changes needed)
* Rules-Requires-Root: no
-- Philipp Huebner <debalance@debian.org> Sun, 16 Feb 2020 00:39:28 +0100
yamllint (1.18.0-1) unstable; urgency=medium
* New upstream version 1.18.0
* Added dh-python to build-depends
* Updated Standards-Version: 4.4.1 (no changes needed)
-- Philipp Huebner <debalance@debian.org> Sun, 20 Oct 2019 19:40:24 +0200
yamllint (1.16.0-1) unstable; urgency=medium
* New upstream version 1.16.0
* Updated debhelper compat version: 12
* Updated Standards-Version: 4.4.0 (no changes needed)
-- Philipp Huebner <debalance@debian.org> Wed, 24 Jul 2019 17:35:30 +0200
yamllint (1.15.0-1) unstable; urgency=medium
* New upstream version 1.15.0
-- Philipp Huebner <debalance@debian.org> Sat, 16 Feb 2019 10:59:13 +0100
yamllint (1.14.0-1) unstable; urgency=medium
* New upstream version 1.14.0
* Updated Standards-Version: 4.3.0 (no changes needed)
* Updated debian/copyright
-- Philipp Huebner <debalance@debian.org> Sat, 19 Jan 2019 16:07:26 +0100
yamllint (1.13.0-1) unstable; urgency=medium
* New upstream version 1.13.0
* Updated Standards-Version: 4.2.1 (no changes needed)
-- Philipp Huebner <debalance@debian.org> Sun, 18 Nov 2018 19:16:27 +0100
yamllint (1.11.1-1) unstable; urgency=medium
* New upstream version 1.11.1
* Updated Standards-Version: 4.1.4 (no changes needed)
-- Philipp Huebner <debalance@debian.org> Wed, 09 May 2018 14:24:10 +0200
yamllint (1.11.0-1) unstable; urgency=medium
* New upstream version 1.11.0
* Update Standards-Version: 4.1.3 (no changes needed)
* Switch to debhelper compat level 11
-- Philipp Huebner <debalance@debian.org> Tue, 06 Mar 2018 17:35:44 +0100
yamllint (1.10.0-1) unstable; urgency=medium
* New upstream version 1.10.0
* Drop patches (fixed upstream)
-- Philipp Huebner <debalance@debian.org> Thu, 16 Nov 2017 19:18:18 +0100
yamllint (1.9.0-1) unstable; urgency=medium
* New upstream version 1.9.0
-- Philipp Huebner <debalance@debian.org> Sun, 22 Oct 2017 09:35:39 +0200
yamllint (1.8.1-1) unstable; urgency=medium
* New upstream version 1.8.1
* Add myself as Uploader
* Depend on python3-pkg-resources (Closes: #860075)
* Build-Depend on python3-pathspec
* Add patch to use python3 in tests
* debian/control: set testsuite to autopkgtest-pkg-python
* Update Standards-Version: 4.1.1 (no changes needed)
-- Philipp Huebner <debalance@debian.org> Wed, 04 Oct 2017 18:50:29 +0200
yamllint (1.5.0-1) unstable; urgency=medium
* Output color only on TTY
* Generalize line-length for mappings
* Fix line-length bug by scanning tokens securely
* New rule: truthy
* Fix distribution (don't distribute tests in site-packages)
* Exclude PTY-related test (doesn't work with pbuilder)
-- Adrien Vergé <adrienverge@gmail.com> Mon, 07 Nov 2016 18:51:37 +0100
yamllint (1.3.2-1) unstable; urgency=medium
* Allow disabling yamllint checks using comments
* Detect user config using `os.path.expanduser()`
* Fix non-ASCII comments bug and add tests
* Update standards version to 3.9.8
-- Adrien Vergé <adrienverge@gmail.com> Mon, 27 Jun 2016 22:17:05 +0200
yamllint (1.2.2-1) unstable; urgency=medium
* Update to new upstream version
-- Adrien Vergé <adrienverge@gmail.com> Fri, 24 Jun 2016 08:54:59 +0200
yamllint (1.2.1-1) unstable; urgency=medium
* Update to new upstream version
-- Adrien Vergé <adrienverge@gmail.com> Sun, 03 Apr 2016 11:30:02 +0200
yamllint (1.2.0-1) unstable; urgency=medium
* Update to new upstream version
* Build and include man page in package
* Fix Vcs-* fields in debian/control
* Fix description-synopsis-starts-with-article lintian warning
-- Adrien Vergé <adrienverge@gmail.com> Tue, 08 Mar 2016 08:20:23 +0100
yamllint (1.1.0-1) unstable; urgency=medium
* Update to new upstream version
-- Adrien Vergé <adrienverge@gmail.com> Fri, 04 Mar 2016 18:54:19 +0100
yamllint (1.0.4-1) unstable; urgency=medium
* Update to new upstream version
-- Adrien Vergé <adrienverge@gmail.com> Fri, 04 Mar 2016 12:54:19 +0100
yamllint (1.0.3-1) unstable; urgency=medium
* Initial release (Closes: #816609)
-- Adrien Vergé <adrienverge@gmail.com> Thu, 03 Mar 2016 09:10:38 +0100

View File

@@ -0,0 +1,30 @@
Source: yamllint
Maintainer: Python Applications Packaging Team <python-apps-team@lists.alioth.debian.org>
Uploaders: Adrien Vergé <adrienverge@gmail.com>, Philipp Huebner <debalance@debian.org>
Section: devel
Priority: optional
Build-Depends: debhelper,
debhelper-compat (= 12),
dh-python,
python3-all,
python3-nose,
python3-pathspec,
python3-setuptools,
python3-sphinx,
python3-yaml
Standards-Version: 4.5.0
Rules-Requires-Root: no
Vcs-Browser: https://salsa.debian.org/python-team/applications/yamllint
Vcs-Git: https://salsa.debian.org/python-team/applications/yamllint.git
Homepage: https://github.com/adrienverge/yamllint
Package: yamllint
Architecture: all
Depends: ${misc:Depends},
${python3:Depends},
${shlibs:Depends},
python3-pkg-resources
Description: Linter for YAML files
yamllint does not only check for syntax validity, but for weirdnesses like key
repetition and cosmetic problems such as lines length, trailing spaces,
indentation, etc.

View File

@@ -0,0 +1,29 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: yamllint
Source: https://github.com/adrienverge/yamllint
Files: *
Copyright: 2016-2019 Adrien Vergé <adrienverge@gmail.com>
License: GPL-3+
Files: debian/*
Copyright: 2016-2019 Adrien Vergé <adrienverge@gmail.com>
2017-2019 Philipp Huebner <debalance@debian.org>
License: GPL-3+
License: GPL-3+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

View File

@@ -0,0 +1,62 @@
Description: clean docs
Remove content unsuitable for Debian
Author: Philipp Huebner <debalance@debian.org>
Index: yamllint/docs/quickstart.rst
===================================================================
--- yamllint.orig/docs/quickstart.rst
+++ yamllint/docs/quickstart.rst
@@ -1,53 +1,6 @@
Quickstart
==========
-Installing yamllint
--------------------
-
-On Fedora / CentOS (note: `EPEL <https://fedoraproject.org/wiki/EPEL>`_ is
-required on CentOS):
-
-.. code:: bash
-
- sudo dnf install yamllint
-
-On Debian 8+ / Ubuntu 16.04+:
-
-.. code:: bash
-
- sudo apt-get install yamllint
-
-On Mac OS 10.11+:
-
-.. code:: bash
-
- brew install yamllint
-
-On FreeBSD:
-
-.. code:: sh
-
- pkg install py36-yamllint
-
-On OpenBSD:
-
-.. code:: sh
-
- doas pkg_add py3-yamllint
-
-Alternatively using pip, the Python package manager:
-
-.. code:: bash
-
- pip install --user yamllint
-
-If you prefer installing from source, you can run, from the source directory:
-
-.. code:: bash
-
- python setup.py sdist
- pip install --user dist/yamllint-*.tar.gz
-
Running yamllint
----------------

View File

@@ -0,0 +1 @@
docs.diff

13
packaging/debian/debian/rules Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/make -f
export DH_VERBOSE=1
%:
NOSE_EXCLUDE=test_run_colored_output \
dh $@ --with python3 --buildsystem=pybuild
override_dh_auto_build:
dh_auto_build
PYTHONPATH=. http_proxy='127.0.0.1:9' sphinx-build -N -bman docs build/man
override_dh_installman:
dh_installman build/man/yamllint.1

View File

@@ -0,0 +1 @@
3.0 (quilt)

View File

@@ -0,0 +1,2 @@
yamllint source: debian-watch-does-not-check-gpg-signature
yamllint source: no-dh-sequencer

View File

@@ -0,0 +1,4 @@
Bug-Database: https://github.com/adrienverge/yamllint/issues
FAQ: https://yamllint.readthedocs.org/
Name: yamllint
Repository: https://github.com/adrienverge/yamllint

View File

@@ -0,0 +1,3 @@
# watch control file for uscan
version=3
https://github.com/adrienverge/yamllint/tags .*/archive/v(\d\S*)\.tar\.gz

17
packaging/links Normal file
View File

@@ -0,0 +1,17 @@
Packaging in Fedora
-------------------
https://bugzilla.redhat.com/show_bug.cgi?id=1309907
Packaging in Debian
-------------------
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816609
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816611
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=817245
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=819866
Packaging in Ubuntu
-------------------
https://bugs.launchpad.net/ubuntu/+bug/1554406

30
packaging/ubuntu/README Normal file
View File

@@ -0,0 +1,30 @@
Upload new version to PPA
=========================
WORKDIR=/tmp/yamllint-ubuntu
VERSION=1.2.0
git clone -b packaging https://github.com/adrienverge/yamllint.git $WORKDIR
cd $WORKDIR
# Build source archive
python3 setup.py sdist
cd dist
mv yamllint-$VERSION.tar.gz yamllint_$VERSION.orig.tar.gz
tar -xvf yamllint_$VERSION.orig.tar.gz
cp -r ../packaging/ubuntu/debian yamllint-$VERSION/
cd yamllint-$VERSION/debian
# Add a new changelog entry:
dch -v $VERSION-0ubuntu1
# Build packages and sign dsc
debuild -S -sa
# Test if it builds
sudo pbuilder --create
sudo pbuilder --build ../yamllint_$VERSION-0ubuntu1.dsc
# Upload to PPA
dput ppa:adrienverge/ppa ../yamllint_$VERSION-0ubuntu1_source.changes

View File

@@ -0,0 +1,17 @@
yamllint (1.2.0-0ubuntu1) trusty; urgency=medium
* Update to new upstream version
-- Adrien Vergé <adrienverge@gmail.com> Mon, 07 Mar 2016 07:54:19 +0100
yamllint (1.0.3-0ubuntu2) trusty; urgency=medium
* Add python3-nose to build dependencies
-- Adrien Vergé <adrienverge@gmail.com> Wed, 02 Mar 2016 14:12:07 +0100
yamllint (1.0.3-0ubuntu1) trusty; urgency=low
* Initial release
-- Adrien Vergé <adrienverge@gmail.com> Wed, 02 Mar 2016 13:26:29 +0100

View File

@@ -0,0 +1 @@
9

View File

@@ -0,0 +1,27 @@
Source: yamllint
Section: devel
Priority: optional
Maintainer: Adrien Vergé <adrienverge@gmail.com>
Build-Depends:
debhelper (>=9),
python3-all,
python3-nose,
python3-setuptools,
python3-sphinx,
python3-yaml,
Standards-Version: 3.9.7
Homepage: https://github.com/adrienverge/yamllint
Vcs-Git: git://anonscm.debian.org/collab-maint/yamllint.git
Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/yamllint.git
Package: yamllint
Architecture: any
Depends:
python3-yaml,
${misc:Depends},
${python3:Depends},
${shlibs:Depends},
Description: A linter for YAML files
yamllint does not only check for syntax validity, but for weirdnesses like key
repetition and cosmetic problems such as lines length, trailing spaces,
indentation, etc.

View File

@@ -0,0 +1,19 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: yamllint
Source: https://github.com/adrienverge/yamllint
Files: *
Copyright: 2016 Adrien Vergé <adrienverge@gmail.com>
License: GPL-3+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@@ -0,0 +1 @@
README.rst

11
packaging/ubuntu/debian/rules Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/make -f
%:
dh $@ --with python3 --buildsystem=pybuild
override_dh_auto_build:
dh_auto_build
PYTHONPATH=. http_proxy='127.0.0.1:9' sphinx-build -N -bman docs build/man
override_dh_installman:
dh_installman build/man/yamllint.1

View File

@@ -0,0 +1 @@
3.0 (quilt)

View File

@@ -0,0 +1,4 @@
Bug-Database: https://github.com/adrienverge/yamllint/issues
FAQ: https://yamllint.readthedocs.org/
Name: yamllint
Repository: https://github.com/adrienverge/yamllint

View File

@@ -0,0 +1,3 @@
# watch control file for uscan
version=3
https://github.com/adrienverge/yamllint/tags .*/archive/v(\d\S*)\.tar\.gz

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 setup, find_packages
from setuptools import find_packages, setup
from yamllint import (__author__, __license__,
APP_NAME, APP_VERSION, APP_DESCRIPTION)

View File

@@ -78,3 +78,22 @@ class EmptyLinesTestCase(RuleTestCase):
'document-start: disable\n')
self.check('non empty\n', conf)
self.check('non empty\n\n', conf, problem=(2, 1))
def test_with_dos_newlines(self):
conf = ('empty-lines: {max: 2, max-start: 0, max-end: 0}\n'
'new-lines: {type: dos}\n'
'document-start: disable\n')
self.check('---\r\n', conf)
self.check('---\r\ntext\r\n\r\ntext\r\n', conf)
self.check('\r\n---\r\ntext\r\n\r\ntext\r\n', conf,
problem=(1, 1))
self.check('\r\n\r\n\r\n---\r\ntext\r\n\r\ntext\r\n', conf,
problem=(3, 1))
self.check('---\r\ntext\r\n\r\n\r\n\r\ntext\r\n', conf,
problem=(5, 1))
self.check('---\r\ntext\r\n\r\n\r\n\r\n\r\n\r\n\r\ntext\r\n', conf,
problem=(8, 1))
self.check('---\r\ntext\r\n\r\ntext\r\n\r\n', conf,
problem=(5, 1))
self.check('---\r\ntext\r\n\r\ntext\r\n\r\n\r\n\r\n', conf,
problem=(7, 1))

View File

@@ -171,3 +171,12 @@ class LineLengthTestCase(RuleTestCase):
'# This is a test to check if “line-length” works nice\n'
'with: “unicode characters” that span accross bytes! ↺\n',
conf, problem1=(2, 53), problem2=(3, 53))
def test_with_dos_newlines(self):
conf = ('line-length: {max: 10}\n'
'new-lines: {type: dos}\n'
'new-line-at-end-of-file: disable\n')
self.check('---\r\nABCD EFGHI', conf)
self.check('---\r\nABCD EFGHI\r\n', conf)
self.check('---\r\nABCD EFGHIJ', conf, problem=(2, 11))
self.check('---\r\nABCD EFGHIJ\r\n', conf, problem=(2, 11))

View File

@@ -31,16 +31,20 @@ class NewLinesTestCase(RuleTestCase):
self.check('---\r\ntext\r\n', conf)
def test_unix_type(self):
conf = 'new-lines: {type: unix}'
conf = ('new-line-at-end-of-file: disable\n'
'new-lines: {type: unix}\n')
self.check('', conf)
self.check('\r', conf)
self.check('\n', conf)
self.check('\r\n', conf, problem=(1, 1))
self.check('---\ntext\n', conf)
self.check('---\r\ntext\r\n', conf, problem=(1, 4))
def test_dos_type(self):
conf = 'new-lines: {type: dos}\n'
conf = ('new-line-at-end-of-file: disable\n'
'new-lines: {type: dos}\n')
self.check('', conf)
self.check('\r', conf)
self.check('\n', conf, problem=(1, 1))
self.check('\r\n', conf)
self.check('---\ntext\n', conf, problem=(1, 4))

View File

@@ -28,7 +28,9 @@ class OctalValuesTestCase(RuleTestCase):
self.check('user-city: 0o10', conf)
def test_implicit_octal_values(self):
conf = ('octal-values: {forbid-implicit-octal: true}\n'
conf = ('octal-values:\n'
' forbid-implicit-octal: true\n'
' forbid-explicit-octal: false\n'
'new-line-at-end-of-file: disable\n'
'document-start: disable\n')
self.check('user-city: 010', conf, problem=(1, 15))
@@ -50,7 +52,9 @@ class OctalValuesTestCase(RuleTestCase):
' - 0e3\n', conf)
def test_explicit_octal_values(self):
conf = ('octal-values: {forbid-explicit-octal: true}\n'
conf = ('octal-values:\n'
' forbid-implicit-octal: false\n'
' forbid-explicit-octal: true\n'
'new-line-at-end-of-file: disable\n'
'document-start: disable\n')
self.check('user-city: 0o10', conf, problem=(1, 16))

View File

@@ -16,12 +16,15 @@
from tests.common import RuleTestCase
from yamllint import config
class QuotedTestCase(RuleTestCase):
rule_id = 'quoted-strings'
def test_disabled(self):
conf = 'quoted-strings: disable'
self.check('---\n'
'foo: bar\n', conf)
self.check('---\n'
@@ -30,23 +33,34 @@ class QuotedTestCase(RuleTestCase):
'foo: \'bar\'\n', conf)
self.check('---\n'
'bar: 123\n', conf)
self.check('---\n'
'bar: "123"\n', conf)
def test_quote_type_any(self):
conf = 'quoted-strings: {quote-type: any}\n'
self.check('---\n'
'boolean1: true\n'
'number1: 123\n'
'string1: foo\n' # fails
'string2: "foo"\n'
'string3: \'bar\'\n'
'string4: !!str genericstring\n'
'string5: !!str 456\n'
'string6: !!str "quotedgenericstring"\n'
'string3: "true"\n'
'string4: "123"\n'
'string5: \'bar\'\n'
'string6: !!str genericstring\n'
'string7: !!str 456\n'
'string8: !!str "quotedgenericstring"\n'
'binary: !!binary binstring\n'
'integer: !!int intstring\n'
'boolean2: !!bool boolstring\n'
'boolean3: !!bool "quotedboolstring"\n',
conf, problem=(4, 10))
'boolean3: !!bool "quotedboolstring"\n'
'block-seq:\n'
' - foo\n' # fails
' - "foo"\n'
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
conf, problem1=(4, 10), problem2=(17, 5),
problem3=(19, 12), problem4=(20, 15))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
@@ -55,7 +69,7 @@ class QuotedTestCase(RuleTestCase):
' word 1\n'
' word 2\n'
'multiline string 3:\n'
' word 1\n'
' word 1\n' # fails
' word 2\n'
'multiline string 4:\n'
' "word 1\\\n'
@@ -64,20 +78,31 @@ class QuotedTestCase(RuleTestCase):
def test_quote_type_single(self):
conf = 'quoted-strings: {quote-type: single}\n'
self.check('---\n'
'boolean1: true\n'
'number1: 123\n'
'string1: foo\n' # fails
'string2: "foo"\n' # fails
'string3: \'bar\'\n'
'string4: !!str genericstring\n'
'string5: !!str 456\n'
'string6: !!str "quotedgenericstring"\n'
'string3: "true"\n' # fails
'string4: "123"\n' # fails
'string5: \'bar\'\n'
'string6: !!str genericstring\n'
'string7: !!str 456\n'
'string8: !!str "quotedgenericstring"\n'
'binary: !!binary binstring\n'
'integer: !!int intstring\n'
'boolean2: !!bool boolstring\n'
'boolean3: !!bool "quotedboolstring"\n',
conf, problem1=(4, 10), problem2=(5, 10))
'boolean3: !!bool "quotedboolstring"\n'
'block-seq:\n'
' - foo\n' # fails
' - "foo"\n' # fails
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
conf, problem1=(4, 10), problem2=(5, 10), problem3=(6, 10),
problem4=(7, 10), problem5=(17, 5), problem6=(18, 5),
problem7=(19, 12), problem8=(19, 17), problem9=(20, 15),
problem10=(20, 23))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
@@ -86,7 +111,7 @@ class QuotedTestCase(RuleTestCase):
' word 1\n'
' word 2\n'
'multiline string 3:\n'
' word 1\n'
' word 1\n' # fails
' word 2\n'
'multiline string 4:\n'
' "word 1\\\n'
@@ -95,20 +120,68 @@ class QuotedTestCase(RuleTestCase):
def test_quote_type_double(self):
conf = 'quoted-strings: {quote-type: double}\n'
self.check('---\n'
'boolean1: true\n'
'number1: 123\n'
'string1: foo\n' # fails
'string2: "foo"\n'
'string3: \'bar\'\n' # fails
'string4: !!str genericstring\n'
'string5: !!str 456\n'
'string6: !!str "quotedgenericstring"\n'
'string3: "true"\n'
'string4: "123"\n'
'string5: \'bar\'\n' # fails
'string6: !!str genericstring\n'
'string7: !!str 456\n'
'string8: !!str "quotedgenericstring"\n'
'binary: !!binary binstring\n'
'integer: !!int intstring\n'
'boolean2: !!bool boolstring\n'
'boolean3: !!bool "quotedboolstring"\n',
conf, problem1=(4, 10), problem2=(6, 10))
'boolean3: !!bool "quotedboolstring"\n'
'block-seq:\n'
' - foo\n' # fails
' - "foo"\n'
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
conf, problem1=(4, 10), problem2=(8, 10), problem3=(17, 5),
problem4=(19, 12), problem5=(20, 15))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
' line 2\n'
'multiline string 2: >\n'
' word 1\n'
' word 2\n'
'multiline string 3:\n'
' word 1\n' # fails
' word 2\n'
'multiline string 4:\n'
' "word 1\\\n'
' word 2"\n',
conf, problem1=(9, 3))
def test_any_quotes_not_required(self):
conf = 'quoted-strings: {quote-type: any, required: false}\n'
self.check('---\n'
'boolean1: true\n'
'number1: 123\n'
'string1: foo\n'
'string2: "foo"\n'
'string3: "true"\n'
'string4: "123"\n'
'string5: \'bar\'\n'
'string6: !!str genericstring\n'
'string7: !!str 456\n'
'string8: !!str "quotedgenericstring"\n'
'binary: !!binary binstring\n'
'integer: !!int intstring\n'
'boolean2: !!bool boolstring\n'
'boolean3: !!bool "quotedboolstring"\n'
'block-seq:\n'
' - foo\n' # fails
' - "foo"\n'
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
conf)
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
@@ -122,4 +195,243 @@ class QuotedTestCase(RuleTestCase):
'multiline string 4:\n'
' "word 1\\\n'
' word 2"\n',
conf, problem1=(9, 3))
conf)
def test_single_quotes_not_required(self):
conf = 'quoted-strings: {quote-type: single, required: false}\n'
self.check('---\n'
'boolean1: true\n'
'number1: 123\n'
'string1: foo\n'
'string2: "foo"\n' # fails
'string3: "true"\n' # fails
'string4: "123"\n' # fails
'string5: \'bar\'\n'
'string6: !!str genericstring\n'
'string7: !!str 456\n'
'string8: !!str "quotedgenericstring"\n'
'binary: !!binary binstring\n'
'integer: !!int intstring\n'
'boolean2: !!bool boolstring\n'
'boolean3: !!bool "quotedboolstring"\n'
'block-seq:\n'
' - foo\n' # fails
' - "foo"\n'
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
conf, problem1=(5, 10), problem2=(6, 10), problem3=(7, 10),
problem4=(18, 5), problem5=(19, 17), problem6=(20, 23))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
' line 2\n'
'multiline string 2: >\n'
' word 1\n'
' word 2\n'
'multiline string 3:\n'
' word 1\n'
' word 2\n'
'multiline string 4:\n'
' "word 1\\\n' # fails
' word 2"\n',
conf, problem1=(12, 3))
def test_only_when_needed(self):
conf = 'quoted-strings: {required: only-when-needed}\n'
self.check('---\n'
'boolean1: true\n'
'number1: 123\n'
'string1: foo\n'
'string2: "foo"\n' # fails
'string3: "true"\n'
'string4: "123"\n'
'string5: \'bar\'\n' # fails
'string6: !!str genericstring\n'
'string7: !!str 456\n'
'string8: !!str "quotedgenericstring"\n'
'binary: !!binary binstring\n'
'integer: !!int intstring\n'
'boolean2: !!bool boolstring\n'
'boolean3: !!bool "quotedboolstring"\n'
'block-seq:\n'
' - foo\n'
' - "foo"\n' # fails
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
conf, problem1=(5, 10), problem2=(8, 10), problem3=(18, 5),
problem4=(19, 17), problem5=(20, 23))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
' line 2\n'
'multiline string 2: >\n'
' word 1\n'
' word 2\n'
'multiline string 3:\n'
' word 1\n'
' word 2\n'
'multiline string 4:\n'
' "word 1\\\n' # fails
' word 2"\n',
conf, problem1=(12, 3))
def test_only_when_needed_single_quotes(self):
conf = ('quoted-strings: {quote-type: single,\n'
' required: only-when-needed}\n')
self.check('---\n'
'boolean1: true\n'
'number1: 123\n'
'string1: foo\n'
'string2: "foo"\n' # fails
'string3: "true"\n' # fails
'string4: "123"\n' # fails
'string5: \'bar\'\n' # fails
'string6: !!str genericstring\n'
'string7: !!str 456\n'
'string8: !!str "quotedgenericstring"\n'
'binary: !!binary binstring\n'
'integer: !!int intstring\n'
'boolean2: !!bool boolstring\n'
'boolean3: !!bool "quotedboolstring"\n'
'block-seq:\n'
' - foo\n'
' - "foo"\n' # fails
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
conf, problem1=(5, 10), problem2=(6, 10), problem3=(7, 10),
problem4=(8, 10), problem5=(18, 5), problem6=(19, 17),
problem7=(20, 23))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
' line 2\n'
'multiline string 2: >\n'
' word 1\n'
' word 2\n'
'multiline string 3:\n'
' word 1\n'
' word 2\n'
'multiline string 4:\n'
' "word 1\\\n' # fails
' word 2"\n',
conf, problem1=(12, 3))
def test_only_when_needed_corner_cases(self):
conf = 'quoted-strings: {required: only-when-needed}\n'
self.check('---\n'
'- ""\n'
'- "- item"\n'
'- "key: value"\n'
'- "%H:%M:%S"\n'
'- "%wheel ALL=(ALL) NOPASSWD: ALL"\n'
'- \'"quoted"\'\n'
'- "\'foo\' == \'bar\'"\n'
'- "\'Mac\' in ansible_facts.product_name"\n',
conf)
self.check('---\n'
'k1: ""\n'
'k2: "- item"\n'
'k3: "key: value"\n'
'k4: "%H:%M:%S"\n'
'k5: "%wheel ALL=(ALL) NOPASSWD: ALL"\n'
'k6: \'"quoted"\'\n'
'k7: "\'foo\' == \'bar\'"\n'
'k8: "\'Mac\' in ansible_facts.product_name"\n',
conf)
self.check('---\n'
'- ---\n'
'- "---"\n' # fails
'- ----------\n'
'- "----------"\n' # fails
'- :wq\n'
'- ":wq"\n', # fails
conf, problem1=(3, 3), problem2=(5, 3), problem3=(7, 3))
self.check('---\n'
'k1: ---\n'
'k2: "---"\n' # fails
'k3: ----------\n'
'k4: "----------"\n' # fails
'k5: :wq\n'
'k6: ":wq"\n', # fails
conf, problem1=(3, 5), problem2=(5, 5), problem3=(7, 5))
def test_only_when_needed_extras(self):
conf = ('quoted-strings:\n'
' required: true\n'
' extra-allowed: [^http://]\n')
self.assertRaises(config.YamlLintConfigError, self.check, '', conf)
conf = ('quoted-strings:\n'
' required: true\n'
' extra-required: [^http://]\n')
self.assertRaises(config.YamlLintConfigError, self.check, '', conf)
conf = ('quoted-strings:\n'
' required: false\n'
' extra-allowed: [^http://]\n')
self.assertRaises(config.YamlLintConfigError, self.check, '', conf)
conf = ('quoted-strings:\n'
' required: true\n')
self.check('---\n'
'- 123\n'
'- "123"\n'
'- localhost\n' # fails
'- "localhost"\n'
'- http://localhost\n' # fails
'- "http://localhost"\n'
'- ftp://localhost\n' # fails
'- "ftp://localhost"\n',
conf, problem1=(4, 3), problem2=(6, 3), problem3=(8, 3))
conf = ('quoted-strings:\n'
' required: only-when-needed\n'
' extra-allowed: [^ftp://]\n'
' extra-required: [^http://]\n')
self.check('---\n'
'- 123\n'
'- "123"\n'
'- localhost\n'
'- "localhost"\n' # fails
'- http://localhost\n' # fails
'- "http://localhost"\n'
'- ftp://localhost\n'
'- "ftp://localhost"\n',
conf, problem1=(5, 3), problem2=(6, 3))
conf = ('quoted-strings:\n'
' required: false\n'
' extra-required: [^http://, ^ftp://]\n')
self.check('---\n'
'- 123\n'
'- "123"\n'
'- localhost\n'
'- "localhost"\n'
'- http://localhost\n' # fails
'- "http://localhost"\n'
'- ftp://localhost\n' # fails
'- "ftp://localhost"\n',
conf, problem1=(6, 3), problem2=(8, 3))
conf = ('quoted-strings:\n'
' required: only-when-needed\n'
' extra-allowed: [^ftp://, ";$", " "]\n')
self.check('---\n'
'- localhost\n'
'- "localhost"\n' # fails
'- ftp://localhost\n'
'- "ftp://localhost"\n'
'- i=i+1\n'
'- "i=i+1"\n' # fails
'- i=i+2;\n'
'- "i=i+2;"\n'
'- foo\n'
'- "foo"\n' # fails
'- foo bar\n'
'- "foo bar"\n',
conf, problem1=(3, 3), problem2=(7, 3), problem3=(11, 3))

View File

@@ -49,6 +49,54 @@ class TruthyTestCase(RuleTestCase):
problem3=(7, 3), problem4=(7, 7),
problem5=(8, 3), problem6=(8, 7))
def test_different_allowed_values(self):
conf = ('truthy:\n'
' allowed-values: ["yes", "no"]\n')
self.check('---\n'
'key1: foo\n'
'key2: yes\n'
'key3: bar\n'
'key4: no\n', conf)
self.check('---\n'
'key1: true\n'
'key2: Yes\n'
'key3: false\n'
'key4: no\n'
'key5: yes\n',
conf,
problem1=(2, 7), problem2=(3, 7),
problem3=(4, 7))
def test_combined_allowed_values(self):
conf = ('truthy:\n'
' allowed-values: ["yes", "no", "true", "false"]\n')
self.check('---\n'
'key1: foo\n'
'key2: yes\n'
'key3: bar\n'
'key4: no\n', conf)
self.check('---\n'
'key1: true\n'
'key2: Yes\n'
'key3: false\n'
'key4: no\n'
'key5: yes\n',
conf, problem1=(3, 7))
def test_no_allowed_values(self):
conf = ('truthy:\n'
' allowed-values: []\n')
self.check('---\n'
'key1: foo\n'
'key2: bar\n', conf)
self.check('---\n'
'key1: true\n'
'key2: yes\n'
'key3: false\n'
'key4: no\n', conf,
problem1=(2, 7), problem2=(3, 7),
problem3=(4, 7), problem4=(5, 7))
def test_explicit_types(self):
conf = 'truthy: enable\n'
self.check('---\n'
@@ -66,3 +114,33 @@ class TruthyTestCase(RuleTestCase):
'boolean5: !!bool off\n'
'boolean6: !!bool NO\n',
conf)
def test_check_keys_disabled(self):
conf = ('truthy:\n'
' allowed-values: []\n'
' check-keys: false\n'
'key-duplicates: disable\n')
self.check('---\n'
'YES: 0\n'
'Yes: 0\n'
'yes: 0\n'
'No: 0\n'
'No: 0\n'
'no: 0\n'
'TRUE: 0\n'
'True: 0\n'
'true: 0\n'
'FALSE: 0\n'
'False: 0\n'
'false: 0\n'
'ON: 0\n'
'On: 0\n'
'on: 0\n'
'OFF: 0\n'
'Off: 0\n'
'off: 0\n'
'YES:\n'
' Yes:\n'
' yes:\n'
' on: 0\n',
conf)

View File

@@ -29,6 +29,30 @@ import unittest
from tests.common import build_temp_workspace
from yamllint import cli
from yamllint import config
class RunContext(object):
"""Context manager for ``cli.run()`` to capture exit code and streams."""
def __init__(self, case):
self.stdout = self.stderr = None
self._raises_ctx = case.assertRaises(SystemExit)
def __enter__(self):
self._raises_ctx.__enter__()
sys.stdout = self.outstream = StringIO()
sys.stderr = self.errstream = StringIO()
return self
def __exit__(self, *exc_info):
self.stdout, sys.stdout = self.outstream.getvalue(), sys.__stdout__
self.stderr, sys.stderr = self.errstream.getvalue(), sys.__stderr__
return self._raises_ctx.__exit__(*exc_info)
@property
def returncode(self):
return self._raises_ctx.exception.code
class CommandLineTestCase(unittest.TestCase):
@@ -58,12 +82,15 @@ class CommandLineTestCase(unittest.TestCase):
'no-yaml.json': '---\n'
'key: value\n',
# non-ASCII chars
'non-ascii/utf-8': (
'non-ascii/éçäγλνπ¥/utf-8': (
u'---\n'
u'- hétérogénéité\n'
u'# 19.99 €\n'
u'- お早う御座います。\n'
u'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'),
# dos line endings yaml
'dos.yml': '---\r\n'
'dos: true',
})
@classmethod
@@ -73,9 +100,11 @@ class CommandLineTestCase(unittest.TestCase):
shutil.rmtree(cls.wd)
def test_find_files_recursively(self):
conf = config.YamlLintConfig('extends: default')
self.assertEqual(
sorted(cli.find_files_recursively([self.wd])),
sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'dos.yml'),
os.path.join(self.wd, 'empty.yml'),
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'),
@@ -85,14 +114,14 @@ class CommandLineTestCase(unittest.TestCase):
items = [os.path.join(self.wd, 'sub/ok.yaml'),
os.path.join(self.wd, 'empty-dir')]
self.assertEqual(
sorted(cli.find_files_recursively(items)),
sorted(cli.find_files_recursively(items, conf)),
[os.path.join(self.wd, 'sub/ok.yaml')],
)
items = [os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 's')]
self.assertEqual(
sorted(cli.find_files_recursively(items)),
sorted(cli.find_files_recursively(items, conf)),
[os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml')],
)
@@ -100,196 +129,219 @@ class CommandLineTestCase(unittest.TestCase):
items = [os.path.join(self.wd, 'sub'),
os.path.join(self.wd, '/etc/another/file')]
self.assertEqual(
sorted(cli.find_files_recursively(items)),
sorted(cli.find_files_recursively(items, conf)),
[os.path.join(self.wd, '/etc/another/file'),
os.path.join(self.wd, 'sub/ok.yaml')],
)
conf = config.YamlLintConfig('extends: default\n'
'yaml-files:\n'
' - \'*.yaml\' \n')
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'a.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')]
)
conf = config.YamlLintConfig('extends: default\n'
'yaml-files:\n'
' - \'*.yml\'\n')
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'dos.yml'),
os.path.join(self.wd, 'empty.yml')]
)
conf = config.YamlLintConfig('extends: default\n'
'yaml-files:\n'
' - \'*.json\'\n')
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'no-yaml.json')]
)
conf = config.YamlLintConfig('extends: default\n'
'yaml-files:\n'
' - \'*\'\n')
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'dos.yml'),
os.path.join(self.wd, 'empty.yml'),
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/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')]
)
conf = config.YamlLintConfig('extends: default\n'
'yaml-files:\n'
' - \'*.yaml\'\n'
' - \'*\'\n'
' - \'**\'\n')
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'a.yaml'),
os.path.join(self.wd, 'dos.yml'),
os.path.join(self.wd, 'empty.yml'),
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/ok.yaml'),
os.path.join(self.wd, 'warn.yaml')]
)
conf = config.YamlLintConfig('extends: default\n'
'yaml-files:\n'
' - \'s/**\'\n'
' - \'**/utf-8\'\n')
self.assertEqual(
sorted(cli.find_files_recursively([self.wd], conf)),
[os.path.join(self.wd, 'non-ascii/éçäγλνπ¥/utf-8')]
)
def test_run_with_bad_arguments(self):
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
with RunContext(self) as ctx:
cli.run(())
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^usage')
self.assertNotEqual(ctx.exception.code, 0)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertRegexpMatches(err, r'^usage')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
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.assertNotEqual(ctx.exception.code, 0)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertRegexpMatches(err, r'^usage')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
with RunContext(self) as ctx:
cli.run(('-c', './conf.yaml', '-d', 'relaxed', 'file'))
self.assertNotEqual(ctx.exception.code, 0)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(
err.splitlines()[-1],
ctx.stderr.splitlines()[-1],
r'^yamllint: error: argument -d\/--config-data: '
r'not allowed with argument -c\/--config-file$'
)
# checks if reading from stdin and files are mutually exclusive
with RunContext(self) as ctx:
cli.run(('-', 'file'))
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^usage')
def test_run_with_bad_config(self):
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
with RunContext(self) as ctx:
cli.run(('-d', 'rules: {a: b}', 'file'))
self.assertEqual(ctx.exception.code, -1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertRegexpMatches(err, r'^invalid config: no such rule')
self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^invalid config: no such rule')
def test_run_with_empty_config(self):
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
with RunContext(self) as ctx:
cli.run(('-d', '', 'file'))
self.assertEqual(ctx.exception.code, -1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertRegexpMatches(err, r'^invalid config: not a dict')
self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(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:
f.write('rules: {trailing-spaces: disable}')
with self.assertRaises(SystemExit) as ctx:
with RunContext(self) as ctx:
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.exception.code, 0)
self.assertEqual(ctx.returncode, 0)
with open(os.path.join(self.wd, 'config'), 'w') as f:
f.write('rules: {trailing-spaces: enable}')
with self.assertRaises(SystemExit) as ctx:
with RunContext(self) as ctx:
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.exception.code, 1)
self.assertEqual(ctx.returncode, 1)
def test_run_with_user_global_config_file(self):
home = os.path.join(self.wd, 'fake-home')
os.mkdir(home)
dir = os.path.join(home, '.config')
os.mkdir(dir)
dir = os.path.join(dir, 'yamllint')
os.mkdir(dir)
dir = os.path.join(home, '.config', 'yamllint')
os.makedirs(dir)
config = os.path.join(dir, 'config')
temp = os.environ['HOME']
self.addCleanup(os.environ.update, HOME=os.environ['HOME'])
os.environ['HOME'] = home
with open(config, 'w') as f:
f.write('rules: {trailing-spaces: disable}')
with self.assertRaises(SystemExit) as ctx:
with RunContext(self) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.exception.code, 0)
self.assertEqual(ctx.returncode, 0)
with open(config, 'w') as f:
f.write('rules: {trailing-spaces: enable}')
with self.assertRaises(SystemExit) as ctx:
with RunContext(self) as ctx:
cli.run((os.path.join(self.wd, 'a.yaml'), ))
self.assertEqual(ctx.exception.code, 1)
os.environ['HOME'] = temp
self.assertEqual(ctx.returncode, 1)
def test_run_version(self):
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
with RunContext(self) as ctx:
cli.run(('--version', ))
self.assertEqual(ctx.exception.code, 0)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertRegexpMatches(out + err, r'yamllint \d+\.\d+')
self.assertEqual(ctx.returncode, 0)
self.assertRegexpMatches(ctx.stdout + ctx.stderr, r'yamllint \d+\.\d+')
def test_run_non_existing_file(self):
file = os.path.join(self.wd, 'i-do-not-exist.yaml')
path = os.path.join(self.wd, 'i-do-not-exist.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', file))
self.assertEqual(ctx.exception.code, -1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertRegexpMatches(err, r'No such file or directory')
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', path))
self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'No such file or directory')
def test_run_one_problem_file(self):
file = os.path.join(self.wd, 'a.yaml')
path = os.path.join(self.wd, 'a.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', file))
self.assertEqual(ctx.exception.code, 1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, (
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', path))
self.assertEqual(ctx.returncode, 1)
self.assertEqual(ctx.stdout, (
'%s:2:4: [error] trailing spaces (trailing-spaces)\n'
'%s:3:4: [error] no new line character at the end of file '
'(new-line-at-end-of-file)\n') % (file, file))
self.assertEqual(err, '')
'(new-line-at-end-of-file)\n' % (path, path)))
self.assertEqual(ctx.stderr, '')
def test_run_one_warning(self):
file = os.path.join(self.wd, 'warn.yaml')
path = os.path.join(self.wd, 'warn.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', file))
self.assertEqual(ctx.exception.code, 0)
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', path))
self.assertEqual(ctx.returncode, 0)
def test_run_warning_in_strict_mode(self):
file = os.path.join(self.wd, 'warn.yaml')
path = os.path.join(self.wd, 'warn.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', '--strict', file))
self.assertEqual(ctx.exception.code, 2)
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', '--strict', path))
self.assertEqual(ctx.returncode, 2)
def test_run_one_ok_file(self):
file = os.path.join(self.wd, 'sub', 'ok.yaml')
path = os.path.join(self.wd, 'sub', 'ok.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', file))
self.assertEqual(ctx.exception.code, 0)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertEqual(err, '')
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', path))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', ''))
def test_run_empty_file(self):
file = os.path.join(self.wd, 'empty.yml')
path = os.path.join(self.wd, 'empty.yml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', file))
self.assertEqual(ctx.exception.code, 0)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertEqual(err, '')
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', path))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', ''))
def test_run_non_ascii_file(self):
file = os.path.join(self.wd, 'non-ascii', 'utf-8')
path = os.path.join(self.wd, 'non-ascii', 'éçäγλνπ¥', 'utf-8')
# Make sure the default localization conditions on this "system"
# support UTF-8 encoding.
@@ -298,63 +350,46 @@ class CommandLineTestCase(unittest.TestCase):
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)
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run(('-f', 'parsable', file))
locale.setlocale(locale.LC_ALL, loc)
self.assertEqual(ctx.exception.code, 0)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, '')
self.assertEqual(err, '')
with RunContext(self) as ctx:
cli.run(('-f', 'parsable', path))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', ''))
def test_run_multiple_files(self):
items = [os.path.join(self.wd, 'empty.yml'),
os.path.join(self.wd, 's')]
file = items[1] + '/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'
path = items[1] + '/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
with RunContext(self) as ctx:
cli.run(['-f', 'parsable'] + items)
self.assertEqual(ctx.exception.code, 1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, (
self.assertEqual((ctx.returncode, ctx.stderr), (1, ''))
self.assertEqual(ctx.stdout, (
'%s:3:1: [error] duplication of key "key" in mapping '
'(key-duplicates)\n') % file)
self.assertEqual(err, '')
'(key-duplicates)\n') % path)
def test_run_piped_output_nocolor(self):
file = os.path.join(self.wd, 'a.yaml')
path = os.path.join(self.wd, 'a.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run((file, ))
self.assertEqual(ctx.exception.code, 1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, (
with RunContext(self) as ctx:
cli.run((path, ))
self.assertEqual((ctx.returncode, ctx.stderr), (1, ''))
self.assertEqual(ctx.stdout, (
'%s\n'
' 2:4 error trailing spaces (trailing-spaces)\n'
' 3:4 error no new line character at the end of file '
'(new-line-at-end-of-file)\n'
'\n' % file))
self.assertEqual(err, '')
'\n' % path))
def test_run_default_format_output_in_tty(self):
file = os.path.join(self.wd, 'a.yaml')
path = os.path.join(self.wd, 'a.yaml')
# Create a pseudo-TTY and redirect stdout to it
master, slave = pty.openpty()
sys.stdout = sys.stderr = os.fdopen(slave, 'w')
with self.assertRaises(SystemExit) as ctx:
cli.run((file, ))
cli.run((path, ))
sys.stdout.flush()
self.assertEqual(ctx.exception.code, 1)
@@ -377,60 +412,108 @@ class CommandLineTestCase(unittest.TestCase):
' \033[2m3:4\033[0m \033[31merror\033[0m '
'no new line character at the end of file '
'\033[2m(new-line-at-end-of-file)\033[0m\n'
'\n' % file))
'\n' % path))
def test_run_default_format_output_without_tty(self):
file = os.path.join(self.wd, 'a.yaml')
path = os.path.join(self.wd, 'a.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run((file, ))
self.assertEqual(ctx.exception.code, 1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, (
with RunContext(self) as ctx:
cli.run((path, ))
expected_out = (
'%s\n'
' 2:4 error trailing spaces (trailing-spaces)\n'
' 3:4 error no new line character at the end of file '
'(new-line-at-end-of-file)\n'
'\n' % file))
self.assertEqual(err, '')
'\n' % path)
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
def test_run_auto_output_without_tty_output(self):
file = os.path.join(self.wd, 'a.yaml')
path = os.path.join(self.wd, 'a.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run((file, '--format', 'auto'))
self.assertEqual(ctx.exception.code, 1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, (
with RunContext(self) as ctx:
cli.run((path, '--format', 'auto'))
expected_out = (
'%s\n'
' 2:4 error trailing spaces (trailing-spaces)\n'
' 3:4 error no new line character at the end of file '
'(new-line-at-end-of-file)\n'
'\n' % file))
self.assertEqual(err, '')
'\n' % path)
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
def test_run_format_colored(self):
file = os.path.join(self.wd, 'a.yaml')
path = os.path.join(self.wd, 'a.yaml')
sys.stdout, sys.stderr = StringIO(), StringIO()
with self.assertRaises(SystemExit) as ctx:
cli.run((file, '--format', 'colored'))
self.assertEqual(ctx.exception.code, 1)
out, err = sys.stdout.getvalue(), sys.stderr.getvalue()
self.assertEqual(out, (
with RunContext(self) as ctx:
cli.run((path, '--format', 'colored'))
expected_out = (
'\033[4m%s\033[0m\n'
' \033[2m2:4\033[0m \033[31merror\033[0m '
'trailing spaces \033[2m(trailing-spaces)\033[0m\n'
' \033[2m3:4\033[0m \033[31merror\033[0m '
'no new line character at the end of file '
'\033[2m(new-line-at-end-of-file)\033[0m\n'
'\n' % file))
self.assertEqual(err, '')
'\n' % 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
self.addCleanup(setattr, sys, 'stdin', sys.__stdin__)
sys.stdin = StringIO(
'I am a string\n'
'therefore: I am an error\n')
with RunContext(self) as ctx:
cli.run(('-', '-f', 'parsable'))
expected_out = (
'stdin:2:10: [error] syntax error: '
'mapping values are not allowed here (syntax)\n')
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
def test_run_no_warnings(self):
path = os.path.join(self.wd, 'a.yaml')
with RunContext(self) as ctx:
cli.run((path, '--no-warnings', '-f', 'auto'))
expected_out = (
'%s\n'
' 2:4 error trailing spaces (trailing-spaces)\n'
' 3:4 error no new line character at the end of file '
'(new-line-at-end-of-file)\n'
'\n' % path)
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))
path = os.path.join(self.wd, 'warn.yaml')
with RunContext(self) as ctx:
cli.run((path, '--no-warnings', '-f', 'auto'))
self.assertEqual(ctx.returncode, 0)
def test_run_no_warnings_and_strict(self):
path = os.path.join(self.wd, 'warn.yaml')
with RunContext(self) as ctx:
cli.run((path, '--no-warnings', '-s'))
self.assertEqual(ctx.returncode, 2)
def test_run_non_universal_newline(self):
path = os.path.join(self.wd, 'dos.yml')
with RunContext(self) as ctx:
cli.run(('-d', 'rules:\n new-lines:\n type: dos', path))
self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', ''))
with RunContext(self) as ctx:
cli.run(('-d', 'rules:\n new-lines:\n type: unix', path))
expected_out = (
'%s\n'
' 1:4 error wrong new line character: expected \\n'
' (new-lines)\n'
'\n' % path)
self.assertEqual(
(ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, ''))

View File

@@ -174,6 +174,25 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertRaises(config.YamlLintConfigError,
config.validate_rule_conf, Rule, {'choice': 'abc'})
Rule.CONF = {'multiple': ['item1', 'item2', 'item3']}
Rule.DEFAULT = {'multiple': ['item1']}
config.validate_rule_conf(Rule, {'multiple': []})
config.validate_rule_conf(Rule, {'multiple': ['item2']})
config.validate_rule_conf(Rule, {'multiple': ['item2', 'item3']})
config.validate_rule_conf(Rule, {})
self.assertRaises(config.YamlLintConfigError,
config.validate_rule_conf, Rule,
{'multiple': 'item1'})
self.assertRaises(config.YamlLintConfigError,
config.validate_rule_conf, Rule,
{'multiple': ['']})
self.assertRaises(config.YamlLintConfigError,
config.validate_rule_conf, Rule,
{'multiple': ['item1', 4]})
self.assertRaises(config.YamlLintConfigError,
config.validate_rule_conf, Rule,
{'multiple': ['item4']})
class ExtendedConfigTestCase(unittest.TestCase):
def test_extend_on_object(self):
@@ -429,11 +448,13 @@ class IgnorePathConfigTestCase(unittest.TestCase):
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,

View File

@@ -302,3 +302,104 @@ class YamllintDirectivesTestCase(RuleTestCase):
' c: [x]\n',
conf,
problem=(6, 2, 'comments-indentation'))
def test_disable_file_directive(self):
conf = ('comments: {min-spaces-from-content: 2}\n'
'comments-indentation: {}\n')
self.check('# yamllint disable-file\n'
'---\n'
'- a: 1 # comment too close\n'
' b:\n'
' # wrong indentation\n'
' c: [x]\n',
conf)
self.check('# yamllint disable-file\n'
'---\n'
'- a: 1 # comment too close\n'
' b:\n'
' # wrong indentation\n'
' c: [x]\n',
conf)
self.check('#yamllint disable-file\n'
'---\n'
'- a: 1 # comment too close\n'
' b:\n'
' # wrong indentation\n'
' c: [x]\n',
conf)
self.check('#yamllint disable-file \n'
'---\n'
'- a: 1 # comment too close\n'
' b:\n'
' # wrong indentation\n'
' c: [x]\n',
conf)
self.check('---\n'
'# yamllint disable-file\n'
'- a: 1 # comment too close\n'
' b:\n'
' # wrong indentation\n'
' c: [x]\n',
conf,
problem1=(3, 8, 'comments'),
problem2=(5, 2, 'comments-indentation'))
self.check('# yamllint disable-file: rules cannot be specified\n'
'---\n'
'- a: 1 # comment too close\n'
' b:\n'
' # wrong indentation\n'
' c: [x]\n',
conf,
problem1=(3, 8, 'comments'),
problem2=(5, 2, 'comments-indentation'))
self.check('AAAA yamllint disable-file\n'
'---\n'
'- a: 1 # comment too close\n'
' b:\n'
' # wrong indentation\n'
' c: [x]\n',
conf,
problem1=(1, 1, 'document-start'),
problem2=(3, 8, 'comments'),
problem3=(5, 2, 'comments-indentation'))
def test_disable_file_directive_not_at_first_position(self):
self.check('# yamllint disable-file\n'
'---\n'
'- bad : colon and spaces \n',
self.conf)
self.check('---\n'
'# yamllint disable-file\n'
'- bad : colon and spaces \n',
self.conf,
problem1=(3, 7, 'colons'),
problem2=(3, 26, 'trailing-spaces'))
def test_disable_file_directive_with_syntax_error(self):
self.check('# This file is not valid YAML (it is a Jinja template)\n'
'{% if extra_info %}\n'
'key1: value1\n'
'{% endif %}\n'
'key2: value2\n',
self.conf,
problem=(2, 2, 'syntax'))
self.check('# yamllint disable-file\n'
'# This file is not valid YAML (it is a Jinja template)\n'
'{% if extra_info %}\n'
'key1: value1\n'
'{% endif %}\n'
'key2: value2\n',
self.conf)
def test_disable_file_directive_with_dos_lines(self):
self.check('# yamllint disable-file\r\n'
'---\r\n'
'- bad : colon and spaces \r\n',
self.conf)
self.check('# yamllint disable-file\r\n'
'# This file is not valid YAML (it is a Jinja template)\r\n'
'{% if extra_info %}\r\n'
'key1: value1\r\n'
'{% endif %}\r\n'
'key2: value2\r\n',
self.conf)

View File

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

View File

@@ -16,24 +16,26 @@
from __future__ import print_function
import os
import sys
import platform
import argparse
import io
import os
import platform
import sys
from yamllint import APP_DESCRIPTION, APP_NAME, APP_VERSION
from yamllint import linter
from yamllint.config import YamlLintConfig, YamlLintConfigError
from yamllint.linter import PROBLEM_LEVELS
from yamllint import linter
def find_files_recursively(items):
def find_files_recursively(items, conf):
for item in items:
if os.path.isdir(item):
for root, dirnames, filenames in os.walk(item):
for filename in [f for f in filenames
if f.endswith(('.yml', '.yaml'))]:
yield os.path.join(root, filename)
for f in filenames:
filepath = os.path.join(root, f)
if conf.is_yaml_file(filepath):
yield filepath
else:
yield item
@@ -83,11 +85,43 @@ class Format(object):
return line
def show_problems(problems, file, args_format, no_warn):
max_level = 0
first = True
for problem in problems:
max_level = max(max_level, PROBLEM_LEVELS[problem.level])
if no_warn and (problem.level != 'error'):
continue
if args_format == 'parsable':
print(Format.parsable(problem, file))
elif args_format == 'colored' or \
(args_format == 'auto' and supports_color()):
if first:
print('\033[4m%s\033[0m' % file)
first = False
print(Format.standard_color(problem, file))
else:
if first:
print(file)
first = False
print(Format.standard(problem, file))
if not first and args_format != 'parsable':
print('')
return max_level
def run(argv=None):
parser = argparse.ArgumentParser(prog=APP_NAME,
description=APP_DESCRIPTION)
parser.add_argument('files', metavar='FILE_OR_DIR', nargs='+',
help='files to check')
files_group = parser.add_mutually_exclusive_group(required=True)
files_group.add_argument('files', metavar='FILE_OR_DIR', nargs='*',
default=(),
help='files to check')
files_group.add_argument('-', action='store_true', dest='stdin',
help='read from standard input')
config_group = parser.add_mutually_exclusive_group()
config_group.add_argument('-c', '--config-file', dest='config_file',
action='store',
@@ -102,11 +136,12 @@ def run(argv=None):
action='store_true',
help='return non-zero exit code on warnings '
'as well as errors')
parser.add_argument('--no-warnings',
action='store_true',
help='output only error level problems')
parser.add_argument('-v', '--version', action='version',
version='{} {}'.format(APP_NAME, APP_VERSION))
# TODO: read from stdin when no filename?
args = parser.parse_args(argv)
# User-global config is supposed to be in ~/.config/yamllint/config
@@ -125,6 +160,10 @@ def run(argv=None):
conf = YamlLintConfig(file=args.config_file)
elif os.path.isfile('.yamllint'):
conf = YamlLintConfig(file='.yamllint')
elif os.path.isfile('.yamllint.yaml'):
conf = YamlLintConfig(file='.yamllint.yaml')
elif os.path.isfile('.yamllint.yml'):
conf = YamlLintConfig(file='.yamllint.yml')
elif os.path.isfile(user_global_config):
conf = YamlLintConfig(file=user_global_config)
else:
@@ -135,35 +174,28 @@ def run(argv=None):
max_level = 0
for file in find_files_recursively(args.files):
for file in find_files_recursively(args.files, conf):
filepath = file[2:] if file.startswith('./') else file
try:
first = True
with open(file) as f:
for problem in linter.run(f, conf, filepath):
if args.format == 'parsable':
print(Format.parsable(problem, file))
elif args.format == 'colored' or \
(args.format == 'auto' and supports_color()):
if first:
print('\033[4m%s\033[0m' % file)
first = False
print(Format.standard_color(problem, file))
else:
if first:
print(file)
first = False
print(Format.standard(problem, file))
max_level = max(max_level, PROBLEM_LEVELS[problem.level])
if not first and args.format != 'parsable':
print('')
with io.open(file, newline='') as f:
problems = linter.run(f, conf, filepath)
except EnvironmentError as e:
print(e, file=sys.stderr)
sys.exit(-1)
prob_level = show_problems(problems, file, args_format=args.format,
no_warn=args.no_warnings)
max_level = max(max_level, prob_level)
# read yaml from stdin
if args.stdin:
try:
problems = linter.run(sys.stdin, conf, '')
except EnvironmentError as e:
print(e, file=sys.stderr)
sys.exit(-1)
prob_level = show_problems(problems, 'stdin', args_format=args.format,
no_warn=args.no_warnings)
max_level = max(max_level, prob_level)
if max_level == PROBLEM_LEVELS['error']:
return_code = 1

View File

@@ -1,5 +1,10 @@
---
yaml-files:
- '*.yaml'
- '*.yml'
- '.yamllint'
rules:
braces: enable
brackets: enable
@@ -13,7 +18,7 @@ rules:
document-start:
level: warning
empty-lines: enable
empty-values: enable
empty-values: disable
hyphens: enable
indentation: enable
key-duplicates: enable
@@ -21,7 +26,7 @@ rules:
line-length: enable
new-line-at-end-of-file: enable
new-lines: enable
octal-values: enable
octal-values: disable
quoted-strings: disable
trailing-spaces: enable
truthy:

View File

@@ -32,6 +32,9 @@ class YamlLintConfig(object):
self.ignore = None
self.yaml_files = pathspec.PathSpec.from_lines(
'gitwildmatch', ['*.yaml', '*.yml', '.yamllint'])
if file is not None:
with open(file) as f:
content = f.read()
@@ -42,6 +45,9 @@ class YamlLintConfig(object):
def is_file_ignored(self, filepath):
return self.ignore and self.ignore.match_file(filepath)
def is_yaml_file(self, filepath):
return self.yaml_files.match_file(filepath)
def enabled_rules(self, filepath):
return [yamllint.rules.get(id) for id, val in self.rules.items()
if val is not False and (
@@ -96,6 +102,15 @@ class YamlLintConfig(object):
self.ignore = pathspec.PathSpec.from_lines(
'gitwildmatch', conf['ignore'].splitlines())
if 'yaml-files' in conf:
if not (isinstance(conf['yaml-files'], list)
and all(isinstance(i, str) for i in conf['yaml-files'])):
raise YamlLintConfigError(
'invalid config: yaml-files '
'should be a list of file patterns')
self.yaml_files = pathspec.PathSpec.from_lines('gitwildmatch',
conf['yaml-files'])
def validate(self):
for id in self.rules:
try:
@@ -134,12 +149,27 @@ def validate_rule_conf(rule, conf):
raise YamlLintConfigError(
'invalid config: unknown option "%s" for rule "%s"' %
(optkey, rule.ID))
# Example: CONF = {option: (bool, 'mixed')}
# → {option: true} → {option: mixed}
if isinstance(options[optkey], tuple):
if (conf[optkey] not in options[optkey] and
type(conf[optkey]) not in options[optkey]):
raise YamlLintConfigError(
'invalid config: option "%s" of "%s" should be in %s'
% (optkey, rule.ID, options[optkey]))
# Example: CONF = {option: ['flag1', 'flag2', int]}
# → {option: [flag1]} → {option: [42, flag1, flag2]}
elif isinstance(options[optkey], list):
if (type(conf[optkey]) is not list or
any(flag not in options[optkey] and
type(flag) not in options[optkey]
for flag in conf[optkey])):
raise YamlLintConfigError(
('invalid config: option "%s" of "%s" should only '
'contain values in %s')
% (optkey, rule.ID, str(options[optkey])))
# Example: CONF = {option: int}
# → {option: 42}
else:
if not isinstance(conf[optkey], options[optkey]):
raise YamlLintConfigError(
@@ -148,6 +178,12 @@ def validate_rule_conf(rule, conf):
for optkey in options:
if optkey not in conf:
conf[optkey] = options_default[optkey]
if hasattr(rule, 'VALIDATE'):
res = rule.VALIDATE(conf)
if res:
raise YamlLintConfigError('invalid config: %s: %s' %
(rule.ID, res))
else:
raise YamlLintConfigError(('invalid config: rule "%s": should be '
'either "enable", "disable" or a dict')

View File

@@ -180,7 +180,7 @@ def get_syntax_error(buffer):
except yaml.error.MarkedYAMLError as e:
problem = LintProblem(e.problem_mark.line + 1,
e.problem_mark.column + 1,
'syntax error: ' + e.problem)
'syntax error: ' + e.problem + ' (syntax)')
problem.level = 'error'
return problem
@@ -189,6 +189,10 @@ def _run(buffer, conf, filepath):
assert hasattr(buffer, '__getitem__'), \
'_run() argument must be a buffer, not a stream'
first_line = next(parser.line_generator(buffer)).content
if re.match(r'^#\s*yamllint disable-file\s*$', first_line):
return
# If the document contains a syntax error, save it and yield it at the
# right line
syntax_error = get_syntax_error(buffer)

View File

@@ -77,7 +77,10 @@ def line_generator(buffer):
cur = 0
next = buffer.find('\n')
while next != -1:
yield Line(line_no, buffer, start=cur, end=next)
if next > 0 and buffer[next - 1] == '\r':
yield Line(line_no, buffer, start=cur, end=next - 1)
else:
yield Line(line_no, buffer, start=cur, end=next)
cur = next + 1
next = buffer.find('\n', cur)
line_no += 1

View File

@@ -72,7 +72,7 @@ Use this rule to control the number of spaces before and after colons (``:``).
import yaml
from yamllint.rules.common import spaces_after, spaces_before, is_explicit_key
from yamllint.rules.common import is_explicit_key, spaces_after, spaces_before
ID = 'colons'

View File

@@ -66,27 +66,37 @@ DEFAULT = {'max': 2,
def check(conf, line):
if line.start == line.end and line.end < len(line.buffer):
# Only alert on the last blank line of a series
if (line.end < len(line.buffer) - 1 and
line.buffer[line.end + 1] == '\n'):
if (line.end + 2 <= len(line.buffer) and
line.buffer[line.end:line.end + 2] == '\n\n'):
return
elif (line.end + 4 <= len(line.buffer) and
line.buffer[line.end:line.end + 4] == '\r\n\r\n'):
return
blank_lines = 0
while (line.start > blank_lines and
line.buffer[line.start - blank_lines - 1] == '\n'):
start = line.start
while start >= 2 and line.buffer[start - 2:start] == '\r\n':
blank_lines += 1
start -= 2
while start >= 1 and line.buffer[start - 1] == '\n':
blank_lines += 1
start -= 1
max = conf['max']
# Special case: start of document
if line.start - blank_lines == 0:
if start == 0:
blank_lines += 1 # first line doesn't have a preceding \n
max = conf['max-start']
# Special case: end of document
# NOTE: The last line of a file is always supposed to end with a new
# line. See POSIX definition of a line at:
if line.end == len(line.buffer) - 1 and line.buffer[line.end] == '\n':
if ((line.end == len(line.buffer) - 1 and
line.buffer[line.end] == '\n') or
(line.end == len(line.buffer) - 2 and
line.buffer[line.end:line.end + 2] == '\r\n')):
# Allow the exception of the one-byte file containing '\n'
if line.end == 0:
return

View File

@@ -75,8 +75,8 @@ ID = 'empty-values'
TYPE = 'token'
CONF = {'forbid-in-block-mappings': bool,
'forbid-in-flow-mappings': bool}
DEFAULT = {'forbid-in-block-mappings': False,
'forbid-in-flow-mappings': False}
DEFAULT = {'forbid-in-block-mappings': True,
'forbid-in-flow-mappings': True}
def check(conf, token, prev, next, nextnext, context):

View File

@@ -193,7 +193,7 @@ Use this rule to control the indentation.
import yaml
from yamllint.linter import LintProblem
from yamllint.rules.common import is_explicit_key, get_real_end_line
from yamllint.rules.common import get_real_end_line, is_explicit_key
ID = 'indentation'

View File

@@ -36,10 +36,11 @@ DEFAULT = {'type': 'unix'}
def check(conf, line):
if line.start == 0 and len(line.buffer) > line.end:
if conf['type'] == 'dos':
if line.buffer[line.end - 1:line.end + 1] != '\r\n':
if (line.end + 2 > len(line.buffer) or
line.buffer[line.end:line.end + 2] != '\r\n'):
yield LintProblem(1, line.end - line.start + 1,
'wrong new line character: expected \\r\\n')
else:
if line.end > 0 and line.buffer[line.end - 1] == '\r':
yield LintProblem(1, line.end - line.start,
if line.buffer[line.end] == '\r':
yield LintProblem(1, line.end - line.start + 1,
'wrong new line character: expected \\n')

View File

@@ -66,8 +66,8 @@ ID = 'octal-values'
TYPE = 'token'
CONF = {'forbid-implicit-octal': bool,
'forbid-explicit-octal': bool}
DEFAULT = {'forbid-implicit-octal': False,
'forbid-explicit-octal': False}
DEFAULT = {'forbid-implicit-octal': True,
'forbid-explicit-octal': True}
def check(conf, token, prev, next, nextnext, context):

View File

@@ -15,15 +15,28 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Use this rule to forbid any string values that are not quoted.
You can also enforce the type of the quote used using the ``quote-type`` option
(``single``, ``double`` or ``any``).
Use this rule to forbid any string values that are not quoted, or to prevent
quoted strings without needing it. You can also enforce the type of the quote
used.
.. rubric:: Options
* ``quote-type`` defines allowed quotes: ``single``, ``double`` or ``any``
(default).
* ``required`` defines whether using quotes in string values is required
(``true``, default) or not (``false``), or only allowed when really needed
(``only-when-needed``).
* ``extra-required`` is a list of PCRE regexes to force string values to be
quoted, if they match any regex. This option can only be used with
``required: false`` and ``required: only-when-needed``.
* ``extra-allowed`` is a list of PCRE regexes to allow quoted string values,
even if ``required: only-when-needed`` is set.
**Note**: Multi-line strings (with ``|`` or ``>``) will not be checked.
.. rubric:: Examples
#. With ``quoted-strings: {quote-type: any}``
#. With ``quoted-strings: {quote-type: any, required: true}``
the following code snippet would **PASS**:
::
@@ -37,42 +50,181 @@ You can also enforce the type of the quote used using the ``quote-type`` option
::
foo: bar
#. With ``quoted-strings: {quote-type: single, required: only-when-needed}``
the following code snippet would **PASS**:
::
foo: bar
bar: foo
not_number: '123'
not_boolean: 'true'
not_comment: '# comment'
not_list: '[1, 2, 3]'
not_map: '{a: 1, b: 2}'
the following code snippet would **FAIL**:
::
foo: 'bar'
#. With ``quoted-strings: {required: false, extra-required: [^http://,
^ftp://]}``
the following code snippet would **PASS**:
::
- localhost
- "localhost"
- "http://localhost"
- "ftp://localhost"
the following code snippet would **FAIL**:
::
- http://localhost
- ftp://localhost
#. With ``quoted-strings: {required: only-when-needed, extra-allowed:
[^http://, ^ftp://], extra-required: [QUOTED]}``
the following code snippet would **PASS**:
::
- localhost
- "http://localhost"
- "ftp://localhost"
- "this is a string that needs to be QUOTED"
the following code snippet would **FAIL**:
::
- "localhost"
- this is a string that needs to be QUOTED
"""
import re
import yaml
from yamllint.linter import LintProblem
ID = 'quoted-strings'
TYPE = 'token'
CONF = {'quote-type': ('any', 'single', 'double')}
DEFAULT = {'quote-type': 'any'}
CONF = {'quote-type': ('any', 'single', 'double'),
'required': (True, False, 'only-when-needed'),
'extra-required': [str],
'extra-allowed': [str]}
DEFAULT = {'quote-type': 'any',
'required': True,
'extra-required': [],
'extra-allowed': []}
def VALIDATE(conf):
if conf['required'] is True and len(conf['extra-allowed']) > 0:
return 'cannot use both "required: true" and "extra-allowed"'
if conf['required'] is True and len(conf['extra-required']) > 0:
return 'cannot use both "required: true" and "extra-required"'
if conf['required'] is False and len(conf['extra-allowed']) > 0:
return 'cannot use both "required: false" and "extra-allowed"'
DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
def _quote_match(quote_type, token_style):
return ((quote_type == 'any') or
(quote_type == 'single' and token_style == "'") or
(quote_type == 'double' and token_style == '"'))
def _quotes_are_needed(string):
loader = yaml.BaseLoader('key: ' + string)
# Remove the 5 first tokens corresponding to 'key: ' (StreamStartToken,
# BlockMappingStartToken, KeyToken, ScalarToken(value=key), ValueToken)
for _ in range(5):
loader.get_token()
try:
a, b = loader.get_token(), loader.get_token()
if (isinstance(a, yaml.ScalarToken) and a.style is None and
isinstance(b, yaml.BlockEndToken)):
return False
return True
except yaml.scanner.ScannerError:
return True
def check(conf, token, prev, next, nextnext, context):
if not (isinstance(token, yaml.tokens.ScalarToken) and
isinstance(prev, (yaml.BlockEntryToken, yaml.FlowEntryToken,
yaml.FlowSequenceStartToken, yaml.TagToken,
yaml.ValueToken))):
return
# Ignore explicit types, e.g. !!str testtest or !!int 42
if (prev and isinstance(prev, yaml.tokens.TagToken) and
prev.value[0] == '!!'):
return
# Ignore numbers, booleans, etc.
resolver = yaml.resolver.Resolver()
tag = resolver.resolve(yaml.nodes.ScalarNode, token.value, (True, False))
if token.plain and tag != DEFAULT_SCALAR_TAG:
return
# Ignore multi-line strings
if (not token.plain) and (token.style == "|" or token.style == ">"):
return
quote_type = conf['quote-type']
if (isinstance(token, yaml.tokens.ScalarToken) and
isinstance(prev, (yaml.ValueToken, yaml.TagToken))):
# Ignore explicit types, e.g. !!str testtest or !!int 42
if (prev and isinstance(prev, yaml.tokens.TagToken) and
prev.value[0] == '!!'):
return
msg = None
if conf['required'] is True:
# Ignore numbers, booleans, etc.
resolver = yaml.resolver.Resolver()
if resolver.resolve(yaml.nodes.ScalarNode, token.value,
(True, False)) != 'tag:yaml.org,2002:str':
return
# Quotes are mandatory and need to match config
if token.style is None or not _quote_match(quote_type, token.style):
msg = "string value is not quoted with %s quotes" % quote_type
# Ignore multi-line strings
if (not token.plain) and (token.style == "|" or token.style == ">"):
return
elif conf['required'] is False:
if ((quote_type == 'single' and token.style != "'") or
(quote_type == 'double' and token.style != '"') or
(quote_type == 'any' and token.style is None)):
yield LintProblem(
token.start_mark.line + 1,
token.start_mark.column + 1,
"string value is not quoted with %s quotes" % (quote_type))
# Quotes are not mandatory but when used need to match config
if token.style and not _quote_match(quote_type, token.style):
msg = "string value is not quoted with %s quotes" % quote_type
elif not token.style:
is_extra_required = any(re.search(r, token.value)
for r in conf['extra-required'])
if is_extra_required:
msg = "string value is not quoted"
elif conf['required'] == 'only-when-needed':
# Quotes are not strictly needed here
if (token.style and tag == DEFAULT_SCALAR_TAG and token.value and
not _quotes_are_needed(token.value)):
is_extra_required = any(re.search(r, token.value)
for r in conf['extra-required'])
is_extra_allowed = any(re.search(r, token.value)
for r in conf['extra-allowed'])
if not (is_extra_required or is_extra_allowed):
msg = "string value is redundantly quoted with %s quotes" % (
quote_type)
# But when used need to match config
elif token.style and not _quote_match(quote_type, token.style):
msg = "string value is not quoted with %s quotes" % quote_type
elif not token.style:
is_extra_required = len(conf['extra-required']) and any(
re.search(r, token.value) for r in conf['extra-required'])
if is_extra_required:
msg = "string value is not quoted"
if msg is not None:
yield LintProblem(
token.start_mark.line + 1,
token.start_mark.column + 1,
msg)

View File

@@ -15,13 +15,25 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Use this rule to forbid non-explictly typed truthy values other than ``true``
and ``false``, for example ``YES``, ``False`` and ``off``.
Use this rule to forbid non-explictly typed truthy values other than allowed
ones (by default: ``true`` and ``false``), for example ``YES`` or ``off``.
This can be useful to prevent surprises from YAML parsers transforming
``[yes, FALSE, Off]`` into ``[true, false, false]`` or
``{y: 1, yes: 2, on: 3, true: 4, True: 5}`` into ``{y: 1, true: 5}``.
.. rubric:: Options
* ``allowed-values`` defines the list of truthy values which will be ignored
during linting. The default is ``['true', 'false']``, but can be changed to
any list containing: ``'TRUE'``, ``'True'``, ``'true'``, ``'FALSE'``,
``'False'``, ``'false'``, ``'YES'``, ``'Yes'``, ``'yes'``, ``'NO'``,
``'No'``, ``'no'``, ``'ON'``, ``'On'``, ``'on'``, ``'OFF'``, ``'Off'``,
``'off'``.
* ``check-keys`` disables verification for keys in mappings. By default,
``truthy`` rule applies to both keys and values. Set this option to ``false``
to prevent this.
.. rubric:: Examples
#. With ``truthy: {}``
@@ -63,29 +75,75 @@ This can be useful to prevent surprises from YAML parsers transforming
yes: 1
on: 2
True: 3
#. With ``truthy: {allowed-values: ["yes", "no"]}``
the following code snippet would **PASS**:
::
- yes
- no
- "true"
- 'false'
- foo
- bar
the following code snippet would **FAIL**:
::
- true
- false
- on
- off
#. With ``truthy: {check-keys: false}``
the following code snippet would **PASS**:
::
yes: 1
on: 2
true: 3
the following code snippet would **FAIL**:
::
yes: Yes
on: On
true: True
"""
import yaml
from yamllint.linter import LintProblem
ID = 'truthy'
TYPE = 'token'
TRUTHY = ['YES', 'Yes', 'yes',
'NO', 'No', 'no',
'TRUE', 'True', # 'true' is a boolean
'FALSE', 'False', # 'false' is a boolean
'TRUE', 'True', 'true',
'FALSE', 'False', 'false',
'ON', 'On', 'on',
'OFF', 'Off', 'off']
ID = 'truthy'
TYPE = 'token'
CONF = {'allowed-values': list(TRUTHY), 'check-keys': bool}
DEFAULT = {'allowed-values': ['true', 'false'], 'check-keys': True}
def check(conf, token, prev, next, nextnext, context):
if prev and isinstance(prev, yaml.tokens.TagToken):
return
if (not conf['check-keys'] and isinstance(prev, yaml.tokens.KeyToken) and
isinstance(token, yaml.tokens.ScalarToken)):
return
if isinstance(token, yaml.tokens.ScalarToken):
if token.value in TRUTHY and token.style is None:
if (token.value in (set(TRUTHY) - set(conf['allowed-values'])) and
token.style is None):
yield LintProblem(token.start_mark.line + 1,
token.start_mark.column + 1,
"truthy value should be true or false")
"truthy value should be one of [" +
", ".join(sorted(conf['allowed-values'])) + "]")