Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix quoted-strings checks in flow maps and sequences #615

Merged
merged 1 commit into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 52 additions & 13 deletions tests/rules/test_quoted_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,14 @@ def test_quote_type_any(self):
' - 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))
'flow-map: {a: foo, b: "foo"}\n' # fails
'flow-seq2: [foo, "foo,bar", "foo[bar]", "foo{bar}"]\n'
'flow-map2: {a: foo, b: "foo,bar"}\n'
'nested-flow1: {a: foo, b: [foo, "foo,bar"]}\n'
'nested-flow2: [{a: foo}, {b: "foo,bar", c: ["d[e]"]}]\n',
conf, problem1=(4, 10), problem2=(17, 5), problem3=(19, 12),
problem4=(20, 15), problem5=(21, 13), problem6=(22, 16),
problem7=(23, 19), problem8=(23, 28), problem9=(24, 20))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
Expand Down Expand Up @@ -97,11 +102,19 @@ def test_quote_type_single(self):
' - foo\n' # fails
' - "foo"\n' # fails
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
'flow-map: {a: foo, b: "foo"}\n' # fails
'flow-seq2: [foo, "foo,bar", "foo[bar]", "foo{bar}"]\n'
'flow-map2: {a: foo, b: "foo,bar"}\n'
'nested-flow1: {a: foo, b: [foo, "foo,bar"]}\n'
'nested-flow2: [{a: foo}, {b: "foo,bar", c: ["d[e]"]}]\n',
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))
problem10=(20, 23), problem11=(21, 13), problem12=(21, 18),
problem13=(21, 29), problem14=(21, 41), problem15=(22, 16),
problem16=(22, 24), problem17=(23, 19), problem18=(23, 28),
problem19=(23, 33), problem20=(24, 20), problem21=(24, 30),
problem22=(24, 45))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
Expand Down Expand Up @@ -139,9 +152,15 @@ def test_quote_type_double(self):
' - foo\n' # fails
' - "foo"\n'
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
'flow-map: {a: foo, b: "foo"}\n' # fails
'flow-seq2: [foo, "foo,bar", "foo[bar]", "foo{bar}"]\n'
'flow-map2: {a: foo, b: "foo,bar"}\n'
'nested-flow1: {a: foo, b: [foo, "foo,bar"]}\n'
'nested-flow2: [{a: foo}, {b: "foo,bar", c: ["d[e]"]}]\n',
conf, problem1=(4, 10), problem2=(8, 10), problem3=(17, 5),
problem4=(19, 12), problem5=(20, 15))
problem4=(19, 12), problem5=(20, 15), problem6=(21, 13),
problem7=(22, 16), problem8=(23, 19), problem9=(23, 28),
problem10=(24, 20))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
Expand Down Expand Up @@ -179,7 +198,11 @@ def test_any_quotes_not_required(self):
' - foo\n' # fails
' - "foo"\n'
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
'flow-map: {a: foo, b: "foo"}\n' # fails
'flow-seq2: [foo, "foo,bar", "foo[bar]", "foo{bar}"]\n'
'flow-map2: {a: foo, b: "foo,bar"}\n'
'nested-flow1: {a: foo, b: [foo, "foo,bar"]}\n'
'nested-flow2: [{a: foo}, {b: "foo,bar", c: ["d[e]"]}]\n',
conf)
self.check('---\n'
'multiline string 1: |\n'
Expand Down Expand Up @@ -218,9 +241,16 @@ def test_single_quotes_not_required(self):
' - foo\n' # fails
' - "foo"\n'
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
'flow-map: {a: foo, b: "foo"}\n' # fails
'flow-seq2: [foo, "foo,bar", "foo[bar]", "foo{bar}"]\n'
'flow-map2: {a: foo, b: "foo,bar"}\n'
'nested-flow1: {a: foo, b: [foo, "foo,bar"]}\n'
'nested-flow2: [{a: foo}, {b: "foo,bar", c: ["d[e]"]}]\n',
conf, problem1=(5, 10), problem2=(6, 10), problem3=(7, 10),
problem4=(18, 5), problem5=(19, 17), problem6=(20, 23))
problem4=(18, 5), problem5=(19, 17), problem6=(20, 23),
problem7=(21, 18), problem8=(21, 29), problem9=(21, 41),
problem10=(22, 24), problem11=(23, 33), problem12=(24, 30),
problem13=(24, 45))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
Expand Down Expand Up @@ -258,7 +288,11 @@ def test_only_when_needed(self):
' - foo\n'
' - "foo"\n' # fails
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
'flow-map: {a: foo, b: "foo"}\n' # fails
'flow-seq2: [foo, "foo,bar", "foo[bar]", "foo{bar}"]\n'
'flow-map2: {a: foo, b: "foo,bar"}\n'
'nested-flow1: {a: foo, b: [foo, "foo,bar"]}\n'
'nested-flow2: [{a: foo}, {b: "foo,bar", c: ["d[e]"]}]\n',
conf, problem1=(5, 10), problem2=(8, 10), problem3=(18, 5),
problem4=(19, 17), problem5=(20, 23))
self.check('---\n'
Expand Down Expand Up @@ -299,10 +333,15 @@ def test_only_when_needed_single_quotes(self):
' - foo\n'
' - "foo"\n' # fails
'flow-seq: [foo, "foo"]\n' # fails
'flow-map: {a: foo, b: "foo"}\n', # fails
'flow-map: {a: foo, b: "foo"}\n' # fails
'flow-seq2: [foo, "foo,bar"]\n' # fails
'flow-map2: {a: foo, b: "foo,bar"}\n' # fails
'nested-flow1: {a: foo, b: [foo, "foo,bar"]}\n'
'nested-flow2: [{a: foo}, {b: "foo,bar", c: ["d[e]"]}]\n',
conf, problem1=(5, 10), problem2=(6, 10), problem3=(7, 10),
problem4=(8, 10), problem5=(18, 5), problem6=(19, 17),
problem7=(20, 23))
problem7=(20, 23), problem8=(21, 18), problem9=(22, 24),
problem10=(23, 33), problem11=(24, 30), problem12=(24, 45))
self.check('---\n'
'multiline string 1: |\n'
' line 1\n'
Expand Down
19 changes: 17 additions & 2 deletions yamllint/rules/quoted_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ def _quote_match(quote_type, token_style):
(quote_type == 'double' and token_style == '"'))


