From 9a9eb74f53948fa425ea6a99f1e03511023bf6c2 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Mon, 27 Sep 2021 16:27:46 +0100 Subject: [PATCH] PEP-654: except *T --> except* T --- pep-0654.rst | 68 ++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/pep-0654.rst b/pep-0654.rst index 2de31d3b0f1..5716694c7de 100644 --- a/pep-0654.rst +++ b/pep-0654.rst @@ -532,11 +532,11 @@ exceptions can be handled by each ``except*`` clause: try: ... - except *SpamError: + except* SpamError: ... - except *FooError as e: + except* FooError as e: ... - except *(BarError, BazError) as e: + except* (BarError, BazError) as e: ... In a traditional ``try-except`` statement there is only one exception to handle, @@ -572,7 +572,7 @@ Exceptions are matched using a subclass check. For example: try: low_level_os_operation() - except *OSError as eg: + except* OSError as eg: for e in eg.exceptions: print(type(e).__name__) @@ -593,9 +593,9 @@ The order of ``except*`` clauses is significant just like with the regular >>> try: ... raise ExceptionGroup("problem", [BlockingIOError()]) - ... except *OSError as e: # Would catch the error + ... except* OSError as e: # Would catch the error ... print(repr(e)) - ... except *BlockingIOError: # Would never run + ... except* BlockingIOError: # Would never run ... print('never') ... ExceptionGroup('problem', [BlockingIOError()]) @@ -619,9 +619,9 @@ recursively, using the ``split()`` method: ... [TypeError('c'), KeyError('d')]) ... ] ... ) - ... except *TypeError as e1: + ... except* TypeError as e1: ... print(f'e1 = {e1!r}') - ... except *Exception as e2: + ... except* Exception as e2: ... print(f'e2 = {e2!r}') ... e1 = ExceptionGroup('eg', [TypeError('b'), ExceptionGroup('nested', [TypeError('c')])]) @@ -644,9 +644,9 @@ clauses, the remaining part of the group is propagated on: ... TypeError('c'), KeyError('e') ... ] ... ) - ... except *ValueError as e: + ... except* ValueError as e: ... print(f'got some ValueErrors: {e!r}') - ... except *TypeError as e: + ... except* TypeError as e: ... print(f'got some TypeErrors: {e!r}') ... except ExceptionGroup as e: ... print(f'propagated: {e!r}') @@ -670,7 +670,7 @@ message string. This is to make the type of ``e`` consistent and statically know >>> try: ... raise BlockingIOError - ... except *OSError as e: + ... except* OSError as e: ... print(repr(e)) ... ExceptionGroup('', [BlockingIOError()]) @@ -683,7 +683,7 @@ naked form: >>> try: ... try: ... raise ValueError(12) - ... except *TypeError as e: + ... except* TypeError as e: ... print('never') ... except ValueError as e: ... print(f'caught ValueError: {e!r}') @@ -755,10 +755,10 @@ the original ``ExceptionGroup``: ... [OSError(4), TypeError(5), ValueError(6)]) ... ] ... ) - ... except *ValueError as e: + ... except* ValueError as e: ... print(f'*ValueError: {e!r}') ... raise - ... except *OSError as e: + ... except* OSError as e: ... print(f'*OSError: {e!r}') ... except ExceptionGroup as e: ... print(repr(e)) @@ -793,10 +793,10 @@ merged with the unhandled ``TypeErrors``. ... [OSError(4), TypeError(5), ValueError(6)]) ... ] ... ) - ... except *ValueError as e: + ... except* ValueError as e: ... print(f'*ValueError: {e!r}') ... raise e - ... except *OSError as e: + ... except* OSError as e: ... print(f'*OSError: {e!r}') ... raise ... @@ -850,7 +850,7 @@ it into the new ``ExceptionGroup``. >>> try: ... raise ExceptionGroup("one", [ValueError('a'), TypeError('b')]) - ... except *ValueError: + ... except* ValueError: ... raise ExceptionGroup("two", [KeyError('x'), KeyError('y')]) ... | ExceptionGroup @@ -897,7 +897,7 @@ chaining: >>> try: ... raise TypeError('bad type') - ... except *TypeError as e: + ... except* TypeError as e: ... raise ValueError('bad value') from e ... | ExceptionGroup @@ -927,9 +927,9 @@ other clauses from the same ``try`` statement: >>> try: ... raise TypeError(1) - ... except *TypeError: + ... except* TypeError: ... raise ValueError(2) from None # <- not caught in the next clause - ... except *ValueError: + ... except* ValueError: ... print('never') ... | ExceptionGroup @@ -952,7 +952,7 @@ direct child of the new exception group created for that: >>> try: ... raise ExceptionGroup("eg", [ValueError('a')]) - ... except *ValueError: + ... except* ValueError: ... raise KeyError('x') ... | ExceptionGroup @@ -975,7 +975,7 @@ direct child of the new exception group created for that: >>> >>> try: ... raise ExceptionGroup("eg", [ValueError('a'), TypeError('b')]) - ... except *ValueError: + ... except* ValueError: ... raise KeyError('x') ... | ExceptionGroup @@ -1013,7 +1013,7 @@ OS errors, while letting all other exceptions propagate. try: low_level_os_operation() - except *OSError as errors: + except* OSError as errors: raise errors.subgroup(lambda e: e.errno != errno.EPIPE) from None @@ -1031,7 +1031,7 @@ exception group. Any modifications to ``e`` will likely be lost: >>> eg.foo = 'foo' >>> try: ... raise eg - ... except *TypeError as e: + ... except* TypeError as e: ... e.foo = 'bar' ... # ^----------- ``e`` is an ephemeral object that might get >>> # destroyed after the ``except*`` clause. @@ -1052,7 +1052,7 @@ It is not possible to use both traditional ``except`` blocks and the new ... except ValueError: pass - except *CancelledError: # <- SyntaxError: + except* CancelledError: # <- SyntaxError: pass # combining ``except`` and ``except*`` # is prohibited @@ -1069,12 +1069,12 @@ ambiguous: try: ... - except *ExceptionGroup: # <- Runtime error + except* ExceptionGroup: # <- Runtime error pass try: ... - except *(TypeError, ExceptionGroup): # <- Runtime error + except* (TypeError, ExceptionGroup): # <- Runtime error pass @@ -1121,7 +1121,7 @@ Once programs begin to use these features, there will be migration issues to consider: * An ``except T:`` clause that wraps code which is now potentially raising - an exception group may need to become ``except *T:``, and its body may + an exception group may need to become ``except* T:``, and its body may need to be updated. This means that raising an exception group is an API-breaking change and will likely be done in new APIs rather than added to existing ones. @@ -1308,7 +1308,7 @@ It is unlikely that asyncio users would want to do something like this: try: async with asyncio.TaskGroup() as g: g.create_task(task1); g.create_task(task2) - except *KeyError: + except* KeyError: # handling KeyError here is meaningless, there's # no context to do anything with it but to log it. @@ -1326,7 +1326,7 @@ exceptions in it. Not Matching Naked Exceptions in ``except*`` -------------------------------------------- -We considered the option of making ``except *T`` match only exception groups +We considered the option of making ``except* T`` match only exception groups that contain ``Ts``, but not naked ``Ts``. To see why we thought this would not be a desirable feature, return to the distinction in the previous paragraph between operation errors and control flow exceptions. If we don't know whether @@ -1352,7 +1352,7 @@ write a separate code block to handle each case: ... except SomeError: # handle the naked exception - except *SomeError: + except* SomeError: # handle the exception group @@ -1361,7 +1361,7 @@ Allow mixing ``except:`` and ``except*:`` in the same ``try`` This option was rejected because it adds complexity without adding useful semantics. Presumably the intention would be that an ``except T:`` block handles -only naked exceptions of type ``T``, while ``except *T:`` handles ``T`` in +only naked exceptions of type ``T``, while ``except* T:`` handles ``T`` in exception groups. We already discussed above why this is unlikely to be useful in practice, and if it is needed then the nested ``try-except`` block can be used instead to achieve the same result. @@ -1379,7 +1379,7 @@ specified in the same place where we state ``T``. Programming Without 'except \*' =============================== -Consider the following simple example of the ``except *`` syntax (pretending +Consider the following simple example of the ``except*`` syntax (pretending Trio natively supported this proposal): .. code-block:: @@ -1389,7 +1389,7 @@ Trio natively supported this proposal): # Make two concurrent calls to child() nursery.start_soon(child) nursery.start_soon(child) - except *ValueError: + except* ValueError: pass Here is how this code would look in Python 3.9: