Skip to content

Commit 60180fd

Browse files
authored
Merge pull request #4531 from charliermarsh/charlie/exception-groups
Implement except* syntax
2 parents 8aa3bc9 + c7ed645 commit 60180fd

6 files changed

Lines changed: 1446 additions & 0 deletions

File tree

‎ast/Python.asdl‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ module Python
4040

4141
| Raise(expr? exc, expr? cause)
4242
| Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
43+
| TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
4344
| Assert(expr test, expr? msg)
4445

4546
| Import(alias* names)

‎ast/src/ast_gen.rs‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ pub enum StmtKind<U = ()> {
139139
orelse: Vec<Stmt<U>>,
140140
finalbody: Vec<Stmt<U>>,
141141
},
142+
TryStar {
143+
body: Vec<Stmt<U>>,
144+
handlers: Vec<Excepthandler<U>>,
145+
orelse: Vec<Stmt<U>>,
146+
finalbody: Vec<Stmt<U>>,
147+
},
142148
Assert {
143149
test: Box<Expr<U>>,
144150
msg: Option<Box<Expr<U>>>,
@@ -726,6 +732,17 @@ pub mod fold {
726732
orelse: Foldable::fold(orelse, folder)?,
727733
finalbody: Foldable::fold(finalbody, folder)?,
728734
}),
735+
StmtKind::TryStar {
736+
body,
737+
handlers,
738+
orelse,
739+
finalbody,
740+
} => Ok(StmtKind::TryStar {
741+
body: Foldable::fold(body, folder)?,
742+
handlers: Foldable::fold(handlers, folder)?,
743+
orelse: Foldable::fold(orelse, folder)?,
744+
finalbody: Foldable::fold(finalbody, folder)?,
745+
}),
729746
StmtKind::Assert { test, msg } => Ok(StmtKind::Assert {
730747
test: Foldable::fold(test, folder)?,
731748
msg: Foldable::fold(msg, folder)?,

‎parser/python.lalrpop‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,27 @@ TryStatement: ast::Stmt = {
451451
},
452452
}
453453
},
454+
<location:@L> "try" ":" <body:Suite> <handlers:ExceptStarClause+> <else_suite:("else" ":" Suite)?> <finally:("finally" ":" Suite)?> <end_location:@R> => {
455+
let orelse = else_suite.map(|s| s.2).unwrap_or_default();
456+
let finalbody = finally.map(|s| s.2).unwrap_or_default();
457+
let end_location = finalbody
458+
.last()
459+
.or_else(|| orelse.last())
460+
.map(|last| last.end_location)
461+
.or_else(|| handlers.last().map(|last| last.end_location))
462+
.unwrap();
463+
ast::Stmt {
464+
custom: (),
465+
location,
466+
end_location,
467+
node: ast::StmtKind::TryStar {
468+
body,
469+
handlers,
470+
orelse,
471+
finalbody,
472+
},
473+
}
474+
},
454475
<location:@L> "try" ":" <body:Suite> <finally:("finally" ":" Suite)> => {
455476
let handlers = vec![];
456477
let orelse = vec![];
@@ -470,6 +491,34 @@ TryStatement: ast::Stmt = {
470491
},
471492
};
472493

494+
ExceptStarClause: ast::Excepthandler = {
495+
<location:@L> "except" "*" <typ:Test<"all">> ":" <body:Suite> => {
496+
let end_location = body.last().unwrap().end_location.unwrap();
497+
ast::Excepthandler::new(
498+
location,
499+
end_location,
500+
ast::ExcepthandlerKind::ExceptHandler {
501+
type_: Some(Box::new(typ)),
502+
name: None,
503+
body,
504+
},
505+
)
506+
},
507+
<location:@L> "except" "*" <x:(Test<"all"> "as" Identifier)> ":" <body:Suite> => {
508+
let end_location = body.last().unwrap().end_location.unwrap();
509+
ast::Excepthandler::new(
510+
location,
511+
end_location,
512+
ast::ExcepthandlerKind::ExceptHandler {
513+
type_: Some(Box::new(x.0)),
514+
name: Some(x.2),
515+
body,
516+
},
517+
)
518+
},
519+
};
520+
521+
473522
ExceptClause: ast::Excepthandler = {
474523
<location:@L> "except" <typ:Test<"all">?> ":" <body:Suite> => {
475524
let end_location = body.last().unwrap().end_location.unwrap();

‎parser/src/parser.rs‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,37 @@ array[3:5, *idxs_to_select]
417417
insta::assert_debug_snapshot!(parse_ast);
418418
}
419419

420+
#[test]
421+
fn test_try() {
422+
let parse_ast = parse_program(
423+
r#"try:
424+
raise ValueError(1)
425+
except TypeError as e:
426+
print(f'caught {type(e)}')
427+
except OSError as e:
428+
print(f'caught {type(e)}')"#,
429+
"<test>",
430+
)
431+
.unwrap();
432+
insta::assert_debug_snapshot!(parse_ast);
433+
}
434+
435+
#[test]
436+
fn test_try_star() {
437+
let parse_ast = parse_program(
438+
r#"try:
439+
raise ExceptionGroup("eg",
440+
[ValueError(1), TypeError(2), OSError(3), OSError(4)])
441+
except* TypeError as e:
442+
print(f'caught {type(e)} with nested {e.exceptions}')
443+
except* OSError as e:
444+
print(f'caught {type(e)} with nested {e.exceptions}')"#,
445+
"<test>",
446+
)
447+
.unwrap();
448+
insta::assert_debug_snapshot!(parse_ast);
449+
}
450+
420451
#[test]
421452
fn test_dict_unpacking() {
422453
let parse_ast = parse_expression(r#"{"a": "b", **c, "d": "e"}"#, "<test>").unwrap();

0 commit comments

Comments
 (0)