Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1ef61cf

Browse files
pablogsallysnikolaouisidenticalmgmacias95sunmy2019
authored
gh-102856: Initial implementation of PEP 701 (#102855)
Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com>Co-authored-by: Batuhan Taskaya <isidentical@gmail.com>Co-authored-by: Marta Gómez Macías <mgmacias@google.com>Co-authored-by: sunmy2019 <59365878+sunmy2019@users.noreply.github.com>
1 parenta6b07b5 commit1ef61cf

27 files changed

+6425
-4139
lines changed

‎Doc/library/token-list.inc‎

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎Grammar/Tokens‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,17 @@ ATEQUAL '@='
5353
RARROW '->'
5454
ELLIPSIS '...'
5555
COLONEQUAL ':='
56+
EXCLAMATION '!'
5657

5758
OP
5859
AWAIT
5960
ASYNC
6061
TYPE_IGNORE
6162
TYPE_COMMENT
6263
SOFT_KEYWORD
64+
FSTRING_START
65+
FSTRING_MIDDLE
66+
FSTRING_END
6367
ERRORTOKEN
6468

6569
# These aren't used by the C tokenizer but are needed for tokenize.py

‎Grammar/python.gram‎

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ yield_stmt[stmt_ty]: y=yield_expr { _PyAST_Expr(y, EXTRA) }
194194

195195
assert_stmt[stmt_ty]: 'assert' a=expression b=[',' z=expression { z }] { _PyAST_Assert(a, b, EXTRA) }
196196

197-
import_stmt[stmt_ty]:
197+
import_stmt[stmt_ty]:
198198
| invalid_import
199199
| import_name
200200
| import_from
@@ -415,8 +415,8 @@ try_stmt[stmt_ty]:
415415
| invalid_try_stmt
416416
| 'try' &&':' b=block f=finally_block { _PyAST_Try(b, NULL, NULL, f, EXTRA) }
417417
| 'try' &&':' b=block ex[asdl_excepthandler_seq*]=except_block+ el=[else_block] f=[finally_block] { _PyAST_Try(b, ex, el, f, EXTRA) }
418-
| 'try' &&':' b=block ex[asdl_excepthandler_seq*]=except_star_block+ el=[else_block] f=[finally_block] {
419-
CHECK_VERSION(stmt_ty, 11, "Exception groups are",
418+
| 'try' &&':' b=block ex[asdl_excepthandler_seq*]=except_star_block+ el=[else_block] f=[finally_block] {
419+
CHECK_VERSION(stmt_ty, 11, "Exception groups are",
420420
_PyAST_TryStar(b, ex, el, f, EXTRA)) }
421421

422422

@@ -807,7 +807,7 @@ atom[expr_ty]:
807807
| 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) }
808808
| 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) }
809809
| 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) }
810-
| &STRING strings
810+
| &(STRING|FSTRING_START) strings
811811
| NUMBER
812812
| &'(' (tuple | group | genexp)
813813
| &'[' (list | listcomp)
@@ -877,7 +877,26 @@ lambda_param[arg_ty]: a=NAME { _PyAST_arg(a->v.Name.id, NULL, NULL, EXTRA) }
877877
# LITERALS
878878
# ========
879879

880-
strings[expr_ty] (memo): a=STRING+ { _PyPegen_concatenate_strings(p, a) }
880+
fstring_middle[expr_ty]:
881+
| fstring_replacement_field
882+
| t=FSTRING_MIDDLE { _PyPegen_constant_from_token(p, t) }
883+
fstring_replacement_field[expr_ty]:
884+
| '{' a=(yield_expr | star_expressions) debug_expr="="? conversion=[fstring_conversion] format=[fstring_full_format_spec] '}' {
885+
_PyPegen_formatted_value(p, a, debug_expr, conversion, format, EXTRA)
886+
}
887+
| invalid_replacement_field
888+
fstring_conversion[expr_ty]:
889+
| conv_token="!" conv=NAME { _PyPegen_check_fstring_conversion(p, conv_token, conv) }
890+
fstring_full_format_spec[expr_ty]:
891+
| ':' spec=fstring_format_spec* { spec ? _PyAST_JoinedStr((asdl_expr_seq*)spec, EXTRA) : NULL }
892+
fstring_format_spec[expr_ty]:
893+
| t=FSTRING_MIDDLE { _PyPegen_constant_from_token(p, t) }
894+
| fstring_replacement_field
895+
fstring[expr_ty]:
896+
| a=FSTRING_START b=fstring_middle* c=FSTRING_END { _PyPegen_joined_str(p, a, (asdl_expr_seq*)b, c) }
897+
898+
string[expr_ty]: s[Token*]=STRING { _PyPegen_constant_from_string(p, s) }
899+
strings[expr_ty] (memo): a[asdl_expr_seq*]=(fstring|string)+ { _PyPegen_concatenate_strings(p, a, EXTRA) }
881900

