quoted-strings: Add check-keys option

This commit is contained in:
Henry Gessau 2023-12-11 08:23:23 -05:00 committed by GitHub
parent e5fdfd2ae5
commit 3288d0596e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 751 additions and 10 deletions

View File

@ -18,7 +18,7 @@ from tests.common import RuleTestCase
from yamllint import config from yamllint import config
class QuotedTestCase(RuleTestCase): class QuotedValuesTestCase(RuleTestCase):
rule_id = 'quoted-strings' rule_id = 'quoted-strings'
def test_disabled(self): def test_disabled(self):
@ -595,3 +595,722 @@ class QuotedTestCase(RuleTestCase):
"foo1: '[barbaz]'\n" "foo1: '[barbaz]'\n"
"foo2: '[bar\"baz]'\n", "foo2: '[bar\"baz]'\n",
conf) conf)
class QuotedKeysTestCase(RuleTestCase):
rule_id = 'quoted-strings'
def test_disabled(self):
conf_disabled = "quoted-strings: {}"
key_strings = ('---\n'
'true: 2\n'
'123: 3\n'
'foo1: 4\n'
'"foo2": 5\n'
'"false": 6\n'
'"234": 7\n'
'\'bar\': 8\n'
'!!str generic_string: 9\n'
'!!str 456: 10\n'
'!!str "quoted_generic_string": 11\n'
'!!binary binstring: 12\n'
'!!int int_string: 13\n'
'!!bool bool_string: 14\n'
'!!bool "quoted_bool_string": 15\n'
# Sequences and mappings
'? - 16\n'
' - 17\n'
': 18\n'
'[119, 219]: 19\n'
'? a: 20\n'
' "b": 21\n'
': 22\n'
'{a: 123, "b": 223}: 23\n'
# Multiline strings
'? |\n'
' line 1\n'
' line 2\n'
': 27\n'
'? >\n'
' line 1\n'
' line 2\n'
': 31\n'
'?\n'
' line 1\n'
' line 2\n'
': 35\n'
'?\n'
' "line 1\\\n'
' line 2"\n'
': 39\n')
self.check(key_strings, conf_disabled)
def test_default(self):
# Default configuration, but with check-keys
conf_default = ("quoted-strings:\n"
" check-keys: true\n")
key_strings = ('---\n'
'true: 2\n'
'123: 3\n'
'foo1: 4\n'
'"foo2": 5\n'
'"false": 6\n'
'"234": 7\n'
'\'bar\': 8\n'
'!!str generic_string: 9\n'
'!!str 456: 10\n'
'!!str "quoted_generic_string": 11\n'
'!!binary binstring: 12\n'
'!!int int_string: 13\n'
'!!bool bool_string: 14\n'
'!!bool "quoted_bool_string": 15\n'
# Sequences and mappings
'? - 16\n'
' - 17\n'
': 18\n'
'[119, 219]: 19\n'
'? a: 20\n'
' "b": 21\n'
': 22\n'
'{a: 123, "b": 223}: 23\n'
# Multiline strings
'? |\n'
' line 1\n'
' line 2\n'
': 27\n'
'? >\n'
' line 1\n'
' line 2\n'
': 31\n'
'?\n'
' line 1\n'
' line 2\n'
': 35\n'
'?\n'
' "line 1\\\n'
' line 2"\n'
': 39\n')
self.check(key_strings, conf_default, problem1=(4, 1),
problem3=(20, 3), problem4=(23, 2), problem5=(33, 3))
def test_quote_type_any(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: any\n')
key_strings = ('---\n'
'true: 2\n'
'123: 3\n'
'foo1: 4\n'
'"foo2": 5\n'
'"false": 6\n'
'"234": 7\n'
'\'bar\': 8\n'
'!!str generic_string: 9\n'
'!!str 456: 10\n'
'!!str "quoted_generic_string": 11\n'
'!!binary binstring: 12\n'
'!!int int_string: 13\n'
'!!bool bool_string: 14\n'
'!!bool "quoted_bool_string": 15\n'
# Sequences and mappings
'? - 16\n'
' - 17\n'
': 18\n'
'[119, 219]: 19\n'
'? a: 20\n'
' "b": 21\n'
': 22\n'
'{a: 123, "b": 223}: 23\n'
# Multiline strings
'? |\n'
' line 1\n'
' line 2\n'
': 27\n'
'? >\n'
' line 1\n'
' line 2\n'
': 31\n'
'?\n'
' line 1\n'
' line 2\n'
': 35\n'
'?\n'
' "line 1\\\n'
' line 2"\n'
': 39\n')
self.check(key_strings, conf,
problem1=(4, 1), problem2=(20, 3), problem3=(23, 2),
problem4=(33, 3))
def test_quote_type_single(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: single\n')
key_strings = ('---\n'
'true: 2\n'
'123: 3\n'
'foo1: 4\n'
'"foo2": 5\n'
'"false": 6\n'
'"234": 7\n'
'\'bar\': 8\n'
'!!str generic_string: 9\n'
'!!str 456: 10\n'
'!!str "quoted_generic_string": 11\n'
'!!binary binstring: 12\n'
'!!int int_string: 13\n'
'!!bool bool_string: 14\n'
'!!bool "quoted_bool_string": 15\n'
# Sequences and mappings
'? - 16\n'
' - 17\n'
': 18\n'
'[119, 219]: 19\n'
'? a: 20\n'
' "b": 21\n'
': 22\n'
'{a: 123, "b": 223}: 23\n'
# Multiline strings
'? |\n'
' line 1\n'
' line 2\n'
': 27\n'
'? >\n'
' line 1\n'
' line 2\n'
': 31\n'
'?\n'
' line 1\n'
' line 2\n'
': 35\n'
'?\n'
' "line 1\\\n'
' line 2"\n'
': 39\n')
self.check(key_strings, conf,
problem1=(4, 1), problem2=(5, 1), problem3=(6, 1),
problem4=(7, 1), problem5=(20, 3), problem6=(21, 3),
problem7=(23, 2), problem8=(23, 10), problem9=(33, 3),
problem10=(37, 3))
def test_quote_type_double(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: double\n')
key_strings = ('---\n'
'true: 2\n'
'123: 3\n'
'foo1: 4\n'
'"foo2": 5\n'
'"false": 6\n'
'"234": 7\n'
'\'bar\': 8\n'
'!!str generic_string: 9\n'
'!!str 456: 10\n'
'!!str "quoted_generic_string": 11\n'
'!!binary binstring: 12\n'
'!!int int_string: 13\n'
'!!bool bool_string: 14\n'
'!!bool "quoted_bool_string": 15\n'
# Sequences and mappings
'? - 16\n'
' - 17\n'
': 18\n'
'[119, 219]: 19\n'
'? a: 20\n'
' "b": 21\n'
': 22\n'
'{a: 123, "b": 223}: 23\n'
# Multiline strings
'? |\n'
' line 1\n'
' line 2\n'
': 27\n'
'? >\n'
' line 1\n'
' line 2\n'
': 31\n'
'?\n'
' line 1\n'
' line 2\n'
': 35\n'
'?\n'
' "line 1\\\n'
' line 2"\n'
': 39\n')
self.check(key_strings, conf,
problem1=(4, 1), problem2=(8, 1), problem3=(20, 3),
problem4=(23, 2), problem5=(33, 3))
def test_any_quotes_not_required(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: any\n'
' required: false\n')
key_strings = ('---\n'
'true: 2\n'
'123: 3\n'
'foo1: 4\n'
'"foo2": 5\n'
'"false": 6\n'
'"234": 7\n'
'\'bar\': 8\n'
'!!str generic_string: 9\n'
'!!str 456: 10\n'
'!!str "quoted_generic_string": 11\n'
'!!binary binstring: 12\n'
'!!int int_string: 13\n'
'!!bool bool_string: 14\n'
'!!bool "quoted_bool_string": 15\n'
# Sequences and mappings
'? - 16\n'
' - 17\n'
': 18\n'
'[119, 219]: 19\n'
'? a: 20\n'
' "b": 21\n'
': 22\n'
'{a: 123, "b": 223}: 23\n'
# Multiline strings
'? |\n'
' line 1\n'
' line 2\n'
': 27\n'
'? >\n'
' line 1\n'
' line 2\n'
': 31\n'
'?\n'
' line 1\n'
' line 2\n'
': 35\n'
'?\n'
' "line 1\\\n'
' line 2"\n'
': 39\n')
self.check(key_strings, conf)
def test_single_quotes_not_required(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: single\n'
' required: false\n')
key_strings = ('---\n'
'true: 2\n'
'123: 3\n'
'foo1: 4\n'
'"foo2": 5\n'
'"false": 6\n'
'"234": 7\n'
'\'bar\': 8\n'
'!!str generic_string: 9\n'
'!!str 456: 10\n'
'!!str "quoted_generic_string": 11\n'
'!!binary binstring: 12\n'
'!!int int_string: 13\n'
'!!bool bool_string: 14\n'
'!!bool "quoted_bool_string": 15\n'
# Sequences and mappings
'? - 16\n'
' - 17\n'
': 18\n'
'[119, 219]: 19\n'
'? a: 20\n'
' "b": 21\n'
': 22\n'
'{a: 123, "b": 223}: 23\n'
# Multiline strings
'? |\n'
' line 1\n'
' line 2\n'
': 27\n'
'? >\n'
' line 1\n'
' line 2\n'
': 31\n'
'?\n'
' line 1\n'
' line 2\n'
': 35\n'
'?\n'
' "line 1\\\n'
' line 2"\n'
': 39\n')
self.check(key_strings, conf,
problem1=(5, 1), problem2=(6, 1), problem3=(7, 1),
problem4=(21, 3), problem5=(23, 10), problem6=(37, 3))
def test_only_when_needed(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' required: only-when-needed\n')
key_strings = ('---\n'
'true: 2\n'
'123: 3\n'
'foo1: 4\n'
'"foo2": 5\n'
'"false": 6\n'
'"234": 7\n'
'\'bar\': 8\n'
'!!str generic_string: 9\n'
'!!str 456: 10\n'
'!!str "quoted_generic_string": 11\n'
'!!binary binstring: 12\n'
'!!int int_string: 13\n'
'!!bool bool_string: 14\n'
'!!bool "quoted_bool_string": 15\n'
# Sequences and mappings
'? - 16\n'
' - 17\n'
': 18\n'
'[119, 219]: 19\n'
'? a: 20\n'
' "b": 21\n'
': 22\n'
'{a: 123, "b": 223}: 23\n'
# Multiline strings
'? |\n'
' line 1\n'
' line 2\n'
': 27\n'
'? >\n'
' line 1\n'
' line 2\n'
': 31\n'
'?\n'
' line 1\n'
' line 2\n'
': 35\n'
'?\n'
' "line 1\\\n'
' line 2"\n'
': 39\n')
self.check(key_strings, conf,
problem1=(5, 1), problem2=(8, 1), problem3=(21, 3),
problem4=(23, 10), problem5=(37, 3))
def test_only_when_needed_single_quotes(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: single\n'
' required: only-when-needed\n')
key_strings = ('---\n'
'true: 2\n'
'123: 3\n'
'foo1: 4\n'
'"foo2": 5\n'
'"false": 6\n'
'"234": 7\n'
'\'bar\': 8\n'
'!!str generic_string: 9\n'
'!!str 456: 10\n'
'!!str "quoted_generic_string": 11\n'
'!!binary binstring: 12\n'
'!!int int_string: 13\n'
'!!bool bool_string: 14\n'
'!!bool "quoted_bool_string": 15\n'
# Sequences and mappings
'? - 16\n'
' - 17\n'
': 18\n'
'[119, 219]: 19\n'
'? a: 20\n'
' "b": 21\n'
': 22\n'
'{a: 123, "b": 223}: 23\n'
# Multiline strings
'? |\n'
' line 1\n'
' line 2\n'
': 27\n'
'? >\n'
' line 1\n'
' line 2\n'
': 31\n'
'?\n'
' line 1\n'
' line 2\n'
': 35\n'
'?\n'
' "line 1\\\n'
' line 2"\n'
': 39\n')
self.check(key_strings, conf,
problem1=(5, 1), problem2=(6, 1), problem3=(7, 1),
problem4=(8, 1), problem5=(21, 3), problem6=(23, 10),
problem7=(37, 3))
def test_only_when_needed_corner_cases(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' required: only-when-needed\n')
self.check('---\n'
'"": 2\n'
'"- item": 3\n'
'"key: value": 4\n'
'"%H:%M:%S": 5\n'
'"%wheel ALL=(ALL) NOPASSWD: ALL": 6\n'
'\'"quoted"\': 7\n'
'"\'foo\' == \'bar\'": 8\n'
'"\'Mac\' in ansible_facts.product_name": 9\n'
'\'foo # bar\': 10\n',
conf)
self.check('---\n'
'"": 2\n'
'"- item": 3\n'
'"key: value": 4\n'
'"%H:%M:%S": 5\n'
'"%wheel ALL=(ALL) NOPASSWD: ALL": 6\n'
'\'"quoted"\': 7\n'
'"\'foo\' == \'bar\'": 8\n'
'"\'Mac\' in ansible_facts.product_name": 9\n',
conf)
self.check('---\n'
'---: 2\n'
'"----": 3\n' # fails
'---------: 4\n'
'"----------": 5\n' # fails
':wq: 6\n'
'":cw": 7\n', # fails
conf, problem1=(3, 1), problem2=(5, 1), problem3=(7, 1))
def test_only_when_needed_extras(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' required: true\n'
' extra-allowed: [^http://]\n')
self.assertRaises(config.YamlLintConfigError, self.check, '', conf)
conf = ('quoted-strings:\n'
' check-keys: true\n'
' required: true\n'
' extra-required: [^http://]\n')
self.assertRaises(config.YamlLintConfigError, self.check, '', conf)
conf = ('quoted-strings:\n'
' check-keys: true\n'
' required: false\n'
' extra-allowed: [^http://]\n')
self.assertRaises(config.YamlLintConfigError, self.check, '', conf)
conf = ('quoted-strings:\n'
' check-keys: true\n'
' required: true\n')
self.check('---\n'
'123: 2\n'
'"234": 3\n'
'localhost: 4\n' # fails
'"host.local": 5\n'
'http://localhost: 6\n' # fails
'"http://host.local": 7\n'
'ftp://localhost: 8\n' # fails
'"ftp://host.local": 9\n',
conf, problem1=(4, 1), problem2=(6, 1), problem3=(8, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' required: only-when-needed\n'
' extra-allowed: [^ftp://]\n'
' extra-required: [^http://]\n')
self.check('---\n'
'123: 2\n'
'"234": 3\n'
'localhost: 4\n'
'"host.local": 5\n' # fails
'http://localhost: 6\n' # fails
'"http://host.local": 7\n'
'ftp://localhost: 8\n'
'"ftp://host.local": 9\n',
conf, problem1=(5, 1), problem2=(6, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' required: false\n'
' extra-required: [^http://, ^ftp://]\n')
self.check('---\n'
'123: 2\n'
'"234": 3\n'
'localhost: 4\n'
'"host.local": 5\n'
'http://localhost: 6\n' # fails
'"http://host.local": 7\n'
'ftp://localhost: 8\n' # fails
'"ftp://host.local": 9\n',
conf, problem1=(6, 1), problem2=(8, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' required: only-when-needed\n'
' extra-allowed: [^ftp://, ";$", " "]\n')
self.check('---\n'
'localhost: 2\n'
'"host.local": 3\n' # fails
'ftp://localhost: 4\n'
'"ftp://host.local": 5\n'
'i=i+1: 6\n'
'"i=i+2": 7\n' # fails
'i=i+3;: 8\n'
'"i=i+4;": 9\n'
'foo1: 10\n'
'"foo2": 11\n' # fails
'foo bar1: 12\n'
'"foo bar2": 13\n',
conf, problem1=(3, 1), problem2=(7, 1), problem3=(11, 1))
def test_octal_values(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' required: true\n')
self.check('---\n'
'100: 2\n'
'0100: 3\n'
'0o100: 4\n'
'777: 5\n'
'0777: 6\n'
'0o777: 7\n'
'800: 8\n'
'0800: 9\n' # fails
'0o800: 10\n' # fails
'"0900": 11\n'
'"0o900": 12\n',
conf,
problem1=(9, 1), problem2=(10, 1))
def test_allow_quoted_quotes(self):
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: single\n'
' required: false\n'
' allow-quoted-quotes: false\n')
self.check('---\n'
'"[barbaz]": 2\n' # fails
'"[bar\'baz]": 3\n', # fails
conf, problem1=(2, 1), problem2=(3, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: single\n'
' required: false\n'
' allow-quoted-quotes: true\n')
self.check('---\n'
'"[barbaz]": 2\n' # fails
'"[bar\'baz]": 3\n',
conf, problem1=(2, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: single\n'
' required: true\n'
' allow-quoted-quotes: false\n')
self.check('---\n'
'"[barbaz]": 2\n' # fails
'"[bar\'baz]": 3\n', # fails
conf, problem1=(2, 1), problem2=(3, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: single\n'
' required: true\n'
' allow-quoted-quotes: true\n')
self.check('---\n'
'"[barbaz]": 2\n' # fails
'"[bar\'baz]": 3\n',
conf, problem1=(2, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: single\n'
' required: only-when-needed\n'
' allow-quoted-quotes: false\n')
self.check('---\n'
'"[barbaz]": 2\n' # fails
'"[bar\'baz]": 3\n', # fails
conf, problem1=(2, 1), problem2=(3, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: single\n'
' required: only-when-needed\n'
' allow-quoted-quotes: true\n')
self.check('---\n'
'"[barbaz]": 2\n' # fails
'"[bar\'baz]": 3\n',
conf, problem1=(2, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: double\n'
' required: false\n'
' allow-quoted-quotes: false\n')
self.check("---\n"
"'[barbaz]': 2\n" # fails
"'[bar\"baz]': 3\n", # fails
conf, problem1=(2, 1), problem2=(3, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: double\n'
' required: false\n'
' allow-quoted-quotes: true\n')
self.check("---\n"
"'[barbaz]': 2\n" # fails
"'[bar\"baz]': 3\n",
conf, problem1=(2, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: double\n'
' required: true\n'
' allow-quoted-quotes: false\n')
self.check("---\n"
"'[barbaz]': 2\n" # fails
"'[bar\"baz]': 3\n", # fails
conf, problem1=(2, 1), problem2=(3, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: double\n'
' required: true\n'
' allow-quoted-quotes: true\n')
self.check("---\n"
"'[barbaz]': 2\n" # fails
"'[bar\"baz]': 3\n",
conf, problem1=(2, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: double\n'
' required: only-when-needed\n'
' allow-quoted-quotes: false\n')
self.check("---\n"
"'[barbaz]': 2\n" # fails
"'[bar\"baz]': 3\n", # fails
conf, problem1=(2, 1), problem2=(3, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: double\n'
' required: only-when-needed\n'
' allow-quoted-quotes: true\n')
self.check("---\n"
"'[barbaz]': 2\n" # fails
"'[bar\"baz]': 3\n",
conf, problem1=(2, 1))
conf = ('quoted-strings:\n'
' check-keys: true\n'
' quote-type: any\n')
self.check("---\n"
"'[barbaz]': 2\n"
"'[bar\"baz]': 3\n",
conf)

View File

@ -32,6 +32,9 @@ used.
even if ``required: only-when-needed`` is set. even if ``required: only-when-needed`` is set.
* ``allow-quoted-quotes`` allows (``true``) using disallowed quotes for strings * ``allow-quoted-quotes`` allows (``true``) using disallowed quotes for strings
with allowed quotes inside. Default ``false``. with allowed quotes inside. Default ``false``.
* ``check-keys`` defines whether to apply the rules to keys in mappings. By
default, ``quoted-strings`` rules apply only to values. Set this option to
``true`` to apply the rules to keys as well.
**Note**: Multi-line strings (with ``|`` or ``>``) will not be checked. **Note**: Multi-line strings (with ``|`` or ``>``) will not be checked.
@ -46,6 +49,7 @@ used.
extra-required: [] extra-required: []
extra-allowed: [] extra-allowed: []
allow-quoted-quotes: false allow-quoted-quotes: false
check-keys: false
.. rubric:: Examples .. rubric:: Examples
@ -135,6 +139,18 @@ used.
foo: 'bar"baz' foo: 'bar"baz'
#. With ``quoted-strings: {required: only-when-needed, check-keys: true,
extra-required: ["[:]"]}``
the following code snippet would **FAIL**:
::
foo:bar: baz
the following code snippet would **PASS**:
::
"foo:bar": baz
""" """
import re import re
@ -149,12 +165,14 @@ CONF = {'quote-type': ('any', 'single', 'double'),
'required': (True, False, 'only-when-needed'), 'required': (True, False, 'only-when-needed'),
'extra-required': [str], 'extra-required': [str],
'extra-allowed': [str], 'extra-allowed': [str],
'allow-quoted-quotes': bool} 'allow-quoted-quotes': bool,
'check-keys': bool}
DEFAULT = {'quote-type': 'any', DEFAULT = {'quote-type': 'any',
'required': True, 'required': True,
'extra-required': [], 'extra-required': [],
'extra-allowed': [], 'extra-allowed': [],
'allow-quoted-quotes': False} 'allow-quoted-quotes': False,
'check-keys': False}
def VALIDATE(conf): def VALIDATE(conf):
@ -226,10 +244,14 @@ def check(conf, token, prev, next, nextnext, context):
if not (isinstance(token, yaml.tokens.ScalarToken) and if not (isinstance(token, yaml.tokens.ScalarToken) and
isinstance(prev, (yaml.BlockEntryToken, yaml.FlowEntryToken, isinstance(prev, (yaml.BlockEntryToken, yaml.FlowEntryToken,
yaml.FlowSequenceStartToken, yaml.TagToken, yaml.FlowSequenceStartToken, yaml.TagToken,
yaml.ValueToken))): yaml.ValueToken, yaml.KeyToken))):
return return
node = 'key' if isinstance(prev, yaml.KeyToken) else 'value'
if node == 'key' and not conf['check-keys']:
return
# Ignore explicit types, e.g. !!str testtest or !!int 42 # Ignore explicit types, e.g. !!str testtest or !!int 42
if (prev and isinstance(prev, yaml.tokens.TagToken) and if (prev and isinstance(prev, yaml.tokens.TagToken) and
prev.value[0] == '!!'): prev.value[0] == '!!'):
@ -254,7 +276,7 @@ def check(conf, token, prev, next, nextnext, context):
if (token.style is None or if (token.style is None or
not (_quote_match(quote_type, token.style) or not (_quote_match(quote_type, token.style) or
(conf['allow-quoted-quotes'] and _has_quoted_quotes(token)))): (conf['allow-quoted-quotes'] and _has_quoted_quotes(token)))):
msg = f"string value is not quoted with {quote_type} quotes" msg = f"string {node} is not quoted with {quote_type} quotes"
elif conf['required'] is False: elif conf['required'] is False:
@ -263,13 +285,13 @@ def check(conf, token, prev, next, nextnext, context):
not _quote_match(quote_type, token.style) and not _quote_match(quote_type, token.style) and
not (conf['allow-quoted-quotes'] and not (conf['allow-quoted-quotes'] and
_has_quoted_quotes(token))): _has_quoted_quotes(token))):
msg = f"string value is not quoted with {quote_type} quotes" msg = f"string {node} is not quoted with {quote_type} quotes"
elif not token.style: elif not token.style:
is_extra_required = any(re.search(r, token.value) is_extra_required = any(re.search(r, token.value)
for r in conf['extra-required']) for r in conf['extra-required'])
if is_extra_required: if is_extra_required:
msg = "string value is not quoted" msg = f"string {node} is not quoted"
elif conf['required'] == 'only-when-needed': elif conf['required'] == 'only-when-needed':
@ -282,20 +304,20 @@ def check(conf, token, prev, next, nextnext, context):
is_extra_allowed = any(re.search(r, token.value) is_extra_allowed = any(re.search(r, token.value)
for r in conf['extra-allowed']) for r in conf['extra-allowed'])
if not (is_extra_required or is_extra_allowed): if not (is_extra_required or is_extra_allowed):
msg = f"string value is redundantly quoted with " \ msg = f"string {node} is redundantly quoted with " \
f"{quote_type} quotes" f"{quote_type} quotes"
# But when used need to match config # But when used need to match config
elif (token.style and elif (token.style and
not _quote_match(quote_type, token.style) and not _quote_match(quote_type, token.style) and
not (conf['allow-quoted-quotes'] and _has_quoted_quotes(token))): not (conf['allow-quoted-quotes'] and _has_quoted_quotes(token))):
msg = f"string value is not quoted with {quote_type} quotes" msg = f"string {node} is not quoted with {quote_type} quotes"
elif not token.style: elif not token.style:
is_extra_required = len(conf['extra-required']) and any( is_extra_required = len(conf['extra-required']) and any(
re.search(r, token.value) for r in conf['extra-required']) re.search(r, token.value) for r in conf['extra-required'])
if is_extra_required: if is_extra_required:
msg = "string value is not quoted" msg = f"string {node} is not quoted"
if msg is not None: if msg is not None:
yield LintProblem( yield LintProblem(