def _quotes_are_needed(string):
def _quotes_are_needed(string, is_inside_a_flow):
# Quotes needed on strings containing flow tokens
if is_inside_a_flow and set(string) & {',', '[', ']', '{', '}'}:
return True

loader = yaml.BaseLoader('key: ' + string)
# Remove the 5 first tokens corresponding to 'key: ' (StreamStartToken,
# BlockMappingStartToken, KeyToken, ScalarToken(value=key), ValueToken)
Expand All @@ -209,6 +213,16 @@ def _has_quoted_quotes(token):


def check(conf, token, prev, next, nextnext, context):
if 'flow_nest_count' not in context:
context['flow_nest_count'] = 0

if isinstance(token, (yaml.FlowMappingStartToken,
yaml.FlowSequenceStartToken)):
context['flow_nest_count'] += 1
elif isinstance(token, (yaml.FlowMappingEndToken,
yaml.FlowSequenceEndToken)):
context['flow_nest_count'] -= 1

if not (isinstance(token, yaml.tokens.ScalarToken) and
isinstance(prev, (yaml.BlockEntryToken, yaml.FlowEntryToken,
yaml.FlowSequenceStartToken, yaml.TagToken,
Expand Down Expand Up @@ -261,7 +275,8 @@ def check(conf, token, prev, next, nextnext, context):

# Quotes are not strictly needed here
if (token.style and tag == DEFAULT_SCALAR_TAG and token.value and
not _quotes_are_needed(token.value)):
not _quotes_are_needed(token.value,
context['flow_nest_count'] > 0)):
is_extra_required = any(re.search(r, token.value)
for r in conf['extra-required'])
is_extra_allowed = any(re.search(r, token.value)
Expand Down
Loading