From aaa8777f1d6ce6eb321c1cb17599293ca5361b59 Mon Sep 17 00:00:00 2001 From: "Guido Wischrop (mgm tp)" <43027257+mgmgwi@users.noreply.github.com> Date: Thu, 4 Oct 2018 16:04:54 +0200 Subject: [PATCH] Add quoted-strings rule * taken from https://github.com/adrienverge/yamllint/pull/110 (submitted by @jurajseffer) * small fixes for generic and multi-line strings * fixes for comments from @adrienverge --- tests/rules/test_quoted_strings.py | 125 +++++++++++++++++++++++++++++ yamllint/conf/default.yaml | 1 + yamllint/rules/__init__.py | 2 + yamllint/rules/quoted_strings.py | 74 +++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 tests/rules/test_quoted_strings.py create mode 100644 yamllint/rules/quoted_strings.py diff --git a/tests/rules/test_quoted_strings.py b/tests/rules/test_quoted_strings.py new file mode 100644 index 0000000..d848639 --- /dev/null +++ b/tests/rules/test_quoted_strings.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2018 ClearScore +# +# 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 QuotedTestCase(RuleTestCase): + rule_id = 'quoted-strings' + + def test_disabled(self): + conf = 'quoted-strings: disable' + self.check('---\n' + 'foo: bar\n', conf) + self.check('---\n' + 'foo: "bar"\n', conf) + self.check('---\n' + 'foo: \'bar\'\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' + 'string1: "foo"\n' + 'number1: 123\n' # fails + 'string2: foo\n' # fails + 'string3: \'bar\'\n' + 'string4: !!str genericstring\n' # fails + 'string5: !!str 456\n' # fails + 'string6: !!str "quotedgenericstring"\n' + 'binary: !!binary binstring\n' + 'integer: !!int intstring\n' + 'boolean1: !!bool boolstring\n' + 'boolean2: !!bool "quotedboolstring"\n', + conf, problem1=(3, 10), problem2=(4, 10), + problem3=(6, 16), problem4=(7, 16)) + 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' + ' word 2"\n', + conf, problem1=(9, 3)) + + def test_quote_type_single(self): + conf = 'quoted-strings: {quote-type: single}\n' + self.check('---\n' + 'string1: "foo"\n' # fails + 'number1: 123\n' # fails + 'string2: foo\n' # fails + 'string3: \'bar\'\n' + 'string4: !!str genericstring\n' # fails + 'string5: !!str 456\n' # fails + 'string6: !!str "quotedgenericstring"\n' # fails + 'binary: !!binary binstring\n' + 'integer: !!int intstring\n' + 'boolean1: !!bool boolstring\n' + 'boolean2: !!bool "quotedboolstring"\n', + conf, problem1=(2, 10), problem2=(3, 10), problem3=(4, 10), + problem4=(6, 16), problem5=(7, 16), problem6=(8, 16)) + 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' + ' word 2"\n', + conf, problem1=(9, 3), problem2=(12, 3)) + + def test_quote_type_double(self): + conf = 'quoted-strings: {quote-type: double}\n' + self.check('---\n' + 'string1: "foo"\n' + 'number1: 123\n' # fails + 'string2: foo\n' # fails + 'string3: \'bar\'\n' # fails + 'string4: !!str genericstring\n' # fails + 'string5: !!str 456\n' # fails + 'string6: !!str "quotedgenericstring"\n' + 'binary: !!binary binstring\n' + 'integer: !!int intstring\n' + 'boolean1: !!bool boolstring\n' + 'boolean2: !!bool "quotedboolstring"\n', + conf, problem1=(3, 10), problem2=(4, 10), problem3=(5, 10), + problem4=(6, 16), problem5=(7, 16)) + 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' + ' word 2"\n', + conf, problem1=(9, 3)) diff --git a/yamllint/conf/default.yaml b/yamllint/conf/default.yaml index 8432c9a..fcece4d 100644 --- a/yamllint/conf/default.yaml +++ b/yamllint/conf/default.yaml @@ -32,6 +32,7 @@ rules: max: 2 max-start: 0 max-end: 0 + quoted-strings: disable empty-values: forbid-in-block-mappings: false forbid-in-flow-mappings: false diff --git a/yamllint/rules/__init__.py b/yamllint/rules/__init__.py index d08a326..a084d6e 100644 --- a/yamllint/rules/__init__.py +++ b/yamllint/rules/__init__.py @@ -33,6 +33,7 @@ from yamllint.rules import ( new_line_at_end_of_file, new_lines, octal_values, + quoted_strings, trailing_spaces, truthy, ) @@ -56,6 +57,7 @@ _RULES = { new_line_at_end_of_file.ID: new_line_at_end_of_file, new_lines.ID: new_lines, octal_values.ID: octal_values, + quoted_strings.ID: quoted_strings, trailing_spaces.ID: trailing_spaces, truthy.ID: truthy, } diff --git a/yamllint/rules/quoted_strings.py b/yamllint/rules/quoted_strings.py new file mode 100644 index 0000000..2380898 --- /dev/null +++ b/yamllint/rules/quoted_strings.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2018 ClearScore +# +# 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 forbid any string values that are not quoted. +You can also enforce the type of the quote used - single or double - using the +``quote-type`` option. + +**Note**: Multi-line strings (with ``|`` or ``>``) will not be checked. + +.. rubric:: Examples + +#. With ``quoted-strings: {quote-type: any}`` + + the following code snippet would **PASS**: + :: + + foo: "bar" + bar: 'foo' + number: 123 + boolean: true + + the following code snippet would **FAIL**: + :: + + foo: bar +""" + +import yaml + +from yamllint.linter import LintProblem + +ID = 'quoted-strings' +TYPE = 'token' +CONF = {'quote-type': ('any', 'single', 'double')} + + +def check(conf, token, prev, next, nextnext, context): + quote_type = conf['quote-type'] + + if prev and isinstance(prev, yaml.tokens.TagToken): + if prev.value[1] != "str": + # we ignore generic strings, e.g. somestring: !!str testtest + return + + if isinstance(token, yaml.tokens.ScalarToken): + if isinstance(prev, yaml.tokens.ValueToken) or \ + isinstance(prev, yaml.tokens.TagToken): + if ((not token.plain) and + ((token.style == "|") or (token.style == ">"))): + # we ignore multi-line strings + return + + 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) + )