| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
40 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -829,6 +829,9 @@ Waiting Primitives | |||
| 829 | 829 | Deprecation warning is emitted if not all awaitable objects in the *aws* | |
| 830 | 830 | iterable are Future-like objects and there is no running event loop. | |
| 831 | 831 | ||
| 832 | + .. versionchanged:: 3.12 | ||
| 833 | + Added support for generators yielding tasks. | ||
| 834 | + | ||
| 832 | 835 | ||
| 833 | 836 | Running in Threads | |
| 834 | 837 | ================== | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -250,7 +250,8 @@ asyncio | |||
| 250 | 250 | :mod:`asyncio` does not support legacy generator-based coroutines. | |
| 251 | 251 | (Contributed by Kumar Aditya in :gh:`102748`.) | |
| 252 | 252 | ||
| 253 | - * :func:`asyncio.wait` now accepts generators yielding tasks. | ||
| 253 | + * :func:`asyncio.wait` and :func:`asyncio.as_completed` now accepts generators | ||
| 254 | + yielding tasks. | ||
| 254 | 255 | (Contributed by Kumar Aditya in :gh:`78530`.) | |
| 255 | 256 | ||
| 256 | 257 | csv | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -53,13 +53,17 @@ ATEQUAL '@=' | |||
| 53 | 53 | RARROW '->' | |
| 54 | 54 | ELLIPSIS '...' | |
| 55 | 55 | COLONEQUAL ':=' | |
| 56 | + EXCLAMATION '!' | ||
| 56 | 57 | ||
| 57 | 58 | OP | |
| 58 | 59 | AWAIT | |
| 59 | 60 | ASYNC | |
| 60 | 61 | TYPE_IGNORE | |
| 61 | 62 | TYPE_COMMENT | |
| 62 | 63 | SOFT_KEYWORD | |
| 64 | + FSTRING_START | ||
| 65 | + FSTRING_MIDDLE | ||
| 66 | + FSTRING_END | ||
| 63 | 67 | ERRORTOKEN | |
| 64 | 68 | ||
| 65 | 69 | # These aren't used by the C tokenizer but are needed for tokenize.py | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -194,7 +194,7 @@ yield_stmt[stmt_ty]: y=yield_expr { _PyAST_Expr(y, EXTRA) } | |||
| 194 | 194 | ||
| 195 | 195 | assert_stmt[stmt_ty]: 'assert' a=expression b=[',' z=expression { z }] { _PyAST_Assert(a, b, EXTRA) } | |
| 196 | 196 | ||
| 197 | - import_stmt[stmt_ty]: | ||
| 197 | + import_stmt[stmt_ty]: | ||
| 198 | 198 | | invalid_import | |
| 199 | 199 | | import_name | |
| 200 | 200 | | import_from | |
@@ -415,8 +415,8 @@ try_stmt[stmt_ty]: | |||
| 415 | 415 | | invalid_try_stmt | |
| 416 | 416 | | 'try' &&':' b=block f=finally_block { _PyAST_Try(b, NULL, NULL, f, EXTRA) } | |
| 417 | 417 | | '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", | ||
| 420 | 420 | _PyAST_TryStar(b, ex, el, f, EXTRA)) } | |
| 421 | 421 | ||
| 422 | 422 | ||
@@ -807,7 +807,7 @@ atom[expr_ty]: | |||
| 807 | 807 | | 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) } | |
| 808 | 808 | | 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) } | |
| 809 | 809 | | 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) } | |
| 810 | - | &STRING strings | ||
| 810 | + | &(STRING|FSTRING_START) strings | ||
| 811 | 811 | | NUMBER | |
| 812 | 812 | | &'(' (tuple | group | genexp) | |
| 813 | 813 | | &'[' (list | listcomp) | |
@@ -877,7 +877,26 @@ lambda_param[arg_ty]: a=NAME { _PyAST_arg(a->v.Name.id, NULL, NULL, EXTRA) } | |||
| 877 | 877 | # LITERALS | |
| 878 | 878 | # ======== | |
| 879 | 879 | ||
| 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) } | ||
| 881 | 900 | ||
| 882 | 901 | list[expr_ty]: | |
| 883 | 902 | | '[' a=[star_named_expressions] ']' { _PyAST_List(a, Load, EXTRA) } | |
@@ -1118,6 +1137,8 @@ invalid_expression: | |||
| 1118 | 1137 | _PyPegen_check_legacy_stmt(p, a) ? NULL : p->tokens[p->mark-1]->level == 0 ? NULL : | |
| 1119 | 1138 | RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Perhaps you forgot a comma?") } | |
| 1120 | 1139 | | 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") } | ||
| 1121 | 1142 | ||
| 1122 | 1143 | invalid_named_expression(memo): | |
| 1123 | 1144 | | a=expression ':=' expression { | |
@@ -1241,7 +1262,7 @@ invalid_group: | |||
| 1241 | 1262 | invalid_import: | |
| 1242 | 1263 | | a='import' dotted_name 'from' dotted_name { | |
| 1243 | 1264 | RAISE_SYNTAX_ERROR_STARTING_FROM(a, "Did you mean to use 'from ... import ...' instead?") } | |
| 1244 | - | ||
| 1265 | + | ||
| 1245 | 1266 | invalid_import_from_targets: | |
| 1246 | 1267 | | import_from_as_names ',' NEWLINE { | |
| 1247 | 1268 | RAISE_SYNTAX_ERROR("trailing comma not allowed without surrounding parentheses") } | |
@@ -1335,3 +1356,24 @@ invalid_kvpair: | |||
| 1335 | 1356 | | expression a=':' &('}'|',') {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") } | |
| 1336 | 1357 | invalid_starred_expression: | |
| 1337 | 1358 | | 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") } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -67,14 +67,18 @@ extern "C" { | |||
| 67 | 67 | #define RARROW 51 | |
| 68 | 68 | #define ELLIPSIS 52 | |
| 69 | 69 | #define COLONEQUAL 53 | |
| 70 | - #define OP 54 | ||
| 71 | - #define AWAIT 55 | ||
| 72 | - #define ASYNC 56 | ||
| 73 | - #define TYPE_IGNORE 57 | ||
| 74 | - #define TYPE_COMMENT 58 | ||
| 75 | - #define SOFT_KEYWORD 59 | ||
| 76 | - #define ERRORTOKEN 60 | ||
| 77 | - #define N_TOKENS 64 | ||
| 70 | + #define EXCLAMATION 54 | ||
| 71 | + #define OP 55 | ||
| 72 | + #define AWAIT 56 | ||
| 73 | + #define ASYNC 57 | ||
| 74 | + #define TYPE_IGNORE 58 | ||
| 75 | + #define TYPE_COMMENT 59 | ||
| 76 | + #define SOFT_KEYWORD 60 | ||
| 77 | + #define FSTRING_START 61 | ||
| 78 | + #define FSTRING_MIDDLE 62 | ||
| 79 | + #define FSTRING_END 63 | ||
| 80 | + #define ERRORTOKEN 64 | ||
| 81 | + #define N_TOKENS 68 | ||
| 78 | 82 | #define NT_OFFSET 256 | |
| 79 | 83 | ||
| 80 | 84 | /* Special definitions for cooperation with parser */ | |
@@ -86,6 +90,8 @@ extern "C" { | |||
| 86 | 90 | (x) == NEWLINE || \ | |
| 87 | 91 | (x) == INDENT || \ | |
| 88 | 92 | (x) == DEDENT) | |
| 93 | + #define ISSTRINGLIT(x) ((x) == STRING || \ | ||
| 94 | + (x) == FSTRING_MIDDLE) | ||
| 89 | 95 | ||
| 90 | 96 | ||
| 91 | 97 | // Symbols exported for test_peg_generator | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1965,6 +1965,11 @@ def replace(self, year=None, month=None, day=None, hour=None, | |||
| 1965 | 1965 | def _local_timezone(self): | |
| 1966 | 1966 | if self.tzinfo is None: | |
| 1967 | 1967 | ts = self._mktime() | |
| 1968 | + # Detect gap | ||
| 1969 | + ts2 = self.replace(fold=1-self.fold)._mktime() | ||
| 1970 | + if ts2 != ts: # This happens in a gap or a fold | ||
| 1971 | + if (ts2 > ts) == self.fold: | ||
| 1972 | + ts = ts2 | ||
| 1968 | 1973 | else: | |
| 1969 | 1974 | ts = (self - _EPOCH) // timedelta(seconds=1) | |
| 1970 | 1975 | localtm = _time.localtime(ts) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6212,6 +6212,10 @@ def test_system_transitions(self): | |||
| 6212 | 6212 | ts1 = dt.replace(fold=1).timestamp() | |
| 6213 | 6213 | self.assertEqual(ts0, s0 + ss / 2) | |
| 6214 | 6214 | self.assertEqual(ts1, s0 - ss / 2) | |
| 6215 | + # gh-83861 | ||
| 6216 | + utc0 = dt.astimezone(timezone.utc) | ||
| 6217 | + utc1 = dt.replace(fold=1).astimezone(timezone.utc) | ||
| 6218 | + self.assertEqual(utc0, utc1 + timedelta(0, ss)) | ||
| 6215 | 6219 | finally: | |
| 6216 | 6220 | if TZ is None: | |
| 6217 | 6221 | del os.environ['TZ'] | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -774,11 +774,6 @@ def test_parenthesized_with_feature_version(self): | |||
| 774 | 774 | ast.parse('with (CtxManager() as example): ...', feature_version=(3, 8)) | |
| 775 | 775 | ast.parse('with CtxManager() as example: ...', feature_version=(3, 8)) | |
| 776 | 776 | ||
| 777 | - def test_debug_f_string_feature_version(self): | ||
| 778 | - ast.parse('f"{x=}"', feature_version=(3, 8)) | ||
| 779 | - with self.assertRaises(SyntaxError): | ||
| 780 | - ast.parse('f"{x=}"', feature_version=(3, 7)) | ||
| 781 | - | ||
| 782 | 777 | def test_assignment_expression_feature_version(self): | |
| 783 | 778 | ast.parse('(x := 0)', feature_version=(3, 8)) | |
| 784 | 779 | with self.assertRaises(SyntaxError): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -636,9 +636,9 @@ def test_syntaxerror_multi_line_fstring(self): | |||
| 636 | 636 | self.assertEqual( | |
| 637 | 637 | stderr.splitlines()[-3:], | |
| 638 | 638 | [ | |
| 639 | - b' foo"""', | ||
| 640 | - b' ^', | ||
| 641 | - b'SyntaxError: f-string: empty expression not allowed', | ||
| 639 | + b' foo = f"""{}', | ||
| 640 | + b' ^', | ||
| 641 | + b'SyntaxError: f-string: valid expression required before \'}\'', | ||
| 642 | 642 | ], | |
| 643 | 643 | ) | |
| 644 | 644 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments