Skip to content
This repository was archived by the owner on Apr 10, 2022. It is now read-only.

Commit c04f7f3

Browse files
committed
Address feedback from Guido
1 parent 00a2487 commit c04f7f3

File tree

1 file changed

+49
-20
lines changed

1 file changed

+49
-20
lines changed

except_star.md

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,30 @@
22

33
## Disclaimer
44

5-
* We use the `ExceptionGroup` name herein, even though there
6-
are other alternatives, e.g. `AggregateException`. Naming of the
7-
"exception group" object is out of scope of this proposal.
5+
* We use the `ExceptionGroup` name, even though there
6+
are other alternatives, e.g. `AggregateException` and `MultiError`.
7+
Naming of the "exception group" object is out of scope of this proposal.
88

99
* We use the term "naked" exception for regular Python exceptions
1010
**not wrapped** in an `ExceptionGroup`. E.g. a regular `ValueError`
1111
propagating through the stack is "naked".
1212

13-
* We assume that `ExceptionGroup` would be an iterable object.
14-
E.g. `list(ExceptionGroup(ValueError('a'), TypeError('b')))` would be
13+
* `ExceptionGroup` is an iterable object.
14+
E.g. `list(ExceptionGroup(ValueError('a'), TypeError('b')))` is
1515
equal to `[ValueError('a'), TypeError('b')]`
1616

17-
* We assume that `ExceptionGroup` won't be an indexable object; essentially
17+
* `ExceptionGroup` is not an indexable object; essentially
1818
it's similar to Python `set`. The motivation for this is that exceptions
1919
can occur in random order, and letting users write `group[0]` to access the
2020
"first" error is error prone. Although the actual implementation of
2121
`ExceptionGroup` will likely use an ordered list of errors to preserve
2222
the actual occurrence order for rendering.
2323

24-
* We assume that `ExceptionGroup` will be a subclass of `BaseException`,
25-
which means it's assignable to `Exception.__context__` and can be
24+
* `ExceptionGroup` is a subclass of `BaseException`,
25+
is assignable to `Exception.__context__`, and can be
2626
directly handled with `try: ... except ExceptionGroup: ...`.
2727

28-
* The behavior of the regular `try..except` block will not be modified.
28+
* The behavior of the regular `try..except` statement will not be modified.
2929

3030
## Syntax
3131

@@ -71,7 +71,7 @@ example, both `except *SpamError:` and `except *(BarError, FooError) as e:`
7171
could get executed during handling of one `ExceptionGroup` object, or all
7272
of the `except*` clauses, or just one of them.
7373

74-
It is not allowed to use both regular `except` clauses and the new `except*`
74+
It is not allowed to use both regular except blocks and the new `except*`
7575
clauses in the same `try` block. E.g. the following example would raise a
7676
`SyntaxErorr`:
7777

@@ -170,7 +170,7 @@ except *ValueError as e:
170170
print(f'got some ValueErrors: {e}')
171171
except *TypeError as e:
172172
print(f'got some TypeErrors: {e}')
173-
raise e
173+
raise
174174
```
175175

176176
The above code would print:
@@ -180,7 +180,7 @@ got some ValueErrors: ExceptionGroup(ValueError('a'))
180180
got some TypeErrors: ExceptionGroup(TypeError('b'), TypeError('c'))
181181
```
182182

183-
and then crash with an unhandled `ExceptionGroup`:
183+
and then terminate with an unhandled `ExceptionGroup`:
184184

185185
```
186186
ExceptionGroup(
@@ -199,15 +199,15 @@ to handle, and then:
199199
* A new empty "result" `ExceptionGroup` would be created by the interpreter.
200200

201201
* Every `except *` clause, run from top to bottom, can filter some of the
202-
exceptions out of the group and process them. If the except block crashes
202+
exceptions out of the group and process them. If the except block terminates
203203
with an error, that error is put to the "result" `ExceptionGroup` (with the
204204
group of unprocessed exceptions referenced via the `__context__` attribute.)
205205

206206
* After there are no more `except*` clauses to evaluate, there are the
207207
following possibilities:
208208

209209
* Both "incoming" and "result" `ExceptionGroup` are empty. This means
210-
that all exceptions were processed and silenced successfully.
210+
that all exceptions were processed and silenced.
211211

212212
* Both "incoming" and "result" `ExceptionGroup` are not empty.
213213
This means that not all of the exceptions were matched, and some were
@@ -258,7 +258,7 @@ except *ValueError:
258258
# ZeroDivisionError()
259259
# )
260260
#
261-
# where the `ZeroDivizionError()` instance would have
261+
# where the `ZeroDivisionError()` instance would have
262262
# its __context__ attribute set to
263263
#
264264
# ExceptionGroup(
@@ -358,7 +358,7 @@ try:
358358
except *TypeError as e:
359359
raise
360360

361-
# would crash with:
361+
# would terminate with:
362362
#
363363
# ExceptionGroup(
364364
# ValueError('a'),
@@ -385,7 +385,7 @@ try:
385385
except *TypeError as e:
386386
raise e
387387

388-
# would crash with:
388+
# would terminate with:
389389
#
390390
# ExceptionGroup(
391391
# ValueError('a'),
@@ -427,10 +427,9 @@ def bar():
427427
try:
428428
1 / 0
429429
except ZeroDivisionError:
430-
pass
430+
return
431431
finally:
432432
print('silence')
433-
return
434433

435434
foo()
436435
bar()
@@ -444,6 +443,36 @@ bar()
444443
We propose to replicate this behavior in the `except*` syntax as it is useful
445444
as an escape hatch when it's clear that all exceptions can be silenced.
446445

446+
That said, the regular try statement allows to return a value from the except
447+
or the finally clause:
448+
449+
```python
450+
def bar():
451+
try:
452+
1 / 0
453+
except ZeroDivisionError:
454+
return 42
455+
456+
print(bar())
457+
458+
# would print "42"
459+
```
460+
461+
Allowing non-None returns in `except*` allows to write unpredictable code,
462+
e.g.:
463+
464+
```python
465+
try:
466+
raise ExceptionGroup(A(), B())
467+
except *A:
468+
return 1
469+
except *B:
470+
return 2
471+
```
472+
473+
Therefore non-None returns are disallowed in `except*` clauses.
474+
475+
447476
## Design Considerations
448477

449478
### Why try..except* syntax
@@ -525,7 +554,7 @@ Which leads to the conclusion that `except *CancelledError as e` should both:
525554
at once with one run of the code in `except *CancelledError` (and not
526555
run the code for every matched individual exception.)
527556

528-
Why "handle all exceptions at once"? Why not run the code in the `except`
557+
Why "handle all exceptions at once"? Why not run the code in the except
529558
clause for every matched exception that we have in the group?
530559
Basically because there's no need to. As we mentioned above, catching
531560
*operation exceptions* should be done with the regular `except KeyError`

0 commit comments

Comments
 (0)