From d42227456338c3042eddb8f4a64c205791a5463f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Fri, 27 Oct 2017 17:18:32 +0200 Subject: [PATCH 1/9] style: Fix E722 errors reported by pycodestyle Since a few days ago pycodestyle (formerly called pep8) has a new check: E722 warning for bare except clauses. Let's fix our code. --- tests/common.py | 2 +- tests/test_cli.py | 2 +- tests/test_config.py | 2 +- tests/test_linter.py | 2 +- tests/test_module.py | 2 +- tests/test_parser.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/common.py b/tests/common.py index 11dd235..ddeb867 100644 --- a/tests/common.py +++ b/tests/common.py @@ -20,7 +20,7 @@ import sys try: assert sys.version_info >= (2, 7) import unittest -except: +except AssertionError: import unittest2 as unittest import yaml diff --git a/tests/test_cli.py b/tests/test_cli.py index bec7078..e903d57 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -27,7 +27,7 @@ import sys try: assert sys.version_info >= (2, 7) import unittest -except: +except AssertionError: import unittest2 as unittest from yamllint import cli diff --git a/tests/test_config.py b/tests/test_config.py index a23da10..7e719b1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -24,7 +24,7 @@ import sys try: assert sys.version_info >= (2, 7) import unittest -except: +except AssertionError: import unittest2 as unittest from yamllint import cli diff --git a/tests/test_linter.py b/tests/test_linter.py index edd803f..6c7ae62 100644 --- a/tests/test_linter.py +++ b/tests/test_linter.py @@ -19,7 +19,7 @@ import sys try: assert sys.version_info >= (2, 7) import unittest -except: +except AssertionError: import unittest2 as unittest from yamllint.config import YamlLintConfig diff --git a/tests/test_module.py b/tests/test_module.py index 678f40c..e7c78b3 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -22,7 +22,7 @@ import sys try: assert sys.version_info >= (2, 7) import unittest -except: +except AssertionError: import unittest2 as unittest diff --git a/tests/test_parser.py b/tests/test_parser.py index c5c51d8..7ed1f98 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -18,7 +18,7 @@ import sys try: assert sys.version_info >= (2, 7) import unittest -except: +except AssertionError: import unittest2 as unittest import yaml From e43768f20342d3c1b03925dda84d5e2d8f31a72f Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Thu, 26 Oct 2017 16:03:14 -0400 Subject: [PATCH 2/9] Better color support check. Not all systems have `isatty` attribute on `sys.stdout` so check for existance of attribute before checking value. Also don't use color in Windows unless environ indicates support. Apparently, Windows can indicate support by either the presence of `ANSICON` environ variable or if the `TERM` environ variable is set to `ANSI`. Fixes #79. No additional tests added, as the relevant tests use fcntl, which is a Unix only lib. In fact, the tests won't even run in Windows. --- yamllint/cli.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/yamllint/cli.py b/yamllint/cli.py index 41695a3..0faee16 100644 --- a/yamllint/cli.py +++ b/yamllint/cli.py @@ -16,9 +16,9 @@ from __future__ import print_function -import os.path +import os import sys - +import platform import argparse from yamllint import APP_DESCRIPTION, APP_NAME, APP_VERSION @@ -38,6 +38,15 @@ def find_files_recursively(items): yield item +def supports_color(): + supported_platform = not (platform.system() == 'Windows' and not + ('ANSICON' in os.environ or + ('TERM' in os.environ and + os.environ['TERM'] == 'ANSI'))) + return (supported_platform and + hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()) + + class Format(object): @staticmethod def parsable(problem, filename): @@ -134,7 +143,7 @@ def run(argv=None): for problem in linter.run(f, conf, filepath): if args.format == 'parsable': print(Format.parsable(problem, file)) - elif sys.stdout.isatty(): + elif supports_color(): if first: print('\033[4m%s\033[0m' % file) first = False From 83ea74e2f81366ca73865b9904e06870a64b348a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Sat, 4 Nov 2017 15:57:39 +0100 Subject: [PATCH 3/9] CI: Compile documentation on Travis --- .travis.yml | 5 ++++- setup.cfg | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bd365c1..c40c6bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,12 +9,15 @@ python: - 3.6 - nightly install: - - pip install pyyaml flake8 flake8-import-order coveralls + - pip install pyyaml flake8 flake8-import-order coveralls sphinx - if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install unittest2; fi - pip install . script: - if [[ $TRAVIS_PYTHON_VERSION != 2.6 ]]; then flake8 .; fi - yamllint --strict $(git ls-files '*.yaml' '*.yml') - coverage run --source=yamllint setup.py test + - if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then + python setup.py build_sphinx; + fi after_success: coveralls diff --git a/setup.cfg b/setup.cfg index 4d5142d..82ba8ec 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,3 +3,9 @@ universal = 1 [flake8] import-order-style = pep8 + +[build_sphinx] +all-files = 1 +source-dir = docs +build-dir = docs/_build +warning-is-error = 1 From 8537b0a164fb93169f090dcdde6fed982a2c9f99 Mon Sep 17 00:00:00 2001 From: Greg Dubicki Date: Tue, 31 Oct 2017 21:32:12 +0100 Subject: [PATCH 4/9] Add rule: empty-values, to forbid implicit nulls only in block mappings for now --- docs/rules.rst | 5 ++ tests/rules/test_empty_values.py | 131 +++++++++++++++++++++++++++++++ yamllint/conf/default.yaml | 2 + yamllint/rules/__init__.py | 2 + yamllint/rules/empty_values.py | 69 ++++++++++++++++ 5 files changed, 209 insertions(+) create mode 100644 tests/rules/test_empty_values.py create mode 100644 yamllint/rules/empty_values.py diff --git a/docs/rules.rst b/docs/rules.rst index dab310d..24318c7 100644 --- a/docs/rules.rst +++ b/docs/rules.rst @@ -59,6 +59,11 @@ empty-lines .. automodule:: yamllint.rules.empty_lines +empty-values +------------ + +.. automodule:: yamllint.rules.empty_values + hyphens ------- diff --git a/tests/rules/test_empty_values.py b/tests/rules/test_empty_values.py new file mode 100644 index 0000000..1cc4f3e --- /dev/null +++ b/tests/rules/test_empty_values.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Greg Dubicki +# +# 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 . + +from tests.common import RuleTestCase + + +class EmptyValuesTestCase(RuleTestCase): + rule_id = 'empty-values' + + def test_disabled_globally(self): + conf = 'empty-values: disable' + self.check('---\n' + 'foo:\n', conf) + + self.check('---\n' + 'foo:\n' + ' bar:\n', conf) + + def test_disabled_forbid_in_block_mappings(self): + conf = 'empty-values: {forbid-in-block-mappings: false}' + self.check('---\n' + 'foo:\n', conf) + + self.check('---\n' + 'foo:\n' + 'bar: aaa\n', conf) + + def test_single_line(self): + conf = 'empty-values: {forbid-in-block-mappings: true}\n' + self.check('---\n' + 'implicitly-null:\n', conf, problem1=(2, 17)) + + self.check('---\n' + 'implicitly-null:with-colons:in-key:\n', conf, + problem1=(2, 36)) + + self.check('---\n' + 'implicitly-null:with-colons:in-key2:\n', conf, + problem1=(2, 37)) + + def test_enabled_all_lines(self): + conf = 'empty-values: {forbid-in-block-mappings: true}\n' + self.check('---\n' + 'foo:\n' + 'bar:\n' + 'foobar:\n', conf, problem1=(2, 5), + problem2=(3, 5), problem3=(4, 8)) + + def test_enabled_explicit_end_of_document(self): + conf = 'empty-values: {forbid-in-block-mappings: true}\n' + self.check('---\n' + 'foo:\n' + '...\n', conf, problem1=(2, 5)) + + def test_enabled_not_end_of_document(self): + conf = 'empty-values: {forbid-in-block-mappings: true}\n' + self.check('---\n' + 'foo:\n' + 'bar:\n' + ' aaa\n', conf, problem1=(2, 5)) + + def test_enabled_different_level(self): + conf = 'empty-values: {forbid-in-block-mappings: true}\n' + self.check('---\n' + 'foo:\n' + ' bar:\n' + 'aaa: bbb\n', conf, problem1=(3, 6)) + + def test_enabled_empty_flow_mapping(self): + conf = 'empty-values: {forbid-in-block-mappings: true}\n' + self.check('---\n' + 'foo: {a:}\n', conf) + + def test_enabled_empty_block_sequence(self): + conf = 'empty-values: {forbid-in-block-mappings: true}\n' + self.check('---\n' + 'foo:\n' + ' -\n', conf) + + def test_enabled_not_empty_or_explicit_null(self): + conf = 'empty-values: {forbid-in-block-mappings: true}\n' + self.check('---\n' + 'foo:\n' + ' bar:\n' + ' aaa\n', conf) + + self.check('---\n' + 'explicitly-null: null\n', conf) + + self.check('---\n' + 'explicitly-null:with-colons:in-key: null\n', conf) + + self.check('---\n' + 'false-null: nulL\n', conf) + + self.check('---\n' + 'empty-string: \'\'\n', conf) + + self.check('---\n' + 'nullable-boolean: false\n', conf) + + self.check('---\n' + 'nullable-int: 0\n', conf) + + self.check('---\n' + 'First occurrence: &anchor Foo\n' + 'Second occurrence: *anchor\n', conf) + + def test_enabled_various_explicit_null(self): + conf = 'empty-values: {forbid-in-block-mappings: true}\n' + self.check('---\n' + 'null-key1: {?: val}\n', conf) + + self.check('---\n' + 'null-alias: ~\n', conf) + + self.check('---\n' + 'null-key2: {? !!null "": val}\n', conf) diff --git a/yamllint/conf/default.yaml b/yamllint/conf/default.yaml index 57cff64..8542d03 100644 --- a/yamllint/conf/default.yaml +++ b/yamllint/conf/default.yaml @@ -32,6 +32,8 @@ rules: max: 2 max-start: 0 max-end: 0 + empty-values: + forbid-in-block-mappings: false hyphens: max-spaces-after: 1 indentation: diff --git a/yamllint/rules/__init__.py b/yamllint/rules/__init__.py index 83dca76..189ec69 100644 --- a/yamllint/rules/__init__.py +++ b/yamllint/rules/__init__.py @@ -24,6 +24,7 @@ from yamllint.rules import ( document_end, document_start, empty_lines, + empty_values, hyphens, indentation, key_duplicates, @@ -45,6 +46,7 @@ _RULES = { document_end.ID: document_end, document_start.ID: document_start, empty_lines.ID: empty_lines, + empty_values.ID: empty_values, hyphens.ID: hyphens, indentation.ID: indentation, key_duplicates.ID: key_duplicates, diff --git a/yamllint/rules/empty_values.py b/yamllint/rules/empty_values.py new file mode 100644 index 0000000..2c1991e --- /dev/null +++ b/yamllint/rules/empty_values.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Greg Dubicki +# +# 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 . + +""" +Use this rule to prevent nodes with empty content, that implicitly result in +``null`` values. + +.. rubric:: Options + +* Use ``forbid-in-block-mappings`` to prevent implicit empty values in block + mappings. + +.. rubric:: Examples + +#. With ``empty-values: {forbid-in-block-mappings: true}`` + the following code snippets would **PASS**: + :: + + some-mapping: + sub-element: correctly indented + + :: + + explicitly-null: null + + the following code snippets would **FAIL**: + :: + + some-mapping: + sub-element: incorrectly indented + + :: + + implicitly-null: + +""" + +import yaml + +from yamllint.linter import LintProblem + + +ID = 'empty-values' +TYPE = 'token' +CONF = {'forbid-in-block-mappings': bool} + + +def check(conf, token, prev, next, nextnext, context): + + if conf['forbid-in-block-mappings']: + if isinstance(token, yaml.ValueToken) and isinstance(next, ( + yaml.KeyToken, yaml.BlockEndToken, + yaml.StreamEndToken, yaml.DocumentEndToken)): + yield LintProblem(token.start_mark.line + 1, + token.end_mark.column + 1, + 'empty value in block mapping') From c4475ece34b05c14b47e6ac1fc77bb6b2c7268f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Sat, 4 Nov 2017 11:25:35 +0100 Subject: [PATCH 5/9] empty-values: Add `forbid-in-flow-mappings` conf This allows preventing implicit `null` from empty values in flow mappings. For example: {a:} {a:, b: 2} { a: { b: , c: { d: 4, e: } }, f: } --- tests/rules/test_empty_values.py | 198 +++++++++++++++++++++++++------ yamllint/conf/default.yaml | 1 + yamllint/rules/empty_values.py | 35 +++++- 3 files changed, 195 insertions(+), 39 deletions(-) diff --git a/tests/rules/test_empty_values.py b/tests/rules/test_empty_values.py index 1cc4f3e..137ec3b 100644 --- a/tests/rules/test_empty_values.py +++ b/tests/rules/test_empty_values.py @@ -20,112 +20,242 @@ from tests.common import RuleTestCase class EmptyValuesTestCase(RuleTestCase): rule_id = 'empty-values' - def test_disabled_globally(self): - conf = 'empty-values: disable' + def test_disabled(self): + conf = ('empty-values: disable\n' + 'braces: disable\n' + 'commas: disable\n') self.check('---\n' 'foo:\n', conf) - self.check('---\n' 'foo:\n' ' bar:\n', conf) + self.check('---\n' + '{a:}\n', conf) + self.check('---\n' + 'foo: {a:}\n', conf) + self.check('---\n' + '- {a:}\n' + '- {a:, b: 2}\n' + '- {a: 1, b:}\n' + '- {a: 1, b: , }\n', conf) + self.check('---\n' + '{a: {b: , c: {d: 4, e:}}, f:}\n', conf) - def test_disabled_forbid_in_block_mappings(self): - conf = 'empty-values: {forbid-in-block-mappings: false}' + def test_in_block_mappings_disabled(self): + conf = ('empty-values: {forbid-in-block-mappings: false,\n' + ' forbid-in-flow-mappings: false}\n') self.check('---\n' 'foo:\n', conf) - self.check('---\n' 'foo:\n' 'bar: aaa\n', conf) - def test_single_line(self): - conf = 'empty-values: {forbid-in-block-mappings: true}\n' + def test_in_block_mappings_single_line(self): + conf = ('empty-values: {forbid-in-block-mappings: true,\n' + ' forbid-in-flow-mappings: false}\n') self.check('---\n' 'implicitly-null:\n', conf, problem1=(2, 17)) - self.check('---\n' 'implicitly-null:with-colons:in-key:\n', conf, problem1=(2, 36)) - self.check('---\n' 'implicitly-null:with-colons:in-key2:\n', conf, problem1=(2, 37)) - def test_enabled_all_lines(self): - conf = 'empty-values: {forbid-in-block-mappings: true}\n' + def test_in_block_mappings_all_lines(self): + conf = ('empty-values: {forbid-in-block-mappings: true,\n' + ' forbid-in-flow-mappings: false}\n') self.check('---\n' 'foo:\n' 'bar:\n' 'foobar:\n', conf, problem1=(2, 5), problem2=(3, 5), problem3=(4, 8)) - def test_enabled_explicit_end_of_document(self): - conf = 'empty-values: {forbid-in-block-mappings: true}\n' + def test_in_block_mappings_explicit_end_of_document(self): + conf = ('empty-values: {forbid-in-block-mappings: true,\n' + ' forbid-in-flow-mappings: false}\n') self.check('---\n' 'foo:\n' '...\n', conf, problem1=(2, 5)) - def test_enabled_not_end_of_document(self): - conf = 'empty-values: {forbid-in-block-mappings: true}\n' + def test_in_block_mappings_not_end_of_document(self): + conf = ('empty-values: {forbid-in-block-mappings: true,\n' + ' forbid-in-flow-mappings: false}\n') self.check('---\n' 'foo:\n' 'bar:\n' ' aaa\n', conf, problem1=(2, 5)) - def test_enabled_different_level(self): - conf = 'empty-values: {forbid-in-block-mappings: true}\n' + def test_in_block_mappings_different_level(self): + conf = ('empty-values: {forbid-in-block-mappings: true,\n' + ' forbid-in-flow-mappings: false}\n') self.check('---\n' 'foo:\n' ' bar:\n' 'aaa: bbb\n', conf, problem1=(3, 6)) - def test_enabled_empty_flow_mapping(self): - conf = 'empty-values: {forbid-in-block-mappings: true}\n' + def test_in_block_mappings_empty_flow_mapping(self): + conf = ('empty-values: {forbid-in-block-mappings: true,\n' + ' forbid-in-flow-mappings: false}\n' + 'braces: disable\n' + 'commas: disable\n') self.check('---\n' 'foo: {a:}\n', conf) + self.check('---\n' + '- {a:, b: 2}\n' + '- {a: 1, b:}\n' + '- {a: 1, b: , }\n', conf) - def test_enabled_empty_block_sequence(self): - conf = 'empty-values: {forbid-in-block-mappings: true}\n' + def test_in_block_mappings_empty_block_sequence(self): + conf = ('empty-values: {forbid-in-block-mappings: true,\n' + ' forbid-in-flow-mappings: false}\n') self.check('---\n' 'foo:\n' ' -\n', conf) - def test_enabled_not_empty_or_explicit_null(self): - conf = 'empty-values: {forbid-in-block-mappings: true}\n' + def test_in_block_mappings_not_empty_or_explicit_null(self): + conf = ('empty-values: {forbid-in-block-mappings: true,\n' + ' forbid-in-flow-mappings: false}\n') self.check('---\n' 'foo:\n' ' bar:\n' ' aaa\n', conf) - self.check('---\n' 'explicitly-null: null\n', conf) - self.check('---\n' 'explicitly-null:with-colons:in-key: null\n', conf) - self.check('---\n' 'false-null: nulL\n', conf) - self.check('---\n' 'empty-string: \'\'\n', conf) - self.check('---\n' 'nullable-boolean: false\n', conf) - self.check('---\n' 'nullable-int: 0\n', conf) - self.check('---\n' 'First occurrence: &anchor Foo\n' 'Second occurrence: *anchor\n', conf) - def test_enabled_various_explicit_null(self): - conf = 'empty-values: {forbid-in-block-mappings: true}\n' + def test_in_block_mappings_various_explicit_null(self): + conf = ('empty-values: {forbid-in-block-mappings: true,\n' + ' forbid-in-flow-mappings: false}\n') + self.check('---\n' + 'null-alias: ~\n', conf) self.check('---\n' 'null-key1: {?: val}\n', conf) + self.check('---\n' + 'null-key2: {? !!null "": val}\n', conf) + def test_in_block_mappings_comments(self): + conf = ('empty-values: {forbid-in-block-mappings: true,\n' + ' forbid-in-flow-mappings: false}\n' + 'comments: disable\n') self.check('---\n' - 'null-alias: ~\n', conf) + 'empty: # comment\n' + 'foo:\n' + ' bar: # comment\n', conf, + problem1=(2, 7), + problem2=(4, 7)) + def test_in_flow_mappings_disabled(self): + conf = ('empty-values: {forbid-in-block-mappings: false,\n' + ' forbid-in-flow-mappings: false}\n' + 'braces: disable\n' + 'commas: disable\n') + self.check('---\n' + '{a:}\n', conf) + self.check('---\n' + 'foo: {a:}\n', conf) + self.check('---\n' + '- {a:}\n' + '- {a:, b: 2}\n' + '- {a: 1, b:}\n' + '- {a: 1, b: , }\n', conf) + self.check('---\n' + '{a: {b: , c: {d: 4, e:}}, f:}\n', conf) + + def test_in_flow_mappings_single_line(self): + conf = ('empty-values: {forbid-in-block-mappings: false,\n' + ' forbid-in-flow-mappings: true}\n' + 'braces: disable\n' + 'commas: disable\n') + self.check('---\n' + '{a:}\n', conf, + problem=(2, 4)) + self.check('---\n' + 'foo: {a:}\n', conf, + problem=(2, 9)) + self.check('---\n' + '- {a:}\n' + '- {a:, b: 2}\n' + '- {a: 1, b:}\n' + '- {a: 1, b: , }\n', conf, + problem1=(2, 6), + problem2=(3, 6), + problem3=(4, 12), + problem4=(5, 12)) + self.check('---\n' + '{a: {b: , c: {d: 4, e:}}, f:}\n', conf, + problem1=(2, 8), + problem2=(2, 23), + problem3=(2, 29)) + + def test_in_flow_mappings_multi_line(self): + conf = ('empty-values: {forbid-in-block-mappings: false,\n' + ' forbid-in-flow-mappings: true}\n' + 'braces: disable\n' + 'commas: disable\n') + self.check('---\n' + 'foo: {\n' + ' a:\n' + '}\n', conf, + problem=(3, 5)) + self.check('---\n' + '{\n' + ' a: {\n' + ' b: ,\n' + ' c: {\n' + ' d: 4,\n' + ' e:\n' + ' }\n' + ' },\n' + ' f:\n' + '}\n', conf, + problem1=(4, 7), + problem2=(7, 9), + problem3=(10, 5)) + + def test_in_flow_mappings_various_explicit_null(self): + conf = ('empty-values: {forbid-in-block-mappings: false,\n' + ' forbid-in-flow-mappings: true}\n' + 'braces: disable\n' + 'commas: disable\n') + self.check('---\n' + '{explicit-null: null}\n', conf) + self.check('---\n' + '{null-alias: ~}\n', conf) + self.check('---\n' + 'null-key1: {?: val}\n', conf) self.check('---\n' 'null-key2: {? !!null "": val}\n', conf) + + def test_in_flow_mappings_comments(self): + conf = ('empty-values: {forbid-in-block-mappings: false,\n' + ' forbid-in-flow-mappings: true}\n' + 'braces: disable\n' + 'commas: disable\n' + 'comments: disable\n') + self.check('---\n' + '{\n' + ' a: {\n' + ' b: , # comment\n' + ' c: {\n' + ' d: 4, # comment\n' + ' e: # comment\n' + ' }\n' + ' },\n' + ' f: # comment\n' + '}\n', conf, + problem1=(4, 7), + problem2=(7, 9), + problem3=(10, 5)) diff --git a/yamllint/conf/default.yaml b/yamllint/conf/default.yaml index 8542d03..8c0e89a 100644 --- a/yamllint/conf/default.yaml +++ b/yamllint/conf/default.yaml @@ -34,6 +34,7 @@ rules: max-end: 0 empty-values: forbid-in-block-mappings: false + forbid-in-flow-mappings: false hyphens: max-spaces-after: 1 indentation: diff --git a/yamllint/rules/empty_values.py b/yamllint/rules/empty_values.py index 2c1991e..daa62b2 100644 --- a/yamllint/rules/empty_values.py +++ b/yamllint/rules/empty_values.py @@ -20,12 +20,13 @@ Use this rule to prevent nodes with empty content, that implicitly result in .. rubric:: Options -* Use ``forbid-in-block-mappings`` to prevent implicit empty values in block - mappings. +* Use ``forbid-in-block-mappings`` to prevent empty values in block mappings. +* Use ``forbid-in-flow-mappings`` to prevent empty values in flow mappings. .. rubric:: Examples #. With ``empty-values: {forbid-in-block-mappings: true}`` + the following code snippets would **PASS**: :: @@ -46,6 +47,23 @@ Use this rule to prevent nodes with empty content, that implicitly result in implicitly-null: +#. With ``empty-values: {forbid-in-flow-mappings: true}`` + + the following code snippet would **PASS**: + :: + + {prop: null} + {a: 1, b: 2, c: 3} + + the following code snippets would **FAIL**: + :: + + {prop: } + + :: + + {a: 1, b:, c: 3} + """ import yaml @@ -55,15 +73,22 @@ from yamllint.linter import LintProblem ID = 'empty-values' TYPE = 'token' -CONF = {'forbid-in-block-mappings': bool} +CONF = {'forbid-in-block-mappings': bool, + 'forbid-in-flow-mappings': bool} def check(conf, token, prev, next, nextnext, context): if conf['forbid-in-block-mappings']: if isinstance(token, yaml.ValueToken) and isinstance(next, ( - yaml.KeyToken, yaml.BlockEndToken, - yaml.StreamEndToken, yaml.DocumentEndToken)): + yaml.KeyToken, yaml.BlockEndToken)): yield LintProblem(token.start_mark.line + 1, token.end_mark.column + 1, 'empty value in block mapping') + + if conf['forbid-in-flow-mappings']: + if isinstance(token, yaml.ValueToken) and isinstance(next, ( + yaml.FlowEntryToken, yaml.FlowMappingEndToken)): + yield LintProblem(token.start_mark.line + 1, + token.end_mark.column + 1, + 'empty value in flow mapping') From 6ec1e7b54a0be31a8e4186c9c283cbde405fa5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Sun, 5 Nov 2017 09:50:46 +0100 Subject: [PATCH 6/9] Distribution: Include tests in dist file Since commit e948509 ("setup.py - don't distribute tests"), tests files are not included in the `.tar.gz` bundle on a fresh repo clone. (On old repos they were still included, because listed in `yamllint.egg-info/SOURCES.txt`.) Let's explicitly include them. --- MANIFEST.in | 1 + setup.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 8c22eeb..792a672 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include LICENSE include README.rst include docs/* +include tests/*.py tests/rules/*.py tests/yaml-1.2-spec-examples/* diff --git a/setup.py b/setup.py index e87980f..0c61d01 100644 --- a/setup.py +++ b/setup.py @@ -44,8 +44,7 @@ setup( packages=find_packages(exclude=['tests', 'tests.*']), entry_points={'console_scripts': ['yamllint=yamllint.cli:run']}, - package_data={'yamllint': ['conf/*.yaml'], - 'tests': ['yaml-1.2-spec-examples/*']}, + package_data={'yamllint': ['conf/*.yaml']}, install_requires=['pathspec >=0.5.3', 'pyyaml'], test_suite='tests', ) From ed5d319df87a7bdced29454a3a0b01954b2b5174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Sun, 5 Nov 2017 09:59:16 +0100 Subject: [PATCH 7/9] tests: Use en_US.UTF-8 locale when C.UTF-8 not available Some operating systems don't have the `C.UTF-8` locale installed yet (for instance, CentOS 7). In such a case, fallback to `en_US.UTF-8` so that tests can be run. This follows commit 92ff315. --- tests/test_cli.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index e903d57..47b5daf 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -299,7 +299,10 @@ class CommandLineTestCase(unittest.TestCase): # Make sure the default localization conditions on this "system" # support UTF-8 encoding. loc = locale.getlocale() - locale.setlocale(locale.LC_ALL, 'C.UTF-8') + try: + locale.setlocale(locale.LC_ALL, 'C.UTF-8') + except locale.Error: + locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') sys.stdout, sys.stderr = StringIO(), StringIO() with self.assertRaises(SystemExit) as ctx: From 501def327deae9716497031c9485227349eaf97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Sun, 5 Nov 2017 10:06:46 +0100 Subject: [PATCH 8/9] tests: Use `sys.executable` instead of hard-coded 'python' To test yamllint as a module, tests run commands like `python -m yamllint`. But some environments (like continuous integration of Debian or CentOS) don't always include the `python` executable (they use `python3` instead). Let's dynamically detect the Python executable path. --- tests/test_module.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/test_module.py b/tests/test_module.py index e7c78b3..8bdffcc 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -26,6 +26,9 @@ except AssertionError: import unittest2 as unittest +PYTHON = sys.executable or 'python' + + @unittest.skipIf(sys.version_info < (2, 7), 'Python 2.6 not supported') class ModuleTestCase(unittest.TestCase): def setUp(self): @@ -46,7 +49,7 @@ class ModuleTestCase(unittest.TestCase): def test_run_module_no_args(self): with self.assertRaises(subprocess.CalledProcessError) as ctx: - subprocess.check_output(['python', '-m', 'yamllint'], + subprocess.check_output([PYTHON, '-m', 'yamllint'], stderr=subprocess.STDOUT) self.assertEqual(ctx.exception.returncode, 2) self.assertRegexpMatches(ctx.exception.output.decode(), @@ -54,7 +57,7 @@ class ModuleTestCase(unittest.TestCase): def test_run_module_on_bad_dir(self): with self.assertRaises(subprocess.CalledProcessError) as ctx: - subprocess.check_output(['python', '-m', 'yamllint', + subprocess.check_output([PYTHON, '-m', 'yamllint', '/does/not/exist'], stderr=subprocess.STDOUT) self.assertRegexpMatches(ctx.exception.output.decode(), @@ -62,7 +65,7 @@ class ModuleTestCase(unittest.TestCase): def test_run_module_on_file(self): out = subprocess.check_output( - ['python', '-m', 'yamllint', os.path.join(self.wd, 'warn.yaml')]) + [PYTHON, '-m', 'yamllint', os.path.join(self.wd, 'warn.yaml')]) lines = out.decode().splitlines() self.assertIn('/warn.yaml', lines[0]) self.assertEqual('\n'.join(lines[1:]), @@ -71,7 +74,7 @@ class ModuleTestCase(unittest.TestCase): def test_run_module_on_dir(self): with self.assertRaises(subprocess.CalledProcessError) as ctx: - subprocess.check_output(['python', '-m', 'yamllint', self.wd]) + subprocess.check_output([PYTHON, '-m', 'yamllint', self.wd]) self.assertEqual(ctx.exception.returncode, 1) files = ctx.exception.output.decode().split('\n\n') From a92743c8ca44bab56389c75504fcf25d33e2c11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Sun, 5 Nov 2017 10:17:55 +0100 Subject: [PATCH 9/9] yamllint version 1.10.0 --- CHANGELOG.rst | 10 ++++++++++ yamllint/__init__.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 73de95f..bc1fec4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,16 @@ Changelog ========= +1.10.0 (2017-11-05) +------------------- + +- Fix colored output on Windows +- Check documentation compilation on continuous integration +- Add a new `empty-values` rule +- Make sure test files are included in dist bundle +- Tests: Use en_US.UTF-8 locale when C.UTF-8 not available +- Tests: Dynamically detect Python executable path + 1.9.0 (2017-10-16) ------------------ diff --git a/yamllint/__init__.py b/yamllint/__init__.py index 831da23..8ff52f9 100644 --- a/yamllint/__init__.py +++ b/yamllint/__init__.py @@ -22,7 +22,7 @@ indentation, etc.""" APP_NAME = 'yamllint' -APP_VERSION = '1.9.0' +APP_VERSION = '1.10.0' APP_DESCRIPTION = __doc__ __author__ = u'Adrien Vergé'