quoted-strings: Add quote-type: consistent

Strings in a document may be single or double, but must be consistent.
The first string found is assumed to be the canonical quote type, and
any subsequent quotes will be compared to it.

Closes: adrienverge/yamllint#763
This commit is contained in:
Ryan Finnie
2025-09-13 07:20:36 +02:00
committed by Adrien Vergé
parent 0b4ddc88c0
commit e3d54cc100
2 changed files with 67 additions and 5 deletions
+41
View File
@@ -186,6 +186,47 @@ class QuotedValuesTestCase(RuleTestCase):
' word 2"\n',
conf, problem1=(9, 3))
def test_quote_type_consistent(self):
conf = 'quoted-strings: {quote-type: consistent}'
self.check('---\n'
'string1: "foo"\n'
'string2: "bar"\n'
'string3: \'baz\'\n' # fails
'string4: "quux"\n',
conf, problem1=(4, 10))
conf = ('quoted-strings:\n'
' quote-type: consistent\n'
' check-keys: true\n')
self.check('---\n'
'"string1": "foo"\n'
'string2: "bar"\n' # fails
'\'string3\': "baz"\n' # fails
'"string4": {"key": "val"}\n'
'"string5": {\'key\': "val"}\n', # fails
conf, problem1=(3, 1), problem2=(4, 1), problem3=(6, 13))
conf = ('quoted-strings:\n'
' quote-type: consistent\n'
' check-keys: true\n'
' required: false\n')
self.check('---\n'
'string1: \'foo\'\n'
'string2: "bar"\n' # fails
'string3: \'baz\'\n'
'string4: {\'key\': "val"}\n' # fails
'string5: {"key": \'val\'}\n' # fails
'string6:\n'
' \'key\': "val"\n' # fails
'string7:\n'
' "key": \'val\'\n' # fails
'string8:\n'
' "string"\n' # fails
'string9: >\n'
' "string"\n',
conf, problem1=(3, 10), problem2=(5, 18), problem3=(6, 11),
problem4=(8, 10), problem5=(10, 3), problem6=(12, 3))
def test_any_quotes_not_required(self):
conf = 'quoted-strings: {quote-type: any, required: false}\n'
+26 -5
View File
@@ -139,6 +139,20 @@ used.
foo: 'bar"baz'
#. With ``quoted-strings: {quote-type: consistent}``
the following code snippet would **PASS**:
::
foo: 'bar'
baz: 'quux'
the following code snippet would **FAIL**:
::
foo: 'bar'
baz: "quux"
#. With ``quoted-strings: {required: only-when-needed, check-keys: true,
extra-required: ["[:]"]}``
@@ -161,7 +175,7 @@ from yamllint.linter import LintProblem
ID = 'quoted-strings'
TYPE = 'token'
CONF = {'quote-type': ('any', 'single', 'double'),
CONF = {'quote-type': ('any', 'single', 'double', 'consistent'),
'required': (True, False, 'only-when-needed'),
'extra-required': [str],
'extra-allowed': [str],
@@ -198,7 +212,14 @@ yaml.resolver.Resolver.add_implicit_resolver(
list('-+0123456789'))
def _quote_match(quote_type, token_style):
def _quote_match(quote_type, token_style, context):
if quote_type == 'consistent' and token_style is not None:
# The canonical token style in a document is assumed to be the first
# one found for the purpose of 'consistent'
if 'quoted_strings_consistent_token_style' not in context:
context['quoted_strings_consistent_token_style'] = token_style
return context['quoted_strings_consistent_token_style'] == token_style
return ((quote_type == 'any') or
(quote_type == 'single' and token_style == "'") or
(quote_type == 'double' and token_style == '"'))
@@ -294,7 +315,7 @@ def check(conf, token, prev, next, nextnext, context):
# Quotes are mandatory and need to match config
if (token.style is None or
not (_quote_match(quote_type, token.style) or
not (_quote_match(quote_type, token.style, context) or
(conf['allow-quoted-quotes'] and _has_quoted_quotes(token)))):
msg = f"string {node} is not quoted with {quote_type} quotes"
@@ -302,7 +323,7 @@ def check(conf, token, prev, next, nextnext, context):
# Quotes are not mandatory but when used need to match config
if (token.style and
not _quote_match(quote_type, token.style) and
not _quote_match(quote_type, token.style, context) and
not (conf['allow-quoted-quotes'] and
_has_quoted_quotes(token))):
msg = f"string {node} is not quoted with {quote_type} quotes"
@@ -328,7 +349,7 @@ def check(conf, token, prev, next, nextnext, context):
# But when used need to match config
elif (token.style and
not _quote_match(quote_type, token.style) and
not _quote_match(quote_type, token.style, context) and
not (conf['allow-quoted-quotes'] and _has_quoted_quotes(token))):
msg = f"string {node} is not quoted with {quote_type} quotes"