Skip to content

Commit 3185871

Browse files
committed
WIP
1 parent 2d10aaa commit 3185871

File tree

2 files changed

+101
-24
lines changed

2 files changed

+101
-24
lines changed

tests/rules/test_quoted_strings.py

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ def test_quote_type_any(self):
7777
' word 1\n' # fails
7878
' word 2\n'
7979
'multiline string 4:\n'
80+
' "word 1\n'
81+
' word 2"\n'
82+
'multiline string 5:\n'
8083
' "word 1\\\n'
8184
' word 2"\n',
8285
conf, problem1=(9, 3))
@@ -127,9 +130,12 @@ def test_quote_type_single(self):
127130
' word 1\n' # fails
128131
' word 2\n'
129132
'multiline string 4:\n'
133+
' "word 1\n'
134+
' word 2"\n'
135+
'multiline string 5:\n'
130136
' "word 1\\\n'
131137
' word 2"\n',
132-
conf, problem1=(9, 3), problem2=(12, 3))
138+
conf, problem1=(9, 3), problem2=(12, 3), problem3=(15, 3))
133139

134140
def test_quote_type_double(self):
135141
conf = 'quoted-strings: {quote-type: double}\n'
@@ -173,6 +179,9 @@ def test_quote_type_double(self):
173179
' word 1\n' # fails
174180
' word 2\n'
175181
'multiline string 4:\n'
182+
' "word 1\n'
183+
' word 2"\n'
184+
'multiline string 5:\n'
176185
' "word 1\\\n'
177186
' word 2"\n',
178187
conf, problem1=(9, 3))
@@ -216,6 +225,9 @@ def test_any_quotes_not_required(self):
216225
' word 1\n'
217226
' word 2\n'
218227
'multiline string 4:\n'
228+
' "word 1\n'
229+
' word 2"\n'
230+
'multiline string 5:\n'
219231
' "word 1\\\n'
220232
' word 2"\n',
221233
conf)
@@ -263,9 +275,12 @@ def test_single_quotes_not_required(self):
263275
' word 1\n'
264276
' word 2\n'
265277
'multiline string 4:\n'
278+
' "word 1\n'
279+
' word 2"\n'
280+
'multiline string 5:\n'
266281
' "word 1\\\n' # fails
267282
' word 2"\n',
268-
conf, problem1=(12, 3))
283+
conf, problem1=(12, 3), problem2=(15, 3))
269284

270285
def test_only_when_needed(self):
271286
conf = 'quoted-strings: {required: only-when-needed}\n'
@@ -307,6 +322,9 @@ def test_only_when_needed(self):
307322
' word 1\n'
308323
' word 2\n'
309324
'multiline string 4:\n'
325+
' "word 1\n'
326+
' word 2"\n'
327+
'multiline string 5:\n'
310328
' "word 1\\\n' # fails
311329
' word 2"\n',
312330
conf, problem1=(12, 3))
@@ -354,9 +372,12 @@ def test_only_when_needed_single_quotes(self):
354372
' word 1\n'
355373
' word 2\n'
356374
'multiline string 4:\n'
375+
' "word 1\n'
376+
' word 2"\n'
377+
'multiline string 5:\n'
357378
' "word 1\\\n' # fails
358379
' word 2"\n',
359-
conf, problem1=(12, 3))
380+
conf, problem1=(12, 3), problem2=(15, 3))
360381

361382
def test_only_when_needed_corner_cases(self):
362383
conf = 'quoted-strings: {required: only-when-needed}\n'
@@ -626,7 +647,8 @@ class QuotedKeysTestCase(RuleTestCase):
626647
rule_id = 'quoted-strings'
627648

628649
def test_disabled(self):
629-
conf_disabled = "quoted-strings: {}"
650+
conf_disabled = ('quoted-strings: {}\n'
651+
'key-duplicates: disable\n')
630652
key_strings = ('---\n'
631653
'true: 2\n'
632654
'123: 3\n'
@@ -665,6 +687,10 @@ def test_disabled(self):
665687
' line 2\n'
666688
': 35\n'
667689
'?\n'
690+
' "line 1\n'
691+
' line 2"\n'
692+
': 37\n'
693+
'?\n'
668694
' "line 1\\\n'
669695
' line 2"\n'
670696
': 39\n')
@@ -673,7 +699,8 @@ def test_disabled(self):
673699
def test_default(self):
674700
# Default configuration, but with check-keys
675701
conf_default = ("quoted-strings:\n"
676-
" check-keys: true\n")
702+
" check-keys: true\n"
703+
"key-duplicates: disable\n")
677704
key_strings = ('---\n'
678705
'true: 2\n'
679706
'123: 3\n'
@@ -712,6 +739,10 @@ def test_default(self):
712739
' line 2\n'
713740
': 35\n'
714741
'?\n'
742+
' "line 1\n'
743+
' line 2"\n'
744+
': 37\n'
745+
'?\n'
715746
' "line 1\\\n'
716747
' line 2"\n'
717748
': 39\n')
@@ -721,7 +752,8 @@ def test_default(self):
721752
def test_quote_type_any(self):
722753
conf = ('quoted-strings:\n'
723754
' check-keys: true\n'
724-
' quote-type: any\n')
755+
' quote-type: any\n'
756+
'key-duplicates: disable\n')
725757