882901
list[expr_ty]:
883902
| '[' a=[star_named_expressions] ']' { _PyAST_List(a, Load, EXTRA) }
@@ -1118,6 +1137,8 @@ invalid_expression:
11181137
_PyPegen_check_legacy_stmt(p, a) ? NULL : p->tokens[p->mark-1]->level == 0 ? NULL :
11191138
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Perhaps you forgot a comma?") }
11201139
| a=disjunction 'if' b=disjunction !('else'|':') { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "expected 'else' after 'if' expression") }
1140+
| a='lambda' [lambda_params] b=':' &(FSTRING_MIDDLE | fstring_replacement_field) {
1141+
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "f-string: lambda expressions are not allowed without parentheses") }
11211142

11221143
invalid_named_expression(memo):
11231144
| a=expression ':=' expression {
@@ -1241,7 +1262,7 @@ invalid_group:
12411262
invalid_import:
12421263
| a='import' dotted_name 'from' dotted_name {
12431264
RAISE_SYNTAX_ERROR_STARTING_FROM(a, "Did you mean to use 'from ... import ...' instead?") }
1244-
1265+
12451266
invalid_import_from_targets:
12461267
| import_from_as_names ',' NEWLINE {
12471268
RAISE_SYNTAX_ERROR("trailing comma not allowed without surrounding parentheses") }
@@ -1335,3 +1356,24 @@ invalid_kvpair:
13351356
| expression a=':' &('}'|',') {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
13361357
invalid_starred_expression:
13371358
| a='*' expression '=' b=expression { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "cannot assign to iterable argument unpacking") }
1359+
invalid_replacement_field:
1360+
| '{' a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '='") }
1361+
| '{' a='!' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '!'") }
1362+
| '{' a=':' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before ':'") }
1363+
| '{' a='}' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '}'") }
1364+
| '{' !(yield_expr | star_expressions) { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting a valid expression after '{'")}
1365+
| '{' (yield_expr | star_expressions) !('=' | '!' | ':' | '}') {
1366+
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '=', or '!', or ':', or '}'") }
1367+
| '{' (yield_expr | star_expressions) '=' !('!' | ':' | '}') {
1368+
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '!', or ':', or '}'") }
1369+
| '{' (yield_expr | star_expressions) '='? invalid_conversion_character
1370+
| '{' (yield_expr | star_expressions) '='? ['!' NAME] !(':' | '}') {
1371+
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting ':' or '}'") }
1372+
| '{' (yield_expr | star_expressions) '='? ['!' NAME] ':' fstring_format_spec* !'}' {
1373+
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}', or format specs") }
1374+
| '{' (yield_expr | star_expressions) '='? ['!' NAME] !'}' {
1375+
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}'") }
1376+
1377+
invalid_conversion_character:
1378+
| '!' &(':' | '}') { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: missing conversion character") }
1379+
| '!' !NAME { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: invalid conversion character") }

‎Include/internal/pycore_token.h‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,18 @@ extern "C" {
6767
#defineRARROW 51
6868
#defineELLIPSIS 52
6969
#defineCOLONEQUAL 53
70-
#defineOP 54
71-
#defineAWAIT 55
72-
#defineASYNC 56
73-
#defineTYPE_IGNORE 57
74-
#defineTYPE_COMMENT 58
75-
#defineSOFT_KEYWORD 59
76-
#defineERRORTOKEN 60
77-
#defineN_TOKENS 64
70+
#defineEXCLAMATION 54
71+
#defineOP 55
72+
#defineAWAIT 56
73+
#defineASYNC 57
74+
#defineTYPE_IGNORE 58
75+
#defineTYPE_COMMENT 59
76+
#defineSOFT_KEYWORD 60
77+
#defineFSTRING_START 61
78+
#defineFSTRING_MIDDLE 62
79+
#defineFSTRING_END 63
80+
#defineERRORTOKEN 64
81+
#defineN_TOKENS 68
7882
#defineNT_OFFSET 256
7983

8084
/* Special definitions for cooperation with parser */
@@ -86,6 +90,8 @@ extern "C" {
8690
(x) == NEWLINE || \
8791
(x) == INDENT || \
8892
(x) == DEDENT)
93+
#defineISSTRINGLIT(x) ((x) == STRING || \
94+
(x) == FSTRING_MIDDLE)
8995

9096

9197
// Symbols exported for test_peg_generator

‎Lib/test/test_ast.py‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -774,11 +774,6 @@ def test_parenthesized_with_feature_version(self):
774774
ast.parse('with (CtxManager() as example): ...',feature_version=(3,8))
775775
ast.parse('with CtxManager() as example: ...',feature_version=(3,8))
776776

777-
deftest_debug_f_string_feature_version(self):
778-
ast.parse('f"{x=}"',feature_version=(3,8))
779-
withself.assertRaises(SyntaxError):
780-
ast.parse('f"{x=}"',feature_version=(3,7))
781-
782777
deftest_assignment_expression_feature_version(self):
783778
ast.parse('(x := 0)',feature_version=(3,8))
784779
withself.assertRaises(SyntaxError):

‎Lib/test/test_cmd_line_script.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -636,9 +636,9 @@ def test_syntaxerror_multi_line_fstring(self):
636636
self.assertEqual(
637637
stderr.splitlines()[-3:],
638638
[
639-
b' foo"""',
640-
b' ^',
641-
b'SyntaxError: f-string:empty expressionnot allowed',
639+
b' foo = f"""{}',
640+
b'^',
641+
b'SyntaxError: f-string:valid expressionrequired before\'}\'',
642642
],
643643
)
644644

‎Lib/test/test_eof.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
fromtestimportsupport
55
fromtest.supportimportos_helper
66
fromtest.supportimportscript_helper
7+
fromtest.supportimportwarnings_helper
78
importunittest
89

910
classEOFTestCase(unittest.TestCase):
@@ -36,10 +37,11 @@ def test_EOFS_with_file(self):
3637
rc,out,err=script_helper.assert_python_failure(file_name)
3738
self.assertIn(b'unterminated triple-quoted string literal (detected at line 3)',err)
3839

40+
@warnings_helper.ignore_warnings(category=SyntaxWarning)
3941
deftest_eof_with_line_continuation(self):
4042
expect="unexpected EOF while parsing (<string>, line 1)"
4143
try:
42-
compile('"\\xhh"\\','<string>','exec',dont_inherit=True)
44+
compile('"\\Xhh"\\','<string>','exec')
4345
exceptSyntaxErrorasmsg:
4446
self.assertEqual(str(msg),expect)
4547
else:

‎Lib/test/test_exceptions.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ def ckmsg(src, msg):
155155

156156
ckmsg(s,"'continue' not properly in loop")
157157
ckmsg("continue\n","'continue' not properly in loop")
158+
ckmsg("f'{6 0}'","invalid syntax. Perhaps you forgot a comma?")
158159

159160
deftestSyntaxErrorMissingParens(self):
160161
defckmsg(src,msg,exception=SyntaxError):
@@ -227,7 +228,7 @@ def testSyntaxErrorOffset(self):
227228
check('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +',1,20)
228229
check(b'# -*- coding: cp1251 -*-\nPython = "\xcf\xb3\xf2\xee\xed" +',
229230
2,19,encoding='cp1251')
230-
check(b'Python = "\xcf\xb3\xf2\xee\xed" +',1,18)
231+
check(b'Python = "\xcf\xb3\xf2\xee\xed" +',1,10)
231232
check('x = "a',1,5)
232233
check('lambda x: x = 2',1,1)
233234
check('f{a + b + c}',1,2)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp