Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cython
Commits
be43235b
Commit
be43235b
authored
Aug 29, 2020
by
Stefan Behnel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update CPython "test_fstring" copy to Py3.9.
parent
4d54aeff
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
430 additions
and
24 deletions
+430
-24
tests/run/test_fstring.pyx
tests/run/test_fstring.pyx
+430
-24
No files found.
tests/run/test_fstring.pyx
View file @
be43235b
...
...
@@ -3,10 +3,10 @@
# tag: allow_unknown_names, f_strings, pep498
import
ast
import
os
import
types
import
decimal
import
unittest
import
contextlib
import
sys
IS_PY2
=
sys
.
version_info
[
0
]
<
3
...
...
@@ -63,9 +63,6 @@ class TestCase(CythonTest):
super
(
TestCase
,
self
).
assertEqual
(
first
,
second
,
msg
)
def
test__format__lookup
(
self
):
if
IS_PY2
:
raise
unittest
.
SkipTest
(
"Py3-only"
)
# Make sure __format__ is looked up on the type, not the instance.
class
X
:
def
__format__
(
self
,
spec
):
...
...
@@ -116,14 +113,263 @@ f'{a * x()}'"""
# Make sure x was called.
self
.
assertTrue
(
x
.
called
)
def
__test_ast_line_numbers
(
self
):
expr
=
"""
a = 10
f'{a * x()}'"""
t
=
ast
.
parse
(
expr
)
self
.
assertEqual
(
type
(
t
),
ast
.
Module
)
self
.
assertEqual
(
len
(
t
.
body
),
2
)
# check `a = 10`
self
.
assertEqual
(
type
(
t
.
body
[
0
]),
ast
.
Assign
)
self
.
assertEqual
(
t
.
body
[
0
].
lineno
,
2
)
# check `f'...'`
self
.
assertEqual
(
type
(
t
.
body
[
1
]),
ast
.
Expr
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
),
ast
.
JoinedStr
)
self
.
assertEqual
(
len
(
t
.
body
[
1
].
value
.
values
),
1
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
0
]),
ast
.
FormattedValue
)
self
.
assertEqual
(
t
.
body
[
1
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
0
].
lineno
,
3
)
# check the binop location
binop
=
t
.
body
[
1
].
value
.
values
[
0
].
value
self
.
assertEqual
(
type
(
binop
),
ast
.
BinOp
)
self
.
assertEqual
(
type
(
binop
.
left
),
ast
.
Name
)
self
.
assertEqual
(
type
(
binop
.
op
),
ast
.
Mult
)
self
.
assertEqual
(
type
(
binop
.
right
),
ast
.
Call
)
self
.
assertEqual
(
binop
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
left
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
right
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
col_offset
,
3
)
self
.
assertEqual
(
binop
.
left
.
col_offset
,
3
)
self
.
assertEqual
(
binop
.
right
.
col_offset
,
7
)
def
__test_ast_line_numbers_multiple_formattedvalues
(
self
):
expr
=
"""
f'no formatted values'
f'eggs {a * x()} spam {b + y()}'"""
t
=
ast
.
parse
(
expr
)
self
.
assertEqual
(
type
(
t
),
ast
.
Module
)
self
.
assertEqual
(
len
(
t
.
body
),
2
)
# check `f'no formatted value'`
self
.
assertEqual
(
type
(
t
.
body
[
0
]),
ast
.
Expr
)
self
.
assertEqual
(
type
(
t
.
body
[
0
].
value
),
ast
.
JoinedStr
)
self
.
assertEqual
(
t
.
body
[
0
].
lineno
,
2
)
# check `f'...'`
self
.
assertEqual
(
type
(
t
.
body
[
1
]),
ast
.
Expr
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
),
ast
.
JoinedStr
)
self
.
assertEqual
(
len
(
t
.
body
[
1
].
value
.
values
),
4
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
0
]),
ast
.
Constant
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
0
].
value
),
str
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
1
]),
ast
.
FormattedValue
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
2
]),
ast
.
Constant
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
2
].
value
),
str
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
3
]),
ast
.
FormattedValue
)
self
.
assertEqual
(
t
.
body
[
1
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
0
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
1
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
2
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
3
].
lineno
,
3
)
# check the first binop location
binop1
=
t
.
body
[
1
].
value
.
values
[
1
].
value
self
.
assertEqual
(
type
(
binop1
),
ast
.
BinOp
)
self
.
assertEqual
(
type
(
binop1
.
left
),
ast
.
Name
)
self
.
assertEqual
(
type
(
binop1
.
op
),
ast
.
Mult
)
self
.
assertEqual
(
type
(
binop1
.
right
),
ast
.
Call
)
self
.
assertEqual
(
binop1
.
lineno
,
3
)
self
.
assertEqual
(
binop1
.
left
.
lineno
,
3
)
self
.
assertEqual
(
binop1
.
right
.
lineno
,
3
)
self
.
assertEqual
(
binop1
.
col_offset
,
8
)
self
.
assertEqual
(
binop1
.
left
.
col_offset
,
8
)
self
.
assertEqual
(
binop1
.
right
.
col_offset
,
12
)
# check the second binop location
binop2
=
t
.
body
[
1
].
value
.
values
[
3
].
value
self
.
assertEqual
(
type
(
binop2
),
ast
.
BinOp
)
self
.
assertEqual
(
type
(
binop2
.
left
),
ast
.
Name
)
self
.
assertEqual
(
type
(
binop2
.
op
),
ast
.
Add
)
self
.
assertEqual
(
type
(
binop2
.
right
),
ast
.
Call
)
self
.
assertEqual
(
binop2
.
lineno
,
3
)
self
.
assertEqual
(
binop2
.
left
.
lineno
,
3
)
self
.
assertEqual
(
binop2
.
right
.
lineno
,
3
)
self
.
assertEqual
(
binop2
.
col_offset
,
23
)
self
.
assertEqual
(
binop2
.
left
.
col_offset
,
23
)
self
.
assertEqual
(
binop2
.
right
.
col_offset
,
27
)
def
__test_ast_line_numbers_nested
(
self
):
expr
=
"""
a = 10
f'{a * f"-{x()}-"}'"""
t
=
ast
.
parse
(
expr
)
self
.
assertEqual
(
type
(
t
),
ast
.
Module
)
self
.
assertEqual
(
len
(
t
.
body
),
2
)
# check `a = 10`
self
.
assertEqual
(
type
(
t
.
body
[
0
]),
ast
.
Assign
)
self
.
assertEqual
(
t
.
body
[
0
].
lineno
,
2
)
# check `f'...'`
self
.
assertEqual
(
type
(
t
.
body
[
1
]),
ast
.
Expr
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
),
ast
.
JoinedStr
)
self
.
assertEqual
(
len
(
t
.
body
[
1
].
value
.
values
),
1
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
0
]),
ast
.
FormattedValue
)
self
.
assertEqual
(
t
.
body
[
1
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
0
].
lineno
,
3
)
# check the binop location
binop
=
t
.
body
[
1
].
value
.
values
[
0
].
value
self
.
assertEqual
(
type
(
binop
),
ast
.
BinOp
)
self
.
assertEqual
(
type
(
binop
.
left
),
ast
.
Name
)
self
.
assertEqual
(
type
(
binop
.
op
),
ast
.
Mult
)
self
.
assertEqual
(
type
(
binop
.
right
),
ast
.
JoinedStr
)
self
.
assertEqual
(
binop
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
left
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
right
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
col_offset
,
3
)
self
.
assertEqual
(
binop
.
left
.
col_offset
,
3
)
self
.
assertEqual
(
binop
.
right
.
col_offset
,
7
)
# check the nested call location
self
.
assertEqual
(
len
(
binop
.
right
.
values
),
3
)
self
.
assertEqual
(
type
(
binop
.
right
.
values
[
0
]),
ast
.
Constant
)
self
.
assertEqual
(
type
(
binop
.
right
.
values
[
0
].
value
),
str
)
self
.
assertEqual
(
type
(
binop
.
right
.
values
[
1
]),
ast
.
FormattedValue
)
self
.
assertEqual
(
type
(
binop
.
right
.
values
[
2
]),
ast
.
Constant
)
self
.
assertEqual
(
type
(
binop
.
right
.
values
[
2
].
value
),
str
)
self
.
assertEqual
(
binop
.
right
.
values
[
0
].
lineno
,
3
)
self
.
assertEqual
(
binop
.
right
.
values
[
1
].
lineno
,
3
)
self
.
assertEqual
(
binop
.
right
.
values
[
2
].
lineno
,
3
)
call
=
binop
.
right
.
values
[
1
].
value
self
.
assertEqual
(
type
(
call
),
ast
.
Call
)
self
.
assertEqual
(
call
.
lineno
,
3
)
self
.
assertEqual
(
call
.
col_offset
,
11
)
def
__test_ast_line_numbers_duplicate_expression
(
self
):
"""Duplicate expression
NOTE: this is currently broken, always sets location of the first
expression.
"""
expr
=
"""
a = 10
f'{a * x()} {a * x()} {a * x()}'
"""
t
=
ast
.
parse
(
expr
)
self
.
assertEqual
(
type
(
t
),
ast
.
Module
)
self
.
assertEqual
(
len
(
t
.
body
),
2
)
# check `a = 10`
self
.
assertEqual
(
type
(
t
.
body
[
0
]),
ast
.
Assign
)
self
.
assertEqual
(
t
.
body
[
0
].
lineno
,
2
)
# check `f'...'`
self
.
assertEqual
(
type
(
t
.
body
[
1
]),
ast
.
Expr
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
),
ast
.
JoinedStr
)
self
.
assertEqual
(
len
(
t
.
body
[
1
].
value
.
values
),
5
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
0
]),
ast
.
FormattedValue
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
1
]),
ast
.
Constant
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
1
].
value
),
str
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
2
]),
ast
.
FormattedValue
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
3
]),
ast
.
Constant
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
3
].
value
),
str
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
4
]),
ast
.
FormattedValue
)
self
.
assertEqual
(
t
.
body
[
1
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
0
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
1
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
2
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
3
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
4
].
lineno
,
3
)
# check the first binop location
binop
=
t
.
body
[
1
].
value
.
values
[
0
].
value
self
.
assertEqual
(
type
(
binop
),
ast
.
BinOp
)
self
.
assertEqual
(
type
(
binop
.
left
),
ast
.
Name
)
self
.
assertEqual
(
type
(
binop
.
op
),
ast
.
Mult
)
self
.
assertEqual
(
type
(
binop
.
right
),
ast
.
Call
)
self
.
assertEqual
(
binop
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
left
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
right
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
col_offset
,
3
)
self
.
assertEqual
(
binop
.
left
.
col_offset
,
3
)
self
.
assertEqual
(
binop
.
right
.
col_offset
,
7
)
# check the second binop location
binop
=
t
.
body
[
1
].
value
.
values
[
2
].
value
self
.
assertEqual
(
type
(
binop
),
ast
.
BinOp
)
self
.
assertEqual
(
type
(
binop
.
left
),
ast
.
Name
)
self
.
assertEqual
(
type
(
binop
.
op
),
ast
.
Mult
)
self
.
assertEqual
(
type
(
binop
.
right
),
ast
.
Call
)
self
.
assertEqual
(
binop
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
left
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
right
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
col_offset
,
3
)
# FIXME: this is wrong
self
.
assertEqual
(
binop
.
left
.
col_offset
,
3
)
# FIXME: this is wrong
self
.
assertEqual
(
binop
.
right
.
col_offset
,
7
)
# FIXME: this is wrong
# check the third binop location
binop
=
t
.
body
[
1
].
value
.
values
[
4
].
value
self
.
assertEqual
(
type
(
binop
),
ast
.
BinOp
)
self
.
assertEqual
(
type
(
binop
.
left
),
ast
.
Name
)
self
.
assertEqual
(
type
(
binop
.
op
),
ast
.
Mult
)
self
.
assertEqual
(
type
(
binop
.
right
),
ast
.
Call
)
self
.
assertEqual
(
binop
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
left
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
right
.
lineno
,
3
)
self
.
assertEqual
(
binop
.
col_offset
,
3
)
# FIXME: this is wrong
self
.
assertEqual
(
binop
.
left
.
col_offset
,
3
)
# FIXME: this is wrong
self
.
assertEqual
(
binop
.
right
.
col_offset
,
7
)
# FIXME: this is wrong
def
__test_ast_line_numbers_multiline_fstring
(
self
):
# See bpo-30465 for details.
expr
=
"""
a = 10
f'''
{a
*
x()}
non-important content
'''
"""
t
=
ast
.
parse
(
expr
)
self
.
assertEqual
(
type
(
t
),
ast
.
Module
)
self
.
assertEqual
(
len
(
t
.
body
),
2
)
# check `a = 10`
self
.
assertEqual
(
type
(
t
.
body
[
0
]),
ast
.
Assign
)
self
.
assertEqual
(
t
.
body
[
0
].
lineno
,
2
)
# check `f'...'`
self
.
assertEqual
(
type
(
t
.
body
[
1
]),
ast
.
Expr
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
),
ast
.
JoinedStr
)
self
.
assertEqual
(
len
(
t
.
body
[
1
].
value
.
values
),
3
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
0
]),
ast
.
Constant
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
0
].
value
),
str
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
1
]),
ast
.
FormattedValue
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
2
]),
ast
.
Constant
)
self
.
assertEqual
(
type
(
t
.
body
[
1
].
value
.
values
[
2
].
value
),
str
)
self
.
assertEqual
(
t
.
body
[
1
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
0
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
1
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
2
].
lineno
,
3
)
self
.
assertEqual
(
t
.
body
[
1
].
col_offset
,
0
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
col_offset
,
0
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
0
].
col_offset
,
0
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
1
].
col_offset
,
0
)
self
.
assertEqual
(
t
.
body
[
1
].
value
.
values
[
2
].
col_offset
,
0
)
# NOTE: the following lineno information and col_offset is correct for
# expressions within FormattedValues.
binop
=
t
.
body
[
1
].
value
.
values
[
1
].
value
self
.
assertEqual
(
type
(
binop
),
ast
.
BinOp
)
self
.
assertEqual
(
type
(
binop
.
left
),
ast
.
Name
)
self
.
assertEqual
(
type
(
binop
.
op
),
ast
.
Mult
)
self
.
assertEqual
(
type
(
binop
.
right
),
ast
.
Call
)
self
.
assertEqual
(
binop
.
lineno
,
4
)
self
.
assertEqual
(
binop
.
left
.
lineno
,
4
)
self
.
assertEqual
(
binop
.
right
.
lineno
,
6
)
self
.
assertEqual
(
binop
.
col_offset
,
4
)
self
.
assertEqual
(
binop
.
left
.
col_offset
,
4
)
self
.
assertEqual
(
binop
.
right
.
col_offset
,
7
)
def
test_docstring
(
self
):
def
f
():
f'''Not a docstring'''
self
.
assert
True
(
f
.
__doc__
is
None
)
self
.
assert
IsNone
(
f
.
__doc__
)
def
g
():
'''Not a docstring'''
\
f''
self
.
assert
True
(
g
.
__doc__
is
None
)
self
.
assert
IsNone
(
g
.
__doc__
)
def
__test_literal_eval
(
self
):
with
self
.
assertRaisesRegex
(
ValueError
,
'malformed node or string'
):
...
...
@@ -159,9 +405,27 @@ f'{a * x()}'"""
])
def
test_mismatched_parens
(
self
):
self
.
assertAllRaise
(
SyntaxError
,
'f-string: mismatched'
,
self
.
assertAllRaise
(
SyntaxError
,
r"f-string: closing parenthesis '\
}
' "
r"does not match opening parenthesis '\
(
'"
,
[
"f'{((}'"
,
])
self
.
assertAllRaise
(
SyntaxError
,
r"f-string: closing parenthesis '\
)
' "
r"does not match opening parenthesis '\
[
'"
,
[
"f'{a[4)}'"
,
])
self
.
assertAllRaise
(
SyntaxError
,
r"f-string: closing parenthesis '\
]
' "
r"does not match opening parenthesis '\
(
'"
,
[
"f'{a(4]}'"
,
])
self
.
assertAllRaise
(
SyntaxError
,
r"f-string: closing parenthesis '\
}
' "
r"does not match opening parenthesis '\
[
'"
,
[
"f'{a[4}'"
,
])
self
.
assertAllRaise
(
SyntaxError
,
r"f-string: closing parenthesis '\
}
' "
r"does not match opening parenthesis '\
(
'"
,
[
"f'{a(4}'"
,
])
self
.
assertRaises
(
SyntaxError
,
eval
,
"f'{"
+
"("
*
500
+
"}'"
)
def
test_double_braces
(
self
):
self
.
assertEqual
(
f'{{'
,
'{'
)
...
...
@@ -239,7 +503,9 @@ f'{a * x()}'"""
[
"f'{1#}'"
,
# error because the expression becomes "(1#)"
"f'{3(#)}'"
,
"f'{#}'"
,
"f'{)#}'"
,
# When wrapped in parens, this becomes
])
self
.
assertAllRaise
(
SyntaxError
,
r"f-string: unmatched '\
)
'"
,
[
"f'{)#}'"
,
# When wrapped in parens, this becomes
# '()#)'. Make sure that doesn't compile.
])
...
...
@@ -256,20 +522,23 @@ f'{a * x()}'"""
# Test around 256.
# for i in range(250, 260):
# self.assertEqual(
cy_eval(build_fstr(i), x=x, width=width
), (x+' ')*i)
# self.assertEqual(
eval(build_fstr(i)
), (x+' ')*i)
self
.
assertEqual
(
cy_eval
(
'['
+
', '
.
join
(
build_fstr
(
i
)
for
i
in
range
(
250
,
260
))
+
']'
,
x
=
x
,
width
=
width
),
[(
x
+
' '
)
*
i
for
i
in
range
(
250
,
260
)],
)
# Test concatenating 2 largs fstrings.
# self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256))
self
.
assertEqual
(
cy_eval
(
build_fstr
(
255
)
*
3
,
x
=
x
,
width
=
width
),
(
x
+
' '
)
*
(
255
*
3
))
# CPython uses 255*256
s
=
build_fstr
(
253
,
'{x:{width}} '
)
# self.assertEqual(eval(s), (x+' ')*254)
self
.
assertEqual
(
cy_eval
(
s
,
x
=
x
,
width
=
width
),
(
x
+
' '
)
*
254
)
# Test lots of expressions and constants, concatenated.
s
=
"f'{1}' 'x' 'y'"
*
1024
# self.assertEqual(eval(s), '1xy' * 1024)
self
.
assertEqual
(
cy_eval
(
s
,
x
=
x
,
width
=
width
),
'1xy'
*
1024
)
def
test_format_specifier_expressions
(
self
):
...
...
@@ -293,7 +562,7 @@ f'{a * x()}'"""
# This looks like a nested format spec.
])
self
.
assertAllRaise
(
SyntaxError
,
"invalid syntax"
,
self
.
assertAllRaise
(
SyntaxError
,
"
f-string:
invalid syntax"
,
[
# Invalid syntax inside a nested spec.
"f'{4:{/5}}'"
,
])
...
...
@@ -357,7 +626,7 @@ f'{a * x()}'"""
])
# Different error message is raised for other whitespace characters.
self
.
assertAllRaise
(
SyntaxError
,
'invalid character in identifier'
,
self
.
assertAllRaise
(
SyntaxError
,
r"invalid non-printable character U\
+
00A0"
,
[
"f'''{
\
xa0
}'''"
,
#"\xa0",
])
...
...
@@ -369,12 +638,12 @@ f'{a * x()}'"""
# are added around it. But we shouldn't go from an invalid
# expression to a valid one. The added parens are just
# supposed to allow whitespace (including newlines).
self
.
assertAllRaise
(
SyntaxError
,
'invalid syntax'
,
self
.
assertAllRaise
(
SyntaxError
,
'
f-string:
invalid syntax'
,
[
"f'{,}'"
,
"f'{,}'"
,
# this is (,), which is an error
])
self
.
assertAllRaise
(
SyntaxError
,
"f-string: expecting '}
'"
,
self
.
assertAllRaise
(
SyntaxError
,
r"f-string: unmatched '\
)
'"
,
[
"f'{3)+(4}'"
,
])
...
...
@@ -488,7 +757,7 @@ f'{a * x()}'"""
# lambda doesn't work without parens, because the colon
# makes the parser think it's a format_spec
self
.
assertAllRaise
(
SyntaxError
,
'
unexpected EOF while parsing
'
,
self
.
assertAllRaise
(
SyntaxError
,
'
f-string: invalid syntax
'
,
[
"f'{lambda x:x}'"
,
])
...
...
@@ -497,9 +766,11 @@ f'{a * x()}'"""
# a function into a generator
def
fn
(
y
):
f'y:
{
yield
y
*
2
}
'
f'
{
yield
}
'
g
=
fn
(
4
)
self
.
assertEqual
(
next
(
g
),
8
)
self
.
assertEqual
(
next
(
g
),
None
)
def
test_yield_send
(
self
):
def
fn
(
x
):
...
...
@@ -616,8 +887,7 @@ f'{a * x()}'"""
self
.
assertEqual
(
f'
{
f"
{
y
}
"*3
}
'
,
'555'
)
def
test_invalid_string_prefixes
(
self
):
self
.
assertAllRaise
(
SyntaxError
,
'unexpected EOF while parsing'
,
[
"fu''"
,
single_quote_cases
=
[
"fu''"
,
"uf''"
,
"Fu''"
,
"fU''"
,
...
...
@@ -638,8 +908,10 @@ f'{a * x()}'"""
"bf''"
,
"bF''"
,
"Bf''"
,
"BF''"
,
])
"BF''"
,]
double_quote_cases
=
[
case
.
replace
(
"'"
,
'"'
)
for
case
in
single_quote_cases
]
self
.
assertAllRaise
(
SyntaxError
,
'unexpected EOF while parsing'
,
single_quote_cases
+
double_quote_cases
)
def
test_leading_trailing_spaces
(
self
):
self
.
assertEqual
(
f'
{
3
}
'
,
'3'
)
...
...
@@ -662,6 +934,12 @@ f'{a * x()}'"""
self
.
assertEqual
(
f'
{
3
!=
4
!
s
}
'
,
'True'
)
self
.
assertEqual
(
f'
{
3
!=
4
!
s
:.
3
}
'
,
'Tru'
)
def
test_equal_equal
(
self
):
# Because an expression ending in = has special meaning,
# there's a special test for ==. Make sure it works.
self
.
assertEqual
(
f'
{
0
==
1
}
'
,
'False'
)
def
test_conversions
(
self
):
self
.
assertEqual
(
f'
{
3.14
:
10.10
}
'
,
' 3.14'
)
self
.
assertEqual
(
f'
{
3.14
!
s
:
10.10
}
'
,
'3.14 '
)
...
...
@@ -801,12 +1079,6 @@ f'{a * x()}'"""
self.assertEqual('
{
d
[
a
]
}
'.format(d=d), '
string
')
self.assertEqual('
{
d
[
0
]
}
'.format(d=d), '
integer
')
def test_invalid_expressions(self):
self.assertAllRaise(SyntaxError, '
invalid
syntax
',
[r"f'
{
a
[
4
)
}
'",
r"f'
{
a
(
4
]
}
'",
])
def test_errors(self):
# see issue 26287
exc = ValueError if sys.version_info < (3, 4) else TypeError
...
...
@@ -819,6 +1091,16 @@ f'{a * x()}'"""
r"f'
{
1000
:
j
}
'",
])
def __test_filename_in_syntaxerror(self):
# see issue 38964
with temp_cwd() as cwd:
file_path = os.path.join(cwd, '
t
.
py
')
with open(file_path, '
w
') as f:
f.write('f"
{
a
b
}
"') # This generates a SyntaxError
_, _, stderr = assert_python_failure(file_path,
PYTHONIOENCODING='ascii')
self.assertIn(file_path.encode('ascii', 'backslashreplace'), stderr)
def test_loop(self):
for i in range(1000):
self.assertEqual(f'i:
{
i
}
', '
i
:
' + str(i))
...
...
@@ -840,5 +1122,129 @@ f'{a * x()}'"""
self.assertEqual(cy_eval('f"
\\\
n
"'), '')
self.assertEqual(cy_eval('f"
\\\
r"'), '')
"""
def __test_debug_conversion(self):
x = 'A string'
self.assertEqual(f'
{
x
=
}
', '
x
=
' + repr(x))
self.assertEqual(f'
{
x
=
}
', '
x
=
' + repr(x))
self.assertEqual(f'
{
x
=
!
s
}
', '
x
=
' + str(x))
self.assertEqual(f'
{
x
=
!
r
}
', '
x
=
' + repr(x))
self.assertEqual(f'
{
x
=
!
a
}
', '
x
=
' + ascii(x))
x = 2.71828
self.assertEqual(f'
{
x
=
:.
2
f
}
', '
x
=
' + format(x, '
.
2
f'))
self.assertEqual(f'
{
x
=
:
}
', '
x
=
' + format(x, ''))
self.assertEqual(f'
{
x
=
!
r
:
^
20
}
', '
x
=
' + format(repr(x), '
^
20
'))
self.assertEqual(f'
{
x
=
!
s
:
^
20
}
', '
x
=
' + format(str(x), '
^
20
'))
self.assertEqual(f'
{
x
=
!
a
:
^
20
}
', '
x
=
' + format(ascii(x), '
^
20
'))
x = 9
self.assertEqual(f'
{
3
*
x
+
15
=
}
', '
3
*
x
+
15
=
42
')
# There is code in ast.c that deals with non-ascii expression values. So,
# use a unicode identifier to trigger that.
tenπ = 31.4
self.assertEqual(f'
{
ten
π
=
:.
2
f
}
', '
ten
π
=
31.40
')
# Also test with Unicode in non-identifiers.
self.assertEqual(f'
{
"Σ"
=
}
', '"Σ"
=
\
'Σ
\
'
'
)
# Make sure nested fstrings still work.
self
.
assertEqual
(
f'
{
f"
{
3.1415
=
:.
1
f
}
":*^20
}
', '
*****
3.1415
=
3.1
*****
')
# Make sure text before and after an expression with = works
# correctly.
pi = '
π
'
self.assertEqual(f'
alpha
α
{
pi
=
}
ω
omega
', "alpha α pi='
π
' ω omega")
# Check multi-line expressions.
self.assertEqual(f'''
{
3
=
}
''', '
\
n
3
\
n
=3')
# Since = is handled specially, make sure all existing uses of
# it still work.
self.assertEqual(f'
{
0
==
1
}
', '
False
')
self.assertEqual(f'
{
0
!=
1
}
', '
True
')
self.assertEqual(f'
{
0
<=
1
}
', '
True
')
self.assertEqual(f'
{
0
>=
1
}
', '
False
')
self.assertEqual(f'
{
(
x
:
=
"5"
)
}
', '
5
')
self.assertEqual(x, '
5
')
self.assertEqual(f'
{
(
x
:
=
5
)
}
', '
5
')
self.assertEqual(x, 5)
self.assertEqual(f'
{
"="
}
', '
=
')
x = 20
# This isn'
t
an
assignment
expression
,
it
's '
x
', with a format
# spec of '
=
10
'. See test_walrus: you need to use parens.
self.assertEqual(f'
{
x
:
=
10
}
', '
20
')
# Test named function parameters, to make sure '
=
' parsing works
# there.
def f(a):
nonlocal x
oldx = x
x = a
return oldx
x = 0
self.assertEqual(f'
{
f
(
a
=
"3="
)
}
', '
0
')
self.assertEqual(x, '
3
=
')
self.assertEqual(f'
{
f
(
a
=
4
)
}
', '
3
=
')
self.assertEqual(x, 4)
# Make sure __format__ is being called.
class C:
def __format__(self, s):
return f'
FORMAT
-
{
s
}
'
def __repr__(self):
return '
REPR
'
self.assertEqual(f'
{
C
()
=
}
', '
C
()
=
REPR
')
self.assertEqual(f'
{
C
()
=
!
r
}
', '
C
()
=
REPR
')
self.assertEqual(f'
{
C
()
=
:
}
', '
C
()
=
FORMAT
-
')
self.assertEqual(f'
{
C
()
=
:
}
', '
C
()
=
FORMAT
-
')
self.assertEqual(f'
{
C
()
=
:
x
}
', '
C
()
=
FORMAT
-
x
')
self.assertEqual(f'
{
C
()
=
!
r
:
*^
20
}
', '
C
()
=********
REPR
********
')
self.assertRaises(SyntaxError, eval, "f'
{
C
=
]
'")
# Make sure leading and following text works.
x = '
foo
'
self.assertEqual(f'
X
{
x
=
}
Y
', '
Xx
=
'+repr(x)+'
Y
')
# Make sure whitespace around the = works.
self.assertEqual(f'
X
{
x
=
}
Y
', '
Xx
=
'+repr(x)+'
Y
')
self.assertEqual(f'
X
{
x
=
}
Y
', '
Xx
=
'+repr(x)+'
Y
')
self.assertEqual(f'
X
{
x
=
}
Y
', '
Xx
=
'+repr(x)+'
Y
')
# These next lines contains tabs. Backslash escapes don'
t
# work in f-strings.
# patchcheck doesn't like these tabs. So the only way to test
# this will be to dynamically created and exec the f-strings. But
# that's such a hassle I'll save it for another day. For now, convert
# the tabs to spaces just to shut up patchcheck.
#self.assertEqual(f'X
{
x
=
}
Y
', '
Xx
\
t
=
'+repr(x)+'
Y
')
#self.assertEqual(f'
X
{
x
=
}
Y
', '
Xx
\
t
=
\
t
'+repr(x)+'
Y
')
"""
def test_walrus(self):
x = 20
# This isn'
t
an
assignment
expression
,
it
's '
x
', with a format
# spec of '
=
10
'.
self.assertEqual(f'
{
x
:
=
10
}
', '
20
')
"""
# This is an assignment expression, which requires parens.
self.assertEqual(f'
{
(
x
:
=
10
)
}
', '
10
')
self.assertEqual(x, 10)
"""
def test_invalid_syntax_error_message(self):
# with self.assertRaisesRegex(SyntaxError, "f-string: invalid syntax"):
# compile("f'
{
a
$
b
}
'", "?", "exec")
self.assertAllRaise(CompileError, "f-string: invalid syntax", ["f'
{
a
$
b
}
'"])
if __name__ == '
__main__
':
unittest.main()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment