mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	gh-131831: Implement PEP 758 – Allow except and except* expressions without parentheses (#131833)
This commit is contained in:
		
							parent
							
								
									053c285f6b
								
							
						
					
					
						commit
						c2ac662f28
					
				
					 7 changed files with 381 additions and 270 deletions
				
			
		|  | @ -232,6 +232,8 @@ Additional information on exceptions can be found in section :ref:`exceptions`, | ||||||
| and information on using the :keyword:`raise` statement to generate exceptions | and information on using the :keyword:`raise` statement to generate exceptions | ||||||
| may be found in section :ref:`raise`. | may be found in section :ref:`raise`. | ||||||
| 
 | 
 | ||||||
|  | .. versionchanged:: next | ||||||
|  |    Support for optionally dropping grouping parentheses when using multiple exception types. See :pep:`758`. | ||||||
| 
 | 
 | ||||||
| .. _except: | .. _except: | ||||||
| 
 | 
 | ||||||
|  | @ -247,7 +249,8 @@ An expression-less :keyword:`!except` clause, if present, must be last; | ||||||
| it matches any exception. | it matches any exception. | ||||||
| 
 | 
 | ||||||
| For an :keyword:`!except` clause with an expression, the | For an :keyword:`!except` clause with an expression, the | ||||||
| expression must evaluate to an exception type or a tuple of exception types. | expression must evaluate to an exception type or a tuple of exception types. Parentheses | ||||||
|  | can be dropped if multiple exception types are provided and the ``as`` clause is not used. | ||||||
| The raised exception matches an :keyword:`!except` clause whose expression evaluates | The raised exception matches an :keyword:`!except` clause whose expression evaluates | ||||||
| to the class or a :term:`non-virtual base class <abstract base class>` of the exception object, | to the class or a :term:`non-virtual base class <abstract base class>` of the exception object, | ||||||
| or to a tuple that contains such a class. | or to a tuple that contains such a class. | ||||||
|  |  | ||||||
|  | @ -90,6 +90,33 @@ If you encounter :exc:`NameError`\s or pickling errors coming out of | ||||||
| New features | New features | ||||||
| ============ | ============ | ||||||
| 
 | 
 | ||||||
|  | .. _whatsnew314-pep758: | ||||||
|  | 
 | ||||||
|  | PEP 758 – Allow except and except* expressions without parentheses | ||||||
|  | ------------------------------------------------------------------ | ||||||
|  | 
 | ||||||
|  | The :keyword:`except` and :keyword:`except* <except_star>` expressions now allow | ||||||
|  | parentheses to be omitted when there are multiple exception types and the ``as`` clause is not used. | ||||||
|  | For example the following expressions are now valid: | ||||||
|  | 
 | ||||||
|  | .. code-block:: python | ||||||
|  | 
 | ||||||
|  |    try: | ||||||
|  |        release_new_sleep_token_album() | ||||||
|  |    except AlbumNotFound, SongsTooGoodToBeReleased: | ||||||
|  |        print("Sorry, no new album this year.") | ||||||
|  | 
 | ||||||
|  |     # The same applies to except* (for exception groups): | ||||||
|  |    try: | ||||||
|  |        release_new_sleep_token_album() | ||||||
|  |    except* AlbumNotFound, SongsTooGoodToBeReleased: | ||||||
|  |        print("Sorry, no new album this year.") | ||||||
|  | 
 | ||||||
|  | Check :pep:`758` for more details. | ||||||
|  | 
 | ||||||
|  | (Contributed by Pablo Galindo and Brett Cannon in :gh:`131831`.) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| .. _whatsnew314-pep649: | .. _whatsnew314-pep649: | ||||||
| 
 | 
 | ||||||
| PEP 649: deferred evaluation of annotations | PEP 649: deferred evaluation of annotations | ||||||
|  |  | ||||||
|  | @ -435,14 +435,18 @@ try_stmt[stmt_ty]: | ||||||
| 
 | 
 | ||||||
| except_block[excepthandler_ty]: | except_block[excepthandler_ty]: | ||||||
|     | invalid_except_stmt_indent |     | invalid_except_stmt_indent | ||||||
|     | 'except' e=expression t=['as' z=NAME { z }] ':' b=block { |     | 'except' e=expressions ':' b=block { | ||||||
|         _PyAST_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) } |         _PyAST_ExceptHandler(e,  NULL, b, EXTRA) } | ||||||
|  |     | 'except' e=expression 'as' t=NAME  ':' b=block { | ||||||
|  |         _PyAST_ExceptHandler(e, ((expr_ty) t)->v.Name.id, b, EXTRA) } | ||||||
|     | 'except' ':' b=block { _PyAST_ExceptHandler(NULL, NULL, b, EXTRA) } |     | 'except' ':' b=block { _PyAST_ExceptHandler(NULL, NULL, b, EXTRA) } | ||||||
|     | invalid_except_stmt |     | invalid_except_stmt | ||||||
| except_star_block[excepthandler_ty]: | except_star_block[excepthandler_ty]: | ||||||
|     | invalid_except_star_stmt_indent |     | invalid_except_star_stmt_indent | ||||||
|     | 'except' '*' e=expression t=['as' z=NAME { z }] ':' b=block { |     | 'except' '*' e=expressions ':' b=block { | ||||||
|         _PyAST_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) } |         _PyAST_ExceptHandler(e, NULL, b, EXTRA) } | ||||||
|  |     | 'except' '*' e=expression 'as' t=NAME ':' b=block { | ||||||
|  |         _PyAST_ExceptHandler(e, ((expr_ty) t)->v.Name.id, b, EXTRA) } | ||||||
|     | invalid_except_star_stmt |     | invalid_except_star_stmt | ||||||
| finally_block[asdl_stmt_seq*]: | finally_block[asdl_stmt_seq*]: | ||||||
|     | invalid_finally_stmt |     | invalid_finally_stmt | ||||||
|  | @ -1356,16 +1360,16 @@ invalid_try_stmt: | ||||||
|     | 'try' ':' block* except_star_block+ a='except' [expression ['as' NAME]] ':' { |     | 'try' ':' block* except_star_block+ a='except' [expression ['as' NAME]] ':' { | ||||||
|         RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "cannot have both 'except' and 'except*' on the same 'try'") } |         RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "cannot have both 'except' and 'except*' on the same 'try'") } | ||||||
| invalid_except_stmt: | invalid_except_stmt: | ||||||
|     | 'except' a=expression ',' expressions ['as' NAME ] ':' { |     | 'except' a=expression ',' expressions 'as' NAME  ':' { | ||||||
|         RAISE_SYNTAX_ERROR_STARTING_FROM(a, "multiple exception types must be parenthesized") } |         RAISE_SYNTAX_ERROR_STARTING_FROM(a, "multiple exception types must be parenthesized when using 'as'") } | ||||||
|     | a='except' expression ['as' NAME ] NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } |     | a='except' expression ['as' NAME ] NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } | ||||||
|     | a='except' NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } |     | a='except' NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } | ||||||
|     | 'except' expression 'as' a=expression { |     | 'except' expression 'as' a=expression { | ||||||
|         RAISE_SYNTAX_ERROR_KNOWN_LOCATION( |         RAISE_SYNTAX_ERROR_KNOWN_LOCATION( | ||||||
|             a, "cannot use except statement with %s", _PyPegen_get_expr_name(a)) } |             a, "cannot use except statement with %s", _PyPegen_get_expr_name(a)) } | ||||||
| invalid_except_star_stmt: | invalid_except_star_stmt: | ||||||
|     | 'except' '*' a=expression ',' expressions ['as' NAME ] ':' { |     | 'except' '*' a=expression ',' expressions 'as' NAME  ':' { | ||||||
|         RAISE_SYNTAX_ERROR_STARTING_FROM(a, "multiple exception types must be parenthesized") } |         RAISE_SYNTAX_ERROR_STARTING_FROM(a, "multiple exception types must be parenthesized when using 'as'") } | ||||||
|     | a='except' '*' expression ['as' NAME ] NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } |     | a='except' '*' expression ['as' NAME ] NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } | ||||||
|     | a='except' '*' (NEWLINE | ':') { RAISE_SYNTAX_ERROR("expected one or more exception types") } |     | a='except' '*' (NEWLINE | ':') { RAISE_SYNTAX_ERROR("expected one or more exception types") } | ||||||
|     | 'except' '*' expression 'as' a=expression { |     | 'except' '*' expression 'as' a=expression { | ||||||
|  |  | ||||||
|  | @ -1375,6 +1375,8 @@ def test_try(self): | ||||||
|         try: 1/0 |         try: 1/0 | ||||||
|         except (EOFError, TypeError, ZeroDivisionError): pass |         except (EOFError, TypeError, ZeroDivisionError): pass | ||||||
|         try: 1/0 |         try: 1/0 | ||||||
|  |         except EOFError, TypeError, ZeroDivisionError: pass | ||||||
|  |         try: 1/0 | ||||||
|         except (EOFError, TypeError, ZeroDivisionError) as msg: pass |         except (EOFError, TypeError, ZeroDivisionError) as msg: pass | ||||||
|         try: pass |         try: pass | ||||||
|         finally: pass |         finally: pass | ||||||
|  | @ -1398,6 +1400,8 @@ def test_try_star(self): | ||||||
|         try: 1/0 |         try: 1/0 | ||||||
|         except* (EOFError, TypeError, ZeroDivisionError): pass |         except* (EOFError, TypeError, ZeroDivisionError): pass | ||||||
|         try: 1/0 |         try: 1/0 | ||||||
|  |         except* EOFError, TypeError, ZeroDivisionError: pass | ||||||
|  |         try: 1/0 | ||||||
|         except* (EOFError, TypeError, ZeroDivisionError) as msg: pass |         except* (EOFError, TypeError, ZeroDivisionError) as msg: pass | ||||||
|         try: pass |         try: pass | ||||||
|         finally: pass |         finally: pass | ||||||
|  |  | ||||||
|  | @ -1667,28 +1667,14 @@ | ||||||
|    SyntaxError: invalid syntax |    SyntaxError: invalid syntax | ||||||
| 
 | 
 | ||||||
| Check that an multiple exception types with missing parentheses | Check that an multiple exception types with missing parentheses | ||||||
| raise a custom exception | raise a custom exception only when using 'as' | ||||||
| 
 |  | ||||||
|    >>> try: |  | ||||||
|    ...   pass |  | ||||||
|    ... except A, B: |  | ||||||
|    ...   pass |  | ||||||
|    Traceback (most recent call last): |  | ||||||
|    SyntaxError: multiple exception types must be parenthesized |  | ||||||
| 
 |  | ||||||
|    >>> try: |  | ||||||
|    ...   pass |  | ||||||
|    ... except A, B, C: |  | ||||||
|    ...   pass |  | ||||||
|    Traceback (most recent call last): |  | ||||||
|    SyntaxError: multiple exception types must be parenthesized |  | ||||||
| 
 | 
 | ||||||
|    >>> try: |    >>> try: | ||||||
|    ...   pass |    ...   pass | ||||||
|    ... except A, B, C as blech: |    ... except A, B, C as blech: | ||||||
|    ...   pass |    ...   pass | ||||||
|    Traceback (most recent call last): |    Traceback (most recent call last): | ||||||
|    SyntaxError: multiple exception types must be parenthesized |    SyntaxError: multiple exception types must be parenthesized when using 'as' | ||||||
| 
 | 
 | ||||||
|    >>> try: |    >>> try: | ||||||
|    ...   pass |    ...   pass | ||||||
|  | @ -1697,29 +1683,15 @@ | ||||||
|    ... finally: |    ... finally: | ||||||
|    ...   pass |    ...   pass | ||||||
|    Traceback (most recent call last): |    Traceback (most recent call last): | ||||||
|    SyntaxError: multiple exception types must be parenthesized |    SyntaxError: multiple exception types must be parenthesized when using 'as' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|    >>> try: |  | ||||||
|    ...   pass |  | ||||||
|    ... except* A, B: |  | ||||||
|    ...   pass |  | ||||||
|    Traceback (most recent call last): |  | ||||||
|    SyntaxError: multiple exception types must be parenthesized |  | ||||||
| 
 |  | ||||||
|    >>> try: |  | ||||||
|    ...   pass |  | ||||||
|    ... except* A, B, C: |  | ||||||
|    ...   pass |  | ||||||
|    Traceback (most recent call last): |  | ||||||
|    SyntaxError: multiple exception types must be parenthesized |  | ||||||
| 
 |  | ||||||
|    >>> try: |    >>> try: | ||||||
|    ...   pass |    ...   pass | ||||||
|    ... except* A, B, C as blech: |    ... except* A, B, C as blech: | ||||||
|    ...   pass |    ...   pass | ||||||
|    Traceback (most recent call last): |    Traceback (most recent call last): | ||||||
|    SyntaxError: multiple exception types must be parenthesized |    SyntaxError: multiple exception types must be parenthesized when using 'as' | ||||||
| 
 | 
 | ||||||
|    >>> try: |    >>> try: | ||||||
|    ...   pass |    ...   pass | ||||||
|  | @ -1728,7 +1700,7 @@ | ||||||
|    ... finally: |    ... finally: | ||||||
|    ...   pass |    ...   pass | ||||||
|    Traceback (most recent call last): |    Traceback (most recent call last): | ||||||
|    SyntaxError: multiple exception types must be parenthesized |    SyntaxError: multiple exception types must be parenthesized when using 'as' | ||||||
| 
 | 
 | ||||||
| Custom exception for 'except*' without an exception type | Custom exception for 'except*' without an exception type | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | Add support for optionally dropping grouping parentheses when using multiple | ||||||
|  | exception types as per :pep:`758`. Patch by Pablo Galindo | ||||||
							
								
								
									
										555
									
								
								Parser/parser.c
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										555
									
								
								Parser/parser.c
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Pablo Galindo Salgado
						Pablo Galindo Salgado