726758
key_strings = ('---\n'
727759
'true: 2\n'
@@ -761,6 +793,10 @@ def test_quote_type_any(self):
761793
' line 2\n'
762794
': 35\n'
763795
'?\n'
796+
' "line 1\n'
797+
' line 2"\n'
798+
': 37\n'
799+
'?\n'
764800
' "line 1\\\n'
765801
' line 2"\n'
766802
': 39\n')
@@ -771,7 +807,8 @@ def test_quote_type_any(self):
771807
def test_quote_type_single(self):
772808
conf = ('quoted-strings:\n'
773809
' check-keys: true\n'
774-
' quote-type: single\n')
810+
' quote-type: single\n'
811+
'key-duplicates: disable\n')
775812

776813
key_strings = ('---\n'
777814
'true: 2\n'
@@ -811,19 +848,24 @@ def test_quote_type_single(self):
811848
' line 2\n'
812849
': 35\n'
813850
'?\n'
851+
' "line 1\n'
852+
' line 2"\n'
853+
': 37\n'
854+
'?\n'
814855
' "line 1\\\n'
815856
' line 2"\n'
816857
': 39\n')
817858
self.check(key_strings, conf,
818859
problem1=(4, 1), problem2=(5, 1), problem3=(6, 1),
819860
problem4=(7, 1), problem5=(20, 3), problem6=(21, 3),
820861
problem7=(23, 2), problem8=(23, 10), problem9=(33, 3),
821-
problem10=(37, 3))
862+
problem10=(37, 3), problem11=(41, 3))
822863

823864
def test_quote_type_double(self):
824865
conf = ('quoted-strings:\n'
825866
' check-keys: true\n'
826-
' quote-type: double\n')
867+
' quote-type: double\n'
868+
'key-duplicates: disable\n')
827869

828870
key_strings = ('---\n'
829871
'true: 2\n'
@@ -863,6 +905,10 @@ def test_quote_type_double(self):
863905
' line 2\n'
864906
': 35\n'
865907
'?\n'
908+
' "line 1\n'
909+
' line 2"\n'
910+
': 37\n'
911+
'?\n'
866912
' "line 1\\\n'
867913
' line 2"\n'
868914
': 39\n')
@@ -874,7 +920,8 @@ def test_any_quotes_not_required(self):
874920
conf = ('quoted-strings:\n'
875921
' check-keys: true\n'
876922
' quote-type: any\n'
877-
' required: false\n')
923+
' required: false\n'
924+
'key-duplicates: disable\n')
878925

879926
key_strings = ('---\n'
880927
'true: 2\n'
@@ -914,6 +961,10 @@ def test_any_quotes_not_required(self):
914961
' line 2\n'
915962
': 35\n'
916963
'?\n'
964+
' "line 1\n'
965+
' line 2"\n'
966+
': 37\n'
967+
'?\n'
917968
' "line 1\\\n'
918969
' line 2"\n'
919970
': 39\n')
@@ -923,7 +974,8 @@ def test_single_quotes_not_required(self):
923974
conf = ('quoted-strings:\n'
924975
' check-keys: true\n'
925976
' quote-type: single\n'
926-
' required: false\n')
977+
' required: false\n'
978+
'key-duplicates: disable\n')
927979

928980
key_strings = ('---\n'
929981
'true: 2\n'
@@ -963,17 +1015,24 @@ def test_single_quotes_not_required(self):
9631015
' line 2\n'
9641016
': 35\n'
9651017
'?\n'
1018+
' "line 1\n'
1019+
' line 2"\n'
1020+
': 37\n'
1021+
'?\n'
9661022
' "line 1\\\n'
9671023
' line 2"\n'
9681024
': 39\n')
1025+
self.maxDiff = None#############
9691026
self.check(key_strings, conf,
9701027
problem1=(5, 1), problem2=(6, 1), problem3=(7, 1),
971-
problem4=(21, 3), problem5=(23, 10), problem6=(37, 3))
1028+
problem4=(21, 3), problem5=(23, 10), problem6=(37, 3),
1029+
problem7=(41, 3))
9721030

9731031
def test_only_when_needed(self):
9741032
conf = ('quoted-strings:\n'
9751033
' check-keys: true\n'
976-
' required: only-when-needed\n')
1034+
' required: only-when-needed\n'
1035+
'key-duplicates: disable\n')
9771036

