Skip to content

Commit d380a69

Browse files
committed
Small tweaks, more tests
"Catch[] needs to make sure it controls the expression evaluation rather than having its expression get evaluated before it has a change to wrap it in a `try`. Internally, a plain `raise` is a bit more tidy Python wise since the Python traceback will the path from raise to handler without intervening tests of inappropriate exception handlers. This is a matter of taste though. Add more unit test tests.
1 parent 86d8b32 commit d380a69

2 files changed

Lines changed: 78 additions & 6 deletions

File tree

mathics/builtin/control.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,7 @@ class Catch(Builtin):
751751
= 24
752752
753753
"""
754+
attributes = ("HoldAll",)
754755

755756
def apply1(self, expr, evaluation):
756757
'Catch[expr_]'
@@ -759,9 +760,9 @@ def apply1(self, expr, evaluation):
759760
except WLThrowInterrupt as e:
760761
return e.value
761762
return ret
762-
763+
763764
def apply3(self, expr, form, f, evaluation):
764-
'Catch[expr_, form_, f__:Identity]'
765+
"Catch[expr_, form_, f__:Identity]"
765766
try:
766767
ret = expr.evaluate(evaluation)
767768
except WLThrowInterrupt as e:
@@ -771,7 +772,9 @@ def apply3(self, expr, form, f, evaluation):
771772
if match.is_true():
772773
return Expression(f, e.value)
773774
else:
774-
raise e
775+
# A plain raise hide, this path and preserves the traceback
776+
# of the call that was originally given.
777+
raise
775778
return ret
776779

777780

@@ -804,6 +807,3 @@ def apply1(self, value, evaluation):
804807
def apply2(self, value, tag, evaluation):
805808
'Throw[value_, tag_]'
806809
raise WLThrowInterrupt(value, tag)
807-
808-
809-

test/test_control.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# -*- coding: utf-8 -*-
2+
from .helper import session, check_evaluation
3+
4+
import sys
5+
from mathics.core.parser import parse, SingleLineFeeder
6+
from mathics.core.definitions import Definitions
7+
from mathics.core.evaluation import Evaluation
8+
import pytest
9+
10+
11+
def test_control():
12+
session.evaluate(
13+
"""
14+
(* Define a function that can "throw an exception": *)
15+
16+
f[x_] := If[x > 10, Throw[overflow], x!]
17+
"""
18+
)
19+
for str_expr, str_expected, message in (
20+
(
21+
"Catch[a; b; Throw[c]; d; e]",
22+
"c",
23+
"Exit to the enclosing Catch as soon as the Throw is evaluated",
24+
),
25+
(
26+
"Catch[f[2] + f[11]]",
27+
"overflow",
28+
"The result of the Catch is just what is thrown by Throw in f[]",
29+
),
30+
(
31+
"Catch[f[2] + f[3]]",
32+
"8",
33+
"A Catch[] where nothing is thrown",
34+
),
35+
(
36+
"Catch[Do[If[i! > 10^10, Throw[i]], {i, 100}]]",
37+
"14",
38+
"Use Throw to exit a loop when a criterion is satisfied",
39+
),
40+
(
41+
"Catch[If[# < 0, Throw[#]] & /@ {1, 2, 0, -1, 5, 6}]",
42+
"-1",
43+
"Catch can catch a Throw from inside essentially any function (1)",
44+
),
45+
(
46+
"Catch[{a, Throw[b], c}]",
47+
"b",
48+
"Catch can catch a Throw from inside essentially any function (2)",
49+
),
50+
(
51+
"Catch[a^2 + b^2 + c^2 /. b :> Throw[bbb]]",
52+
"bbb",
53+
"Catch can catch a Throw from inside essentially any function (3)",
54+
),
55+
(
56+
"Catch[{Catch[{a, Throw[b], c}], d, e}]",
57+
"{b, d, e}",
58+
"The nearest enclosing Catch catches the Throw",
59+
),
60+
(
61+
"Catch[{Throw[a], Throw[b], Throw[c]}]",
62+
"a",
63+
"Catch picks up the first Throw that is evaluated (1)",
64+
),
65+
(
66+
"Catch[Throw /@ {a, b, c}]",
67+
"a",
68+
"Catch picks up the first Throw that is evaluated (2)",
69+
),
70+
71+
):
72+
check_evaluation(str_expr, str_expected, message)

0 commit comments

Comments
 (0)