Skip to content

Commit fbaa98e

Browse files
committed
improve tests
1 parent e1320af commit fbaa98e

File tree

3 files changed

+85
-15
lines changed

3 files changed

+85
-15
lines changed

Lib/test/test_dstring.py

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,100 @@
11
import unittest
22

33

4+
_dstring_prefixes = "d db df dt dr drb drf drt".split()
5+
_dstring_prefixes += [p.upper() for p in _dstring_prefixes]
6+
47
class DStringTestCase(unittest.TestCase):
58
def assertAllRaise(self, exception_type, regex, error_strings):
69
for str in error_strings:
710
with self.subTest(str=str):
811
with self.assertRaisesRegex(exception_type, regex) as cm:
912
eval(str)
10-
# print("Testing expression:", repr(str))
11-
# print(repr(cm.exception))
12-
# print(repr(cm.exception.text))
1313

1414
def test_single_quote(self):
1515
exprs = [
16-
"d'hello'",
17-
'D"hello"',
18-
"d'hello\\nworld'",
16+
f"{p}'hello, world'" for p in _dstring_prefixes
17+
] + [
18+
f'{p}"hello, world"' for p in _dstring_prefixes
1919
]
2020
self.assertAllRaise(SyntaxError, "d-string must be triple-quoted", exprs)
2121

2222
def test_empty_dstring(self):
2323
exprs = [
24-
"d''''''",
25-
'D""""""',
24+
f"{p}''''''" for p in _dstring_prefixes
25+
] + [
26+
f'{p}""""""' for p in _dstring_prefixes
2627
]
2728
self.assertAllRaise(SyntaxError, "d-string must start with a newline", exprs)
2829

29-
def test_simple_dstring(self):
30-
self.assertEqual(eval('d"""\n hello world\n """'), "hello world\n")
31-
self.assertEqual(eval('d"""\n hello world\n """'), " hello world\n")
32-
self.assertEqual(eval('d"""\n hello world\n"""'), " hello world\n")
33-
self.assertEqual(eval('d"""\n hello world\\\n """'), " hello world")
34-
self.assertEqual(eval('dr"""\n hello world\\\n """'), " hello world\\\n")
30+
for prefix in _dstring_prefixes:
31+
expr = f"{prefix}'''\n'''"
32+
expr2 = f'{prefix}"""\n"""'
33+
with self.subTest(expr=expr):
34+
v = eval(expr)
35+
v2 = eval(expr2)
36+
if 't' in prefix.lower():
37+
self.assertEqual(v.strings, ("",))
38+
self.assertEqual(v2.strings, ("",))
39+
elif 'b' in prefix.lower():
40+
self.assertEqual(v, b"")
41+
self.assertEqual(v2, b"")
42+
else:
43+
self.assertEqual(v, "")
44+
self.assertEqual(v2, "")
45+
46+
def test_dedent(self):
47+
# Basic dedent - remove common leading whitespace
48+
result = eval("""\
49+
None or d'''
50+
hello
51+
world
52+
'''""")
53+
self.assertEqual(result, "hello\nworld\n")
54+
55+
# Dedent with varying indentation
56+
result = eval("""\
57+
None or d'''
58+
line1
59+
line2
60+
line3
61+
'''""")
62+
self.assertEqual(result, " line1\n line2\nline3\n ")
63+
64+
# Dedent with tabs
65+
result = eval("""\
66+
None or d'''
67+
\thello
68+
\tworld
69+
\t'''""")
70+
self.assertEqual(result, "hello\nworld\n")
71+
72+
# Mixed spaces and tabs (using common leading whitespace)
73+
result = eval("""\
74+
None or d'''
75+
\t\t hello
76+
\t\t world
77+
\t\t '''""")
78+
self.assertEqual(result, " hello\n world\n")
79+
80+
# Empty lines do not affect the calculation of common leading whitespace
81+
result = eval("""\
82+
None or d'''
83+
hello
84+
85+
world
86+
'''""")
87+
self.assertEqual(result, "hello\n\nworld\n")
88+
89+
# Lines with only whitespace also have their indentation removed.
90+
result = eval("""\
91+
None or d'''
92+
hello
93+
\n\
94+
\n\
95+
world
96+
'''""")
97+
self.assertEqual(result, "hello\n\n \nworld\n")
3598

3699

37100
if __name__ == '__main__':

Parser/action_helpers.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,13 @@ _get_resized_exprs(Parser *p, Token *a, asdl_expr_seq *raw_expressions, Token *b
14811481
Py_ssize_t common_indent_len = 0;
14821482

14831483
if (is_dedent) {
1484+
if (total_items == 0) {
1485+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
1486+
a,
1487+
"d-string must start with a newline"
1488+
);
1489+
return NULL;
1490+
}
14841491
expr_ty first_item = asdl_seq_GET(raw_expressions, 0);
14851492
if (first_item->kind != Constant_kind
14861493
|| PyUnicode_ReadChar(first_item->v.Constant.value, 0) != '\n') {

Parser/lexer/lexer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1124,7 +1124,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
11241124
case 'd':
11251125
case 'D':
11261126
if (quote_size != 3) {
1127-
return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "d-string must be a multiline string"));
1127+
return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "d-string must be triple-quoted"));
11281128
}
11291129
break;
11301130
default:

0 commit comments

Comments
 (0)