9781037
key_strings = ('---\n'
9791038
'true: 2\n'
@@ -1013,6 +1072,10 @@ def test_only_when_needed(self):
10131072
' line 2\n'
10141073
': 35\n'
10151074
'?\n'
1075+
' "line 1\n'
1076+
' line 2"\n'
1077+
': 37\n'
1078+
'?\n'
10161079
' "line 1\\\n'
10171080
' line 2"\n'
10181081
': 39\n')
@@ -1024,7 +1087,8 @@ def test_only_when_needed_single_quotes(self):
10241087
conf = ('quoted-strings:\n'
10251088
' check-keys: true\n'
10261089
' quote-type: single\n'
1027-
' required: only-when-needed\n')
1090+
' required: only-when-needed\n'
1091+
'key-duplicates: disable\n')
10281092

10291093
key_strings = ('---\n'
10301094
'true: 2\n'
@@ -1064,13 +1128,17 @@ def test_only_when_needed_single_quotes(self):
10641128
' line 2\n'
10651129
': 35\n'
10661130
'?\n'
1131+
' "line 1\n'
1132+
' line 2"\n'
1133+
': 37\n'
1134+
'?\n'
10671135
' "line 1\\\n'
10681136
' line 2"\n'
10691137
': 39\n')
10701138
self.check(key_strings, conf,
10711139
problem1=(5, 1), problem2=(6, 1), problem3=(7, 1),
10721140
problem4=(8, 1), problem5=(21, 3), problem6=(23, 10),
1073-
problem7=(37, 3))
1141+
problem7=(37, 3), problem8=(41, 3))
10741142

10751143
def test_only_when_needed_corner_cases(self):
10761144
conf = ('quoted-strings:\n'

yamllint/rules/quoted_strings.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,20 +204,25 @@ def _quote_match(quote_type, token_style):
204204
(quote_type == 'double' and token_style == '"'))
205205

206206

207-
def _quotes_are_needed(string, style, is_inside_a_flow):
207+
def _quotes_are_needed(token, is_inside_a_flow):
208208
# Quotes needed on strings containing flow tokens
209-
if is_inside_a_flow and set(string) & {',', '[', ']', '{', '}'}:
209+
if is_inside_a_flow and set(token.value) & {',', '[', ']', '{', '}'}:
210210
return True
211211

212-
if style == '"':
212+
if token.style == '"':
213213
try:
214-
yaml.reader.Reader('').check_printable('key: ' + string)
214+
yaml.reader.Reader('').check_printable('key: ' + token.value)
215215
except yaml.reader.ReaderError:
216216
# Special characters in a double-quoted string are assumed to have
217217
# been backslash-escaped
218218
return True
219219

220-
loader = yaml.BaseLoader('key: ' + string)
220+
if (not token.plain and
221+
token.start_mark.line != token.end_mark.line and
222+
_has_backslashes_on_line_endings(token)):
223+
return True
224+
225+
loader = yaml.BaseLoader('key: ' + token.value)
221226
# Remove the 5 first tokens corresponding to 'key: ' (StreamStartToken,
222227
# BlockMappingStartToken, KeyToken, ScalarToken(value=key), ValueToken)
223228
for _ in range(5):
@@ -228,7 +233,7 @@ def _quotes_are_needed(string, style, is_inside_a_flow):
228233
return True
229234
else:
230235
if (isinstance(a, yaml.ScalarToken) and a.style is None and
231-
isinstance(b, yaml.BlockEndToken) and a.value == string):
236+
isinstance(b, yaml.BlockEndToken) and a.value == token.value):
232237
return False
233238
return True
234239

@@ -239,6 +244,12 @@ def _has_quoted_quotes(token):
239244
(token.style == '"' and "'" in token.value)))
240245

241246

247+
def _has_backslashes_on_line_endings(token):
248+
buffer = token.start_mark.buffer[
249+
token.start_mark.index + 1:token.end_mark.index - 1]
250+
return all(line[-1] in ('\\', '\\\r') for line in buffer.split('\n')[:-1])
251+
252+
242253
def check(conf, token, prev, next, nextnext, context):
243254
if 'flow_nest_count' not in context:
244255
context['flow_nest_count'] = 0
@@ -306,9 +317,7 @@ def check(conf, token, prev, next, nextnext, context):
306317

307318
# Quotes are not strictly needed here
308319
if (token.style and tag == DEFAULT_SCALAR_TAG and token.value and
309-
not _quotes_are_needed(token.value,
310-
token.style,
311-
context['flow_nest_count'] > 0)):
320+
not _quotes_are_needed(token, context['flow_nest_count'] > 0)):
312321
is_extra_required = any(re.search(r, token.value)
313322
for r in conf['extra-required'])
314323
is_extra_allowed = any(re.search(r, token.value)

0 commit comments

Comments
 (0)